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