feat: mount point flags check
[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     }
371
372     lru_use_one(dnode_lru, &dnode->lru);
373
374     return dnode;
375 }
376
377 void
378 vfs_d_free(struct v_dnode* dnode)
379 {
380     assert(dnode->ref_count == 1);
381
382     if (dnode->inode) {
383         assert(dnode->inode->link_count > 0);
384         dnode->inode->link_count--;
385     }
386
387     vfs_dcache_remove(dnode);
388     // Make sure the children de-referencing their parent.
389     // With lru presented, the eviction will be propagated over the entire
390     // detached subtree eventually
391     struct v_dnode *pos, *n;
392     llist_for_each(pos, n, &dnode->children, siblings)
393     {
394         vfs_dcache_remove(pos);
395     }
396
397     vfree(dnode->name.value);
398     cake_release(dnode_pile, dnode);
399 }
400
401 struct v_inode*
402 vfs_i_find(struct v_superblock* sb, uint32_t i_id)
403 {
404     struct hbucket* slot = &sb->i_cache[i_id & VFS_HASH_MASK];
405     struct v_inode *pos, *n;
406     hashtable_bucket_foreach(slot, pos, n, hash_list)
407     {
408         if (pos->id == i_id) {
409             lru_use_one(inode_lru, &pos->lru);
410             return pos;
411         }
412     }
413
414     return NULL;
415 }
416
417 void
418 vfs_i_addhash(struct v_inode* inode)
419 {
420     struct hbucket* slot = &inode->sb->i_cache[inode->id & VFS_HASH_MASK];
421
422     hlist_delete(&inode->hash_list);
423     hlist_add(&slot->head, &inode->hash_list);
424 }
425
426 struct v_inode*
427 vfs_i_alloc(struct v_superblock* sb)
428 {
429     assert(sb->ops.init_inode);
430
431     struct v_inode* inode;
432     if (!(inode = cake_grab(inode_pile))) {
433         lru_evict_half(inode_lru);
434         if (!(inode = cake_grab(inode_pile))) {
435             return NULL;
436         }
437     }
438
439     memset(inode, 0, sizeof(*inode));
440     mutex_init(&inode->lock);
441     llist_init_head(&inode->xattrs);
442
443     sb->ops.init_inode(sb, inode);
444
445     inode->sb = sb;
446     inode->ctime = clock_unixtime();
447     inode->atime = inode->ctime;
448     inode->mtime = inode->ctime;
449
450 done:
451     lru_use_one(inode_lru, &inode->lru);
452     return inode;
453 }
454
455 void
456 vfs_i_free(struct v_inode* inode)
457 {
458     if (inode->pg_cache) {
459         pcache_release(inode->pg_cache);
460         vfree(inode->pg_cache);
461     }
462     inode->ops->sync(inode);
463     hlist_delete(&inode->hash_list);
464     cake_release(inode_pile, inode);
465 }
466
467 /* ---- System call definition and support ---- */
468
469 #define FLOCATE_CREATE_EMPTY 1
470
471 int
472 vfs_getfd(int fd, struct v_fd** fd_s)
473 {
474     if (TEST_FD(fd) && (*fd_s = __current->fdtable->fds[fd])) {
475         return 0;
476     }
477     return EBADF;
478 }
479
480 int
481 __vfs_try_locate_file(const char* path,
482                       struct v_dnode** fdir,
483                       struct v_dnode** file,
484                       int options)
485 {
486     char name_str[VFS_NAME_MAXLEN];
487     struct hstr name = HSTR(name_str, 0);
488     int errno;
489     if ((errno = vfs_walk_proc(path, fdir, &name, VFS_WALK_PARENT))) {
490         return errno;
491     }
492
493     errno = vfs_walk(*fdir, name.value, file, NULL, 0);
494     if (errno != ENOENT || !(options & FLOCATE_CREATE_EMPTY)) {
495         return errno;
496     }
497
498     struct v_dnode* parent = *fdir;
499     struct v_dnode* file_new = vfs_d_alloc(parent, &name);
500
501     if (!file_new) {
502         return ENOMEM;
503     }
504
505     lock_dnode(parent);
506
507     if (!(errno = parent->inode->ops->create(parent->inode, file_new))) {
508         vfs_dcache_add(parent, file_new);
509         *file = file_new;
510     } else {
511         vfs_d_free(file_new);
512     }
513
514     unlock_dnode(parent);
515
516     return errno;
517 }
518
519 int
520 vfs_do_open(const char* path, int options)
521 {
522     int errno, fd;
523     struct v_dnode *dentry, *file;
524     struct v_file* ofile = 0;
525
526     errno = __vfs_try_locate_file(
527       path, &dentry, &file, (options & FO_CREATE) ? FLOCATE_CREATE_EMPTY : 0);
528
529     if (errno || (errno = vfs_open(file, &ofile))) {
530         return errno;
531     }
532
533     struct v_inode* o_inode = ofile->inode;
534
535     if (!errno && !(errno = vfs_alloc_fdslot(&fd))) {
536         struct v_fd* fd_s = vzalloc(sizeof(*fd_s));
537         ofile->f_pos = ofile->inode->fsize & -((options & FO_APPEND) != 0);
538         fd_s->file = ofile;
539         fd_s->flags = options;
540         __current->fdtable->fds[fd] = fd_s;
541         return fd;
542     }
543
544     return errno;
545 }
546
547 __DEFINE_LXSYSCALL2(int, open, const char*, path, int, options)
548 {
549     int errno = vfs_do_open(path, options);
550     return DO_STATUS_OR_RETURN(errno);
551 }
552
553 __DEFINE_LXSYSCALL1(int, close, int, fd)
554 {
555     struct v_fd* fd_s;
556     int errno = 0;
557     if ((errno = vfs_getfd(fd, &fd_s))) {
558         goto done_err;
559     }
560
561     if (fd_s->file->ref_count > 1) {
562         fd_s->file->ref_count--;
563     } else if ((errno = vfs_close(fd_s->file))) {
564         goto done_err;
565     }
566
567     vfree(fd_s);
568     __current->fdtable->fds[fd] = 0;
569
570 done_err:
571     return DO_STATUS(errno);
572 }
573
574 void
575 __vfs_readdir_callback(struct dir_context* dctx,
576                        const char* name,
577                        const int len,
578                        const int dtype)
579 {
580     struct dirent* dent = (struct dirent*)dctx->cb_data;
581     strncpy(dent->d_name, name, DIRENT_NAME_MAX_LEN);
582     dent->d_nlen = len;
583     dent->d_type = dtype;
584 }
585
586 __DEFINE_LXSYSCALL2(int, readdir, int, fd, struct dirent*, dent)
587 {
588     struct v_fd* fd_s;
589     int errno;
590
591     if ((errno = vfs_getfd(fd, &fd_s))) {
592         goto done;
593     }
594
595     struct v_inode* inode = fd_s->file->inode;
596
597     lock_inode(inode);
598
599     if (!(inode->itype & VFS_IFDIR)) {
600         errno = ENOTDIR;
601     } else {
602         struct dir_context dctx =
603           (struct dir_context){ .cb_data = dent,
604                                 .index = dent->d_offset,
605                                 .read_complete_callback =
606                                   __vfs_readdir_callback };
607         errno = 1;
608         if (dent->d_offset == 0) {
609             __vfs_readdir_callback(&dctx, vfs_dot.value, vfs_dot.len, 0);
610         } else if (dent->d_offset == 1) {
611             __vfs_readdir_callback(&dctx, vfs_ddot.value, vfs_ddot.len, 0);
612         } else {
613             dctx.index -= 2;
614             if ((errno = fd_s->file->ops->readdir(fd_s->file, &dctx)) != 1) {
615                 unlock_inode(inode);
616                 goto done;
617             }
618         }
619         dent->d_offset++;
620     }
621
622     unlock_inode(inode);
623
624 done:
625     return DO_STATUS_OR_RETURN(errno);
626 }
627
628 __DEFINE_LXSYSCALL3(int, read, int, fd, void*, buf, size_t, count)
629 {
630     int errno = 0;
631     struct v_fd* fd_s;
632     if ((errno = vfs_getfd(fd, &fd_s))) {
633         goto done;
634     }
635
636     struct v_file* file = fd_s->file;
637     if ((file->inode->itype & VFS_IFDIR)) {
638         errno = EISDIR;
639         goto done;
640     }
641
642     lock_inode(file->inode);
643
644     file->inode->atime = clock_unixtime();
645
646     __SYSCALL_INTERRUPTIBLE({
647         if ((file->inode->itype & VFS_IFSEQDEV) || (fd_s->flags & FO_DIRECT)) {
648             errno = file->ops->read(file->inode, buf, count, file->f_pos);
649         } else {
650             errno = pcache_read(file->inode, buf, count, file->f_pos);
651         }
652     })
653
654     if (errno > 0) {
655         file->f_pos += errno;
656         unlock_inode(file->inode);
657         return errno;
658     }
659
660     unlock_inode(file->inode);
661
662 done:
663     return DO_STATUS(errno);
664 }
665
666 __DEFINE_LXSYSCALL3(int, write, int, fd, void*, buf, size_t, count)
667 {
668     int errno = 0;
669     struct v_fd* fd_s;
670     if ((errno = vfs_getfd(fd, &fd_s))) {
671         goto done;
672     }
673
674     struct v_file* file = fd_s->file;
675
676     if ((errno = vfs_check_writable(file->dnode))) {
677         goto done;
678     }
679
680     if ((file->inode->itype & VFS_IFDIR)) {
681         errno = EISDIR;
682         goto done;
683     }
684
685     lock_inode(file->inode);
686
687     file->inode->mtime = clock_unixtime();
688
689     __SYSCALL_INTERRUPTIBLE({
690         if ((file->inode->itype & VFS_IFSEQDEV) || (fd_s->flags & FO_DIRECT)) {
691             errno = file->ops->write(file->inode, buf, count, file->f_pos);
692         } else {
693             errno = pcache_write(file->inode, buf, count, file->f_pos);
694         }
695     })
696
697     if (errno > 0) {
698         file->f_pos += errno;
699         unlock_inode(file->inode);
700         return errno;
701     }
702
703     unlock_inode(file->inode);
704
705 done:
706     return DO_STATUS(errno);
707 }
708
709 __DEFINE_LXSYSCALL3(int, lseek, int, fd, int, offset, int, options)
710 {
711     int errno = 0;
712     struct v_fd* fd_s;
713     if ((errno = vfs_getfd(fd, &fd_s))) {
714         goto done;
715     }
716
717     struct v_file* file = fd_s->file;
718
719     if (!file->ops->seek) {
720         errno = ENOTSUP;
721         goto done;
722     }
723
724     lock_inode(file->inode);
725
726     int overflow = 0;
727     int fpos = file->f_pos;
728     switch (options) {
729         case FSEEK_CUR:
730             overflow = __builtin_sadd_overflow((int)file->f_pos, offset, &fpos);
731             break;
732         case FSEEK_END:
733             overflow =
734               __builtin_sadd_overflow((int)file->inode->fsize, offset, &fpos);
735             break;
736         case FSEEK_SET:
737             fpos = offset;
738             break;
739     }
740     if (overflow) {
741         errno = EOVERFLOW;
742     } else if (!(errno = file->ops->seek(file->inode, fpos))) {
743         file->f_pos = fpos;
744     }
745
746     unlock_inode(file->inode);
747
748 done:
749     return DO_STATUS(errno);
750 }
751
752 int
753 vfs_get_path(struct v_dnode* dnode, char* buf, size_t size, int depth)
754 {
755     if (!dnode || dnode->parent == dnode) {
756         return 0;
757     }
758
759     if (depth > 64) {
760         return ENAMETOOLONG;
761     }
762
763     size_t len = vfs_get_path(dnode->parent, buf, size, depth + 1);
764
765     if (len >= size) {
766         return len;
767     }
768
769     buf[len++] = VFS_PATH_DELIM;
770
771     size_t cpy_size = MIN(dnode->name.len, size - len);
772     strncpy(buf + len, dnode->name.value, cpy_size);
773     len += cpy_size;
774
775     return len;
776 }
777
778 int
779 vfs_readlink(struct v_dnode* dnode, char* buf, size_t size)
780 {
781     const char* link;
782     struct v_inode* inode = dnode->inode;
783     if (inode->ops->read_symlink) {
784         lock_inode(inode);
785
786         int errno = inode->ops->read_symlink(inode, &link);
787         strncpy(buf, link, size);
788
789         unlock_inode(inode);
790         return errno;
791     }
792     return 0;
793 }
794
795 __DEFINE_LXSYSCALL3(int, realpathat, int, fd, char*, buf, size_t, size)
796 {
797     int errno;
798     struct v_fd* fd_s;
799     if ((errno = vfs_getfd(fd, &fd_s))) {
800         goto done;
801     }
802
803     struct v_dnode* dnode;
804     errno = vfs_get_path(fd_s->file->dnode, buf, size, 0);
805
806     if (errno >= 0) {
807         return errno;
808     }
809
810 done:
811     return DO_STATUS(errno);
812 }
813
814 __DEFINE_LXSYSCALL3(int, readlink, const char*, path, char*, buf, size_t, size)
815 {
816     int errno;
817     struct v_dnode* dnode;
818     if (!(errno = vfs_walk_proc(path, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
819         errno = vfs_readlink(dnode, buf, size);
820     }
821
822     if (errno >= 0) {
823         return errno;
824     }
825
826     return DO_STATUS(errno);
827 }
828
829 __DEFINE_LXSYSCALL4(int,
830                     readlinkat,
831                     int,
832                     dirfd,
833                     const char*,
834                     pathname,
835                     char*,
836                     buf,
837                     size_t,
838                     size)
839 {
840     int errno;
841     struct v_fd* fd_s;
842     if ((errno = vfs_getfd(dirfd, &fd_s))) {
843         goto done;
844     }
845
846     struct v_dnode* dnode;
847     if (!(errno = vfs_walk(
848             fd_s->file->dnode, pathname, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
849         errno = vfs_readlink(fd_s->file->dnode, buf, size);
850     }
851
852     if (errno >= 0) {
853         return errno;
854     }
855
856 done:
857     return DO_STATUS(errno);
858 }
859
860 /*
861     NOTE
862     When we perform operation that could affect the layout of
863     directory (i.e., rename, mkdir, rmdir). We must lock the parent dir
864     whenever possible. This will blocking any ongoing path walking to reach
865     it hence avoid any partial state.
866 */
867
868 __DEFINE_LXSYSCALL1(int, rmdir, const char*, pathname)
869 {
870     int errno;
871     struct v_dnode* dnode;
872     if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
873         return DO_STATUS(errno);
874     }
875
876     lock_dnode(dnode);
877
878     if ((errno = vfs_check_writable(dnode))) {
879         goto done;
880     }
881
882     if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
883         errno = EROFS;
884         goto done;
885     }
886
887     if (dnode->ref_count > 1 || dnode->inode->open_count) {
888         errno = EBUSY;
889         goto done;
890     }
891
892     if (!llist_empty(&dnode->children)) {
893         errno = ENOTEMPTY;
894         goto done;
895     }
896
897     struct v_dnode* parent = dnode->parent;
898
899     if (!parent) {
900         errno = EINVAL;
901         goto done;
902     }
903
904     lock_dnode(parent);
905     lock_inode(parent->inode);
906
907     if ((dnode->inode->itype & VFS_IFDIR)) {
908         errno = parent->inode->ops->rmdir(parent->inode, dnode);
909         if (!errno) {
910             vfs_dcache_remove(dnode);
911         }
912     } else {
913         errno = ENOTDIR;
914     }
915
916     unlock_inode(parent->inode);
917     unlock_dnode(parent);
918
919 done:
920     unlock_dnode(dnode);
921     return DO_STATUS(errno);
922 }
923
924 __DEFINE_LXSYSCALL1(int, mkdir, const char*, path)
925 {
926     int errno = 0;
927     struct v_dnode *parent, *dir;
928     char name_value[VFS_NAME_MAXLEN];
929     struct hstr name = HHSTR(name_value, 0, 0);
930
931     if ((errno = vfs_walk_proc(path, &parent, &name, VFS_WALK_PARENT))) {
932         goto done;
933     }
934
935     if ((errno = vfs_check_writable(parent))) {
936         goto done;
937     }
938
939     if (!(dir = vfs_d_alloc(parent, &name))) {
940         errno = ENOMEM;
941         goto done;
942     }
943
944     lock_dnode(parent);
945     lock_inode(parent->inode);
946
947     if ((parent->super_block->fs->types & FSTYPE_ROFS)) {
948         errno = ENOTSUP;
949     } else if (!parent->inode->ops->mkdir) {
950         errno = ENOTSUP;
951     } else if (!(parent->inode->itype & VFS_IFDIR)) {
952         errno = ENOTDIR;
953     } else if (!(errno = parent->inode->ops->mkdir(parent->inode, dir))) {
954         vfs_dcache_add(parent, dir);
955         goto cleanup;
956     }
957
958     vfs_d_free(dir);
959
960 cleanup:
961     unlock_inode(parent->inode);
962     unlock_dnode(parent);
963 done:
964     return DO_STATUS(errno);
965 }
966
967 int
968 __vfs_do_unlink(struct v_dnode* dnode)
969 {
970     int errno;
971     struct v_inode* inode = dnode->inode;
972
973     if (dnode->ref_count > 1) {
974         return EBUSY;
975     }
976
977     if ((errno = vfs_check_writable(dnode))) {
978         return errno;
979     }
980
981     lock_inode(inode);
982
983     if (inode->open_count) {
984         errno = EBUSY;
985     } else if (!(inode->itype & VFS_IFDIR)) {
986         // The underlying unlink implementation should handle
987         //  symlink case
988         errno = inode->ops->unlink(inode);
989         if (!errno) {
990             vfs_d_free(dnode);
991         }
992     } else {
993         errno = EISDIR;
994     }
995
996     unlock_inode(inode);
997
998     return errno;
999 }
1000
1001 __DEFINE_LXSYSCALL1(int, unlink, const char*, pathname)
1002 {
1003     int errno;
1004     struct v_dnode* dnode;
1005     if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
1006         goto done;
1007     }
1008
1009     errno = __vfs_do_unlink(dnode);
1010
1011 done:
1012     return DO_STATUS(errno);
1013 }
1014
1015 __DEFINE_LXSYSCALL2(int, unlinkat, int, fd, const char*, pathname)
1016 {
1017     int errno;
1018     struct v_fd* fd_s;
1019     if ((errno = vfs_getfd(fd, &fd_s))) {
1020         goto done;
1021     }
1022
1023     struct v_dnode* dnode;
1024     if (!(errno = vfs_walk(fd_s->file->dnode, pathname, &dnode, NULL, 0))) {
1025         errno = __vfs_do_unlink(dnode);
1026     }
1027
1028 done:
1029     return DO_STATUS(errno);
1030 }
1031
1032 __DEFINE_LXSYSCALL2(int, link, const char*, oldpath, const char*, newpath)
1033 {
1034     int errno;
1035     struct v_dnode *dentry, *to_link, *name_dentry, *name_file;
1036
1037     errno = __vfs_try_locate_file(oldpath, &dentry, &to_link, 0);
1038     if (!errno) {
1039         errno = __vfs_try_locate_file(
1040           newpath, &name_dentry, &name_file, FLOCATE_CREATE_EMPTY);
1041         if (!errno) {
1042             errno = EEXIST;
1043         } else if (name_file) {
1044             errno = vfs_link(to_link, name_file);
1045         }
1046     }
1047     return DO_STATUS(errno);
1048 }
1049
1050 __DEFINE_LXSYSCALL1(int, fsync, int, fildes)
1051 {
1052     int errno;
1053     struct v_fd* fd_s;
1054
1055     if (!(errno = vfs_getfd(fildes, &fd_s))) {
1056         errno = vfs_fsync(fd_s->file);
1057     }
1058
1059     return DO_STATUS(errno);
1060 }
1061
1062 int
1063 vfs_dup_fd(struct v_fd* old, struct v_fd** new)
1064 {
1065     int errno = 0;
1066     struct v_fd* copied = cake_grab(fd_pile);
1067
1068     memcpy(copied, old, sizeof(struct v_fd));
1069
1070     atomic_fetch_add(&old->file->ref_count, 1);
1071
1072     *new = copied;
1073
1074     return errno;
1075 }
1076
1077 int
1078 vfs_dup2(int oldfd, int newfd)
1079 {
1080     if (newfd == oldfd) {
1081         return newfd;
1082     }
1083
1084     int errno;
1085     struct v_fd *oldfd_s, *newfd_s;
1086     if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
1087         goto done;
1088     }
1089
1090     if (!TEST_FD(newfd)) {
1091         errno = EBADF;
1092         goto done;
1093     }
1094
1095     newfd_s = __current->fdtable->fds[newfd];
1096     if (newfd_s && (errno = vfs_close(newfd_s->file))) {
1097         goto done;
1098     }
1099
1100     if (!(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1101         __current->fdtable->fds[newfd] = newfd_s;
1102         return newfd;
1103     }
1104
1105 done:
1106     return DO_STATUS(errno);
1107 }
1108
1109 __DEFINE_LXSYSCALL2(int, dup2, int, oldfd, int, newfd)
1110 {
1111     return vfs_dup2(oldfd, newfd);
1112 }
1113
1114 __DEFINE_LXSYSCALL1(int, dup, int, oldfd)
1115 {
1116     int errno, newfd;
1117     struct v_fd *oldfd_s, *newfd_s;
1118     if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
1119         goto done;
1120     }
1121
1122     if (!(errno = vfs_alloc_fdslot(&newfd)) &&
1123         !(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1124         __current->fdtable->fds[newfd] = newfd_s;
1125         return newfd;
1126     }
1127
1128 done:
1129     return DO_STATUS(errno);
1130 }
1131
1132 __DEFINE_LXSYSCALL2(int,
1133                     symlink,
1134                     const char*,
1135                     pathname,
1136                     const char*,
1137                     link_target)
1138 {
1139     int errno;
1140     struct v_dnode* dnode;
1141     if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
1142         goto done;
1143     }
1144
1145     if (errno = vfs_check_writable(dnode)) {
1146         goto done;
1147     }
1148
1149     if (!dnode->inode->ops->set_symlink) {
1150         errno = ENOTSUP;
1151         goto done;
1152     }
1153
1154     lock_inode(dnode->inode);
1155
1156     errno = dnode->inode->ops->set_symlink(dnode->inode, link_target);
1157
1158     unlock_inode(dnode->inode);
1159
1160 done:
1161     return DO_STATUS(errno);
1162 }
1163
1164 int
1165 __vfs_do_chdir(struct v_dnode* dnode)
1166 {
1167     int errno = 0;
1168
1169     lock_dnode(dnode);
1170
1171     if (!(dnode->inode->itype & VFS_IFDIR)) {
1172         errno = ENOTDIR;
1173         goto done;
1174     }
1175
1176     if (__current->cwd) {
1177         atomic_fetch_sub(&__current->cwd->ref_count, 1);
1178         mnt_chillax(__current->cwd->mnt);
1179     }
1180
1181     atomic_fetch_add(&dnode->ref_count, 1);
1182     mnt_mkbusy(dnode->mnt);
1183     __current->cwd = dnode;
1184
1185     unlock_dnode(dnode);
1186
1187 done:
1188     return errno;
1189 }
1190
1191 __DEFINE_LXSYSCALL1(int, chdir, const char*, path)
1192 {
1193     struct v_dnode* dnode;
1194     int errno = 0;
1195
1196     if ((errno = vfs_walk_proc(path, &dnode, NULL, 0))) {
1197         goto done;
1198     }
1199
1200     errno = __vfs_do_chdir(dnode);
1201
1202 done:
1203     return DO_STATUS(errno);
1204 }
1205
1206 __DEFINE_LXSYSCALL1(int, fchdir, int, fd)
1207 {
1208     struct v_fd* fd_s;
1209     int errno = 0;
1210
1211     if ((errno = vfs_getfd(fd, &fd_s))) {
1212         goto done;
1213     }
1214
1215     errno = __vfs_do_chdir(fd_s->file->dnode);
1216
1217 done:
1218     return DO_STATUS(errno);
1219 }
1220
1221 __DEFINE_LXSYSCALL2(char*, getcwd, char*, buf, size_t, size)
1222 {
1223     int errno = 0;
1224     char* ret_ptr = 0;
1225     if (size < 2) {
1226         errno = ERANGE;
1227         goto done;
1228     }
1229
1230     size_t len = 0;
1231
1232     if (!__current->cwd) {
1233         *buf = VFS_PATH_DELIM;
1234         len = 1;
1235     } else {
1236         len = vfs_get_path(__current->cwd, buf, size, 0);
1237         if (len == size) {
1238             errno = ERANGE;
1239             goto done;
1240         }
1241     }
1242
1243     buf[len + 1] = '\0';
1244
1245     ret_ptr = buf;
1246
1247 done:
1248     __current->k_status = errno;
1249     return ret_ptr;
1250 }
1251
1252 int
1253 vfs_do_rename(struct v_dnode* current, struct v_dnode* target)
1254 {
1255     int errno = 0;
1256     if (current->inode->id == target->inode->id) {
1257         // hard link
1258         return 0;
1259     }
1260
1261     if (errno = vfs_check_writable(current)) {
1262         return errno;
1263     }
1264
1265     if (current->ref_count > 1 || target->ref_count > 1) {
1266         return EBUSY;
1267     }
1268
1269     if (current->super_block != target->super_block) {
1270         return EXDEV;
1271     }
1272
1273     struct v_dnode* oldparent = current->parent;
1274     struct v_dnode* newparent = target->parent;
1275
1276     lock_dnode(current);
1277     lock_dnode(target);
1278     if (oldparent)
1279         lock_dnode(oldparent);
1280     if (newparent)
1281         lock_dnode(newparent);
1282
1283     if (!llist_empty(&target->children)) {
1284         errno = ENOTEMPTY;
1285         unlock_dnode(target);
1286         goto cleanup;
1287     }
1288
1289     if ((errno =
1290            current->inode->ops->rename(current->inode, current, target))) {
1291         unlock_dnode(target);
1292         goto cleanup;
1293     }
1294
1295     // re-position current
1296     hstrcpy(&current->name, &target->name);
1297     vfs_dcache_rehash(newparent, current);
1298
1299     // detach target
1300     vfs_d_free(target);
1301
1302     unlock_dnode(target);
1303
1304 cleanup:
1305     unlock_dnode(current);
1306     if (oldparent)
1307         unlock_dnode(oldparent);
1308     if (newparent)
1309         unlock_dnode(newparent);
1310
1311     return errno;
1312 }
1313
1314 __DEFINE_LXSYSCALL2(int, rename, const char*, oldpath, const char*, newpath)
1315 {
1316     struct v_dnode *cur, *target_parent, *target;
1317     struct hstr name = HSTR(valloc(VFS_NAME_MAXLEN), 0);
1318     int errno = 0;
1319
1320     if ((errno = vfs_walk_proc(oldpath, &cur, NULL, 0))) {
1321         goto done;
1322     }
1323
1324     if ((errno = vfs_walk(
1325            __current->cwd, newpath, &target_parent, &name, VFS_WALK_PARENT))) {
1326         goto done;
1327     }
1328
1329     errno = vfs_walk(target_parent, name.value, &target, NULL, 0);
1330     if (errno == ENOENT) {
1331         target = vfs_d_alloc(target_parent, &name);
1332         vfs_dcache_add(target_parent, target);
1333     } else if (errno) {
1334         goto done;
1335     }
1336
1337     if (!target) {
1338         errno = ENOMEM;
1339         goto done;
1340     }
1341
1342     errno = vfs_do_rename(cur, target);
1343
1344 done:
1345     vfree(name.value);
1346     return DO_STATUS(errno);
1347 }