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