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