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);
76 __vfs_try_evict_dnode(struct lru_node* obj);
79 __vfs_try_evict_inode(struct lru_node* obj);
84 // 为他们专门创建一个蛋糕堆,而不使用valloc,这样我们可以最小化内碎片的产生
85 dnode_pile = cake_new_pile("dnode_cache", sizeof(struct v_dnode), 1, 0);
86 inode_pile = cake_new_pile("inode_cache", sizeof(struct v_inode), 1, 0);
87 file_pile = cake_new_pile("file_cache", sizeof(struct v_file), 1, 0);
88 fd_pile = cake_new_pile("fd_cache", sizeof(struct v_fd), 1, 0);
90 cake_new_pile("sb_cache", sizeof(struct v_superblock), 1, 0);
92 dnode_cache = vzalloc(VFS_HASHTABLE_SIZE * sizeof(struct hbucket));
94 dnode_lru = lru_new_zone("vfs_dnode", __vfs_try_evict_dnode);
95 inode_lru = lru_new_zone("vfs_inode", __vfs_try_evict_inode);
97 hstr_rehash(&vfs_ddot, HSTR_FULL_HASH);
98 hstr_rehash(&vfs_dot, HSTR_FULL_HASH);
101 vfs_sysroot = vfs_d_alloc(NULL, &vfs_empty);
102 vfs_sysroot->parent = vfs_sysroot;
103 atomic_fetch_add(&vfs_sysroot->ref_count, 1);
106 static inline struct hbucket*
107 __dcache_hash(struct v_dnode* parent, u32_t* hash)
111 _hash = _hash ^ (_hash >> VFS_HASHBITS);
112 // 与parent的指针值做加法,来减小碰撞的可能性。
113 _hash += (u32_t)__ptr(parent);
115 return &dnode_cache[_hash & VFS_HASH_MASK];
119 __sync_inode_nolock(struct v_inode* inode)
121 pcache_commit_all(inode);
124 if (inode->ops->sync) {
125 errno = inode->ops->sync(inode);
132 vfs_dcache_lookup(struct v_dnode* parent, struct hstr* str)
134 if (!str->len || HSTR_EQ(str, &vfs_dot))
137 if (HSTR_EQ(str, &vfs_ddot)) {
138 return parent->parent;
141 u32_t hash = str->hash;
142 struct hbucket* slot = __dcache_hash(parent, &hash);
144 struct v_dnode *pos, *n;
145 hashtable_bucket_foreach(slot, pos, n, hash_list)
147 if (pos->name.hash == hash && pos->parent == parent) {
155 vfs_dcache_add(struct v_dnode* parent, struct v_dnode* dnode)
159 atomic_fetch_add(&dnode->ref_count, 1);
160 dnode->parent = parent;
161 llist_append(&parent->children, &dnode->siblings);
163 struct hbucket* bucket = __dcache_hash(parent, &dnode->name.hash);
164 hlist_add(&bucket->head, &dnode->hash_list);
168 vfs_dcache_remove(struct v_dnode* dnode)
171 assert(dnode->ref_count == 1);
173 llist_delete(&dnode->siblings);
174 llist_delete(&dnode->aka_list);
175 hlist_delete(&dnode->hash_list);
177 dnode->parent = NULL;
178 atomic_fetch_sub(&dnode->ref_count, 1);
182 vfs_dcache_rehash(struct v_dnode* new_parent, struct v_dnode* dnode)
186 hstr_rehash(&dnode->name, HSTR_FULL_HASH);
187 vfs_dcache_remove(dnode);
188 vfs_dcache_add(new_parent, dnode);
192 vfs_open(struct v_dnode* dnode, struct v_file** file)
194 if (!dnode->inode || !dnode->inode->ops->open) {
198 struct v_inode* inode = dnode->inode;
202 struct v_file* vfile = cake_grab(file_pile);
203 memset(vfile, 0, sizeof(*vfile));
205 vfile->dnode = dnode;
206 vfile->inode = inode;
207 vfile->ref_count = ATOMIC_VAR_INIT(1);
208 vfile->ops = inode->default_fops;
210 if (check_file_node(inode) && !inode->pg_cache) {
211 struct pcache* pcache = vzalloc(sizeof(struct pcache));
213 pcache->master = inode;
214 inode->pg_cache = pcache;
217 int errno = inode->ops->open(inode, vfile);
219 cake_release(file_pile, vfile);
221 atomic_fetch_add(&dnode->ref_count, 1);
223 mnt_mkbusy(dnode->mnt);
234 vfs_assign_inode(struct v_dnode* assign_to, struct v_inode* inode)
236 if (assign_to->inode) {
237 llist_delete(&assign_to->aka_list);
238 assign_to->inode->link_count--;
241 llist_append(&inode->aka_dnodes, &assign_to->aka_list);
242 assign_to->inode = inode;
247 vfs_link(struct v_dnode* to_link, struct v_dnode* name)
251 if ((errno = vfs_check_writable(to_link))) {
255 lock_inode(to_link->inode);
256 if (to_link->super_block->root != name->super_block->root) {
258 } else if (!to_link->inode->ops->link) {
260 } else if (!(errno = to_link->inode->ops->link(to_link->inode, name))) {
261 vfs_assign_inode(name, to_link->inode);
263 unlock_inode(to_link->inode);
269 vfs_pclose(struct v_file* file, pid_t pid)
271 struct v_inode* inode;
274 if (file->ref_count > 1) {
275 atomic_fetch_sub(&file->ref_count, 1);
283 * This happened when process is terminated while blocking on read.
284 * In that case, the process is still holding the inode lock and it
285 will never get released.
286 * The unlocking should also include ownership check.
288 * To see why, consider two process both open the same file both with
290 * Process A: busy on reading x
291 * Process B: do nothing with x
292 * Assuming that, after a very short time, process B get terminated
293 * while process A is still busy in it's reading business. By this
294 * design, the inode lock of this file x is get released by B rather
295 * than A. And this will cause a probable race condition on A if other
296 * process is writing to this file later after B exit.
299 if (mutex_on_hold(&inode->lock)) {
300 mutex_unlock_for(&inode->lock, pid);
305 pcache_commit_all(inode);
306 if ((errno = file->ops->close(file))) {
310 atomic_fetch_sub(&file->dnode->ref_count, 1);
313 if (!inode->open_count) {
314 __sync_inode_nolock(inode);
317 mnt_chillax(file->dnode->mnt);
318 cake_release(file_pile, file);
326 vfs_close(struct v_file* file)
328 return vfs_pclose(file, __current->pid);
332 vfs_free_fd(struct v_fd* fd)
334 cake_release(fd_pile, fd);
338 vfs_isync(struct v_inode* inode)
342 int errno = __sync_inode_nolock(inode);
350 vfs_fsync(struct v_file* file)
353 if ((errno = vfs_check_writable(file->dnode))) {
357 return vfs_isync(file->inode);
361 vfs_alloc_fdslot(int* fd)
363 for (size_t i = 0; i < VFS_MAX_FD; i++) {
364 if (!__current->fdtable->fds[i]) {
375 struct v_superblock* sb = cake_grab(superblock_pile);
376 memset(sb, 0, sizeof(*sb));
377 llist_init_head(&sb->sb_list);
378 sb->i_cache = vzalloc(VFS_HASHTABLE_SIZE * sizeof(struct hbucket));
384 vfs_sb_ref(struct v_superblock* sb)
390 vfs_sb_free(struct v_superblock* sb)
392 assert(sb->ref_count);
399 if (sb->ops.release) {
404 cake_release(superblock_pile, sb);
408 __vfs_try_evict_dnode(struct lru_node* obj)
410 struct v_dnode* dnode = container_of(obj, struct v_dnode, lru);
412 if (!dnode->ref_count) {
420 __vfs_try_evict_inode(struct lru_node* obj)
422 struct v_inode* inode = container_of(obj, struct v_inode, lru);
424 if (!inode->link_count && !inode->open_count) {
432 vfs_d_alloc(struct v_dnode* parent, struct hstr* name)
434 struct v_dnode* dnode = cake_grab(dnode_pile);
436 lru_evict_half(dnode_lru);
438 if (!(dnode = cake_grab(dnode_pile))) {
443 memset(dnode, 0, sizeof(*dnode));
444 llist_init_head(&dnode->children);
445 llist_init_head(&dnode->siblings);
446 llist_init_head(&dnode->aka_list);
447 mutex_init(&dnode->lock);
449 dnode->ref_count = ATOMIC_VAR_INIT(0);
450 dnode->name = HHSTR(vzalloc(VFS_NAME_MAXLEN), 0, 0);
452 hstrcpy(&dnode->name, name);
455 vfs_d_assign_sb(dnode, parent->super_block);
456 dnode->mnt = parent->mnt;
459 lru_use_one(dnode_lru, &dnode->lru);
465 vfs_d_free(struct v_dnode* dnode)
467 assert(dnode->ref_count == 1);
470 assert(dnode->inode->link_count > 0);
471 dnode->inode->link_count--;
474 vfs_dcache_remove(dnode);
475 // Make sure the children de-referencing their parent.
476 // With lru presented, the eviction will be propagated over the entire
477 // detached subtree eventually
478 struct v_dnode *pos, *n;
479 llist_for_each(pos, n, &dnode->children, siblings)
481 vfs_dcache_remove(pos);
484 if (dnode->destruct) {
485 dnode->destruct(dnode);
488 vfs_sb_free(dnode->super_block);
489 vfree((void*)dnode->name.value);
490 cake_release(dnode_pile, dnode);
494 vfs_i_find(struct v_superblock* sb, u32_t i_id)
496 struct hbucket* slot = &sb->i_cache[i_id & VFS_HASH_MASK];
497 struct v_inode *pos, *n;
498 hashtable_bucket_foreach(slot, pos, n, hash_list)
500 if (pos->id == i_id) {
501 lru_use_one(inode_lru, &pos->lru);
510 vfs_i_addhash(struct v_inode* inode)
512 struct hbucket* slot = &inode->sb->i_cache[inode->id & VFS_HASH_MASK];
514 hlist_delete(&inode->hash_list);
515 hlist_add(&slot->head, &inode->hash_list);
519 vfs_i_alloc(struct v_superblock* sb)
521 assert(sb->ops.init_inode);
523 struct v_inode* inode;
524 if (!(inode = cake_grab(inode_pile))) {
525 lru_evict_half(inode_lru);
526 if (!(inode = cake_grab(inode_pile))) {
531 memset(inode, 0, sizeof(*inode));
532 mutex_init(&inode->lock);
533 llist_init_head(&inode->xattrs);
534 llist_init_head(&inode->aka_dnodes);
536 sb->ops.init_inode(sb, inode);
538 inode->ctime = clock_unixtime();
539 inode->atime = inode->ctime;
540 inode->mtime = inode->ctime;
542 vfs_i_assign_sb(inode, sb);
543 lru_use_one(inode_lru, &inode->lru);
548 vfs_i_free(struct v_inode* inode)
550 if (inode->pg_cache) {
551 pcache_release(inode->pg_cache);
552 vfree(inode->pg_cache);
554 // we don't need to sync inode.
555 // If an inode can be free, then it must be properly closed.
556 // Hence it must be synced already!
557 if (inode->destruct) {
558 inode->destruct(inode);
561 vfs_sb_free(inode->sb);
562 hlist_delete(&inode->hash_list);
563 cake_release(inode_pile, inode);
566 /* ---- System call definition and support ---- */
568 // make a new name when not exists
569 #define FLOC_MAYBE_MKNAME 1
571 // name must be non-exist and made.
572 #define FLOC_MKNAME 2
575 #define FLOC_NOFOLLOW 4
578 vfs_getfd(int fd, struct v_fd** fd_s)
580 if (TEST_FD(fd) && (*fd_s = __current->fdtable->fds[fd])) {
587 __vfs_mknod(struct v_inode* parent, struct v_dnode* dnode,
588 unsigned int itype, dev_t* dev)
592 errno = parent->ops->create(parent, dnode, itype);
600 struct file_locator {
602 struct v_dnode* file;
607 * @brief unlock the file locator (floc) if possible.
608 * If the file to be located if not exists, and
609 * any FLOC_*MKNAME flag is set, then the parent
610 * dnode will be locked until the file has been properly
611 * finalised by subsequent logic.
616 __floc_try_unlock(struct file_locator* floc)
620 unlock_dnode(floc->dir);
625 __vfs_try_locate_file(const char* path,
626 struct file_locator* floc,
629 char name_str[VFS_NAME_MAXLEN];
630 struct v_dnode *fdir, *file;
631 struct hstr name = HSTR(name_str, 0);
632 int errno, woption = 0;
634 if ((options & FLOC_NOFOLLOW)) {
635 woption |= VFS_WALK_NOFOLLOW;
636 options &= ~FLOC_NOFOLLOW;
641 errno = vfs_walk_proc(path, &fdir, &name, woption | VFS_WALK_PARENT);
646 errno = vfs_walk(fdir, name.value, &file, NULL, woption);
648 if (errno && errno != ENOENT) {
653 if ((options & FLOC_MKNAME)) {
664 errno = vfs_check_writable(fdir);
671 file = vfs_d_alloc(fdir, &name);
679 vfs_dcache_add(fdir, file);
689 vfs_do_open(const char* path, int options)
691 int errno, fd, loptions = 0;
692 struct v_dnode *dentry, *file;
693 struct v_file* ofile = NULL;
694 struct file_locator floc;
695 struct v_inode* inode;
697 if ((options & FO_CREATE)) {
698 loptions |= FLOC_MAYBE_MKNAME;
699 } else if ((options & FO_NOFOLLOW)) {
700 loptions |= FLOC_NOFOLLOW;
703 errno = __vfs_try_locate_file(path, &floc, loptions);
705 if (errno || (errno = vfs_alloc_fdslot(&fd))) {
713 errno = __vfs_mknod(dentry->inode, file, VFS_IFFILE, NULL);
716 __floc_try_unlock(&floc);
720 __floc_try_unlock(&floc);
724 if ((errno = vfs_open(file, &ofile))) {
728 inode = ofile->inode;
731 struct v_fd* fd_s = cake_grab(fd_pile);
732 memset(fd_s, 0, sizeof(*fd_s));
734 if ((options & O_TRUNC)) {
735 file->inode->fsize = 0;
738 if (vfs_get_dtype(inode->itype) == DT_DIR) {
743 fd_s->flags = options;
744 __current->fdtable->fds[fd] = fd_s;
751 __DEFINE_LXSYSCALL2(int, open, const char*, path, int, options)
753 int errno = vfs_do_open(path, options);
754 return DO_STATUS_OR_RETURN(errno);
757 __DEFINE_LXSYSCALL1(int, close, int, fd)
761 if ((errno = vfs_getfd(fd, &fd_s))) {
765 if ((errno = vfs_close(fd_s->file))) {
769 cake_release(fd_pile, fd_s);
770 __current->fdtable->fds[fd] = 0;
773 return DO_STATUS(errno);
777 __vfs_readdir_callback(struct dir_context* dctx,
782 struct lx_dirent* dent = (struct lx_dirent*)dctx->cb_data;
783 strncpy(dent->d_name, name, MIN(len, DIRENT_NAME_MAX_LEN));
785 dent->d_type = dtype;
788 __DEFINE_LXSYSCALL2(int, sys_readdir, int, fd, struct lx_dirent*, dent)
793 if ((errno = vfs_getfd(fd, &fd_s))) {
797 struct v_inode* inode = fd_s->file->inode;
801 if (!check_directory_node(inode)) {
806 struct dir_context dctx = (struct dir_context) {
808 .read_complete_callback = __vfs_readdir_callback
811 if ((errno = fd_s->file->ops->readdir(fd_s->file, &dctx)) != 1) {
821 return DO_STATUS_OR_RETURN(errno);
824 __DEFINE_LXSYSCALL3(int, read, int, fd, void*, buf, size_t, count)
828 if ((errno = vfs_getfd(fd, &fd_s))) {
832 struct v_file* file = fd_s->file;
833 if (check_directory_node(file->inode)) {
838 lock_inode(file->inode);
840 file->inode->atime = clock_unixtime();
842 if (check_seqdev_node(file->inode) || (fd_s->flags & FO_DIRECT)) {
843 errno = file->ops->read(file->inode, buf, count, file->f_pos);
845 errno = pcache_read(file->inode, buf, count, file->f_pos);
849 file->f_pos += errno;
850 unlock_inode(file->inode);
854 unlock_inode(file->inode);
857 return DO_STATUS(errno);
860 __DEFINE_LXSYSCALL3(int, write, int, fd, void*, buf, size_t, count)
864 if ((errno = vfs_getfd(fd, &fd_s))) {
868 struct v_inode* inode;
869 struct v_file* file = fd_s->file;
871 if ((errno = vfs_check_writable(file->dnode))) {
875 if (check_directory_node(file->inode)) {
883 inode->mtime = clock_unixtime();
884 if ((fd_s->flags & O_APPEND)) {
885 file->f_pos = inode->fsize;
888 if (check_seqdev_node(inode) || (fd_s->flags & FO_DIRECT)) {
889 errno = file->ops->write(inode, buf, count, file->f_pos);
891 errno = pcache_write(inode, buf, count, file->f_pos);
895 file->f_pos += errno;
896 inode->fsize = MAX(inode->fsize, file->f_pos);
905 return DO_STATUS(errno);
908 __DEFINE_LXSYSCALL3(int, lseek, int, fd, int, offset, int, options)
912 if ((errno = vfs_getfd(fd, &fd_s))) {
916 struct v_file* file = fd_s->file;
917 struct v_inode* inode = file->inode;
919 if (!file->ops->seek) {
927 int fpos = file->f_pos;
929 if (vfs_get_dtype(inode->itype) == DT_DIR) {
930 options = (options != FSEEK_END) ? options : FSEEK_SET;
935 overflow = sadd_of((int)file->f_pos, offset, &fpos);
938 overflow = sadd_of((int)inode->fsize, offset, &fpos);
949 errno = file->ops->seek(file, fpos);
955 return DO_STATUS(errno);
959 vfs_get_path(struct v_dnode* dnode, char* buf, size_t size, int depth)
971 if (dnode->parent != dnode) {
972 len = vfs_get_path(dnode->parent, buf, size, depth + 1);
979 if (!len || buf[len - 1] != VFS_PATH_DELIM) {
980 buf[len++] = VFS_PATH_DELIM;
983 size_t cpy_size = MIN(dnode->name.len, size - len);
984 strncpy(buf + len, dnode->name.value, cpy_size);
991 vfs_readlink(struct v_dnode* dnode, char* buf, size_t size)
994 struct v_inode* inode = dnode->inode;
996 if (!check_symlink_node(inode)) {
1000 if (!inode->ops->read_symlink) {
1006 int errno = inode->ops->read_symlink(inode, &link);
1008 strncpy(buf, link, MIN(size, (size_t)errno));
1011 unlock_inode(inode);
1016 vfs_get_dtype(int itype)
1018 int dtype = DT_FILE;
1019 if (check_itype(itype, VFS_IFSYMLINK)) {
1020 dtype |= DT_SYMLINK;
1023 if (check_itype(itype, VFS_IFDIR)) {
1033 __DEFINE_LXSYSCALL3(int, realpathat, int, fd, char*, buf, size_t, size)
1037 if ((errno = vfs_getfd(fd, &fd_s))) {
1041 struct v_dnode* dnode;
1042 errno = vfs_get_path(fd_s->file->dnode, buf, size, 0);
1049 return DO_STATUS(errno);
1052 __DEFINE_LXSYSCALL3(int, readlink, const char*, path, char*, buf, size_t, size)
1055 struct v_dnode* dnode;
1056 if (!(errno = vfs_walk_proc(path, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
1057 errno = vfs_readlink(dnode, buf, size);
1064 return DO_STATUS(errno);
1067 __DEFINE_LXSYSCALL4(
1068 int, readlinkat, int, dirfd, const char*, pathname, char*, buf, size_t, size)
1072 if ((errno = vfs_getfd(dirfd, &fd_s))) {
1076 pathname = pathname ? pathname : "";
1078 struct v_dnode* dnode;
1079 if (!(errno = vfs_walk(
1080 fd_s->file->dnode, pathname, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
1081 errno = vfs_readlink(fd_s->file->dnode, buf, size);
1089 return DO_STATUS(errno);
1094 When we perform operation that could affect the layout of
1095 directory (i.e., rename, mkdir, rmdir). We must lock the parent dir
1096 whenever possible. This will blocking any ongoing path walking to reach
1097 it hence avoid any partial state.
1100 __DEFINE_LXSYSCALL1(int, rmdir, const char*, pathname)
1103 struct v_dnode* dnode;
1104 if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
1105 return DO_STATUS(errno);
1110 if ((errno = vfs_check_writable(dnode))) {
1114 if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
1119 if (dnode->ref_count > 1 || dnode->inode->open_count) {
1124 if (!llist_empty(&dnode->children)) {
1129 struct v_dnode* parent = dnode->parent;
1137 lock_inode(parent->inode);
1139 if (check_directory_node(dnode->inode)) {
1140 errno = parent->inode->ops->rmdir(parent->inode, dnode);
1142 vfs_dcache_remove(dnode);
1148 unlock_inode(parent->inode);
1149 unlock_dnode(parent);
1152 unlock_dnode(dnode);
1153 return DO_STATUS(errno);
1156 __DEFINE_LXSYSCALL1(int, mkdir, const char*, path)
1159 struct v_dnode *parent, *dir;
1160 char name_value[VFS_NAME_MAXLEN];
1161 struct hstr name = HHSTR(name_value, 0, 0);
1163 if ((errno = vfs_walk_proc(path, &parent, &name, VFS_WALK_PARENT))) {
1167 if (!(errno = vfs_walk(parent, name_value, &dir, NULL, 0))) {
1172 if ((errno = vfs_check_writable(parent))) {
1176 if (!(dir = vfs_d_alloc(parent, &name))) {
1181 struct v_inode* inode = parent->inode;
1186 if ((parent->super_block->fs->types & FSTYPE_ROFS)) {
1188 } else if (!inode->ops->mkdir) {
1190 } else if (!check_directory_node(inode)) {
1192 } else if (!(errno = inode->ops->mkdir(inode, dir))) {
1193 vfs_dcache_add(parent, dir);
1200 unlock_inode(inode);
1201 unlock_dnode(parent);
1203 return DO_STATUS(errno);
1207 __vfs_do_unlink(struct v_dnode* dnode)
1210 struct v_inode* inode = dnode->inode;
1212 if (dnode->ref_count > 1) {
1216 if ((errno = vfs_check_writable(dnode))) {
1222 if (inode->open_count) {
1224 } else if (!check_directory_node(inode)) {
1225 errno = inode->ops->unlink(inode, dnode);
1233 unlock_inode(inode);
1238 __DEFINE_LXSYSCALL1(int, unlink, const char*, pathname)
1241 struct v_dnode* dnode;
1242 if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
1246 errno = __vfs_do_unlink(dnode);
1249 return DO_STATUS(errno);
1252 __DEFINE_LXSYSCALL2(int, unlinkat, int, fd, const char*, pathname)
1256 if ((errno = vfs_getfd(fd, &fd_s))) {
1260 struct v_dnode* dnode;
1261 if (!(errno = vfs_walk(fd_s->file->dnode, pathname, &dnode, NULL, 0))) {
1262 errno = __vfs_do_unlink(dnode);
1266 return DO_STATUS(errno);
1269 __DEFINE_LXSYSCALL2(int, link, const char*, oldpath, const char*, newpath)
1272 struct file_locator floc;
1273 struct v_dnode *to_link, *name_file;
1275 errno = __vfs_try_locate_file(oldpath, &floc, 0);
1280 __floc_try_unlock(&floc);
1282 to_link = floc.file;
1283 errno = __vfs_try_locate_file(newpath, &floc, FLOC_MKNAME);
1288 name_file = floc.file;
1289 errno = vfs_link(to_link, name_file);
1291 vfs_d_free(name_file);
1295 __floc_try_unlock(&floc);
1296 return DO_STATUS(errno);
1299 __DEFINE_LXSYSCALL1(int, fsync, int, fildes)
1304 if (!(errno = vfs_getfd(fildes, &fd_s))) {
1305 errno = vfs_fsync(fd_s->file);
1308 return DO_STATUS(errno);
1312 vfs_dup_fd(struct v_fd* old, struct v_fd** new)
1315 struct v_fd* copied = cake_grab(fd_pile);
1317 memcpy(copied, old, sizeof(struct v_fd));
1319 atomic_fetch_add(&old->file->ref_count, 1);
1327 vfs_dup2(int oldfd, int newfd)
1329 if (newfd == oldfd) {
1334 struct v_fd *oldfd_s, *newfd_s;
1335 if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
1339 if (!TEST_FD(newfd)) {
1344 newfd_s = __current->fdtable->fds[newfd];
1345 if (newfd_s && (errno = vfs_close(newfd_s->file))) {
1349 if (!(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1350 __current->fdtable->fds[newfd] = newfd_s;
1355 return DO_STATUS(errno);
1358 __DEFINE_LXSYSCALL2(int, dup2, int, oldfd, int, newfd)
1360 return vfs_dup2(oldfd, newfd);
1363 __DEFINE_LXSYSCALL1(int, dup, int, oldfd)
1366 struct v_fd *oldfd_s, *newfd_s;
1367 if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
1371 if (!(errno = vfs_alloc_fdslot(&newfd)) &&
1372 !(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1373 __current->fdtable->fds[newfd] = newfd_s;
1378 return DO_STATUS(errno);
1381 __DEFINE_LXSYSCALL2(
1382 int, symlink, const char*, pathname, const char*, link_target)
1385 struct file_locator floc;
1386 struct v_dnode *file;
1387 struct v_inode *f_ino;
1389 errno = __vfs_try_locate_file(pathname, &floc, FLOC_MKNAME);
1395 errno = __vfs_mknod(floc.dir->inode, file, VFS_IFSYMLINK, NULL);
1401 f_ino = file->inode;
1405 errno = vfs_check_writable(file);
1410 if (!f_ino->ops->set_symlink) {
1417 errno = f_ino->ops->set_symlink(f_ino, link_target);
1419 unlock_inode(f_ino);
1422 __floc_try_unlock(&floc);
1423 return DO_STATUS(errno);
1427 vfs_ref_file(struct v_file* file)
1429 atomic_fetch_add(&file->ref_count, 1);
1433 vfs_ref_dnode(struct v_dnode* dnode)
1435 atomic_fetch_add(&dnode->ref_count, 1);
1438 mnt_mkbusy(dnode->mnt);
1443 vfs_unref_dnode(struct v_dnode* dnode)
1445 atomic_fetch_sub(&dnode->ref_count, 1);
1447 mnt_chillax(dnode->mnt);
1452 vfs_do_chdir(struct proc_info* proc, struct v_dnode* dnode)
1458 if (!check_directory_node(dnode->inode)) {
1464 vfs_unref_dnode(proc->cwd);
1467 vfs_ref_dnode(dnode);
1470 unlock_dnode(dnode);
1476 __DEFINE_LXSYSCALL1(int, chdir, const char*, path)
1478 struct v_dnode* dnode;
1481 if ((errno = vfs_walk_proc(path, &dnode, NULL, 0))) {
1485 errno = vfs_do_chdir((struct proc_info*)__current, dnode);
1488 return DO_STATUS(errno);
1491 __DEFINE_LXSYSCALL1(int, fchdir, int, fd)
1496 if ((errno = vfs_getfd(fd, &fd_s))) {
1500 errno = vfs_do_chdir((struct proc_info*)__current, fd_s->file->dnode);
1503 return DO_STATUS(errno);
1506 __DEFINE_LXSYSCALL2(char*, getcwd, char*, buf, size_t, size)
1517 if (!__current->cwd) {
1518 *buf = VFS_PATH_DELIM;
1521 len = vfs_get_path(__current->cwd, buf, size, 0);
1533 syscall_result(errno);
1538 vfs_do_rename(struct v_dnode* current, struct v_dnode* target)
1541 if (current->inode->id == target->inode->id) {
1546 if ((errno = vfs_check_writable(current))) {
1550 if (current->ref_count > 1 || target->ref_count > 1) {
1554 if (current->super_block != target->super_block) {
1558 struct v_dnode* oldparent = current->parent;
1559 struct v_dnode* newparent = target->parent;
1561 lock_dnode(current);
1564 lock_dnode(oldparent);
1566 lock_dnode(newparent);
1568 if (!llist_empty(&target->children)) {
1570 unlock_dnode(target);
1575 current->inode->ops->rename(current->inode, current, target))) {
1576 unlock_dnode(target);
1580 // re-position current
1581 hstrcpy(¤t->name, &target->name);
1582 vfs_dcache_rehash(newparent, current);
1587 unlock_dnode(target);
1590 unlock_dnode(current);
1592 unlock_dnode(oldparent);
1594 unlock_dnode(newparent);
1599 __DEFINE_LXSYSCALL2(int, rename, const char*, oldpath, const char*, newpath)
1601 struct v_dnode *cur, *target_parent, *target;
1602 struct hstr name = HSTR(valloc(VFS_NAME_MAXLEN), 0);
1605 if ((errno = vfs_walk_proc(oldpath, &cur, NULL, 0))) {
1609 if ((errno = vfs_walk(
1610 __current->cwd, newpath, &target_parent, &name, VFS_WALK_PARENT))) {
1614 errno = vfs_walk(target_parent, name.value, &target, NULL, 0);
1615 if (errno == ENOENT) {
1616 target = vfs_d_alloc(target_parent, &name);
1617 vfs_dcache_add(target_parent, target);
1627 errno = vfs_do_rename(cur, target);
1630 vfree((void*)name.value);
1631 return DO_STATUS(errno);
1634 __DEFINE_LXSYSCALL2(int, fstat, int, fd, struct file_stat*, stat)
1639 if ((errno = vfs_getfd(fd, &fds))) {
1643 struct v_inode* vino = fds->file->inode;
1644 struct device* fdev = vino->sb->dev;
1646 *stat = (struct file_stat){.st_ino = vino->id,
1647 .st_blocks = vino->lb_usage,
1648 .st_size = vino->fsize,
1649 .mode = vino->itype,
1650 .st_ioblksize = PAGE_SIZE,
1651 .st_blksize = vino->sb->blksize};
1653 if (check_device_node(vino)) {
1654 struct device* rdev = resolve_device(vino->data);
1655 if (!rdev || rdev->magic != DEV_STRUCT_MAGIC) {
1660 stat->st_rdev = (dev_t){.meta = rdev->ident.fn_grp,
1661 .unique = rdev->ident.unique,
1662 .index = rdev->dev_uid};
1666 stat->st_dev = (dev_t){.meta = fdev->ident.fn_grp,
1667 .unique = fdev->ident.unique,
1668 .index = fdev->dev_uid};
1672 return DO_STATUS(errno);