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