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