3 * @author Lunaixsky (zelong56@gmail.com)
4 * @brief Lunaix virtual file system - an abstraction layer for all file system.
8 * @copyright Copyright (c) 2022
12 // Welcome to The Mountain O'Shit! :)
15 TODO vfs & device todos checklist
17 It is overseen by Twilight Sparkle ;)
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
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)
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>
56 #include <lunaix/fs/twifs.h>
58 #include <usr/lunaix/dirent_defs.h>
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;
66 struct v_dnode* vfs_sysroot;
67 static struct hbucket* dnode_cache;
69 struct lru_zone *dnode_lru, *inode_lru;
71 struct hstr vfs_ddot = HSTR("..", 2);
72 struct hstr vfs_dot = HSTR(".", 1);
73 struct hstr vfs_empty = HSTR("", 0);
79 vfs_sb_free(struct v_superblock* sb);
82 __vfs_try_evict_dnode(struct lru_node* obj);
85 __vfs_try_evict_inode(struct lru_node* obj);
90 // 为他们专门创建一个蛋糕堆,而不使用valloc,这样我们可以最小化内碎片的产生
91 dnode_pile = cake_new_pile("dnode_cache", sizeof(struct v_dnode), 1, 0);
92 inode_pile = cake_new_pile("inode_cache", sizeof(struct v_inode), 1, 0);
93 file_pile = cake_new_pile("file_cache", sizeof(struct v_file), 1, 0);
94 fd_pile = cake_new_pile("fd_cache", sizeof(struct v_fd), 1, 0);
96 cake_new_pile("sb_cache", sizeof(struct v_superblock), 1, 0);
98 dnode_cache = vzalloc(VFS_HASHTABLE_SIZE * sizeof(struct hbucket));
100 dnode_lru = lru_new_zone(__vfs_try_evict_dnode);
101 inode_lru = lru_new_zone(__vfs_try_evict_inode);
103 hstr_rehash(&vfs_ddot, HSTR_FULL_HASH);
104 hstr_rehash(&vfs_dot, HSTR_FULL_HASH);
107 vfs_sysroot = vfs_d_alloc(NULL, &vfs_empty);
108 vfs_sysroot->parent = vfs_sysroot;
109 atomic_fetch_add(&vfs_sysroot->ref_count, 1);
112 static inline struct hbucket*
113 __dcache_hash(struct v_dnode* parent, u32_t* hash)
117 _hash = _hash ^ (_hash >> VFS_HASHBITS);
118 // 与parent的指针值做加法,来减小碰撞的可能性。
119 _hash += (u32_t)parent;
121 return &dnode_cache[_hash & VFS_HASH_MASK];
125 vfs_dcache_lookup(struct v_dnode* parent, struct hstr* str)
127 if (!str->len || HSTR_EQ(str, &vfs_dot))
130 if (HSTR_EQ(str, &vfs_ddot)) {
131 return parent->parent;
134 u32_t hash = str->hash;
135 struct hbucket* slot = __dcache_hash(parent, &hash);
137 struct v_dnode *pos, *n;
138 hashtable_bucket_foreach(slot, pos, n, hash_list)
140 if (pos->name.hash == hash) {
148 vfs_dcache_add(struct v_dnode* parent, struct v_dnode* dnode)
152 atomic_fetch_add(&dnode->ref_count, 1);
153 dnode->parent = parent;
154 llist_append(&parent->children, &dnode->siblings);
156 struct hbucket* bucket = __dcache_hash(parent, &dnode->name.hash);
157 hlist_add(&bucket->head, &dnode->hash_list);
161 vfs_dcache_remove(struct v_dnode* dnode)
164 assert(dnode->ref_count == 1);
166 llist_delete(&dnode->siblings);
167 llist_delete(&dnode->aka_list);
168 hlist_delete(&dnode->hash_list);
170 dnode->parent = NULL;
171 atomic_fetch_sub(&dnode->ref_count, 1);
175 vfs_dcache_rehash(struct v_dnode* new_parent, struct v_dnode* dnode)
179 hstr_rehash(&dnode->name, HSTR_FULL_HASH);
180 vfs_dcache_remove(dnode);
181 vfs_dcache_add(new_parent, dnode);
185 vfs_open(struct v_dnode* dnode, struct v_file** file)
187 if (!dnode->inode || !dnode->inode->ops->open) {
191 struct v_inode* inode = dnode->inode;
195 struct v_file* vfile = cake_grab(file_pile);
196 memset(vfile, 0, sizeof(*vfile));
198 vfile->dnode = dnode;
199 vfile->inode = inode;
200 vfile->ref_count = ATOMIC_VAR_INIT(1);
201 vfile->ops = inode->default_fops;
203 if ((inode->itype & F_MFILE) && !inode->pg_cache) {
204 struct pcache* pcache = vzalloc(sizeof(struct pcache));
206 pcache->master = inode;
207 inode->pg_cache = pcache;
210 int errno = inode->ops->open(inode, vfile);
212 cake_release(file_pile, vfile);
214 atomic_fetch_add(&dnode->ref_count, 1);
216 mnt_mkbusy(dnode->mnt);
227 vfs_assign_inode(struct v_dnode* assign_to, struct v_inode* inode)
229 if (assign_to->inode) {
230 llist_delete(&assign_to->aka_list);
231 assign_to->inode->link_count--;
233 llist_append(&inode->aka_dnodes, &assign_to->aka_list);
234 assign_to->inode = inode;
239 vfs_link(struct v_dnode* to_link, struct v_dnode* name)
243 if ((errno = vfs_check_writable(to_link))) {
247 lock_inode(to_link->inode);
248 if (to_link->super_block->root != name->super_block->root) {
250 } else if (!to_link->inode->ops->link) {
252 } else if (!(errno = to_link->inode->ops->link(to_link->inode, name))) {
253 vfs_assign_inode(name, to_link->inode);
255 unlock_inode(to_link->inode);
261 vfs_pclose(struct v_file* file, pid_t pid)
264 if (file->ref_count > 1) {
265 atomic_fetch_sub(&file->ref_count, 1);
266 } else if (!(errno = file->ops->close(file))) {
267 atomic_fetch_sub(&file->dnode->ref_count, 1);
268 file->inode->open_count--;
272 * This happened when process is terminated while blocking on read.
273 * In that case, the process is still holding the inode lock and it
274 will never get released.
275 * The unlocking should also include ownership check.
277 * To see why, consider two process both open the same file both with
279 * Process A: busy on reading x
280 * Process B: do nothing with x
281 * Assuming that, after a very short time, process B get terminated
282 * while process A is still busy in it's reading business. By this
283 * design, the inode lock of this file x is get released by B rather
284 * than A. And this will cause a probable race condition on A if other
285 * process is writing to this file later after B exit.
287 if (mutex_on_hold(&file->inode->lock)) {
288 mutex_unlock_for(&file->inode->lock, pid);
290 mnt_chillax(file->dnode->mnt);
292 pcache_commit_all(file->inode);
293 cake_release(file_pile, file);
299 vfs_close(struct v_file* file)
301 return vfs_pclose(file, __current->pid);
305 vfs_free_fd(struct v_fd* fd)
307 cake_release(fd_pile, fd);
311 vfs_fsync(struct v_file* file)
314 if ((errno = vfs_check_writable(file->dnode))) {
318 lock_inode(file->inode);
320 pcache_commit_all(file->inode);
323 if (file->ops->sync) {
324 errno = file->ops->sync(file);
327 unlock_inode(file->inode);
333 vfs_alloc_fdslot(int* fd)
335 for (size_t i = 0; i < VFS_MAX_FD; i++) {
336 if (!__current->fdtable->fds[i]) {
347 struct v_superblock* sb = cake_grab(superblock_pile);
348 memset(sb, 0, sizeof(*sb));
349 llist_init_head(&sb->sb_list);
350 sb->i_cache = vzalloc(VFS_HASHTABLE_SIZE * sizeof(struct hbucket));
355 vfs_sb_free(struct v_superblock* sb)
358 cake_release(superblock_pile, sb);
362 __vfs_try_evict_dnode(struct lru_node* obj)
364 struct v_dnode* dnode = container_of(obj, struct v_dnode, lru);
366 if (!dnode->ref_count) {
374 __vfs_try_evict_inode(struct lru_node* obj)
376 struct v_inode* inode = container_of(obj, struct v_inode, lru);
378 if (!inode->link_count && !inode->open_count) {
386 vfs_d_alloc(struct v_dnode* parent, struct hstr* name)
388 struct v_dnode* dnode = cake_grab(dnode_pile);
390 lru_evict_half(dnode_lru);
392 if (!(dnode = cake_grab(dnode_pile))) {
397 memset(dnode, 0, sizeof(*dnode));
398 llist_init_head(&dnode->children);
399 llist_init_head(&dnode->siblings);
400 llist_init_head(&dnode->aka_list);
401 mutex_init(&dnode->lock);
403 dnode->ref_count = ATOMIC_VAR_INIT(0);
404 dnode->name = HHSTR(vzalloc(VFS_NAME_MAXLEN), 0, 0);
406 hstrcpy(&dnode->name, name);
409 dnode->super_block = parent->super_block;
410 dnode->mnt = parent->mnt;
413 lru_use_one(dnode_lru, &dnode->lru);
419 vfs_d_free(struct v_dnode* dnode)
421 assert(dnode->ref_count == 1);
424 assert(dnode->inode->link_count > 0);
425 dnode->inode->link_count--;
428 vfs_dcache_remove(dnode);
429 // Make sure the children de-referencing their parent.
430 // With lru presented, the eviction will be propagated over the entire
431 // detached subtree eventually
432 struct v_dnode *pos, *n;
433 llist_for_each(pos, n, &dnode->children, siblings)
435 vfs_dcache_remove(pos);
438 vfree((void*)dnode->name.value);
439 cake_release(dnode_pile, dnode);
443 vfs_i_find(struct v_superblock* sb, u32_t i_id)
445 struct hbucket* slot = &sb->i_cache[i_id & VFS_HASH_MASK];
446 struct v_inode *pos, *n;
447 hashtable_bucket_foreach(slot, pos, n, hash_list)
449 if (pos->id == i_id) {
450 lru_use_one(inode_lru, &pos->lru);
459 vfs_i_addhash(struct v_inode* inode)
461 struct hbucket* slot = &inode->sb->i_cache[inode->id & VFS_HASH_MASK];
463 hlist_delete(&inode->hash_list);
464 hlist_add(&slot->head, &inode->hash_list);
468 vfs_i_alloc(struct v_superblock* sb)
470 assert(sb->ops.init_inode);
472 struct v_inode* inode;
473 if (!(inode = cake_grab(inode_pile))) {
474 lru_evict_half(inode_lru);
475 if (!(inode = cake_grab(inode_pile))) {
480 memset(inode, 0, sizeof(*inode));
481 mutex_init(&inode->lock);
482 llist_init_head(&inode->xattrs);
483 llist_init_head(&inode->aka_dnodes);
485 sb->ops.init_inode(sb, inode);
488 inode->ctime = clock_unixtime();
489 inode->atime = inode->ctime;
490 inode->mtime = inode->ctime;
492 lru_use_one(inode_lru, &inode->lru);
497 vfs_i_free(struct v_inode* inode)
499 if (inode->pg_cache) {
500 pcache_release(inode->pg_cache);
501 vfree(inode->pg_cache);
503 // we don't need to sync inode.
504 // If an inode can be free, then it must be properly closed.
505 // Hence it must be synced already!
506 if (inode->destruct) {
507 inode->destruct(inode);
509 hlist_delete(&inode->hash_list);
510 cake_release(inode_pile, inode);
513 /* ---- System call definition and support ---- */
515 #define FLOCATE_CREATE_EMPTY 1
516 #define FLOCATE_CREATE_ONLY 2
517 #define FLOCATE_NOFOLLOW 4
520 vfs_getfd(int fd, struct v_fd** fd_s)
522 if (TEST_FD(fd) && (*fd_s = __current->fdtable->fds[fd])) {
529 __vfs_try_locate_file(const char* path,
530 struct v_dnode** fdir,
531 struct v_dnode** file,
534 char name_str[VFS_NAME_MAXLEN];
535 struct hstr name = HSTR(name_str, 0);
536 int errno, woption = 0;
538 if ((options & FLOCATE_NOFOLLOW)) {
539 woption |= VFS_WALK_NOFOLLOW;
543 if ((errno = vfs_walk_proc(path, fdir, &name, woption | VFS_WALK_PARENT))) {
547 errno = vfs_walk(*fdir, name.value, file, NULL, woption);
549 if (errno != ENOENT && (options & FLOCATE_CREATE_ONLY)) {
553 if (errno != ENOENT ||
554 !(options & (FLOCATE_CREATE_EMPTY | FLOCATE_CREATE_ONLY))) {
558 struct v_dnode* parent = *fdir;
559 struct v_dnode* file_new = vfs_d_alloc(parent, &name);
567 if (!(errno = parent->inode->ops->create(parent->inode, file_new))) {
568 vfs_dcache_add(parent, file_new);
571 vfs_d_free(file_new);
574 unlock_dnode(parent);
580 vfs_do_open(const char* path, int options)
582 int errno, fd, loptions = 0;
583 struct v_dnode *dentry, *file;
584 struct v_file* ofile = NULL;
586 if ((options & FO_CREATE)) {
587 loptions |= FLOCATE_CREATE_EMPTY;
588 } else if ((options & FO_NOFOLLOW)) {
589 loptions |= FLOCATE_NOFOLLOW;
592 errno = __vfs_try_locate_file(path, &dentry, &file, loptions);
594 if (!errno && !(errno = vfs_alloc_fdslot(&fd))) {
596 if (errno || (errno = vfs_open(file, &ofile))) {
600 struct v_fd* fd_s = cake_grab(fd_pile);
601 memset(fd_s, 0, sizeof(*fd_s));
603 ofile->f_pos = ofile->inode->fsize & -((options & FO_APPEND) != 0);
605 fd_s->flags = options;
606 __current->fdtable->fds[fd] = fd_s;
613 __DEFINE_LXSYSCALL2(int, open, const char*, path, int, options)
615 int errno = vfs_do_open(path, options);
616 return DO_STATUS_OR_RETURN(errno);
619 __DEFINE_LXSYSCALL1(int, close, int, fd)
623 if ((errno = vfs_getfd(fd, &fd_s))) {
627 if ((errno = vfs_close(fd_s->file))) {
631 cake_release(fd_pile, fd_s);
632 __current->fdtable->fds[fd] = 0;
635 return DO_STATUS(errno);
639 __vfs_readdir_callback(struct dir_context* dctx,
644 struct lx_dirent* dent = (struct lx_dirent*)dctx->cb_data;
645 strncpy(dent->d_name, name, DIRENT_NAME_MAX_LEN);
647 dent->d_type = dtype;
650 __DEFINE_LXSYSCALL2(int, sys_readdir, int, fd, struct lx_dirent*, dent)
655 if ((errno = vfs_getfd(fd, &fd_s))) {
659 struct v_inode* inode = fd_s->file->inode;
663 if ((inode->itype & F_FILE)) {
666 struct dir_context dctx = (struct dir_context){
668 .index = dent->d_offset,
669 .read_complete_callback = __vfs_readdir_callback};
671 if (dent->d_offset == 0) {
672 __vfs_readdir_callback(&dctx, vfs_dot.value, vfs_dot.len, DT_DIR);
673 } else if (dent->d_offset == 1) {
674 __vfs_readdir_callback(&dctx, vfs_ddot.value, vfs_ddot.len, DT_DIR);
677 if ((errno = fd_s->file->ops->readdir(fd_s->file, &dctx)) != 1) {
688 return DO_STATUS_OR_RETURN(errno);
691 __DEFINE_LXSYSCALL3(int, read, int, fd, void*, buf, size_t, count)
695 if ((errno = vfs_getfd(fd, &fd_s))) {
699 struct v_file* file = fd_s->file;
700 if (!(file->inode->itype & F_FILE)) {
705 lock_inode(file->inode);
707 file->inode->atime = clock_unixtime();
709 if ((file->inode->itype & VFS_IFSEQDEV) || (fd_s->flags & FO_DIRECT)) {
710 errno = file->ops->read(file->inode, buf, count, file->f_pos);
712 errno = pcache_read(file->inode, buf, count, file->f_pos);
716 file->f_pos += errno;
717 unlock_inode(file->inode);
721 unlock_inode(file->inode);
724 return DO_STATUS(errno);
727 __DEFINE_LXSYSCALL3(int, write, int, fd, void*, buf, size_t, count)
731 if ((errno = vfs_getfd(fd, &fd_s))) {
735 struct v_file* file = fd_s->file;
737 if ((errno = vfs_check_writable(file->dnode))) {
741 if (!(file->inode->itype & F_FILE)) {
746 lock_inode(file->inode);
748 file->inode->mtime = clock_unixtime();
750 if ((file->inode->itype & VFS_IFSEQDEV) || (fd_s->flags & FO_DIRECT)) {
751 errno = file->ops->write(file->inode, buf, count, file->f_pos);
753 errno = pcache_write(file->inode, buf, count, file->f_pos);
757 file->f_pos += errno;
758 unlock_inode(file->inode);
762 unlock_inode(file->inode);
765 return DO_STATUS(errno);
768 __DEFINE_LXSYSCALL3(int, lseek, int, fd, int, offset, int, options)
772 if ((errno = vfs_getfd(fd, &fd_s))) {
776 struct v_file* file = fd_s->file;
778 if (!file->ops->seek) {
783 lock_inode(file->inode);
786 int fpos = file->f_pos;
789 overflow = sadd_overflow((int)file->f_pos, offset, &fpos);
792 overflow = sadd_overflow((int)file->inode->fsize, offset, &fpos);
800 } else if (!(errno = file->ops->seek(file->inode, fpos))) {
804 unlock_inode(file->inode);
807 return DO_STATUS(errno);
811 vfs_get_path(struct v_dnode* dnode, char* buf, size_t size, int depth)
823 if (dnode->parent != dnode) {
824 len = vfs_get_path(dnode->parent, buf, size, depth + 1);
831 if (!len || buf[len - 1] != VFS_PATH_DELIM) {
832 buf[len++] = VFS_PATH_DELIM;
835 size_t cpy_size = MIN(dnode->name.len, size - len);
836 strncpy(buf + len, dnode->name.value, cpy_size);
843 vfs_readlink(struct v_dnode* dnode, char* buf, size_t size)
846 struct v_inode* inode = dnode->inode;
847 if (inode->ops->read_symlink) {
850 int errno = inode->ops->read_symlink(inode, &link);
851 strncpy(buf, link, size);
860 vfs_get_dtype(int itype)
862 if ((itype & VFS_IFSYMLINK) == VFS_IFSYMLINK) {
864 } else if (!(itype & VFS_IFFILE)) {
871 __DEFINE_LXSYSCALL3(int, realpathat, int, fd, char*, buf, size_t, size)
875 if ((errno = vfs_getfd(fd, &fd_s))) {
879 struct v_dnode* dnode;
880 errno = vfs_get_path(fd_s->file->dnode, buf, size, 0);
887 return DO_STATUS(errno);
890 __DEFINE_LXSYSCALL3(int, readlink, const char*, path, char*, buf, size_t, size)
893 struct v_dnode* dnode;
894 if (!(errno = vfs_walk_proc(path, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
895 errno = vfs_readlink(dnode, buf, size);
902 return DO_STATUS(errno);
906 int, readlinkat, int, dirfd, const char*, pathname, char*, buf, size_t, size)
910 if ((errno = vfs_getfd(dirfd, &fd_s))) {
914 pathname = pathname ? pathname : "";
916 struct v_dnode* dnode;
917 if (!(errno = vfs_walk(
918 fd_s->file->dnode, pathname, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
919 errno = vfs_readlink(fd_s->file->dnode, buf, size);
927 return DO_STATUS(errno);
932 When we perform operation that could affect the layout of
933 directory (i.e., rename, mkdir, rmdir). We must lock the parent dir
934 whenever possible. This will blocking any ongoing path walking to reach
935 it hence avoid any partial state.
938 __DEFINE_LXSYSCALL1(int, rmdir, const char*, pathname)
941 struct v_dnode* dnode;
942 if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
943 return DO_STATUS(errno);
948 if ((errno = vfs_check_writable(dnode))) {
952 if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
957 if (dnode->ref_count > 1 || dnode->inode->open_count) {
962 if (!llist_empty(&dnode->children)) {
967 struct v_dnode* parent = dnode->parent;
975 lock_inode(parent->inode);
977 if (!(dnode->inode->itype & F_MFILE)) {
978 errno = parent->inode->ops->rmdir(parent->inode, dnode);
980 vfs_dcache_remove(dnode);
986 unlock_inode(parent->inode);
987 unlock_dnode(parent);
991 return DO_STATUS(errno);
994 __DEFINE_LXSYSCALL1(int, mkdir, const char*, path)
997 struct v_dnode *parent, *dir;
998 char name_value[VFS_NAME_MAXLEN];
999 struct hstr name = HHSTR(name_value, 0, 0);
1001 if ((errno = vfs_walk_proc(path, &parent, &name, VFS_WALK_PARENT))) {
1005 if (!(errno = vfs_walk(parent, name_value, &dir, NULL, 0))) {
1010 if ((errno = vfs_check_writable(parent))) {
1014 if (!(dir = vfs_d_alloc(parent, &name))) {
1020 lock_inode(parent->inode);
1022 if ((parent->super_block->fs->types & FSTYPE_ROFS)) {
1024 } else if (!parent->inode->ops->mkdir) {
1026 } else if ((parent->inode->itype & F_FILE)) {
1028 } else if (!(errno = parent->inode->ops->mkdir(parent->inode, dir))) {
1029 vfs_dcache_add(parent, dir);
1036 unlock_inode(parent->inode);
1037 unlock_dnode(parent);
1039 return DO_STATUS(errno);
1043 __vfs_do_unlink(struct v_dnode* dnode)
1046 struct v_inode* inode = dnode->inode;
1048 if (dnode->ref_count > 1) {
1052 if ((errno = vfs_check_writable(dnode))) {
1058 if (inode->open_count) {
1060 } else if ((inode->itype & F_MFILE)) {
1061 errno = inode->ops->unlink(inode);
1069 unlock_inode(inode);
1074 __DEFINE_LXSYSCALL1(int, unlink, const char*, pathname)
1077 struct v_dnode* dnode;
1078 if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
1082 errno = __vfs_do_unlink(dnode);
1085 return DO_STATUS(errno);
1088 __DEFINE_LXSYSCALL2(int, unlinkat, int, fd, const char*, pathname)
1092 if ((errno = vfs_getfd(fd, &fd_s))) {
1096 struct v_dnode* dnode;
1097 if (!(errno = vfs_walk(fd_s->file->dnode, pathname, &dnode, NULL, 0))) {
1098 errno = __vfs_do_unlink(dnode);
1102 return DO_STATUS(errno);
1105 __DEFINE_LXSYSCALL2(int, link, const char*, oldpath, const char*, newpath)
1108 struct v_dnode *dentry, *to_link, *name_dentry, *name_file;
1110 errno = __vfs_try_locate_file(oldpath, &dentry, &to_link, 0);
1112 errno = __vfs_try_locate_file(
1113 newpath, &name_dentry, &name_file, FLOCATE_CREATE_ONLY);
1115 errno = vfs_link(to_link, name_file);
1118 return DO_STATUS(errno);
1121 __DEFINE_LXSYSCALL1(int, fsync, int, fildes)
1126 if (!(errno = vfs_getfd(fildes, &fd_s))) {
1127 errno = vfs_fsync(fd_s->file);
1130 return DO_STATUS(errno);
1134 vfs_dup_fd(struct v_fd* old, struct v_fd** new)
1137 struct v_fd* copied = cake_grab(fd_pile);
1139 memcpy(copied, old, sizeof(struct v_fd));
1141 atomic_fetch_add(&old->file->ref_count, 1);
1149 vfs_dup2(int oldfd, int newfd)
1151 if (newfd == oldfd) {
1156 struct v_fd *oldfd_s, *newfd_s;
1157 if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
1161 if (!TEST_FD(newfd)) {
1166 newfd_s = __current->fdtable->fds[newfd];
1167 if (newfd_s && (errno = vfs_close(newfd_s->file))) {
1171 if (!(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1172 __current->fdtable->fds[newfd] = newfd_s;
1177 return DO_STATUS(errno);
1180 __DEFINE_LXSYSCALL2(int, dup2, int, oldfd, int, newfd)
1182 return vfs_dup2(oldfd, newfd);
1185 __DEFINE_LXSYSCALL1(int, dup, int, oldfd)
1188 struct v_fd *oldfd_s, *newfd_s;
1189 if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
1193 if (!(errno = vfs_alloc_fdslot(&newfd)) &&
1194 !(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1195 __current->fdtable->fds[newfd] = newfd_s;
1200 return DO_STATUS(errno);
1203 __DEFINE_LXSYSCALL2(
1204 int, symlink, const char*, pathname, const char*, link_target)
1207 struct v_dnode *dnode, *file;
1208 if ((errno = __vfs_try_locate_file(
1209 pathname, &dnode, &file, FLOCATE_CREATE_ONLY))) {
1213 if ((errno = vfs_check_writable(file))) {
1217 if (!file->inode->ops->set_symlink) {
1222 lock_inode(file->inode);
1224 errno = file->inode->ops->set_symlink(file->inode, link_target);
1226 unlock_inode(file->inode);
1229 return DO_STATUS(errno);
1233 vfs_ref_file(struct v_file* file)
1235 atomic_fetch_add(&file->ref_count, 1);
1239 vfs_ref_dnode(struct v_dnode* dnode)
1241 atomic_fetch_add(&dnode->ref_count, 1);
1244 mnt_mkbusy(dnode->mnt);
1249 vfs_unref_dnode(struct v_dnode* dnode)
1251 atomic_fetch_sub(&dnode->ref_count, 1);
1253 mnt_chillax(dnode->mnt);
1258 vfs_do_chdir(struct proc_info* proc, struct v_dnode* dnode)
1264 if ((dnode->inode->itype & F_FILE)) {
1270 vfs_unref_dnode(proc->cwd);
1273 vfs_ref_dnode(dnode);
1276 unlock_dnode(dnode);
1282 __DEFINE_LXSYSCALL1(int, chdir, const char*, path)
1284 struct v_dnode* dnode;
1287 if ((errno = vfs_walk_proc(path, &dnode, NULL, 0))) {
1291 errno = vfs_do_chdir((struct proc_info*)__current, dnode);
1294 return DO_STATUS(errno);
1297 __DEFINE_LXSYSCALL1(int, fchdir, int, fd)
1302 if ((errno = vfs_getfd(fd, &fd_s))) {
1306 errno = vfs_do_chdir((struct proc_info*)__current, fd_s->file->dnode);
1309 return DO_STATUS(errno);
1312 __DEFINE_LXSYSCALL2(char*, getcwd, char*, buf, size_t, size)
1323 if (!__current->cwd) {
1324 *buf = VFS_PATH_DELIM;
1327 len = vfs_get_path(__current->cwd, buf, size, 0);
1339 syscall_result(errno);
1344 vfs_do_rename(struct v_dnode* current, struct v_dnode* target)
1347 if (current->inode->id == target->inode->id) {
1352 if ((errno = vfs_check_writable(current))) {
1356 if (current->ref_count > 1 || target->ref_count > 1) {
1360 if (current->super_block != target->super_block) {
1364 struct v_dnode* oldparent = current->parent;
1365 struct v_dnode* newparent = target->parent;
1367 lock_dnode(current);
1370 lock_dnode(oldparent);
1372 lock_dnode(newparent);
1374 if (!llist_empty(&target->children)) {
1376 unlock_dnode(target);
1381 current->inode->ops->rename(current->inode, current, target))) {
1382 unlock_dnode(target);
1386 // re-position current
1387 hstrcpy(¤t->name, &target->name);
1388 vfs_dcache_rehash(newparent, current);
1393 unlock_dnode(target);
1396 unlock_dnode(current);
1398 unlock_dnode(oldparent);
1400 unlock_dnode(newparent);
1405 __DEFINE_LXSYSCALL2(int, rename, const char*, oldpath, const char*, newpath)
1407 struct v_dnode *cur, *target_parent, *target;
1408 struct hstr name = HSTR(valloc(VFS_NAME_MAXLEN), 0);
1411 if ((errno = vfs_walk_proc(oldpath, &cur, NULL, 0))) {
1415 if ((errno = vfs_walk(
1416 __current->cwd, newpath, &target_parent, &name, VFS_WALK_PARENT))) {
1420 errno = vfs_walk(target_parent, name.value, &target, NULL, 0);
1421 if (errno == ENOENT) {
1422 target = vfs_d_alloc(target_parent, &name);
1423 vfs_dcache_add(target_parent, target);
1433 errno = vfs_do_rename(cur, target);
1436 vfree((void*)name.value);
1437 return DO_STATUS(errno);
1440 __DEFINE_LXSYSCALL2(int, fstat, int, fd, struct file_stat*, stat)
1445 if ((errno = vfs_getfd(fd, &fds))) {
1449 struct v_inode* vino = fds->file->inode;
1450 struct device* fdev = vino->sb->dev;
1452 *stat = (struct file_stat){.st_ino = vino->id,
1453 .st_blocks = vino->lb_usage,
1454 .st_size = vino->fsize,
1455 .mode = vino->itype,
1456 .st_ioblksize = PAGE_SIZE,
1457 .st_blksize = vino->sb->blksize};
1459 if (VFS_DEVFILE(vino->itype)) {
1460 struct device* rdev = resolve_device(vino->data);
1461 if (!rdev || rdev->magic != DEV_STRUCT_MAGIC) {
1466 stat->st_rdev = (dev_t){.meta = rdev->ident.fn_grp,
1467 .unique = rdev->ident.unique,
1468 .index = rdev->dev_uid};
1472 stat->st_dev = (dev_t){.meta = fdev->ident.fn_grp,
1473 .unique = fdev->ident.unique,
1474 .index = fdev->dev_uid};
1478 return DO_STATUS(errno);