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