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