reduce the size of ppage by 8 bytes using signly linked list
[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 (CHECKED)
44 */
45
46 #include <klibc/string.h>
47 #include <lunaix/foptions.h>
48 #include <lunaix/fs.h>
49 #include <lunaix/mm/cake.h>
50 #include <lunaix/mm/valloc.h>
51 #include <lunaix/process.h>
52 #include <lunaix/spike.h>
53 #include <lunaix/syscall.h>
54 #include <lunaix/syscall_utils.h>
55
56 #include <lunaix/fs/twifs.h>
57
58 #include <usr/lunaix/dirent_defs.h>
59
60 static struct cake_pile* dnode_pile;
61 static struct cake_pile* inode_pile;
62 static struct cake_pile* file_pile;
63 static struct cake_pile* superblock_pile;
64 static struct cake_pile* fd_pile;
65
66 struct v_dnode* vfs_sysroot;
67
68 struct lru_zone *dnode_lru, *inode_lru;
69
70 struct hstr vfs_ddot = HSTR("..", 2);
71 struct hstr vfs_dot = HSTR(".", 1);
72 struct hstr vfs_empty = HSTR("", 0);
73
74 static int
75 __vfs_try_evict_dnode(struct lru_node* obj);
76
77 static int
78 __vfs_try_evict_inode(struct lru_node* obj);
79
80 void
81 vfs_init()
82 {
83     // 为他们专门创建一个蛋糕堆,而不使用valloc,这样我们可以最小化内碎片的产生
84     dnode_pile = cake_new_pile("dnode_cache", sizeof(struct v_dnode), 1, 0);
85     inode_pile = cake_new_pile("inode_cache", sizeof(struct v_inode), 1, 0);
86     file_pile = cake_new_pile("file_cache", sizeof(struct v_file), 1, 0);
87     fd_pile = cake_new_pile("fd_cache", sizeof(struct v_fd), 1, 0);
88     superblock_pile =
89       cake_new_pile("sb_cache", sizeof(struct v_superblock), 1, 0);
90
91     dnode_lru = lru_new_zone("vfs_dnode", __vfs_try_evict_dnode);
92     inode_lru = lru_new_zone("vfs_inode", __vfs_try_evict_inode);
93
94     hstr_rehash(&vfs_ddot, HSTR_FULL_HASH);
95     hstr_rehash(&vfs_dot, HSTR_FULL_HASH);
96
97     // 创建一个根dnode。
98     vfs_sysroot = vfs_d_alloc(NULL, &vfs_empty);
99     vfs_sysroot->parent = vfs_sysroot;
100     
101     vfs_ref_dnode(vfs_sysroot);
102 }
103
104 static inline struct hbucket*
105 __dcache_hash(struct v_dnode* parent, u32_t* hash)
106 {
107     struct hbucket* d_cache;
108     u32_t _hash;
109     
110     d_cache = parent->super_block->d_cache;
111     _hash = *hash;
112     _hash = _hash ^ (_hash >> VFS_HASHBITS);
113     _hash += (u32_t)__ptr(parent);
114
115     *hash = _hash;
116     return &d_cache[_hash & VFS_HASH_MASK];
117 }
118
119 static inline int
120 __sync_inode_nolock(struct v_inode* inode)
121 {
122     pcache_commit_all(inode);
123
124     int errno = ENOTSUP;
125     if (inode->ops->sync) {
126         errno = inode->ops->sync(inode);
127     }
128
129     return errno;
130 }
131
132 struct v_dnode*
133 vfs_dcache_lookup(struct v_dnode* parent, struct hstr* str)
134 {
135     if (!str->len || HSTR_EQ(str, &vfs_dot))
136         return parent;
137
138     if (HSTR_EQ(str, &vfs_ddot)) {
139         return parent->parent;
140     }
141
142     u32_t hash = str->hash;
143     struct hbucket* slot = __dcache_hash(parent, &hash);
144
145     struct v_dnode *pos, *n;
146     hashtable_bucket_foreach(slot, pos, n, hash_list)
147     {
148         if (pos->name.hash == hash && pos->parent == parent) {
149             return pos;
150         }
151     }
152     return NULL;
153 }
154
155 void
156 vfs_dcache_add(struct v_dnode* parent, struct v_dnode* dnode)
157 {
158     assert(parent);
159
160     dnode->ref_count = 1;
161     dnode->parent = parent;
162     llist_append(&parent->children, &dnode->siblings);
163
164     struct hbucket* bucket = __dcache_hash(parent, &dnode->name.hash);
165     hlist_add(&bucket->head, &dnode->hash_list);
166 }
167
168 void
169 vfs_dcache_remove(struct v_dnode* dnode)
170 {
171     assert(dnode);
172     assert(dnode->ref_count == 1);
173
174     llist_delete(&dnode->siblings);
175     llist_delete(&dnode->aka_list);
176     hlist_delete(&dnode->hash_list);
177
178     dnode->parent = NULL;
179     dnode->ref_count = 0;
180 }
181
182 void
183 vfs_dcache_rehash(struct v_dnode* new_parent, struct v_dnode* dnode)
184 {
185     assert(new_parent);
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_open(struct v_dnode* dnode, struct v_file** file)
194 {
195     if (!dnode->inode || !dnode->inode->ops->open) {
196         return ENOTSUP;
197     }
198
199     struct v_inode* inode = dnode->inode;
200
201     lock_inode(inode);
202
203     struct v_file* vfile = cake_grab(file_pile);
204     memset(vfile, 0, sizeof(*vfile));
205
206     vfile->dnode = dnode;
207     vfile->inode = inode;
208     vfile->ref_count = 1;
209     vfile->ops = inode->default_fops;
210
211     if (check_regfile_node(inode) && !inode->pg_cache) {
212         struct pcache* pcache = vzalloc(sizeof(struct pcache));
213         pcache_init(pcache);
214         pcache->master = inode;
215         inode->pg_cache = pcache;
216     }
217
218     int errno = inode->ops->open(inode, vfile);
219     if (errno) {
220         cake_release(file_pile, vfile);
221     } else {
222         vfs_ref_dnode(dnode);
223         inode->open_count++;
224
225         *file = vfile;
226     }
227
228     unlock_inode(inode);
229
230     return errno;
231 }
232
233 void
234 vfs_assign_inode(struct v_dnode* assign_to, struct v_inode* inode)
235 {
236     if (assign_to->inode) {
237         llist_delete(&assign_to->aka_list);
238         assign_to->inode->link_count--;
239     }
240
241     llist_append(&inode->aka_dnodes, &assign_to->aka_list);
242     assign_to->inode = inode;
243     inode->link_count++;
244 }
245
246 int
247 vfs_link(struct v_dnode* to_link, struct v_dnode* name)
248 {
249     int errno;
250
251     if ((errno = vfs_check_writable(to_link))) {
252         return errno;
253     }
254
255     lock_inode(to_link->inode);
256     if (to_link->super_block->root != name->super_block->root) {
257         errno = EXDEV;
258     } else if (!to_link->inode->ops->link) {
259         errno = ENOTSUP;
260     } else if (!(errno = to_link->inode->ops->link(to_link->inode, name))) {
261         vfs_assign_inode(name, to_link->inode);
262     }
263     unlock_inode(to_link->inode);
264
265     return errno;
266 }
267
268 int
269 vfs_pclose(struct v_file* file, pid_t pid)
270 {
271     struct v_inode* inode;
272     int errno = 0;
273
274     inode = file->inode;
275
276     /*
277      * Prevent dead lock.
278      * This happened when process is terminated while blocking on read.
279      * In that case, the process is still holding the inode lock and it
280          will never get released.
281      * The unlocking should also include ownership check.
282      *
283      * To see why, consider two process both open the same file both with
284      * fd=x.
285      *      Process A: busy on reading x
286      *      Process B: do nothing with x
287      * Assuming that, after a very short time, process B get terminated
288      * while process A is still busy in it's reading business. By this
289      * design, the inode lock of this file x is get released by B rather
290      * than A. And this will cause a probable race condition on A if other
291      * process is writing to this file later after B exit.
292     */
293
294     mutex_unlock_for(&inode->lock, pid);
295     
296     if (vfs_check_duped_file(file)) {
297         vfs_unref_file(file);
298         return 0;
299     }
300
301     if ((errno = file->ops->close(file))) {
302         goto done;
303     }
304
305     vfs_unref_dnode(file->dnode);
306     cake_release(file_pile, file);
307
308     /*
309         if the current inode is not being locked by other 
310         threads that does not share same open context,
311         then we can try to do sync opportunistically
312     */
313     if (mutex_on_hold(&inode->lock)) {
314         goto done;
315     }
316     
317     lock_inode(inode);
318
319     pcache_commit_all(inode);
320     inode->open_count--;
321
322     if (!inode->open_count) {
323         __sync_inode_nolock(inode);
324     }
325
326     unlock_inode(inode);
327
328 done:
329     return errno;
330 }
331
332 int
333 vfs_close(struct v_file* file)
334 {
335     return vfs_pclose(file, __current->pid);
336 }
337
338 void
339 vfs_free_fd(struct v_fd* fd)
340 {
341     cake_release(fd_pile, fd);
342 }
343
344 int
345 vfs_isync(struct v_inode* inode)
346 {
347     lock_inode(inode);
348
349     int errno = __sync_inode_nolock(inode);
350
351     unlock_inode(inode);
352
353     return errno;
354 }
355
356 int
357 vfs_fsync(struct v_file* file)
358 {
359     int errno;
360     if ((errno = vfs_check_writable(file->dnode))) {
361         return errno;
362     }
363
364     return vfs_isync(file->inode);
365 }
366
367 int
368 vfs_alloc_fdslot(int* fd)
369 {
370     for (size_t i = 0; i < VFS_MAX_FD; i++) {
371         if (!__current->fdtable->fds[i]) {
372             *fd = i;
373             return 0;
374         }
375     }
376     return EMFILE;
377 }
378
379 struct v_superblock*
380 vfs_sb_alloc()
381 {
382     struct v_superblock* sb = cake_grab(superblock_pile);
383     memset(sb, 0, sizeof(*sb));
384     llist_init_head(&sb->sb_list);
385     
386     sb->i_cache = vzalloc(VFS_HASHTABLE_SIZE * sizeof(struct hbucket));
387     sb->d_cache = vzalloc(VFS_HASHTABLE_SIZE * sizeof(struct hbucket));
388
389     sb->ref_count = 1;
390     return sb;
391 }
392
393 void
394 vfs_sb_ref(struct v_superblock* sb)
395 {
396     sb->ref_count++;
397 }
398
399 void
400 vfs_sb_unref(struct v_superblock* sb)
401 {
402     assert(sb->ref_count);
403
404     sb->ref_count--;
405     if (likely(sb->ref_count)) {
406         return;
407     }
408
409     if (sb->ops.release) {
410         sb->ops.release(sb);
411     }
412
413     vfree(sb->i_cache);
414     vfree(sb->d_cache);
415     
416     cake_release(superblock_pile, sb);
417 }
418
419 static int
420 __vfs_try_evict_dnode(struct lru_node* obj)
421 {
422     struct v_dnode* dnode = container_of(obj, struct v_dnode, lru);
423
424     if (!dnode->ref_count) {
425         vfs_d_free(dnode);
426         return 1;
427     }
428     return 0;
429 }
430
431 static int
432 __vfs_try_evict_inode(struct lru_node* obj)
433 {
434     struct v_inode* inode = container_of(obj, struct v_inode, lru);
435
436     if (!inode->link_count && !inode->open_count) {
437         vfs_i_free(inode);
438         return 1;
439     }
440     return 0;
441 }
442
443 struct v_dnode*
444 vfs_d_alloc(struct v_dnode* parent, struct hstr* name)
445 {
446     struct v_dnode* dnode = cake_grab(dnode_pile);
447     if (!dnode) {
448         lru_evict_half(dnode_lru);
449
450         if (!(dnode = cake_grab(dnode_pile))) {
451             return NULL;
452         }
453     }
454
455     memset(dnode, 0, sizeof(*dnode));
456     llist_init_head(&dnode->children);
457     llist_init_head(&dnode->siblings);
458     llist_init_head(&dnode->aka_list);
459     mutex_init(&dnode->lock);
460
461     dnode->name = HHSTR(vzalloc(VFS_NAME_MAXLEN), 0, 0);
462
463     hstrcpy(&dnode->name, name);
464
465     if (parent) {
466         vfs_d_assign_sb(dnode, parent->super_block);
467         dnode->mnt = parent->mnt;
468     }
469
470     lru_use_one(dnode_lru, &dnode->lru);
471
472     return dnode;
473 }
474
475 void
476 vfs_d_free(struct v_dnode* dnode)
477 {
478     assert(dnode->ref_count == 1);
479
480     if (dnode->inode) {
481         assert(dnode->inode->link_count > 0);
482         dnode->inode->link_count--;
483     }
484
485     vfs_dcache_remove(dnode);
486     // Make sure the children de-referencing their parent.
487     // With lru presented, the eviction will be propagated over the entire
488     // detached subtree eventually
489     struct v_dnode *pos, *n;
490     llist_for_each(pos, n, &dnode->children, siblings)
491     {
492         vfs_dcache_remove(pos);
493     }
494
495     if (dnode->destruct) {
496         dnode->destruct(dnode);
497     }
498
499     vfs_sb_unref(dnode->super_block);
500     vfree((void*)dnode->name.value);
501     cake_release(dnode_pile, dnode);
502 }
503
504 struct v_inode*
505 vfs_i_find(struct v_superblock* sb, u32_t i_id)
506 {
507     struct hbucket* slot = &sb->i_cache[i_id & VFS_HASH_MASK];
508     struct v_inode *pos, *n;
509     hashtable_bucket_foreach(slot, pos, n, hash_list)
510     {
511         if (pos->id == i_id) {
512             lru_use_one(inode_lru, &pos->lru);
513             return pos;
514         }
515     }
516
517     return NULL;
518 }
519
520 void
521 vfs_i_addhash(struct v_inode* inode)
522 {
523     struct hbucket* slot = &inode->sb->i_cache[inode->id & VFS_HASH_MASK];
524
525     hlist_delete(&inode->hash_list);
526     hlist_add(&slot->head, &inode->hash_list);
527 }
528
529 struct v_inode*
530 vfs_i_alloc(struct v_superblock* sb)
531 {
532     assert(sb->ops.init_inode);
533
534     struct v_inode* inode;
535     if (!(inode = cake_grab(inode_pile))) {
536         lru_evict_half(inode_lru);
537         if (!(inode = cake_grab(inode_pile))) {
538             return NULL;
539         }
540     }
541
542     memset(inode, 0, sizeof(*inode));
543     mutex_init(&inode->lock);
544     llist_init_head(&inode->xattrs);
545     llist_init_head(&inode->aka_dnodes);
546
547     sb->ops.init_inode(sb, inode);
548
549     inode->ctime = clock_unixtime();
550     inode->atime = inode->ctime;
551     inode->mtime = inode->ctime;
552
553     vfs_i_assign_sb(inode, sb);
554     lru_use_one(inode_lru, &inode->lru);
555     return inode;
556 }
557
558 void
559 vfs_i_free(struct v_inode* inode)
560 {
561     assert(inode->link_count == 0);
562     
563     if (inode->pg_cache) {
564         pcache_release(inode->pg_cache);
565         vfree(inode->pg_cache);
566     }
567     // we don't need to sync inode.
568     // If an inode can be free, then it must be properly closed.
569     // Hence it must be synced already!
570     if (inode->destruct) {
571         inode->destruct(inode);
572     }
573
574     vfs_sb_unref(inode->sb);
575     hlist_delete(&inode->hash_list);
576     cake_release(inode_pile, inode);
577 }
578
579 /* ---- System call definition and support ---- */
580
581 // make a new name when not exists
582 #define FLOC_MAYBE_MKNAME 1
583
584 // name must be non-exist and made.
585 #define FLOC_MKNAME 2
586
587 // no follow symlink
588 #define FLOC_NOFOLLOW 4
589
590 int
591 vfs_getfd(int fd, struct v_fd** fd_s)
592 {
593     if (TEST_FD(fd) && (*fd_s = __current->fdtable->fds[fd])) {
594         return 0;
595     }
596     return EBADF;
597 }
598
599 static int
600 __vfs_mknod(struct v_inode* parent, struct v_dnode* dnode, 
601             unsigned int itype, dev_t* dev)
602 {
603     int errno;
604
605     errno = parent->ops->create(parent, dnode, itype);
606     if (errno) {
607         return errno;
608     }
609
610     return 0;
611 }
612
613 struct file_locator {
614     struct v_dnode* dir;
615     struct v_dnode* file;
616     bool fresh;
617 };
618
619 /**
620  * @brief unlock the file locator (floc) if possible.
621  *        If the file to be located if not exists, and
622  *        any FLOC_*MKNAME flag is set, then the parent
623  *        dnode will be locked until the file has been properly
624  *        finalised by subsequent logic.
625  * 
626  * @param floc 
627  */
628 static inline void
629 __floc_try_unlock(struct file_locator* floc)
630 {
631     if (floc->fresh) {
632         assert(floc->dir);
633         unlock_dnode(floc->dir);
634     }
635 }
636
637 static int
638 __vfs_try_locate_file(const char* path,
639                       struct file_locator* floc,
640                       int options)
641 {
642     char name_str[VFS_NAME_MAXLEN];
643     struct v_dnode *fdir, *file;
644     struct hstr name = HSTR(name_str, 0);
645     int errno, woption = 0;
646
647     if ((options & FLOC_NOFOLLOW)) {
648         woption |= VFS_WALK_NOFOLLOW;
649         options &= ~FLOC_NOFOLLOW;
650     }
651
652     floc->fresh = false;
653     name_str[0] = 0;
654     errno = vfs_walk_proc(path, &fdir, &name, woption | VFS_WALK_PARENT);
655     if (errno) {
656         return errno;
657     }
658
659     errno = vfs_walk(fdir, name.value, &file, NULL, woption);
660
661     if (errno && errno != ENOENT) {
662         goto done;
663     }
664     
665     if (!errno) {
666         if ((options & FLOC_MKNAME)) {
667             errno = EEXIST;
668         }
669         goto done;
670     }
671
672     // errno == ENOENT
673     if (!options) {
674         goto done;
675     }
676
677     errno = vfs_check_writable(fdir);
678     if (errno) {
679         goto done;
680     }
681
682     floc->fresh = true;
683
684     file = vfs_d_alloc(fdir, &name);
685
686     if (!file) {
687         return ENOMEM;
688     }
689
690     lock_dnode(fdir);
691
692     vfs_dcache_add(fdir, file);
693
694 done:
695     floc->dir   = fdir;
696     floc->file  = file;
697
698     return errno;
699 }
700
701 int
702 vfs_do_open(const char* path, int options)
703 {
704     int errno, fd, loptions = 0;
705     struct v_dnode *dentry, *file;
706     struct v_file* ofile = NULL;
707     struct file_locator floc;
708     struct v_inode* inode;
709
710     if ((options & FO_CREATE)) {
711         loptions |= FLOC_MAYBE_MKNAME;
712     } else if ((options & FO_NOFOLLOW)) {
713         loptions |= FLOC_NOFOLLOW;
714     }
715
716     errno = __vfs_try_locate_file(path, &floc, loptions);
717
718     if (errno || (errno = vfs_alloc_fdslot(&fd))) {
719         return errno;
720     }
721
722     file   = floc.file;
723     dentry = floc.dir;
724
725     if (floc.fresh) {
726         errno = __vfs_mknod(dentry->inode, file, VFS_IFFILE, NULL);
727         if (errno) {
728             vfs_d_free(file);
729             __floc_try_unlock(&floc);
730             return errno;
731         }
732
733         __floc_try_unlock(&floc);
734     }
735
736
737     if ((errno = vfs_open(file, &ofile))) {
738         return errno;
739     }
740
741     inode = ofile->inode;
742     lock_inode(inode);
743
744     struct v_fd* fd_s = cake_grab(fd_pile);
745     memset(fd_s, 0, sizeof(*fd_s));
746
747     if ((options & O_TRUNC)) {
748         file->inode->fsize = 0;   
749     }
750
751     if (vfs_get_dtype(inode->itype) == DT_DIR) {
752         ofile->f_pos = 0;
753     }
754     
755     fd_s->file = ofile;
756     fd_s->flags = options;
757     __current->fdtable->fds[fd] = fd_s;
758
759     unlock_inode(inode);
760     
761     return fd;
762 }
763
764 __DEFINE_LXSYSCALL2(int, open, const char*, path, int, options)
765 {
766     int errno = vfs_do_open(path, options);
767     return DO_STATUS_OR_RETURN(errno);
768 }
769
770 __DEFINE_LXSYSCALL1(int, close, int, fd)
771 {
772     struct v_fd* fd_s;
773     int errno = 0;
774     if ((errno = vfs_getfd(fd, &fd_s))) {
775         goto done_err;
776     }
777
778     if ((errno = vfs_close(fd_s->file))) {
779         goto done_err;
780     }
781
782     cake_release(fd_pile, fd_s);
783     __current->fdtable->fds[fd] = 0;
784
785 done_err:
786     return DO_STATUS(errno);
787 }
788
789 void
790 __vfs_readdir_callback(struct dir_context* dctx,
791                        const char* name,
792                        const int len,
793                        const int dtype)
794 {
795     struct lx_dirent* dent = (struct lx_dirent*)dctx->cb_data;
796     int len_ = MIN(len, DIRENT_NAME_MAX_LEN - 1);
797
798     strncpy(dent->d_name, name, len_);
799     dent->d_name[len_] = 0;
800
801     dent->d_nlen = len_;
802     dent->d_type = dtype;
803 }
804
805 __DEFINE_LXSYSCALL2(int, sys_readdir, int, fd, struct lx_dirent*, dent)
806 {
807     struct v_fd* fd_s;
808     int errno;
809
810     if ((errno = vfs_getfd(fd, &fd_s))) {
811         goto done;
812     }
813
814     struct v_inode* inode = fd_s->file->inode;
815
816     lock_inode(inode);
817
818     if (!check_directory_node(inode)) {
819         errno = ENOTDIR;
820         goto unlock;
821     }
822
823     struct dir_context dctx = (struct dir_context) {
824         .cb_data = dent,
825         .read_complete_callback = __vfs_readdir_callback
826     };
827
828     if ((errno = fd_s->file->ops->readdir(fd_s->file, &dctx)) != 1) {
829         goto unlock;
830     }
831     dent->d_offset++;
832     fd_s->file->f_pos++;
833
834 unlock:
835     unlock_inode(inode);
836
837 done:
838     return DO_STATUS_OR_RETURN(errno);
839 }
840
841 static inline bool
842 check_pcache_eligibility(struct v_fd* fd_s)
843 {
844     struct v_inode* inode;
845
846     inode = fd_s->file->inode;
847     return !check_seqdev_node(inode) \
848         && !fsm_check_pseudo_fs(inode->sb->fs) \
849         && !(fd_s->flags & FO_DIRECT);
850 }
851
852 __DEFINE_LXSYSCALL3(int, read, int, fd, void*, buf, size_t, count)
853 {
854     int errno = 0;
855     struct v_fd* fd_s;
856     if ((errno = vfs_getfd(fd, &fd_s))) {
857         goto done;
858     }
859
860     struct v_file* file = fd_s->file;
861     if (check_directory_node(file->inode)) {
862         errno = EISDIR;
863         goto done;
864     }
865
866     lock_inode(file->inode);
867
868     file->inode->atime = clock_unixtime();
869
870     if (!check_pcache_eligibility(fd_s)) {
871         errno = file->ops->read(file->inode, buf, count, file->f_pos);
872     } else {
873         errno = pcache_read(file->inode, buf, count, file->f_pos);
874     }
875
876     if (errno > 0) {
877         file->f_pos += errno;
878         unlock_inode(file->inode);
879         return errno;
880     }
881
882     unlock_inode(file->inode);
883
884 done:
885     return DO_STATUS(errno);
886 }
887
888 __DEFINE_LXSYSCALL3(int, write, int, fd, void*, buf, size_t, count)
889 {
890     int errno = 0;
891     struct v_fd* fd_s;
892     if ((errno = vfs_getfd(fd, &fd_s))) {
893         goto done;
894     }
895
896     struct v_inode* inode;
897     struct v_file* file = fd_s->file;
898
899     if ((errno = vfs_check_writable(file->dnode))) {
900         goto done;
901     }
902
903     if (check_directory_node(file->inode)) {
904         errno = EISDIR;
905         goto done;
906     }
907
908     inode = file->inode;
909     lock_inode(inode);
910
911     inode->mtime = clock_unixtime();
912     if ((fd_s->flags & O_APPEND)) {
913         file->f_pos = inode->fsize;
914     }
915
916     if (!check_pcache_eligibility(fd_s)) {
917         errno = file->ops->write(inode, buf, count, file->f_pos);
918     } else {
919         errno = pcache_write(inode, buf, count, file->f_pos);
920     }
921
922     if (errno > 0) {
923         file->f_pos += errno;
924         inode->fsize = MAX(inode->fsize, file->f_pos);
925
926         unlock_inode(inode);
927         return errno;
928     }
929
930     unlock_inode(inode);
931
932 done:
933     return DO_STATUS(errno);
934 }
935
936 __DEFINE_LXSYSCALL3(int, lseek, int, fd, int, offset, int, options)
937 {
938     int errno = 0;
939     struct v_fd* fd_s;
940     if ((errno = vfs_getfd(fd, &fd_s))) {
941         goto done;
942     }
943
944     struct v_file* file = fd_s->file;
945     struct v_inode* inode = file->inode;
946
947     if (!file->ops->seek) {
948         errno = ENOTSUP;
949         goto done;
950     }
951
952     lock_inode(inode);
953
954     int overflow = 0;
955     int fpos = file->f_pos;
956
957     if (vfs_get_dtype(inode->itype) == DT_DIR) {
958         options = (options != FSEEK_END) ? options : FSEEK_SET;
959     }
960     
961     switch (options) {
962         case FSEEK_CUR:
963             overflow = sadd_of((int)file->f_pos, offset, &fpos);
964             break;
965         case FSEEK_END:
966             overflow = sadd_of((int)inode->fsize, offset, &fpos);
967             break;
968         case FSEEK_SET:
969             fpos = offset;
970             break;
971     }
972
973     if (overflow) {
974         errno = EOVERFLOW;
975     }
976     else {
977         errno = file->ops->seek(file, fpos);
978     }
979
980     unlock_inode(inode);
981
982 done:
983     return DO_STATUS(errno);
984 }
985
986 int
987 vfs_get_path(struct v_dnode* dnode, char* buf, size_t size, int depth)
988 {
989     if (!dnode) {
990         return 0;
991     }
992
993     if (depth > 64) {
994         return ENAMETOOLONG;
995     }
996
997     size_t len = 0;
998
999     if (dnode->parent != dnode) {
1000         len = vfs_get_path(dnode->parent, buf, size, depth + 1);
1001     }
1002
1003     if (len >= size) {
1004         return len;
1005     }
1006
1007     if (!len || buf[len - 1] != VFS_PATH_DELIM) {
1008         buf[len++] = VFS_PATH_DELIM;
1009     }
1010
1011     size_t cpy_size = MIN(dnode->name.len, size - len);
1012     strncpy(buf + len, dnode->name.value, cpy_size);
1013     buf[len + cpy_size] = 0;
1014
1015     len += cpy_size;
1016
1017     return len;
1018 }
1019
1020 int
1021 vfs_readlink(struct v_dnode* dnode, char* buf, size_t size)
1022 {
1023     const char* link;
1024     struct v_inode* inode = dnode->inode;
1025
1026     if (!check_symlink_node(inode)) {
1027         return EINVAL;
1028     }
1029
1030     if (!inode->ops->read_symlink) {
1031         return ENOTSUP;
1032     }
1033
1034     lock_inode(inode);
1035
1036     int errno = inode->ops->read_symlink(inode, &link);
1037     if (errno >= 0) {
1038         strncpy(buf, link, MIN(size, (size_t)errno));
1039     }
1040
1041     unlock_inode(inode);
1042     return errno;
1043 }
1044
1045 int
1046 vfs_get_dtype(int itype)
1047 {
1048     int dtype = DT_FILE;
1049     if (check_itype(itype, VFS_IFSYMLINK)) {
1050         dtype |= DT_SYMLINK;
1051     }
1052     
1053     if (check_itype(itype, VFS_IFDIR)) {
1054         dtype |= DT_DIR;
1055         return dtype;
1056     }
1057
1058     // TODO other types
1059     
1060     return dtype;
1061 }
1062
1063 __DEFINE_LXSYSCALL3(int, realpathat, int, fd, char*, buf, size_t, size)
1064 {
1065     int errno;
1066     struct v_fd* fd_s;
1067     if ((errno = vfs_getfd(fd, &fd_s))) {
1068         goto done;
1069     }
1070
1071     struct v_dnode* dnode;
1072     errno = vfs_get_path(fd_s->file->dnode, buf, size, 0);
1073
1074     if (errno >= 0) {
1075         return errno;
1076     }
1077
1078 done:
1079     return DO_STATUS(errno);
1080 }
1081
1082 __DEFINE_LXSYSCALL3(int, readlink, const char*, path, char*, buf, size_t, size)
1083 {
1084     int errno;
1085     struct v_dnode* dnode;
1086     if (!(errno = vfs_walk_proc(path, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
1087         errno = vfs_readlink(dnode, buf, size);
1088     }
1089
1090     if (errno >= 0) {
1091         return errno;
1092     }
1093
1094     return DO_STATUS(errno);
1095 }
1096
1097 __DEFINE_LXSYSCALL4(
1098   int, readlinkat, int, dirfd, const char*, pathname, char*, buf, size_t, size)
1099 {
1100     int errno;
1101     struct v_fd* fd_s;
1102     if ((errno = vfs_getfd(dirfd, &fd_s))) {
1103         goto done;
1104     }
1105
1106     pathname = pathname ? pathname : "";
1107
1108     struct v_dnode* dnode;
1109     if (!(errno = vfs_walk(
1110             fd_s->file->dnode, pathname, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
1111         errno = vfs_readlink(fd_s->file->dnode, buf, size);
1112     }
1113
1114     if (errno >= 0) {
1115         return errno;
1116     }
1117
1118 done:
1119     return DO_STATUS(errno);
1120 }
1121
1122 /*
1123     NOTE
1124     When we perform operation that could affect the layout of
1125     directory (i.e., rename, mkdir, rmdir). We must lock the parent dir
1126     whenever possible. This will blocking any ongoing path walking to reach
1127     it hence avoid any partial state.
1128 */
1129
1130 __DEFINE_LXSYSCALL1(int, rmdir, const char*, pathname)
1131 {
1132     int errno;
1133     struct v_dnode* dnode;
1134     if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
1135         return DO_STATUS(errno);
1136     }
1137
1138     lock_dnode(dnode);
1139
1140     if ((errno = vfs_check_writable(dnode))) {
1141         goto done;
1142     }
1143
1144     if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
1145         errno = EROFS;
1146         goto done;
1147     }
1148
1149     if (dnode->ref_count > 1 || dnode->inode->open_count) {
1150         errno = EBUSY;
1151         goto done;
1152     }
1153
1154     if (!llist_empty(&dnode->children)) {
1155         errno = ENOTEMPTY;
1156         goto done;
1157     }
1158
1159     struct v_dnode* parent = dnode->parent;
1160
1161     if (!parent) {
1162         errno = EINVAL;
1163         goto done;
1164     }
1165
1166     lock_dnode(parent);
1167     lock_inode(parent->inode);
1168
1169     if (check_directory_node(dnode->inode)) {
1170         errno = parent->inode->ops->rmdir(parent->inode, dnode);
1171         if (!errno) {
1172             vfs_dcache_remove(dnode);
1173         }
1174     } else {
1175         errno = ENOTDIR;
1176     }
1177
1178     unlock_inode(parent->inode);
1179     unlock_dnode(parent);
1180
1181 done:
1182     unlock_dnode(dnode);
1183     return DO_STATUS(errno);
1184 }
1185
1186 __DEFINE_LXSYSCALL1(int, mkdir, const char*, path)
1187 {
1188     int errno = 0;
1189     struct v_dnode *parent, *dir;
1190     char name_value[VFS_NAME_MAXLEN];
1191     struct hstr name = HHSTR(name_value, 0, 0);
1192
1193     if ((errno = vfs_walk_proc(path, &parent, &name, VFS_WALK_PARENT))) {
1194         goto done;
1195     }
1196
1197     if (!(errno = vfs_walk(parent, name_value, &dir, NULL, 0))) {
1198         errno = EEXIST;
1199         goto done;
1200     }
1201
1202     if ((errno = vfs_check_writable(parent))) {
1203         goto done;
1204     }
1205
1206     if (!(dir = vfs_d_alloc(parent, &name))) {
1207         errno = ENOMEM;
1208         goto done;
1209     }
1210
1211     struct v_inode* inode = parent->inode;
1212
1213     lock_dnode(parent);
1214     lock_inode(inode);
1215
1216     if ((parent->super_block->fs->types & FSTYPE_ROFS)) {
1217         errno = ENOTSUP;
1218     } else if (!inode->ops->mkdir) {
1219         errno = ENOTSUP;
1220     } else if (!check_directory_node(inode)) {
1221         errno = ENOTDIR;
1222     } else if (!(errno = inode->ops->mkdir(inode, dir))) {
1223         vfs_dcache_add(parent, dir);
1224         goto cleanup;
1225     }
1226
1227     vfs_d_free(dir);
1228
1229 cleanup:
1230     unlock_inode(inode);
1231     unlock_dnode(parent);
1232 done:
1233     return DO_STATUS(errno);
1234 }
1235
1236 int
1237 __vfs_do_unlink(struct v_dnode* dnode)
1238 {
1239     int errno;
1240     struct v_inode* inode = dnode->inode;
1241
1242     if (dnode->ref_count > 1) {
1243         return EBUSY;
1244     }
1245
1246     if ((errno = vfs_check_writable(dnode))) {
1247         return errno;
1248     }
1249
1250     lock_inode(inode);
1251
1252     if (inode->open_count) {
1253         errno = EBUSY;
1254     } else if (!check_directory_node(inode)) {
1255         errno = inode->ops->unlink(inode, dnode);
1256         if (!errno) {
1257             vfs_d_free(dnode);
1258         }
1259     } else {
1260         errno = EISDIR;
1261     }
1262
1263     unlock_inode(inode);
1264
1265     return errno;
1266 }
1267
1268 __DEFINE_LXSYSCALL1(int, unlink, const char*, pathname)
1269 {
1270     int errno;
1271     struct v_dnode* dnode;
1272     if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
1273         goto done;
1274     }
1275
1276     errno = __vfs_do_unlink(dnode);
1277
1278 done:
1279     return DO_STATUS(errno);
1280 }
1281
1282 __DEFINE_LXSYSCALL2(int, unlinkat, int, fd, const char*, pathname)
1283 {
1284     int errno;
1285     struct v_fd* fd_s;
1286     if ((errno = vfs_getfd(fd, &fd_s))) {
1287         goto done;
1288     }
1289
1290     struct v_dnode* dnode;
1291     if (!(errno = vfs_walk(fd_s->file->dnode, pathname, &dnode, NULL, 0))) {
1292         errno = __vfs_do_unlink(dnode);
1293     }
1294
1295 done:
1296     return DO_STATUS(errno);
1297 }
1298
1299 __DEFINE_LXSYSCALL2(int, link, const char*, oldpath, const char*, newpath)
1300 {
1301     int errno;
1302     struct file_locator floc;
1303     struct v_dnode *to_link, *name_file;
1304
1305     errno = __vfs_try_locate_file(oldpath, &floc, 0);
1306     if (errno) {
1307         goto done;
1308     }
1309
1310     __floc_try_unlock(&floc);
1311
1312     to_link = floc.file;
1313     errno = __vfs_try_locate_file(newpath, &floc, FLOC_MKNAME);
1314     if (!errno) {
1315         goto done;       
1316     }
1317
1318     name_file = floc.file;
1319     errno = vfs_link(to_link, name_file);
1320     if (errno) {
1321         vfs_d_free(name_file);
1322     }
1323
1324 done:
1325     __floc_try_unlock(&floc);
1326     return DO_STATUS(errno);
1327 }
1328
1329 __DEFINE_LXSYSCALL1(int, fsync, int, fildes)
1330 {
1331     int errno;
1332     struct v_fd* fd_s;
1333
1334     if (!(errno = vfs_getfd(fildes, &fd_s))) {
1335         errno = vfs_fsync(fd_s->file);
1336     }
1337
1338     return DO_STATUS(errno);
1339 }
1340
1341 int
1342 vfs_dup_fd(struct v_fd* old, struct v_fd** new)
1343 {
1344     int errno = 0;
1345     struct v_fd* copied = cake_grab(fd_pile);
1346
1347     memcpy(copied, old, sizeof(struct v_fd));
1348
1349     vfs_ref_file(old->file);
1350
1351     *new = copied;
1352
1353     return errno;
1354 }
1355
1356 int
1357 vfs_dup2(int oldfd, int newfd)
1358 {
1359     if (newfd == oldfd) {
1360         return newfd;
1361     }
1362
1363     int errno;
1364     struct v_fd *oldfd_s, *newfd_s;
1365     if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
1366         goto done;
1367     }
1368
1369     if (!TEST_FD(newfd)) {
1370         errno = EBADF;
1371         goto done;
1372     }
1373
1374     newfd_s = __current->fdtable->fds[newfd];
1375     if (newfd_s && (errno = vfs_close(newfd_s->file))) {
1376         goto done;
1377     }
1378
1379     if (!(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1380         __current->fdtable->fds[newfd] = newfd_s;
1381         return newfd;
1382     }
1383
1384 done:
1385     return DO_STATUS(errno);
1386 }
1387
1388 __DEFINE_LXSYSCALL2(int, dup2, int, oldfd, int, newfd)
1389 {
1390     return vfs_dup2(oldfd, newfd);
1391 }
1392
1393 __DEFINE_LXSYSCALL1(int, dup, int, oldfd)
1394 {
1395     int errno, newfd;
1396     struct v_fd *oldfd_s, *newfd_s;
1397     if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
1398         goto done;
1399     }
1400
1401     if (!(errno = vfs_alloc_fdslot(&newfd)) &&
1402         !(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1403         __current->fdtable->fds[newfd] = newfd_s;
1404         return newfd;
1405     }
1406
1407 done:
1408     return DO_STATUS(errno);
1409 }
1410
1411 __DEFINE_LXSYSCALL2(
1412   int, symlink, const char*, pathname, const char*, link_target)
1413 {
1414     int errno;
1415     struct file_locator floc;
1416     struct v_dnode *file;
1417     struct v_inode *f_ino;
1418     
1419     errno = __vfs_try_locate_file(pathname, &floc, FLOC_MKNAME);
1420     if (errno) {
1421         goto done;
1422     }
1423
1424     file = floc.file;
1425     errno = __vfs_mknod(floc.dir->inode, file, VFS_IFSYMLINK, NULL);
1426     if (errno) {
1427         vfs_d_free(file);
1428         goto done;
1429     }
1430
1431     f_ino = file->inode;
1432
1433     assert(f_ino);
1434
1435     errno = vfs_check_writable(file);
1436     if (errno) {
1437         goto done;
1438     }
1439
1440     if (!f_ino->ops->set_symlink) {
1441         errno = ENOTSUP;
1442         goto done;
1443     }
1444
1445     lock_inode(f_ino);
1446
1447     errno = f_ino->ops->set_symlink(f_ino, link_target);
1448
1449     unlock_inode(f_ino);
1450
1451 done:
1452     __floc_try_unlock(&floc);
1453     return DO_STATUS(errno);
1454 }
1455
1456 int
1457 vfs_do_chdir(struct proc_info* proc, struct v_dnode* dnode)
1458 {
1459     int errno = 0;
1460
1461     lock_dnode(dnode);
1462
1463     if (!check_directory_node(dnode->inode)) {
1464         errno = ENOTDIR;
1465         goto done;
1466     }
1467
1468     if (proc->cwd) {
1469         vfs_unref_dnode(proc->cwd);
1470     }
1471
1472     vfs_ref_dnode(dnode);
1473     proc->cwd = dnode;
1474
1475 done:
1476     unlock_dnode(dnode);
1477
1478     return errno;
1479 }
1480
1481 __DEFINE_LXSYSCALL1(int, chdir, const char*, path)
1482 {
1483     struct v_dnode* dnode;
1484     int errno = 0;
1485
1486     if ((errno = vfs_walk_proc(path, &dnode, NULL, 0))) {
1487         goto done;
1488     }
1489
1490     errno = vfs_do_chdir((struct proc_info*)__current, dnode);
1491
1492 done:
1493     return DO_STATUS(errno);
1494 }
1495
1496 __DEFINE_LXSYSCALL1(int, fchdir, int, fd)
1497 {
1498     struct v_fd* fd_s;
1499     int errno = 0;
1500
1501     if ((errno = vfs_getfd(fd, &fd_s))) {
1502         goto done;
1503     }
1504
1505     errno = vfs_do_chdir((struct proc_info*)__current, fd_s->file->dnode);
1506
1507 done:
1508     return DO_STATUS(errno);
1509 }
1510
1511 __DEFINE_LXSYSCALL2(char*, getcwd, char*, buf, size_t, size)
1512 {
1513     int errno = 0;
1514     char* ret_ptr = 0;
1515     if (size < 2) {
1516         errno = ERANGE;
1517         goto done;
1518     }
1519
1520     size_t len = 0;
1521
1522     if (!__current->cwd) {
1523         *buf = VFS_PATH_DELIM;
1524         len = 1;
1525     } else {
1526         len = vfs_get_path(__current->cwd, buf, size, 0);
1527         if (len == size) {
1528             errno = ERANGE;
1529             goto done;
1530         }
1531     }
1532
1533     buf[len] = '\0';
1534
1535     ret_ptr = buf;
1536
1537 done:
1538     syscall_result(errno);
1539     return ret_ptr;
1540 }
1541
1542 int
1543 vfs_do_rename(struct v_dnode* current, struct v_dnode* target)
1544 {
1545     int errno = 0;
1546     if (current->inode->id == target->inode->id) {
1547         // hard link
1548         return 0;
1549     }
1550
1551     if ((errno = vfs_check_writable(current))) {
1552         return errno;
1553     }
1554
1555     if (current->ref_count > 1 || target->ref_count > 1) {
1556         return EBUSY;
1557     }
1558
1559     if (current->super_block != target->super_block) {
1560         return EXDEV;
1561     }
1562
1563     struct v_dnode* oldparent = current->parent;
1564     struct v_dnode* newparent = target->parent;
1565
1566     lock_dnode(current);
1567     lock_dnode(target);
1568     if (oldparent)
1569         lock_dnode(oldparent);
1570     if (newparent)
1571         lock_dnode(newparent);
1572
1573     if (!llist_empty(&target->children)) {
1574         errno = ENOTEMPTY;
1575         unlock_dnode(target);
1576         goto cleanup;
1577     }
1578
1579     if ((errno =
1580            current->inode->ops->rename(current->inode, current, target))) {
1581         unlock_dnode(target);
1582         goto cleanup;
1583     }
1584
1585     // re-position current
1586     hstrcpy(&current->name, &target->name);
1587     vfs_dcache_rehash(newparent, current);
1588
1589     // detach target
1590     vfs_d_free(target);
1591
1592     unlock_dnode(target);
1593
1594 cleanup:
1595     unlock_dnode(current);
1596     if (oldparent)
1597         unlock_dnode(oldparent);
1598     if (newparent)
1599         unlock_dnode(newparent);
1600
1601     return errno;
1602 }
1603
1604 __DEFINE_LXSYSCALL2(int, rename, const char*, oldpath, const char*, newpath)
1605 {
1606     struct v_dnode *cur, *target_parent, *target;
1607     struct hstr name = HSTR(valloc(VFS_NAME_MAXLEN), 0);
1608     int errno = 0;
1609
1610     if ((errno = vfs_walk_proc(oldpath, &cur, NULL, 0))) {
1611         goto done;
1612     }
1613
1614     if ((errno = vfs_walk(
1615            __current->cwd, newpath, &target_parent, &name, VFS_WALK_PARENT))) {
1616         goto done;
1617     }
1618
1619     errno = vfs_walk(target_parent, name.value, &target, NULL, 0);
1620     if (errno == ENOENT) {
1621         target = vfs_d_alloc(target_parent, &name);
1622         vfs_dcache_add(target_parent, target);
1623     } else if (errno) {
1624         goto done;
1625     }
1626
1627     if (!target) {
1628         errno = ENOMEM;
1629         goto done;
1630     }
1631
1632     errno = vfs_do_rename(cur, target);
1633
1634 done:
1635     vfree((void*)name.value);
1636     return DO_STATUS(errno);
1637 }
1638
1639 __DEFINE_LXSYSCALL2(int, fstat, int, fd, struct file_stat*, stat)
1640 {
1641     int errno = 0;
1642     struct v_fd* fds;
1643
1644     if ((errno = vfs_getfd(fd, &fds))) {
1645         goto done;
1646     }
1647
1648     struct v_inode* vino = fds->file->inode;
1649     struct device* fdev = vino->sb->dev;
1650
1651     *stat = (struct file_stat){.st_ino = vino->id,
1652                                .st_blocks = vino->lb_usage,
1653                                .st_size = vino->fsize,
1654                                .mode = vino->itype,
1655                                .st_ioblksize = PAGE_SIZE,
1656                                .st_blksize = vino->sb->blksize};
1657
1658     if (check_device_node(vino)) {
1659         struct device* rdev = resolve_device(vino->data);
1660         if (!rdev) {
1661             errno = EINVAL;
1662             goto done;
1663         }
1664
1665         stat->st_rdev = (dev_t){.meta = rdev->ident.fn_grp,
1666                                 .unique = rdev->ident.unique,
1667                                 .index = dev_uid(rdev) };
1668     }
1669
1670     if (fdev) {
1671         stat->st_dev = (dev_t){.meta = fdev->ident.fn_grp,
1672                                .unique = fdev->ident.unique,
1673                                .index = dev_uid(fdev) };
1674     }
1675
1676 done:
1677     return DO_STATUS(errno);
1678 }