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