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