feat: ability to evict dnode and inode cache
[lunaix-os.git] / lunaix-os / kernel / fs / vfs.c
1 /**
2  * @file vfs.c
3  * @author Lunaixsky (zelong56@gmail.com)
4  * @brief Lunaix virtual file system - an abstraction layer for all file system.
5  * @version 0.1
6  * @date 2022-07-24
7  *
8  * @copyright Copyright (c) 2022
9  *
10  */
11
12 // Welcome to The Mountain O'Shit! :)
13
14 /*
15  TODO vfs & device todos checklist
16
17     It is overseen by Twilight Sparkle ;)
18
19  1. Get inodes hooked into lru (CHECKED)
20  2. Get dnodes hooked into lru (CHECKED)
21  3. Get inodes properly hashed so they can be reused by underling fs (CHECKED)
22  4. (lru) Add a callback function (or destructor) for eviction. (CHECKED)
23         [good idea] or a constructor/destructor pattern in cake allocator ?
24  5. (mount) Figure out a way to identify a busy mount point before unmount
25             maybe a unified mount_point structure that maintain a referencing
26             counter on any dnodes within the subtree? Such a counter will only
27             increament if a file is opened or a dnode is being used as working
28             directory and decreamenting conversely.
29  6. (mount) Ability to track all mount points (including sub-mounts)
30             so we can be confident to clean up everything when we unmount.
31  7. (mount) Figure out a way to acquire the device represented by a dnode.
32             so it can be used to mount. (e.g. we wish to get `struct device*`
33             out of the dnode at /dev/sda)
34             [tip] we should pay attention at twifs and add a private_data field
35             under struct v_dnode?
36  8. (mount) Then, we should refactor on mount/unmount mechanism.
37  9. (mount) (future) Ability to mount any thing? e.g. Linux can mount a disk
38                     image file using a so called "loopback" pseudo device. Maybe
39                     we can do similar thing in Lunaix? A block device emulation
40                     above the regular file when we mount it on.
41  10. (device) device number (dev_t) allocation
42             [good idea] <class>:<subclass>:<uniq_id> composition
43 */
44
45 #include <klibc/string.h>
46 #include <lunaix/dirent.h>
47 #include <lunaix/foptions.h>
48 #include <lunaix/fs.h>
49 #include <lunaix/mm/cake.h>
50 #include <lunaix/mm/page.h>
51 #include <lunaix/mm/valloc.h>
52 #include <lunaix/process.h>
53 #include <lunaix/spike.h>
54 #include <lunaix/syscall.h>
55
56 #include <lunaix/fs/twifs.h>
57
58 #define PATH_DELIM '/'
59 #define HASHTABLE_BITS 10
60 #define HASHTABLE_SIZE (1 << HASHTABLE_BITS)
61 #define HASH_MASK (HASHTABLE_SIZE - 1)
62 #define HASHBITS (32 - HASHTABLE_BITS)
63
64 #define unlock_inode(inode) mutex_unlock(&inode->lock)
65 #define lock_inode(inode)                                                      \
66     ({                                                                         \
67         mutex_lock(&inode->lock);                                              \
68         lru_use_one(inode_lru, &inode->lru);                                   \
69     })
70
71 #define unlock_dnode(dnode) mutex_unlock(&dnode->lock)
72 #define lock_dnode(dnode)                                                      \
73     ({                                                                         \
74         mutex_lock(&dnode->lock);                                              \
75         lru_use_one(dnode_lru, &dnode->lru);                                   \
76     })
77
78 static struct cake_pile* dnode_pile;
79 static struct cake_pile* inode_pile;
80 static struct cake_pile* file_pile;
81 static struct cake_pile* superblock_pile;
82 static struct cake_pile* fd_pile;
83
84 static struct v_superblock* root_sb;
85 static struct hbucket *dnode_cache, *inode_cache;
86
87 static struct lru_zone *dnode_lru, *inode_lru;
88
89 struct hstr vfs_ddot = HSTR("..", 2);
90 struct hstr vfs_dot = HSTR(".", 1);
91 struct hstr vfs_empty = HSTR("", 0);
92
93 struct v_superblock*
94 vfs_sb_alloc();
95
96 void
97 vfs_sb_free(struct v_superblock* sb);
98
99 static int
100 __vfs_try_evict_dnode(struct lru_node* obj);
101
102 static int
103 __vfs_try_evict_inode(struct lru_node* obj);
104
105 void
106 vfs_init()
107 {
108     // 为他们专门创建一个蛋糕堆,而不使用valloc,这样我们可以最小化内碎片的产生
109     dnode_pile = cake_new_pile("dnode_cache", sizeof(struct v_dnode), 1, 0);
110     inode_pile = cake_new_pile("inode_cache", sizeof(struct v_inode), 1, 0);
111     file_pile = cake_new_pile("file_cache", sizeof(struct v_file), 1, 0);
112     fd_pile = cake_new_pile("fd_cache", sizeof(struct v_fd), 1, 0);
113     superblock_pile =
114       cake_new_pile("sb_cache", sizeof(struct v_superblock), 1, 0);
115
116     dnode_cache = vzalloc(HASHTABLE_SIZE * sizeof(struct hbucket));
117     inode_cache = vzalloc(HASHTABLE_SIZE * sizeof(struct hbucket));
118
119     dnode_lru = lru_new_zone(__vfs_try_evict_dnode);
120     inode_lru = lru_new_zone(__vfs_try_evict_inode);
121
122     hstr_rehash(&vfs_ddot, HSTR_FULL_HASH);
123     hstr_rehash(&vfs_dot, HSTR_FULL_HASH);
124
125     // 创建一个根superblock,用来蕴含我们的根目录。
126     root_sb = vfs_sb_alloc();
127     root_sb->root = vfs_d_alloc();
128 }
129
130 inline struct hbucket*
131 __dcache_hash(struct v_dnode* parent, uint32_t* hash)
132 {
133     uint32_t _hash = *hash;
134     // 与parent的指针值做加法,来减小碰撞的可能性。
135     _hash += (uint32_t)parent;
136     // 确保低位更加随机
137     _hash = _hash ^ (_hash >> HASHBITS);
138     *hash = _hash;
139     return &dnode_cache[_hash & HASH_MASK];
140 }
141
142 struct v_dnode*
143 vfs_dcache_lookup(struct v_dnode* parent, struct hstr* str)
144 {
145     if (!str->len || HSTR_EQ(str, &vfs_dot))
146         return parent;
147
148     if (HSTR_EQ(str, &vfs_ddot)) {
149         return parent->parent ? parent->parent : parent;
150     }
151
152     uint32_t hash = str->hash;
153     struct hbucket* slot = __dcache_hash(parent, &hash);
154
155     struct v_dnode *pos, *n;
156     hashtable_bucket_foreach(slot, pos, n, hash_list)
157     {
158         if (pos->name.hash == hash) {
159             return pos;
160         }
161     }
162     return NULL;
163 }
164
165 void
166 vfs_dcache_add(struct v_dnode* parent, struct v_dnode* dnode)
167 {
168     atomic_fetch_add(&dnode->ref_count, 1);
169     dnode->parent = parent;
170     llist_append(&parent->children, &dnode->siblings);
171     struct hbucket* bucket = __dcache_hash(parent, &dnode->name.hash);
172     hlist_add(&bucket->head, &dnode->hash_list);
173 }
174
175 void
176 vfs_dcache_remove(struct v_dnode* dnode)
177 {
178     assert(dnode->ref_count == 1);
179
180     llist_delete(&dnode->siblings);
181     hlist_delete(&dnode->hash_list);
182
183     dnode->parent = NULL;
184     atomic_fetch_sub(&dnode->ref_count, 1);
185 }
186
187 void
188 vfs_dcache_rehash(struct v_dnode* new_parent, struct v_dnode* dnode)
189 {
190     hstr_rehash(&dnode->name, HSTR_FULL_HASH);
191     vfs_dcache_remove(dnode);
192     vfs_dcache_add(new_parent, dnode);
193 }
194
195 int
196 __vfs_walk(struct v_dnode* start,
197            const char* path,
198            struct v_dnode** dentry,
199            struct hstr* component,
200            int walk_options)
201 {
202     int errno = 0;
203     int i = 0, j = 0;
204
205     if (path[0] == PATH_DELIM || !start) {
206         if ((walk_options & VFS_WALK_FSRELATIVE) && start) {
207             start = start->super_block->root;
208         } else {
209             start = root_sb->root;
210         }
211         i++;
212     }
213
214     struct v_dnode* dnode;
215     struct v_dnode* current_level = start;
216
217     char name_content[VFS_NAME_MAXLEN];
218     struct hstr name = HSTR(name_content, 0);
219
220     char current = path[i++], lookahead;
221     while (current) {
222         lookahead = path[i++];
223         if (current != PATH_DELIM) {
224             if (j >= VFS_NAME_MAXLEN - 1) {
225                 return ENAMETOOLONG;
226             }
227             if (!VFS_VALID_CHAR(current)) {
228                 return EINVAL;
229             }
230             name_content[j++] = current;
231             if (lookahead) {
232                 goto cont;
233             }
234         }
235
236         // handling cases like /^.*(\/+).*$/
237         if (lookahead == PATH_DELIM) {
238             goto cont;
239         }
240
241         lock_dnode(current_level);
242
243         name_content[j] = 0;
244         name.len = j;
245         hstr_rehash(&name, HSTR_FULL_HASH);
246
247         if (!lookahead && (walk_options & VFS_WALK_PARENT)) {
248             if (component) {
249                 component->hash = name.hash;
250                 component->len = j;
251                 strcpy(component->value, name_content);
252             }
253             unlock_dnode(current_level);
254             break;
255         }
256
257         dnode = vfs_dcache_lookup(current_level, &name);
258
259         if (!dnode) {
260             dnode = vfs_d_alloc();
261
262             if (!dnode) {
263                 errno = ENOMEM;
264                 goto error;
265             }
266
267             hstrcpy(&dnode->name, &name);
268
269             lock_inode(current_level->inode);
270
271             errno =
272               current_level->inode->ops.dir_lookup(current_level->inode, dnode);
273
274             if (errno == ENOENT && (walk_options & VFS_WALK_MKPARENT)) {
275                 if (!current_level->inode->ops.mkdir) {
276                     errno = ENOTSUP;
277                 } else {
278                     errno = current_level->inode->ops.mkdir(
279                       current_level->inode, dnode);
280                 }
281             }
282
283             unlock_inode(current_level->inode);
284
285             if (errno) {
286                 unlock_dnode(current_level);
287                 vfree(dnode->name.value);
288                 goto cleanup;
289             }
290
291             vfs_dcache_add(current_level, dnode);
292         }
293
294         unlock_dnode(current_level);
295
296         j = 0;
297         current_level = dnode;
298     cont:
299         current = lookahead;
300     };
301
302     *dentry = current_level;
303     return 0;
304
305 cleanup:
306     vfs_d_free(dnode);
307 error:
308     *dentry = NULL;
309     return errno;
310 }
311
312 #define VFS_MAX_SYMLINK 16
313
314 int
315 vfs_walk(struct v_dnode* start,
316          const char* path,
317          struct v_dnode** dentry,
318          struct hstr* component,
319          int options)
320 {
321     struct v_dnode* interim;
322     const char* pathname = path;
323     int errno = __vfs_walk(start, path, &interim, component, options);
324     int counter = 0;
325
326     while (!errno && interim->inode && (options & VFS_WALK_NOFOLLOW)) {
327         if (counter >= VFS_MAX_SYMLINK) {
328             errno = ELOOP;
329             continue;
330         }
331         if ((interim->inode->itype & VFS_IFSYMLINK) &&
332             interim->inode->ops.read_symlink) {
333
334             lock_inode(interim->inode);
335             errno = interim->inode->ops.read_symlink(interim->inode, &pathname);
336             unlock_inode(interim->inode);
337
338             if (errno) {
339                 break;
340             }
341         } else {
342             break;
343         }
344         errno = __vfs_walk(start, pathname, &interim, component, options);
345         counter++;
346     }
347
348     *dentry = errno ? 0 : interim;
349
350     return errno;
351 }
352
353 int
354 vfs_mount(const char* target, const char* fs_name, struct device* device)
355 {
356     int errno;
357     struct v_dnode* mnt;
358
359     if (!(errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
360         errno = vfs_mount_at(fs_name, device, mnt);
361     }
362
363     return errno;
364 }
365
366 int
367 vfs_unmount(const char* target)
368 {
369     int errno;
370     struct v_dnode* mnt;
371
372     if (!(errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
373         errno = vfs_unmount_at(mnt);
374     }
375
376     return errno;
377 }
378
379 int
380 vfs_mount_at(const char* fs_name,
381              struct device* device,
382              struct v_dnode* mnt_point)
383 {
384     if (mnt_point->inode && !(mnt_point->inode->itype & VFS_IFDIR)) {
385         return ENOTDIR;
386     }
387
388     struct filesystem* fs = fsm_get(fs_name);
389     if (!fs) {
390         return ENODEV;
391     }
392
393     struct v_superblock* sb = vfs_sb_alloc();
394     sb->dev = device;
395     sb->fs_id = fs->fs_id;
396
397     int errno = 0;
398     if (!(errno = fs->mount(sb, mnt_point))) {
399         sb->fs = fs;
400         sb->root = mnt_point;
401         mnt_point->super_block = sb;
402         llist_append(&root_sb->sb_list, &sb->sb_list);
403     }
404
405     return errno;
406 }
407
408 int
409 vfs_unmount_at(struct v_dnode* mnt_point)
410 {
411     // FIXME deal with the detached dcache subtree
412     int errno = 0;
413     struct v_superblock* sb = mnt_point->super_block;
414     if (!sb) {
415         return EINVAL;
416     }
417
418     if (sb->root != mnt_point) {
419         return EINVAL;
420     }
421
422     if (!(errno = sb->fs->unmount(sb))) {
423         struct v_dnode* fs_root = sb->root;
424         vfs_dcache_remove(fs_root);
425
426         llist_delete(&sb->sb_list);
427         vfs_sb_free(sb);
428         vfs_d_free(fs_root);
429     }
430     return errno;
431 }
432
433 int
434 vfs_open(struct v_dnode* dnode, struct v_file** file)
435 {
436     if (!dnode->inode || !dnode->inode->ops.open) {
437         return ENOTSUP;
438     }
439
440     struct v_inode* inode = dnode->inode;
441     struct v_file* vfile = cake_grab(file_pile);
442     memset(vfile, 0, sizeof(*vfile));
443
444     vfile->dnode = dnode;
445     vfile->inode = inode;
446     vfile->ref_count = ATOMIC_VAR_INIT(1);
447     vfile->ops = inode->default_fops;
448
449     if ((inode->itype & VFS_IFFILE) && !inode->pg_cache) {
450         struct pcache* pcache = vzalloc(sizeof(struct pcache));
451         pcache_init(pcache);
452         pcache->master = inode;
453         inode->pg_cache = pcache;
454     }
455
456     int errno = inode->ops.open(inode, vfile);
457     if (errno) {
458         cake_release(file_pile, vfile);
459     } else {
460         atomic_fetch_add(&dnode->ref_count, 1);
461         inode->open_count++;
462
463         *file = vfile;
464     }
465
466     return errno;
467 }
468
469 void
470 vfs_assign_inode(struct v_dnode* assign_to, struct v_inode* inode)
471 {
472     if (assign_to->inode) {
473         assign_to->inode->link_count--;
474     }
475     assign_to->inode = inode;
476     inode->link_count++;
477 }
478
479 int
480 vfs_link(struct v_dnode* to_link, struct v_dnode* name)
481 {
482     int errno;
483
484     lock_inode(to_link->inode);
485     if (to_link->super_block->root != name->super_block->root) {
486         errno = EXDEV;
487     } else if (!to_link->inode->ops.link) {
488         errno = ENOTSUP;
489     } else if (!(errno = to_link->inode->ops.link(to_link->inode, name))) {
490         vfs_assign_inode(name, to_link->inode);
491     }
492     unlock_inode(to_link->inode);
493
494     return errno;
495 }
496
497 int
498 vfs_close(struct v_file* file)
499 {
500     int errno = 0;
501     if (!file->ops.close || !(errno = file->ops.close(file))) {
502         atomic_fetch_sub(&file->dnode->ref_count, 1);
503         file->inode->open_count--;
504
505         pcache_commit_all(file->inode);
506         cake_release(file_pile, file);
507     }
508     return errno;
509 }
510
511 int
512 vfs_fsync(struct v_file* file)
513 {
514     lock_inode(file->inode);
515
516     int errno = ENOTSUP;
517     pcache_commit_all(file->inode);
518     if (file->ops.sync) {
519         errno = file->ops.sync(file->inode);
520     }
521
522     unlock_inode(file->inode);
523
524     return errno;
525 }
526
527 int
528 vfs_alloc_fdslot(int* fd)
529 {
530     for (size_t i = 0; i < VFS_MAX_FD; i++) {
531         if (!__current->fdtable->fds[i]) {
532             *fd = i;
533             return 0;
534         }
535     }
536     return EMFILE;
537 }
538
539 struct v_superblock*
540 vfs_sb_alloc()
541 {
542     struct v_superblock* sb = cake_grab(superblock_pile);
543     memset(sb, 0, sizeof(*sb));
544     llist_init_head(&sb->sb_list);
545     return sb;
546 }
547
548 void
549 vfs_sb_free(struct v_superblock* sb)
550 {
551     cake_release(superblock_pile, sb);
552 }
553
554 static int
555 __vfs_try_evict_dnode(struct lru_node* obj)
556 {
557     struct v_dnode* dnode = container_of(obj, struct v_dnode, lru);
558
559     if (!dnode->ref_count) {
560         vfs_d_free(dnode);
561         return 1;
562     }
563     return 0;
564 }
565
566 static int
567 __vfs_try_evict_inode(struct lru_node* obj)
568 {
569     struct v_inode* inode = container_of(obj, struct v_inode, lru);
570
571     if (!inode->link_count && !inode->open_count) {
572         vfs_i_free(inode);
573         return 1;
574     }
575     return 0;
576 }
577
578 struct v_dnode*
579 vfs_d_alloc()
580 {
581     struct v_dnode* dnode = cake_grab(dnode_pile);
582     if (!dnode) {
583         lru_evict_half(dnode_lru);
584
585         if (!(dnode = cake_grab(dnode_pile))) {
586             return NULL;
587         }
588     }
589
590     memset(dnode, 0, sizeof(*dnode));
591     llist_init_head(&dnode->children);
592     llist_init_head(&dnode->siblings);
593     mutex_init(&dnode->lock);
594
595     dnode->ref_count = ATOMIC_VAR_INIT(0);
596     dnode->name = HHSTR(vzalloc(VFS_NAME_MAXLEN), 0, 0);
597
598     lru_use_one(dnode_lru, &dnode->lru);
599
600     return dnode;
601 }
602
603 void
604 vfs_d_free(struct v_dnode* dnode)
605 {
606     assert(dnode->ref_count == 0);
607
608     if (dnode->inode) {
609         assert(dnode->inode->link_count > 0);
610         dnode->inode->link_count--;
611     }
612
613     // Make sure the children de-referencing their parent.
614     // With lru presented, the eviction will be propagated over the entire
615     // detached subtree eventually
616     struct v_dnode *pos, *n;
617     llist_for_each(pos, n, &dnode->children, siblings)
618     {
619         vfs_dcache_remove(pos);
620     }
621
622     vfree(dnode->name.value);
623     cake_release(dnode_pile, dnode);
624 }
625
626 struct v_inode*
627 vfs_i_alloc(dev_t device_id, uint32_t inode_id)
628 {
629     // 我们这里假设每个文件系统与设备是一一对应(毕竟一个分区不可能有两个不同的文件系统)
630     // 而每个文件系统所产生的 v_inode 缓存必须要和其他文件系统产生的区分开来。
631     // 这也就是说,每个 v_inode 的 id
632     // 必须要由设备ID,和该虚拟inode缓存所对应的物理inode
633     // 相对于其所在的文件系统的id,进行组成!
634     inode_id = hash_32(inode_id ^ (-device_id), HASH_SIZE_BITS);
635     inode_id = (inode_id >> HASHBITS) ^ inode_id;
636
637     struct hbucket* slot = &inode_cache[inode_id & HASH_MASK];
638     struct v_inode *pos, *n;
639     hashtable_bucket_foreach(slot, pos, n, hash_list)
640     {
641         if (pos->id == inode_id) {
642             goto done;
643         }
644     }
645
646     if (!(pos = cake_grab(inode_pile))) {
647         lru_evict_half(inode_lru);
648         if (!(pos = cake_grab(inode_pile))) {
649             return NULL;
650         }
651     }
652
653     memset(pos, 0, sizeof(*pos));
654
655     pos->id = inode_id;
656
657     mutex_init(&pos->lock);
658
659     hlist_add(&slot->head, &pos->hash_list);
660
661 done:
662     lru_use_one(inode_lru, &pos->lru);
663     return pos;
664 }
665
666 void
667 vfs_i_free(struct v_inode* inode)
668 {
669     hlist_delete(&inode->hash_list);
670     cake_release(inode_pile, inode);
671 }
672
673 /* ---- System call definition and support ---- */
674
675 #define FLOCATE_CREATE_EMPTY 1
676
677 #define DO_STATUS(errno) SYSCALL_ESTATUS(__current->k_status = errno)
678 #define DO_STATUS_OR_RETURN(errno) ({ errno < 0 ? DO_STATUS(errno) : errno; })
679
680 #define TEST_FD(fd) (fd >= 0 && fd < VFS_MAX_FD)
681
682 int
683 __vfs_getfd(int fd, struct v_fd** fd_s)
684 {
685     if (TEST_FD(fd) && (*fd_s = __current->fdtable->fds[fd])) {
686         return 0;
687     }
688     return EBADF;
689 }
690
691 int
692 __vfs_try_locate_file(const char* path,
693                       struct v_dnode** fdir,
694                       struct v_dnode** file,
695                       int options)
696 {
697     char name_str[VFS_NAME_MAXLEN];
698     struct hstr name = HSTR(name_str, 0);
699     int errno;
700     if ((errno =
701            vfs_walk(__current->cwd, path, fdir, &name, VFS_WALK_PARENT))) {
702         return errno;
703     }
704
705     errno = vfs_walk(*fdir, name.value, file, NULL, 0);
706     if (errno != ENOENT || !(options & FLOCATE_CREATE_EMPTY)) {
707         return errno;
708     }
709
710     struct v_dnode* parent = *fdir;
711     struct v_dnode* file_new = vfs_d_alloc();
712
713     if (!file_new) {
714         return ENOMEM;
715     }
716
717     hstrcpy(&file_new->name, &name);
718
719     lock_dnode(parent);
720
721     if (!(errno = parent->inode->ops.create(parent->inode, file_new))) {
722         *file = file_new;
723
724         vfs_dcache_add(parent, file_new);
725         llist_append(&parent->children, &file_new->siblings);
726     } else {
727         vfs_d_free(file_new);
728     }
729
730     unlock_dnode(parent);
731
732     return errno;
733 }
734
735 int
736 vfs_do_open(const char* path, int options)
737 {
738     int errno, fd;
739     struct v_dnode *dentry, *file;
740     struct v_file* ofile = 0;
741
742     errno = __vfs_try_locate_file(
743       path, &dentry, &file, (options & FO_CREATE) ? FLOCATE_CREATE_EMPTY : 0);
744
745     if (errno || (errno = vfs_open(file, &ofile))) {
746         return errno;
747     }
748
749     struct v_inode* o_inode = ofile->inode;
750     if (!(o_inode->itype & VFS_IFSEQDEV) && !(options & FO_DIRECT)) {
751         // XXX Change here accordingly when signature of pcache_r/w changed.
752         ofile->ops.read = pcache_read;
753         ofile->ops.write = pcache_write;
754     }
755
756     if (!errno && !(errno = vfs_alloc_fdslot(&fd))) {
757         struct v_fd* fd_s = vzalloc(sizeof(*fd_s));
758         ofile->f_pos = ofile->inode->fsize & -((options & FO_APPEND) != 0);
759         fd_s->file = ofile;
760         fd_s->flags = options;
761         __current->fdtable->fds[fd] = fd_s;
762         return fd;
763     }
764
765     return errno;
766 }
767
768 __DEFINE_LXSYSCALL2(int, open, const char*, path, int, options)
769 {
770     int errno = vfs_do_open(path, options);
771     return DO_STATUS_OR_RETURN(errno);
772 }
773
774 __DEFINE_LXSYSCALL1(int, close, int, fd)
775 {
776     struct v_fd* fd_s;
777     int errno = 0;
778     if ((errno = __vfs_getfd(fd, &fd_s))) {
779         goto done_err;
780     }
781
782     if (fd_s->file->ref_count > 1) {
783         fd_s->file->ref_count--;
784     } else if ((errno = vfs_close(fd_s->file))) {
785         goto done_err;
786     }
787
788     vfree(fd_s);
789     __current->fdtable->fds[fd] = 0;
790
791 done_err:
792     return DO_STATUS(errno);
793 }
794
795 void
796 __vfs_readdir_callback(struct dir_context* dctx,
797                        const char* name,
798                        const int len,
799                        const int dtype)
800 {
801     struct dirent* dent = (struct dirent*)dctx->cb_data;
802     strncpy(dent->d_name, name, DIRENT_NAME_MAX_LEN);
803     dent->d_nlen = len;
804     dent->d_type = dtype;
805 }
806
807 __DEFINE_LXSYSCALL2(int, readdir, int, fd, struct dirent*, dent)
808 {
809     struct v_fd* fd_s;
810     int errno;
811
812     if ((errno = __vfs_getfd(fd, &fd_s))) {
813         goto done;
814     }
815
816     struct v_inode* inode = fd_s->file->inode;
817
818     lock_inode(inode);
819
820     if (!(fd_s->file->inode->itype & VFS_IFDIR)) {
821         errno = ENOTDIR;
822     } else {
823         struct dir_context dctx =
824           (struct dir_context){ .cb_data = dent,
825                                 .index = dent->d_offset,
826                                 .read_complete_callback =
827                                   __vfs_readdir_callback };
828         if (dent->d_offset == 0) {
829             __vfs_readdir_callback(&dctx, vfs_dot.value, vfs_dot.len, 0);
830         } else if (dent->d_offset == 1) {
831             __vfs_readdir_callback(&dctx, vfs_ddot.value, vfs_ddot.len, 0);
832         } else {
833             dctx.index -= 2;
834             if ((errno = fd_s->file->ops.readdir(inode, &dctx))) {
835                 unlock_inode(inode);
836                 goto done;
837             }
838         }
839         errno = 0;
840         dent->d_offset++;
841     }
842
843     unlock_inode(inode);
844
845 done:
846     return DO_STATUS(errno);
847 }
848
849 __DEFINE_LXSYSCALL3(int, read, int, fd, void*, buf, size_t, count)
850 {
851     int errno = 0;
852     struct v_fd* fd_s;
853     if ((errno = __vfs_getfd(fd, &fd_s))) {
854         goto done;
855     }
856
857     struct v_file* file = fd_s->file;
858     if ((file->inode->itype & VFS_IFDIR)) {
859         errno = EISDIR;
860         goto done;
861     }
862
863     lock_inode(file->inode);
864
865     file->inode->atime = clock_unixtime();
866
867     __SYSCALL_INTERRUPTIBLE(
868       { errno = file->ops.read(file->inode, buf, count, file->f_pos); })
869
870     if (errno > 0) {
871         file->f_pos += errno;
872         unlock_inode(file->inode);
873         return errno;
874     }
875
876     unlock_inode(file->inode);
877
878 done:
879     return DO_STATUS(errno);
880 }
881
882 __DEFINE_LXSYSCALL3(int, write, int, fd, void*, buf, size_t, count)
883 {
884     int errno = 0;
885     struct v_fd* fd_s;
886     if ((errno = __vfs_getfd(fd, &fd_s))) {
887         goto done;
888     }
889
890     struct v_file* file = fd_s->file;
891     if ((file->inode->itype & VFS_IFDIR)) {
892         errno = EISDIR;
893         goto done;
894     }
895
896     lock_inode(file->inode);
897
898     file->inode->mtime = clock_unixtime();
899
900     __SYSCALL_INTERRUPTIBLE(
901       { errno = file->ops.write(file->inode, buf, count, file->f_pos); })
902
903     if (errno > 0) {
904         file->f_pos += errno;
905         unlock_inode(file->inode);
906         return errno;
907     }
908
909     unlock_inode(file->inode);
910
911 done:
912     return DO_STATUS(errno);
913 }
914
915 __DEFINE_LXSYSCALL3(int, lseek, int, fd, int, offset, int, options)
916 {
917     int errno = 0;
918     struct v_fd* fd_s;
919     if ((errno = __vfs_getfd(fd, &fd_s))) {
920         goto done;
921     }
922
923     struct v_file* file = fd_s->file;
924
925     lock_inode(file->inode);
926
927     size_t fpos = file->f_pos;
928     switch (options) {
929         case FSEEK_CUR:
930             fpos = (size_t)((int)file->f_pos + offset);
931             break;
932         case FSEEK_END:
933             fpos = (size_t)((int)file->inode->fsize + offset);
934             break;
935         case FSEEK_SET:
936             fpos = offset;
937             break;
938     }
939     if (!file->ops.seek || !(errno = file->ops.seek(file->inode, fpos))) {
940         file->f_pos = fpos;
941     }
942
943     unlock_inode(file->inode);
944
945 done:
946     return DO_STATUS(errno);
947 }
948
949 int
950 vfs_get_path(struct v_dnode* dnode, char* buf, size_t size, int depth)
951 {
952     if (!dnode) {
953         return 0;
954     }
955
956     if (depth > 64) {
957         return ELOOP;
958     }
959
960     size_t len = vfs_get_path(dnode->parent, buf, size, depth + 1);
961
962     if (len >= size) {
963         return len;
964     }
965
966     size_t cpy_size = MIN(dnode->name.len, size - len);
967     strncpy(buf + len, dnode->name.value, cpy_size);
968     len += cpy_size;
969
970     if (len < size) {
971         buf[len++] = PATH_DELIM;
972     }
973
974     return len;
975 }
976
977 int
978 vfs_readlink(struct v_dnode* dnode, char* buf, size_t size)
979 {
980     const char* link;
981     struct v_inode* inode = dnode->inode;
982     if (inode->ops.read_symlink) {
983         lock_inode(inode);
984
985         int errno = inode->ops.read_symlink(inode, &link);
986         strncpy(buf, link, size);
987
988         unlock_inode(inode);
989         return errno;
990     }
991     return 0;
992 }
993
994 __DEFINE_LXSYSCALL3(int, realpathat, int, fd, char*, buf, size_t, size)
995 {
996     int errno;
997     struct v_fd* fd_s;
998     if ((errno = __vfs_getfd(fd, &fd_s))) {
999         goto done;
1000     }
1001
1002     struct v_dnode* dnode;
1003     errno = vfs_get_path(fd_s->file->dnode, buf, size, 0);
1004
1005     if (errno >= 0) {
1006         return errno;
1007     }
1008
1009 done:
1010     return DO_STATUS(errno);
1011 }
1012
1013 __DEFINE_LXSYSCALL3(int, readlink, const char*, path, char*, buf, size_t, size)
1014 {
1015     int errno;
1016     struct v_dnode* dnode;
1017     if (!(errno =
1018             vfs_walk(__current->cwd, path, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
1019         errno = vfs_readlink(dnode, buf, size);
1020     }
1021
1022     if (errno >= 0) {
1023         return errno;
1024     }
1025
1026     return DO_STATUS(errno);
1027 }
1028
1029 __DEFINE_LXSYSCALL4(int,
1030                     readlinkat,
1031                     int,
1032                     dirfd,
1033                     const char*,
1034                     pathname,
1035                     char*,
1036                     buf,
1037                     size_t,
1038                     size)
1039 {
1040     int errno;
1041     struct v_fd* fd_s;
1042     if ((errno = __vfs_getfd(dirfd, &fd_s))) {
1043         goto done;
1044     }
1045
1046     struct v_dnode* dnode;
1047     if (!(errno = vfs_walk(
1048             fd_s->file->dnode, pathname, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
1049         errno = vfs_readlink(fd_s->file->dnode, buf, size);
1050     }
1051
1052     if (errno >= 0) {
1053         return errno;
1054     }
1055
1056 done:
1057     return DO_STATUS(errno);
1058 }
1059
1060 /*
1061     NOTE
1062     When we perform operation that could affect the layout of
1063     directory (i.e., rename, mkdir, rmdir). We must lock the parent dir
1064     whenever possible. This will blocking any ongoing path walking to reach
1065     it hence avoid any partial state.
1066 */
1067
1068 __DEFINE_LXSYSCALL1(int, rmdir, const char*, pathname)
1069 {
1070     int errno;
1071     struct v_dnode* dnode;
1072     if ((errno = vfs_walk(__current->cwd, pathname, &dnode, NULL, 0))) {
1073         return DO_STATUS(errno);
1074     }
1075
1076     lock_dnode(dnode);
1077
1078     if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
1079         errno = EROFS;
1080         goto done;
1081     }
1082
1083     if (dnode->ref_count > 1 || dnode->inode->open_count) {
1084         errno = EBUSY;
1085         goto done;
1086     }
1087
1088     if (!llist_empty(&dnode->children)) {
1089         errno = ENOTEMPTY;
1090         goto done;
1091     }
1092
1093     struct v_dnode* parent = dnode->parent;
1094
1095     if (!parent) {
1096         errno = EINVAL;
1097         goto done;
1098     }
1099
1100     lock_dnode(parent);
1101     lock_inode(parent->inode);
1102
1103     if ((dnode->inode->itype & VFS_IFDIR)) {
1104         errno = parent->inode->ops.rmdir(parent->inode, dnode);
1105         if (!errno) {
1106             vfs_dcache_remove(dnode);
1107         }
1108     } else {
1109         errno = ENOTDIR;
1110     }
1111
1112     unlock_inode(parent->inode);
1113     unlock_dnode(parent);
1114
1115 done:
1116     unlock_dnode(dnode);
1117     return DO_STATUS(errno);
1118 }
1119
1120 __DEFINE_LXSYSCALL1(int, mkdir, const char*, path)
1121 {
1122     int errno = 0;
1123     struct v_dnode *parent, *dir = vfs_d_alloc();
1124
1125     if (!dir) {
1126         errno = ENOMEM;
1127         goto done;
1128     }
1129
1130     if ((errno = vfs_walk(
1131            __current->cwd, path, &parent, &dir->name, VFS_WALK_PARENT))) {
1132         goto done;
1133     }
1134
1135     lock_dnode(parent);
1136     lock_inode(parent->inode);
1137
1138     if ((parent->super_block->fs->types & FSTYPE_ROFS)) {
1139         errno = ENOTSUP;
1140     } else if (!parent->inode->ops.mkdir) {
1141         errno = ENOTSUP;
1142     } else if (!(parent->inode->itype & VFS_IFDIR)) {
1143         errno = ENOTDIR;
1144     } else if (!(errno = parent->inode->ops.mkdir(parent->inode, dir))) {
1145         llist_append(&parent->children, &dir->siblings);
1146         goto cleanup;
1147     }
1148
1149     vfs_d_free(dir);
1150
1151 cleanup:
1152     unlock_inode(parent->inode);
1153     unlock_dnode(parent);
1154 done:
1155     return DO_STATUS(errno);
1156 }
1157
1158 int
1159 __vfs_do_unlink(struct v_dnode* dnode)
1160 {
1161     struct v_inode* inode = dnode->inode;
1162
1163     if (dnode->ref_count > 1) {
1164         return EBUSY;
1165     }
1166
1167     lock_inode(inode);
1168
1169     int errno;
1170     if (inode->open_count) {
1171         errno = EBUSY;
1172     } else if (!(inode->itype & VFS_IFDIR)) {
1173         // The underlying unlink implementation should handle
1174         //  symlink case
1175         errno = inode->ops.unlink(inode);
1176         if (!errno) {
1177             vfs_dcache_remove(dnode);
1178             vfs_d_free(dnode);
1179         }
1180     } else {
1181         errno = EISDIR;
1182     }
1183
1184     unlock_inode(inode);
1185
1186     return errno;
1187 }
1188
1189 __DEFINE_LXSYSCALL1(int, unlink, const char*, pathname)
1190 {
1191     int errno;
1192     struct v_dnode* dnode;
1193     if ((errno = vfs_walk(__current->cwd, pathname, &dnode, NULL, 0))) {
1194         goto done;
1195     }
1196     if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
1197         errno = EROFS;
1198         goto done;
1199     }
1200
1201     errno = __vfs_do_unlink(dnode);
1202
1203 done:
1204     return DO_STATUS(errno);
1205 }
1206
1207 __DEFINE_LXSYSCALL2(int, unlinkat, int, fd, const char*, pathname)
1208 {
1209     int errno;
1210     struct v_fd* fd_s;
1211     if ((errno = __vfs_getfd(fd, &fd_s))) {
1212         goto done;
1213     }
1214
1215     struct v_dnode* dnode;
1216     if (!(errno = vfs_walk(fd_s->file->dnode, pathname, &dnode, NULL, 0))) {
1217         errno = __vfs_do_unlink(dnode);
1218     }
1219
1220 done:
1221     return DO_STATUS(errno);
1222 }
1223
1224 __DEFINE_LXSYSCALL2(int, link, const char*, oldpath, const char*, newpath)
1225 {
1226     int errno;
1227     struct v_dnode *dentry, *to_link, *name_dentry, *name_file;
1228
1229     errno = __vfs_try_locate_file(oldpath, &dentry, &to_link, 0);
1230     if (!errno) {
1231         errno = __vfs_try_locate_file(
1232           newpath, &name_dentry, &name_file, FLOCATE_CREATE_EMPTY);
1233         if (!errno) {
1234             errno = EEXIST;
1235         } else if (name_file) {
1236             errno = vfs_link(to_link, name_file);
1237         }
1238     }
1239     return DO_STATUS(errno);
1240 }
1241
1242 __DEFINE_LXSYSCALL1(int, fsync, int, fildes)
1243 {
1244     int errno;
1245     struct v_fd* fd_s;
1246     if (!(errno = __vfs_getfd(fildes, &fd_s))) {
1247         errno = vfs_fsync(fd_s->file);
1248     }
1249
1250     return DO_STATUS(errno);
1251 }
1252
1253 int
1254 vfs_dup_fd(struct v_fd* old, struct v_fd** new)
1255 {
1256     int errno = 0;
1257     struct v_fd* copied = cake_grab(fd_pile);
1258
1259     memcpy(copied, old, sizeof(struct v_fd));
1260
1261     atomic_fetch_add(&old->file->ref_count, 1);
1262
1263     *new = copied;
1264
1265     return errno;
1266 }
1267
1268 int
1269 vfs_dup2(int oldfd, int newfd)
1270 {
1271     if (newfd == oldfd) {
1272         return newfd;
1273     }
1274
1275     int errno;
1276     struct v_fd *oldfd_s, *newfd_s;
1277     if ((errno = __vfs_getfd(oldfd, &oldfd_s))) {
1278         goto done;
1279     }
1280
1281     if (!TEST_FD(newfd)) {
1282         errno = EBADF;
1283         goto done;
1284     }
1285
1286     newfd_s = __current->fdtable->fds[newfd];
1287     if (newfd_s && (errno = vfs_close(newfd_s->file))) {
1288         goto done;
1289     }
1290
1291     if (!(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1292         __current->fdtable->fds[newfd] = newfd_s;
1293         return newfd;
1294     }
1295
1296 done:
1297     return DO_STATUS(errno);
1298 }
1299
1300 __DEFINE_LXSYSCALL2(int, dup2, int, oldfd, int, newfd)
1301 {
1302     return vfs_dup2(oldfd, newfd);
1303 }
1304
1305 __DEFINE_LXSYSCALL1(int, dup, int, oldfd)
1306 {
1307     int errno, newfd;
1308     struct v_fd *oldfd_s, *newfd_s;
1309     if ((errno = __vfs_getfd(oldfd, &oldfd_s))) {
1310         goto done;
1311     }
1312
1313     if (!(errno = vfs_alloc_fdslot(&newfd)) &&
1314         !(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1315         __current->fdtable->fds[newfd] = newfd_s;
1316         return newfd;
1317     }
1318
1319 done:
1320     return DO_STATUS(errno);
1321 }
1322
1323 __DEFINE_LXSYSCALL2(int,
1324                     symlink,
1325                     const char*,
1326                     pathname,
1327                     const char*,
1328                     link_target)
1329 {
1330     int errno;
1331     struct v_dnode* dnode;
1332     if ((errno = vfs_walk(__current->cwd, pathname, &dnode, NULL, 0))) {
1333         goto done;
1334     }
1335     if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
1336         errno = EROFS;
1337         goto done;
1338     }
1339     if (!dnode->inode->ops.set_symlink) {
1340         errno = ENOTSUP;
1341         goto done;
1342     }
1343
1344     lock_inode(dnode->inode);
1345
1346     errno = dnode->inode->ops.set_symlink(dnode->inode, link_target);
1347
1348     unlock_inode(dnode->inode);
1349
1350 done:
1351     return DO_STATUS(errno);
1352 }
1353
1354 int
1355 __vfs_do_chdir(struct v_dnode* dnode)
1356 {
1357     int errno = 0;
1358
1359     lock_dnode(dnode);
1360
1361     if (!(dnode->inode->itype & VFS_IFDIR)) {
1362         errno = ENOTDIR;
1363         goto done;
1364     }
1365
1366     if (__current->cwd) {
1367         atomic_fetch_add(&__current->cwd->ref_count, 1);
1368     }
1369
1370     atomic_fetch_sub(&dnode->ref_count, 1);
1371     __current->cwd = dnode;
1372
1373     unlock_dnode(dnode);
1374
1375 done:
1376     return errno;
1377 }
1378
1379 __DEFINE_LXSYSCALL1(int, chdir, const char*, path)
1380 {
1381     struct v_dnode* dnode;
1382     int errno = 0;
1383
1384     if ((errno = vfs_walk(__current->cwd, path, &dnode, NULL, 0))) {
1385         goto done;
1386     }
1387
1388     errno = __vfs_do_chdir(dnode);
1389
1390 done:
1391     return DO_STATUS(errno);
1392 }
1393
1394 __DEFINE_LXSYSCALL1(int, fchdir, int, fd)
1395 {
1396     struct v_fd* fd_s;
1397     int errno = 0;
1398
1399     if ((errno = __vfs_getfd(fd, &fd_s))) {
1400         goto done;
1401     }
1402
1403     errno = __vfs_do_chdir(fd_s->file->dnode);
1404
1405 done:
1406     return DO_STATUS(errno);
1407 }
1408
1409 __DEFINE_LXSYSCALL2(char*, getcwd, char*, buf, size_t, size)
1410 {
1411     int errno = 0;
1412     char* ret_ptr = 0;
1413     if (size < 2) {
1414         errno = ERANGE;
1415         goto done;
1416     }
1417
1418     size_t len = 0;
1419
1420     if (!__current->cwd) {
1421         *buf = PATH_DELIM;
1422         len = 1;
1423     } else {
1424         len = vfs_get_path(__current->cwd, buf, size, 0);
1425         if (len == size) {
1426             errno = ERANGE;
1427             goto done;
1428         }
1429     }
1430
1431     buf[len + 1] = '\0';
1432
1433     ret_ptr = buf;
1434
1435 done:
1436     __current->k_status = errno;
1437     return ret_ptr;
1438 }
1439
1440 int
1441 vfs_do_rename(struct v_dnode* current, struct v_dnode* target)
1442 {
1443     if (current->inode->id == target->inode->id) {
1444         // hard link
1445         return 0;
1446     }
1447
1448     if (current->ref_count > 1 || target->ref_count > 1) {
1449         return EBUSY;
1450     }
1451
1452     if (current->super_block != target->super_block) {
1453         return EXDEV;
1454     }
1455
1456     int errno = 0;
1457
1458     struct v_dnode* oldparent = current->parent;
1459     struct v_dnode* newparent = target->parent;
1460
1461     lock_dnode(current);
1462     lock_dnode(target);
1463     if (oldparent)
1464         lock_dnode(oldparent);
1465     if (newparent)
1466         lock_dnode(newparent);
1467
1468     if (!llist_empty(&target->children)) {
1469         errno = ENOTEMPTY;
1470         unlock_dnode(target);
1471         goto cleanup;
1472     }
1473
1474     if ((errno = current->inode->ops.rename(current->inode, current, target))) {
1475         unlock_dnode(target);
1476         goto cleanup;
1477     }
1478
1479     // re-position current
1480     hstrcpy(&current->name, &target->name);
1481     vfs_dcache_rehash(newparent, current);
1482
1483     // detach target
1484     vfs_dcache_remove(target);
1485
1486     unlock_dnode(target);
1487
1488 cleanup:
1489     unlock_dnode(current);
1490     if (oldparent)
1491         unlock_dnode(oldparent);
1492     if (newparent)
1493         unlock_dnode(newparent);
1494
1495     return errno;
1496 }
1497
1498 __DEFINE_LXSYSCALL2(int, rename, const char*, oldpath, const char*, newpath)
1499 {
1500     struct v_dnode *cur, *target_parent, *target;
1501     struct hstr name = HSTR(valloc(VFS_NAME_MAXLEN), 0);
1502     int errno = 0;
1503
1504     if ((errno = vfs_walk(__current->cwd, oldpath, &cur, NULL, 0))) {
1505         goto done;
1506     }
1507
1508     if ((errno = vfs_walk(
1509            __current->cwd, newpath, &target_parent, &name, VFS_WALK_PARENT))) {
1510         goto done;
1511     }
1512
1513     errno = vfs_walk(target_parent, name.value, &target, NULL, 0);
1514     if (errno == ENOENT) {
1515         target = vfs_d_alloc();
1516     } else if (errno) {
1517         goto done;
1518     }
1519
1520     if (!target) {
1521         errno = ENOMEM;
1522         goto done;
1523     }
1524
1525     hstrcpy(&target->name, &name);
1526
1527     if (!(errno = vfs_do_rename(cur, target))) {
1528         vfs_d_free(target);
1529     }
1530
1531 done:
1532     vfree(name.value);
1533     return DO_STATUS(errno);
1534 }
1535
1536 __DEFINE_LXSYSCALL3(int,
1537                     mount,
1538                     const char*,
1539                     source,
1540                     const char*,
1541                     target,
1542                     const char*,
1543                     fstype)
1544 {
1545     struct v_dnode *dev, *mnt;
1546     int errno = 0;
1547
1548     if ((errno = vfs_walk(__current->cwd, source, &dev, NULL, 0))) {
1549         goto done;
1550     }
1551
1552     if ((errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
1553         goto done;
1554     }
1555
1556     if (!(dev->inode->itype & VFS_IFVOLDEV)) {
1557         errno = ENOTDEV;
1558         goto done;
1559     }
1560
1561     if (mnt->ref_count > 1) {
1562         errno = EBUSY;
1563         goto done;
1564     }
1565
1566     // FIXME should not touch the underlying fs!
1567     struct device* device =
1568       (struct device*)((struct twifs_node*)dev->inode->data)->data;
1569
1570     errno = vfs_mount_at(fstype, device, mnt);
1571
1572 done:
1573     return DO_STATUS(errno);
1574 }
1575
1576 __DEFINE_LXSYSCALL1(int, unmount, const char*, target)
1577 {
1578     return vfs_unmount(target);
1579 }