1 
2 #include <stdbool.h>
3 #include <stdint.h>
4 #include <string.h>
5 #include <errno.h>
6 
7 #include <sys/kassert.h>
8 #include <sys/kmem.h>
9 #include <sys/spinlock.h>
10 #include <sys/disk.h>
11 #include <sys/bufcache.h>
12 #include <sys/vfs.h>
13 #include <sys/dirent.h>
14 #include <sys/thread.h>
15 
16 #include "o2fs.h"
17 
18 VFS *O2FS_Mount(Disk *disk);
19 int O2FS_Unmount(VFS *fs);
20 int O2FS_GetRoot(VFS *fs, VNode **dn);
21 int O2FS_Lookup(VNode *dn, VNode **fn, const char *name);
22 int O2FS_Open(VNode *fn);
23 int O2FS_Close(VNode *fn);
24 int O2FS_Stat(VNode *fn, struct stat *statinfo);
25 int O2FS_Read(VNode *fn, void *buf, uint64_t off, uint64_t len);
26 int O2FS_Write(VNode *fn, void *buf, uint64_t off, uint64_t len);
27 int O2FS_ReadDir(VNode *fn, void *buf, uint64_t len, uint64_t *off);
28 
29 static VFSOp O2FSOperations = {
30     .unmount = O2FS_Unmount,
31     .getroot = O2FS_GetRoot,
32     .lookup = O2FS_Lookup,
33     .open = O2FS_Open,
34     .close = O2FS_Close,
35     .stat = O2FS_Stat,
36     .read = O2FS_Read,
37     .write = O2FS_Write,
38     .readdir = O2FS_ReadDir,
39 };
40 
41 VFS *
O2FS_Mount(Disk * disk)42 O2FS_Mount(Disk *disk)
43 {
44     int status;
45     VFS *fs = VFS_Alloc();
46     BufCacheEntry *entry;
47     SuperBlock *sb;
48 
49     ASSERT(sizeof(BDirEntry) == 512);
50 
51     if (!fs)
52 	return NULL;
53 
54     status = BufCache_Read(disk, 0, &entry);
55     if (status < 0) {
56 	Alert(o2fs, "Disk cache read failed\n");
57 	return NULL;
58     }
59 
60     // Read superblock
61     sb = entry->buffer;
62     if (memcmp(sb->magic, SUPERBLOCK_MAGIC, 8) != 0) {
63 	Alert(o2fs, "Invalid file system\n");
64 	BufCache_Release(entry);
65 	return NULL;
66     }
67     if (sb->versionMajor != O2FS_VERSION_MAJOR ||
68 	sb->versionMinor != O2FS_VERSION_MINOR) {
69 	Alert(o2fs, "Unsupported file system version\n");
70 	BufCache_Release(entry);
71 	return NULL;
72     }
73 
74     // Read bitmap
75     for (int i = 0; i < sb->bitmapSize; i++) {
76 	ASSERT(i < 16);
77 
78 	BufCacheEntry *bentry;
79 	uint64_t offset = sb->bitmapOffset + i * sb->blockSize;
80 
81 	if (BufCache_Read(disk, offset, &bentry) < 0) {
82 	    Alert(o2fs, "Bitmap read failed\n");
83 	    for (i = 0; i < 16; i++)
84 		BufCache_Release(fs->bitmap[i]);
85 	    BufCache_Release(entry);
86 	    return NULL;
87 	}
88 
89 	fs->bitmap[i] = bentry;
90     }
91 
92     DLOG(o2fs, "File system mounted\n");
93     DLOG(o2fs, "Root @ 0x%llx\n", sb->root.offset);
94 
95     fs->fsptr = entry;
96     fs->fsval = sb->root.offset;
97     fs->blksize = sb->blockSize;
98 
99     // Setup VFS structure
100     fs->op = &O2FSOperations;
101     fs->disk = disk;
102     Spinlock_Init(&fs->lock, "O2FS Lock", SPINLOCK_TYPE_NORMAL);
103     fs->refCount = 1;
104     fs->root = NULL;
105 
106     status = O2FS_GetRoot(fs, &fs->root);
107     if (status < 0) {
108 	Alert(o2fs, "Mount failed");
109 	BufCache_Release(entry);
110 	return NULL;
111     }
112 
113     return fs;
114 }
115 
116 int
O2FS_Unmount(VFS * fs)117 O2FS_Unmount(VFS *fs)
118 {
119     NOT_IMPLEMENTED();
120     return -1;
121 }
122 
123 /**
124  * O2FSBAlloc --
125  *
126  * Allocate a block.
127  *
128  * @param [in] vfs VFS Instance.
129  *
130  * @return Block number.
131  */
132 uint64_t
O2FSBAlloc(VFS * fs)133 O2FSBAlloc(VFS *fs)
134 {
135     for (int i = 0; i < 16; i++) {
136 	char *bitmap;
137 	BufCacheEntry *bentry = fs->bitmap[i];
138 
139 	if (fs->bitmap[i] == NULL)
140 	    break;
141 
142 	bitmap = bentry->buffer;
143 
144 	// XXX: Check for end of disk
145 	for (int b = 0; b < fs->blksize; b++) {
146 	    for (int bi = 0; bi < 8; bi++) {
147 		if (((bitmap[b] >> bi) & 0x1) == 0) {
148 		    /* Set bit */
149 		    bitmap[b] |= (1 << bi);
150 
151 		    /* Write bitmap */
152 		    BufCache_Write(bentry);
153 
154 		    /*
155 		     * Block index is the sum of:
156 		     *   blksize*8 blocks per bitmap entry
157 		     *   8 blocks per bitmap byte
158 		     *   bit #
159 		     */
160 		    uint64_t blk = fs->blksize*8*i + 8*b + bi;
161 		    DLOG(o2fs, "BAlloc %lu\n", blk);
162 		    return blk;
163 		}
164 	    }
165 	}
166     }
167 
168     Alert(o2fs, "Out of space!\n");
169     return 0;
170 }
171 
172 /**
173  * O2FSBFree --
174  *
175  * Free a block.
176  *
177  * @param [in] vfs VFS Instance.
178  * @param [in] block Block number.
179  */
180 void
O2FSBFree(VFS * fs,uint64_t block)181 O2FSBFree(VFS *fs, uint64_t block)
182 {
183     uint64_t bitoff = block & 0x7;
184     uint64_t bytoff = (block >> 8) % fs->blksize;
185     uint64_t bufoff = block / (fs->blksize*8);
186 
187     DLOG(o2fs, "BFree %lu\n", block);
188 
189     BufCacheEntry *bentry = fs->bitmap[bufoff];
190     ASSERT(bentry != NULL);
191 
192     char *bitmap = bentry->buffer;
193 
194     /* Mask out the bit */
195     bitmap[bytoff] &= ~(1 << bitoff);
196 
197     /* Write the bitmap */
198     BufCache_Write(bentry);
199 }
200 
201 /**
202  * O2FSLoadVNode --
203  *
204  * Load a VNode from the disk given an ObjID.
205  *
206  * @param [in] vfs VFS Instance.
207  * @param [in] oobjid Object ID.
208  */
209 VNode *
O2FSLoadVNode(VFS * fs,ObjID * objid)210 O2FSLoadVNode(VFS *fs, ObjID *objid)
211 {
212     int status;
213     VNode *vn;
214     BNode *bn;
215     BufCacheEntry *entry;
216 
217     status = BufCache_Read(fs->disk, objid->offset, &entry);
218     if (status < 0) {
219 	Alert(o2fs, "disk read error\n");
220 	return NULL;
221     }
222 
223     bn = entry->buffer;
224     if (memcmp(&bn->magic, BNODE_MAGIC, 8) != 0) {
225 	Alert(o2fs, "bad BNode magic\n");
226 	BufCache_Release(entry);
227 	return NULL;
228     }
229     if (bn->versionMajor != O2FS_VERSION_MAJOR ||
230 	bn->versionMinor != O2FS_VERSION_MINOR) {
231 	Alert(o2fs, "unsupported BNode version\n");
232 	BufCache_Release(entry);
233 	return NULL;
234     }
235 
236     vn = VNode_Alloc();
237     if (!vn) {
238 	return NULL;
239     }
240 
241     vn->op = &O2FSOperations;
242     vn->disk = fs->disk;
243     Spinlock_Init(&vn->lock, "VNode Lock", SPINLOCK_TYPE_NORMAL);
244     vn->refCount = 1;
245     vn->fsptr = entry;
246     vn->vfs = fs;
247 
248     return vn;
249 }
250 
251 /**
252  * O2FSGrowVNode --
253  *
254  * Grow a VNode.
255  *
256  * @param [in] vfs VFS Instance.
257  * @param [in] bn BNode for the object.
258  * @param [in] filesz New file size.
259  *
260  * @return 0 on success, otherwise error code.
261  */
262 int
O2FSGrowVNode(VNode * vn,uint64_t filesz)263 O2FSGrowVNode(VNode *vn, uint64_t filesz)
264 {
265     VFS *vfs = vn->vfs;
266     BufCacheEntry *vnEntry = (BufCacheEntry *)vn->fsptr;
267     BNode *bn = vnEntry->buffer;
268     uint64_t blkstart = (bn->size + vfs->blksize - 1) / vfs->blksize;
269 
270     if (filesz > (vfs->blksize * O2FS_DIRECT_PTR))
271 	return -EINVAL;
272 
273     for (int i = blkstart; i < ((filesz + vfs->blksize - 1) / vfs->blksize); i++) {
274 	if (bn->direct[i].offset != 0)
275 		continue;
276 
277 	uint64_t blkno = O2FSBAlloc(vfs);
278 	if (blkno == 0) {
279 		return -ENOSPC;
280 	}
281 
282 	bn->direct[i].offset = blkno * vfs->blksize;
283 
284     }
285 
286     DLOG(o2fs, "Growing: %d\n", filesz);
287     bn->size = filesz;
288 
289     BufCache_Write(vnEntry);
290 
291     return 0;
292 }
293 
294 /**
295  * O2FSRetainVNode --
296  *
297  * Increment VNode reference count.
298  *
299  * @param [in] vn VNode.
300  */
301 void
O2FSRetainVNode(VNode * vn)302 O2FSRetainVNode(VNode *vn)
303 {
304     vn->refCount++;
305 }
306 
307 /**
308  * O2FSReleaseVNode --
309  *
310  * Decrement VNode reference count and release it if reaches zero.
311  *
312  * @param [in] vn VNode.
313  */
314 void
O2FSReleaseVNode(VNode * vn)315 O2FSReleaseVNode(VNode *vn)
316 {
317     vn->refCount--;
318     if (vn->refCount == 0) {
319 	BufCache_Release(vn->fsptr);
320 	Spinlock_Destroy(&vn->lock);
321 	VNode_Free(vn);
322     }
323 }
324 
325 int
O2FSResolveBuf(VNode * vn,uint64_t b,BufCacheEntry ** dentp)326 O2FSResolveBuf(VNode *vn, uint64_t b, BufCacheEntry **dentp)
327 {
328     BufCacheEntry *vnent = (BufCacheEntry *)vn->fsptr;
329     BufCacheEntry *dent;
330     BNode *bn = vnent->buffer;
331     int status;
332 
333     status = BufCache_Read(vn->disk, bn->direct[b].offset, &dent);
334     if (status < 0)
335         return status;
336 
337     *dentp = dent;
338 
339     return status;
340 }
341 
342 /**
343  * O2FS_GetRoot --
344  *
345  * Read the root directory Inode.
346  *
347  * @param [in] fs VFS Instance.
348  * @param [out] dn VNode of the root directory.
349  *
350  * @return 0 on success, otherwise error.
351  */
352 int
O2FS_GetRoot(VFS * fs,VNode ** dn)353 O2FS_GetRoot(VFS *fs, VNode **dn)
354 {
355     int status;
356     VNode *vn;
357     BufCacheEntry *entry;
358     BNode *bn;
359 
360     if (fs->root) {
361 	fs->root->refCount++;
362 	*dn = fs->root;
363 	return 0;
364     }
365 
366     status = BufCache_Read(fs->disk, fs->fsval, &entry);
367     if (status < 0) {
368 	Alert(o2fs, "disk read error\n");
369 	return status;
370     }
371 
372     bn = entry->buffer;
373     if (memcmp(&bn->magic, BNODE_MAGIC, 8) != 0) {
374 	Alert(o2fs, "bad BNode magic\n");
375 	BufCache_Release(entry);
376 	return -1;
377     }
378     if (bn->versionMajor != O2FS_VERSION_MAJOR ||
379 	bn->versionMinor != O2FS_VERSION_MINOR) {
380 	Alert(o2fs, "unsupported BNode version\n");
381 	BufCache_Release(entry);
382 	return -1;
383     }
384 
385     vn = VNode_Alloc();
386     vn->op = &O2FSOperations;
387     vn->disk = fs->disk;
388     Spinlock_Init(&vn->lock, "VNode Lock", SPINLOCK_TYPE_NORMAL);
389     vn->refCount = 1;
390     vn->fsptr = entry;
391     vn->vfs = fs;
392 
393     *dn = vn;
394 
395     return 0;
396 }
397 
398 void
O2FSDumpDirEntry(BDirEntry * entry)399 O2FSDumpDirEntry(BDirEntry *entry)
400 {
401     VLOG(o2fs, "%16s %08llx %08llx\n", entry->name, entry->objId.offset, entry->size);
402 }
403 
404 /**
405  * O2FS_Lookup --
406  *
407  * Lookup a directory entry within a given directory.
408  *
409  * @param [in] vn VNode of the directory to look through.
410  * @param [out] fn VNode of the entry if found.
411  * @param [in] name Name of the file.
412  *
413  * @return 0 on success, otherwise error.
414  */
415 int
O2FS_Lookup(VNode * dn,VNode ** fn,const char * name)416 O2FS_Lookup(VNode *dn, VNode **fn, const char *name)
417 {
418     int status;
419     VFS *vfs = dn->vfs;
420     BufCacheEntry *sbEntry = (BufCacheEntry *)vfs->fsptr;
421     SuperBlock *sb = sbEntry->buffer;
422     BufCacheEntry *dirEntry = (BufCacheEntry *)dn->fsptr;
423     BNode *dirBN = dirEntry->buffer;
424     uint64_t blocks = (dirBN->size + sb->blockSize - 1) / sb->blockSize;
425     uint64_t b;
426 
427     DLOG(o2fs, "Lookup %lld %d\n", dirBN->size, blocks);
428 
429     for (b = 0; b < blocks; b++) {
430 	// Read block
431 	int e;
432 	int entryPerBlock = sb->blockSize / sizeof(BDirEntry);
433 	BufCacheEntry *entry;
434 	BDirEntry *dir;
435 
436 	status = O2FSResolveBuf(dn, b, &entry);
437 	if (status != 0)
438 		return status;
439 
440 	dir = (BDirEntry *)entry->buffer;
441 	for (e = 0; e < entryPerBlock; e++) {
442 	    if (strcmp((char *)dir[e].magic, BDIR_MAGIC) == 0) {
443 		O2FSDumpDirEntry(&dir[e]);
444 
445 		if (strcmp((char *)dir[e].name, name) == 0) {
446 		    *fn = O2FSLoadVNode(vfs, &dir[e].objId);
447 		    return 0;
448 		}
449 	    }
450 	}
451 
452 	BufCache_Release(entry);
453     }
454 
455     return -1;
456 }
457 
458 int
O2FS_Open(VNode * fn)459 O2FS_Open(VNode *fn)
460 {
461     return 0;
462 }
463 
464 int
O2FS_Close(VNode * fn)465 O2FS_Close(VNode *fn)
466 {
467     return 0;
468 }
469 
470 /**
471  * O2FS_Stat --
472  *
473  * Stat a VNode.
474  *
475  * @param [in] fn VNode of the file to stat.
476  * @param [out] statinfo Stat structure.
477  *
478  * @return 0 on success.
479  */
480 int
O2FS_Stat(VNode * fn,struct stat * statinfo)481 O2FS_Stat(VNode *fn, struct stat *statinfo)
482 {
483     VFS *vfs = fn->vfs;
484     BufCacheEntry *sbEntry = (BufCacheEntry *)vfs->fsptr;
485     SuperBlock *sb = sbEntry->buffer;
486     BufCacheEntry *fileEntry = (BufCacheEntry *)fn->fsptr;
487     BNode *fileBN = fileEntry->buffer;
488 
489     DLOG(o2fs, "O2FS %p %d\n", fileBN, fileBN->size);
490 
491     statinfo->st_ino = fileEntry->diskOffset;
492     statinfo->st_size = fileBN->size;
493     statinfo->st_blocks = (fileBN->size + sb->blockSize - 1) / sb->blockSize;
494     statinfo->st_blksize = sb->blockSize;
495 
496     return 0;
497 }
498 
499 /**
500  * O2FS_Read --
501  *
502  * Read from a VNode.
503  *
504  * @param [in] fn VNode of the file.
505  * @param [out] buf Buffer to read into.
506  * @param [in] off Offset within the file.
507  * @param [in] len Length of the buffer to read.
508  *
509  * @return number of bytes on success, otherwise negative error code.
510  */
511 int
O2FS_Read(VNode * fn,void * buf,uint64_t off,uint64_t len)512 O2FS_Read(VNode *fn, void *buf, uint64_t off, uint64_t len)
513 {
514     int status;
515     VFS *vfs = fn->vfs;
516     BufCacheEntry *sbEntry = (BufCacheEntry *)vfs->fsptr;
517     SuperBlock *sb = sbEntry->buffer;
518     BufCacheEntry *fileEntry = (BufCacheEntry *)fn->fsptr;
519     BNode *fileBN = fileEntry->buffer;
520     uint64_t blocks = (fileBN->size + sb->blockSize - 1) / sb->blockSize;
521     uint64_t readBytes = 0;
522 
523     DLOG(o2fs, "Read %lld %d\n", fileBN->size, blocks);
524 
525     if (off > fileBN->size) {
526 	return 0;
527     }
528 
529     if (off + len > fileBN->size) {
530 	len = fileBN->size - off;
531     }
532 
533     while (1) {
534 	uint64_t b = off / sb->blockSize;
535 	uint64_t bOff = off % sb->blockSize;
536 	uint64_t bLen;
537 	BufCacheEntry *entry;
538 
539 	if (bOff + len > sb->blockSize) {
540 	    bLen = sb->blockSize - bOff;
541 	} else {
542 	    bLen = len;
543 	}
544 
545 	status = O2FSResolveBuf(fn, b, &entry);
546 	if (status != 0)
547 		return status;
548 
549 	DLOG(o2fs, "READ %lx %lx %lld\n", buf, entry->buffer, bLen);
550 	memcpy(buf, entry->buffer + bOff, bLen);
551 	BufCache_Release(entry);
552 
553 	readBytes += bLen;
554 	buf += bLen;
555 	off += bLen;
556 	len -= bLen;
557 
558 	if (len == 0)
559 	    break;
560     }
561 
562     return readBytes;
563 }
564 
565 /**
566  * O2FS_Write --
567  *
568  * Write to a VNode.
569  *
570  * @param [in] fn VNode of the file.
571  * @param [in] buf Buffer to write out.
572  * @param [in] off Offset within the file.
573  * @param [in] len Length of the buffer to write.
574  *
575  * @return number of bytes on success, otherwise negative error code.
576  */
577 int
O2FS_Write(VNode * fn,void * buf,uint64_t off,uint64_t len)578 O2FS_Write(VNode *fn, void *buf, uint64_t off, uint64_t len)
579 {
580     int status;
581     VFS *vfs = fn->vfs;
582     BufCacheEntry *sbEntry = (BufCacheEntry *)vfs->fsptr;
583     SuperBlock *sb = sbEntry->buffer;
584     BufCacheEntry *fileEntry = (BufCacheEntry *)fn->fsptr;
585     BNode *fileBN = fileEntry->buffer;
586     uint64_t blocks = (fileBN->size + sb->blockSize - 1) / sb->blockSize;
587     uint64_t readBytes = 0;
588 
589     DLOG(o2fs, "Write %lld %d\n", fileBN->size, blocks);
590 
591     // XXX: Check permissions
592 
593     if (fileBN->size < (off+len)) {
594 	status = O2FSGrowVNode(fn, off+len);
595 	if (status < 0)
596 	    return status;
597     }
598 
599     while (1) {
600 	uint64_t b = off / sb->blockSize;
601 	uint64_t bOff = off % sb->blockSize;
602 	uint64_t bLen;
603 	BufCacheEntry *entry;
604 
605 	if (bOff + len > sb->blockSize) {
606 	    bLen = sb->blockSize - bOff;
607 	} else {
608 	    bLen = len;
609 	}
610 
611 	status = O2FSResolveBuf(fn, b, &entry);
612 	if (status != 0)
613 		return status;
614 
615 	DLOG(o2fs, "WRITE %lx %lx %lld\n", buf, entry->buffer, bLen);
616 	memcpy(entry->buffer + bOff, buf, bLen);
617 
618 	BufCache_Write(entry);
619 	BufCache_Release(entry);
620 
621 	readBytes += bLen;
622 	buf += bLen;
623 	off += bLen;
624 	len -= bLen;
625 
626 	if (len == 0)
627 	    break;
628     }
629 
630     return readBytes;
631 }
632 
633 /**
634  * O2FS_ReadDir --
635  *
636  * Read a directory entry.
637  *
638  * @param [in] fn VNode of the directory.
639  * @param [out] buf Buffer to read the directory entry into.
640  * @param [in] len Length of the buffer.
641  * @param [inout] off Offset to start from and return the next offset.
642  *
643  * @return 0 on success, otherwise error.
644  */
645 int
O2FS_ReadDir(VNode * fn,void * buf,uint64_t len,uint64_t * off)646 O2FS_ReadDir(VNode *fn, void *buf, uint64_t len, uint64_t *off)
647 {
648     int count = 0;
649     int status;
650     BufCacheEntry *fileEntry = (BufCacheEntry *)fn->fsptr;
651     BNode *fileBN = fileEntry->buffer;
652     BDirEntry dirEntry;
653     struct dirent de;
654 
655     while (len >= sizeof(de)) {
656 	if (*off == fileBN->size)
657 	    return count;
658 	if (*off > fileBN->size)
659 	    return -EINVAL;
660 
661 	// XXX: Check offset
662 
663 	status = O2FS_Read(fn, &dirEntry, *off, sizeof(dirEntry));
664 	if (status != sizeof(dirEntry)) {
665 	    kprintf("Unexpected error reading directory: %d\n", status);
666 	    return -ENOTDIR;
667 	}
668 	if (strncmp((char *)&dirEntry.magic[0], BDIR_MAGIC, sizeof(dirEntry.magic)) != 0) {
669 	    return -ENOTDIR;
670 	}
671 
672 	// XXX: Validation and fill in all fields
673 	strcpy(de.d_name, (char *)dirEntry.name);
674 
675 	status = Copy_Out(&de, (uintptr_t)buf, sizeof(de));
676 	if (status != 0)
677 	    return status;
678 
679 	*off += sizeof(dirEntry);
680 	buf += sizeof(de);
681 	len -= sizeof(de);
682 
683 	count++;
684     }
685 
686     return count;
687 }
688 
689