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