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.
29 6. (mount) Ability to track all mount points (including sub-mounts)
30 so we can be confident to clean up everything when we unmount.
31 7. (mount) Figure out a way to acquire the device represented by a dnode.
32 so it can be used to mount. (e.g. we wish to get `struct device*`
33 out of the dnode at /dev/sda)
34 [tip] we should pay attention at twifs and add a private_data field
36 8. (mount) Then, we should refactor on mount/unmount mechanism.
37 9. (mount) (future) Ability to mount any thing? e.g. Linux can mount a disk
38 image file using a so called "loopback" pseudo device. Maybe
39 we can do similar thing in Lunaix? A block device emulation
40 above the regular file when we mount it on.
41 10. (device) device number (dev_t) allocation
42 [good idea] <class>:<subclass>:<uniq_id> composition
45 #include <klibc/string.h>
46 #include <lunaix/dirent.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>
56 #include <lunaix/fs/twifs.h>
58 #define PATH_DELIM '/'
59 #define HASHTABLE_BITS 10
60 #define HASHTABLE_SIZE (1 << HASHTABLE_BITS)
61 #define HASH_MASK (HASHTABLE_SIZE - 1)
62 #define HASHBITS (32 - HASHTABLE_BITS)
64 #define unlock_inode(inode) mutex_unlock(&inode->lock)
65 #define lock_inode(inode) \
67 mutex_lock(&inode->lock); \
68 lru_use_one(inode_lru, &inode->lru); \
71 #define unlock_dnode(dnode) mutex_unlock(&dnode->lock)
72 #define lock_dnode(dnode) \
74 mutex_lock(&dnode->lock); \
75 lru_use_one(dnode_lru, &dnode->lru); \
78 static struct cake_pile* dnode_pile;
79 static struct cake_pile* inode_pile;
80 static struct cake_pile* file_pile;
81 static struct cake_pile* superblock_pile;
82 static struct cake_pile* fd_pile;
84 static struct v_superblock* root_sb;
85 static struct hbucket *dnode_cache, *inode_cache;
87 static struct lru_zone *dnode_lru, *inode_lru;
89 struct hstr vfs_ddot = HSTR("..", 2);
90 struct hstr vfs_dot = HSTR(".", 1);
91 struct hstr vfs_empty = HSTR("", 0);
97 vfs_sb_free(struct v_superblock* sb);
100 __vfs_try_evict_dnode(struct lru_node* obj);
103 __vfs_try_evict_inode(struct lru_node* obj);
108 // 为他们专门创建一个蛋糕堆,而不使用valloc,这样我们可以最小化内碎片的产生
109 dnode_pile = cake_new_pile("dnode_cache", sizeof(struct v_dnode), 1, 0);
110 inode_pile = cake_new_pile("inode_cache", sizeof(struct v_inode), 1, 0);
111 file_pile = cake_new_pile("file_cache", sizeof(struct v_file), 1, 0);
112 fd_pile = cake_new_pile("fd_cache", sizeof(struct v_fd), 1, 0);
114 cake_new_pile("sb_cache", sizeof(struct v_superblock), 1, 0);
116 dnode_cache = vzalloc(HASHTABLE_SIZE * sizeof(struct hbucket));
117 inode_cache = vzalloc(HASHTABLE_SIZE * sizeof(struct hbucket));
119 dnode_lru = lru_new_zone(__vfs_try_evict_dnode);
120 inode_lru = lru_new_zone(__vfs_try_evict_inode);
122 hstr_rehash(&vfs_ddot, HSTR_FULL_HASH);
123 hstr_rehash(&vfs_dot, HSTR_FULL_HASH);
125 // 创建一个根superblock,用来蕴含我们的根目录。
126 root_sb = vfs_sb_alloc();
127 root_sb->root = vfs_d_alloc();
130 inline struct hbucket*
131 __dcache_hash(struct v_dnode* parent, uint32_t* hash)
133 uint32_t _hash = *hash;
134 // 与parent的指针值做加法,来减小碰撞的可能性。
135 _hash += (uint32_t)parent;
137 _hash = _hash ^ (_hash >> HASHBITS);
139 return &dnode_cache[_hash & HASH_MASK];
143 vfs_dcache_lookup(struct v_dnode* parent, struct hstr* str)
145 if (!str->len || HSTR_EQ(str, &vfs_dot))
148 if (HSTR_EQ(str, &vfs_ddot)) {
149 return parent->parent ? parent->parent : parent;
152 uint32_t hash = str->hash;
153 struct hbucket* slot = __dcache_hash(parent, &hash);
155 struct v_dnode *pos, *n;
156 hashtable_bucket_foreach(slot, pos, n, hash_list)
158 if (pos->name.hash == hash) {
166 vfs_dcache_add(struct v_dnode* parent, struct v_dnode* dnode)
168 atomic_fetch_add(&dnode->ref_count, 1);
169 dnode->parent = parent;
170 llist_append(&parent->children, &dnode->siblings);
171 struct hbucket* bucket = __dcache_hash(parent, &dnode->name.hash);
172 hlist_add(&bucket->head, &dnode->hash_list);
176 vfs_dcache_remove(struct v_dnode* dnode)
178 assert(dnode->ref_count == 1);
180 llist_delete(&dnode->siblings);
181 hlist_delete(&dnode->hash_list);
183 dnode->parent = NULL;
184 atomic_fetch_sub(&dnode->ref_count, 1);
188 vfs_dcache_rehash(struct v_dnode* new_parent, struct v_dnode* dnode)
190 hstr_rehash(&dnode->name, HSTR_FULL_HASH);
191 vfs_dcache_remove(dnode);
192 vfs_dcache_add(new_parent, dnode);
196 __vfs_walk(struct v_dnode* start,
198 struct v_dnode** dentry,
199 struct hstr* component,
205 if (path[0] == PATH_DELIM || !start) {
206 if ((walk_options & VFS_WALK_FSRELATIVE) && start) {
207 start = start->super_block->root;
209 start = root_sb->root;
214 struct v_dnode* dnode;
215 struct v_dnode* current_level = start;
217 char name_content[VFS_NAME_MAXLEN];
218 struct hstr name = HSTR(name_content, 0);
220 char current = path[i++], lookahead;
222 lookahead = path[i++];
223 if (current != PATH_DELIM) {
224 if (j >= VFS_NAME_MAXLEN - 1) {
227 if (!VFS_VALID_CHAR(current)) {
230 name_content[j++] = current;
236 // handling cases like /^.*(\/+).*$/
237 if (lookahead == PATH_DELIM) {
241 lock_dnode(current_level);
245 hstr_rehash(&name, HSTR_FULL_HASH);
247 if (!lookahead && (walk_options & VFS_WALK_PARENT)) {
249 component->hash = name.hash;
251 strcpy(component->value, name_content);
253 unlock_dnode(current_level);
257 dnode = vfs_dcache_lookup(current_level, &name);
260 dnode = vfs_d_alloc();
267 hstrcpy(&dnode->name, &name);
269 lock_inode(current_level->inode);
272 current_level->inode->ops.dir_lookup(current_level->inode, dnode);
274 if (errno == ENOENT && (walk_options & VFS_WALK_MKPARENT)) {
275 if (!current_level->inode->ops.mkdir) {
278 errno = current_level->inode->ops.mkdir(
279 current_level->inode, dnode);
283 unlock_inode(current_level->inode);
286 unlock_dnode(current_level);
287 vfree(dnode->name.value);
291 vfs_dcache_add(current_level, dnode);
294 unlock_dnode(current_level);
297 current_level = dnode;
302 *dentry = current_level;
312 #define VFS_MAX_SYMLINK 16
315 vfs_walk(struct v_dnode* start,
317 struct v_dnode** dentry,
318 struct hstr* component,
321 struct v_dnode* interim;
322 const char* pathname = path;
323 int errno = __vfs_walk(start, path, &interim, component, options);
326 while (!errno && interim->inode && (options & VFS_WALK_NOFOLLOW)) {
327 if (counter >= VFS_MAX_SYMLINK) {
331 if ((interim->inode->itype & VFS_IFSYMLINK) &&
332 interim->inode->ops.read_symlink) {
334 lock_inode(interim->inode);
335 errno = interim->inode->ops.read_symlink(interim->inode, &pathname);
336 unlock_inode(interim->inode);
344 errno = __vfs_walk(start, pathname, &interim, component, options);
348 *dentry = errno ? 0 : interim;
354 vfs_mount(const char* target, const char* fs_name, struct device* device)
359 if (!(errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
360 errno = vfs_mount_at(fs_name, device, mnt);
367 vfs_unmount(const char* target)
372 if (!(errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
373 errno = vfs_unmount_at(mnt);
380 vfs_mount_at(const char* fs_name,
381 struct device* device,
382 struct v_dnode* mnt_point)
384 if (mnt_point->inode && !(mnt_point->inode->itype & VFS_IFDIR)) {
388 struct filesystem* fs = fsm_get(fs_name);
393 struct v_superblock* sb = vfs_sb_alloc();
395 sb->fs_id = fs->fs_id;
398 if (!(errno = fs->mount(sb, mnt_point))) {
400 sb->root = mnt_point;
401 mnt_point->super_block = sb;
402 llist_append(&root_sb->sb_list, &sb->sb_list);
409 vfs_unmount_at(struct v_dnode* mnt_point)
411 // FIXME deal with the detached dcache subtree
413 struct v_superblock* sb = mnt_point->super_block;
418 if (sb->root != mnt_point) {
422 if (!(errno = sb->fs->unmount(sb))) {
423 struct v_dnode* fs_root = sb->root;
424 vfs_dcache_remove(fs_root);
426 llist_delete(&sb->sb_list);
434 vfs_open(struct v_dnode* dnode, struct v_file** file)
436 if (!dnode->inode || !dnode->inode->ops.open) {
440 struct v_inode* inode = dnode->inode;
441 struct v_file* vfile = cake_grab(file_pile);
442 memset(vfile, 0, sizeof(*vfile));
444 vfile->dnode = dnode;
445 vfile->inode = inode;
446 vfile->ref_count = ATOMIC_VAR_INIT(1);
447 vfile->ops = inode->default_fops;
449 if ((inode->itype & VFS_IFFILE) && !inode->pg_cache) {
450 struct pcache* pcache = vzalloc(sizeof(struct pcache));
452 pcache->master = inode;
453 inode->pg_cache = pcache;
456 int errno = inode->ops.open(inode, vfile);
458 cake_release(file_pile, vfile);
460 atomic_fetch_add(&dnode->ref_count, 1);
470 vfs_assign_inode(struct v_dnode* assign_to, struct v_inode* inode)
472 if (assign_to->inode) {
473 assign_to->inode->link_count--;
475 assign_to->inode = inode;
480 vfs_link(struct v_dnode* to_link, struct v_dnode* name)
484 lock_inode(to_link->inode);
485 if (to_link->super_block->root != name->super_block->root) {
487 } else if (!to_link->inode->ops.link) {
489 } else if (!(errno = to_link->inode->ops.link(to_link->inode, name))) {
490 vfs_assign_inode(name, to_link->inode);
492 unlock_inode(to_link->inode);
498 vfs_close(struct v_file* file)
501 if (!file->ops.close || !(errno = file->ops.close(file))) {
502 atomic_fetch_sub(&file->dnode->ref_count, 1);
503 file->inode->open_count--;
505 pcache_commit_all(file->inode);
506 cake_release(file_pile, file);
512 vfs_fsync(struct v_file* file)
514 lock_inode(file->inode);
517 pcache_commit_all(file->inode);
518 if (file->ops.sync) {
519 errno = file->ops.sync(file->inode);
522 unlock_inode(file->inode);
528 vfs_alloc_fdslot(int* fd)
530 for (size_t i = 0; i < VFS_MAX_FD; i++) {
531 if (!__current->fdtable->fds[i]) {
542 struct v_superblock* sb = cake_grab(superblock_pile);
543 memset(sb, 0, sizeof(*sb));
544 llist_init_head(&sb->sb_list);
549 vfs_sb_free(struct v_superblock* sb)
551 cake_release(superblock_pile, sb);
555 __vfs_try_evict_dnode(struct lru_node* obj)
557 struct v_dnode* dnode = container_of(obj, struct v_dnode, lru);
559 if (!dnode->ref_count) {
567 __vfs_try_evict_inode(struct lru_node* obj)
569 struct v_inode* inode = container_of(obj, struct v_inode, lru);
571 if (!inode->link_count && !inode->open_count) {
581 struct v_dnode* dnode = cake_grab(dnode_pile);
583 lru_evict_half(dnode_lru);
585 if (!(dnode = cake_grab(dnode_pile))) {
590 memset(dnode, 0, sizeof(*dnode));
591 llist_init_head(&dnode->children);
592 llist_init_head(&dnode->siblings);
593 mutex_init(&dnode->lock);
595 dnode->ref_count = ATOMIC_VAR_INIT(0);
596 dnode->name = HHSTR(vzalloc(VFS_NAME_MAXLEN), 0, 0);
598 lru_use_one(dnode_lru, &dnode->lru);
604 vfs_d_free(struct v_dnode* dnode)
606 assert(dnode->ref_count == 0);
609 assert(dnode->inode->link_count > 0);
610 dnode->inode->link_count--;
613 // Make sure the children de-referencing their parent.
614 // With lru presented, the eviction will be propagated over the entire
615 // detached subtree eventually
616 struct v_dnode *pos, *n;
617 llist_for_each(pos, n, &dnode->children, siblings)
619 vfs_dcache_remove(pos);
622 vfree(dnode->name.value);
623 cake_release(dnode_pile, dnode);
627 vfs_i_alloc(dev_t device_id, uint32_t inode_id)
629 // 我们这里假设每个文件系统与设备是一一对应(毕竟一个分区不可能有两个不同的文件系统)
630 // 而每个文件系统所产生的 v_inode 缓存必须要和其他文件系统产生的区分开来。
631 // 这也就是说,每个 v_inode 的 id
632 // 必须要由设备ID,和该虚拟inode缓存所对应的物理inode
633 // 相对于其所在的文件系统的id,进行组成!
634 inode_id = hash_32(inode_id ^ (-device_id), HASH_SIZE_BITS);
635 inode_id = (inode_id >> HASHBITS) ^ inode_id;
637 struct hbucket* slot = &inode_cache[inode_id & HASH_MASK];
638 struct v_inode *pos, *n;
639 hashtable_bucket_foreach(slot, pos, n, hash_list)
641 if (pos->id == inode_id) {
646 if (!(pos = cake_grab(inode_pile))) {
647 lru_evict_half(inode_lru);
648 if (!(pos = cake_grab(inode_pile))) {
653 memset(pos, 0, sizeof(*pos));
657 mutex_init(&pos->lock);
659 hlist_add(&slot->head, &pos->hash_list);
662 lru_use_one(inode_lru, &pos->lru);
667 vfs_i_free(struct v_inode* inode)
669 hlist_delete(&inode->hash_list);
670 cake_release(inode_pile, inode);
673 /* ---- System call definition and support ---- */
675 #define FLOCATE_CREATE_EMPTY 1
677 #define DO_STATUS(errno) SYSCALL_ESTATUS(__current->k_status = errno)
678 #define DO_STATUS_OR_RETURN(errno) ({ errno < 0 ? DO_STATUS(errno) : errno; })
680 #define TEST_FD(fd) (fd >= 0 && fd < VFS_MAX_FD)
683 __vfs_getfd(int fd, struct v_fd** fd_s)
685 if (TEST_FD(fd) && (*fd_s = __current->fdtable->fds[fd])) {
692 __vfs_try_locate_file(const char* path,
693 struct v_dnode** fdir,
694 struct v_dnode** file,
697 char name_str[VFS_NAME_MAXLEN];
698 struct hstr name = HSTR(name_str, 0);
701 vfs_walk(__current->cwd, path, fdir, &name, VFS_WALK_PARENT))) {
705 errno = vfs_walk(*fdir, name.value, file, NULL, 0);
706 if (errno != ENOENT || !(options & FLOCATE_CREATE_EMPTY)) {
710 struct v_dnode* parent = *fdir;
711 struct v_dnode* file_new = vfs_d_alloc();
717 hstrcpy(&file_new->name, &name);
721 if (!(errno = parent->inode->ops.create(parent->inode, file_new))) {
724 vfs_dcache_add(parent, file_new);
725 llist_append(&parent->children, &file_new->siblings);
727 vfs_d_free(file_new);
730 unlock_dnode(parent);
736 vfs_do_open(const char* path, int options)
739 struct v_dnode *dentry, *file;
740 struct v_file* ofile = 0;
742 errno = __vfs_try_locate_file(
743 path, &dentry, &file, (options & FO_CREATE) ? FLOCATE_CREATE_EMPTY : 0);
745 if (errno || (errno = vfs_open(file, &ofile))) {
749 struct v_inode* o_inode = ofile->inode;
750 if (!(o_inode->itype & VFS_IFSEQDEV) && !(options & FO_DIRECT)) {
751 // XXX Change here accordingly when signature of pcache_r/w changed.
752 ofile->ops.read = pcache_read;
753 ofile->ops.write = pcache_write;
756 if (!errno && !(errno = vfs_alloc_fdslot(&fd))) {
757 struct v_fd* fd_s = vzalloc(sizeof(*fd_s));
758 ofile->f_pos = ofile->inode->fsize & -((options & FO_APPEND) != 0);
760 fd_s->flags = options;
761 __current->fdtable->fds[fd] = fd_s;
768 __DEFINE_LXSYSCALL2(int, open, const char*, path, int, options)
770 int errno = vfs_do_open(path, options);
771 return DO_STATUS_OR_RETURN(errno);
774 __DEFINE_LXSYSCALL1(int, close, int, fd)
778 if ((errno = __vfs_getfd(fd, &fd_s))) {
782 if (fd_s->file->ref_count > 1) {
783 fd_s->file->ref_count--;
784 } else if ((errno = vfs_close(fd_s->file))) {
789 __current->fdtable->fds[fd] = 0;
792 return DO_STATUS(errno);
796 __vfs_readdir_callback(struct dir_context* dctx,
801 struct dirent* dent = (struct dirent*)dctx->cb_data;
802 strncpy(dent->d_name, name, DIRENT_NAME_MAX_LEN);
804 dent->d_type = dtype;
807 __DEFINE_LXSYSCALL2(int, readdir, int, fd, struct dirent*, dent)
812 if ((errno = __vfs_getfd(fd, &fd_s))) {
816 struct v_inode* inode = fd_s->file->inode;
820 if (!(fd_s->file->inode->itype & VFS_IFDIR)) {
823 struct dir_context dctx =
824 (struct dir_context){ .cb_data = dent,
825 .index = dent->d_offset,
826 .read_complete_callback =
827 __vfs_readdir_callback };
828 if (dent->d_offset == 0) {
829 __vfs_readdir_callback(&dctx, vfs_dot.value, vfs_dot.len, 0);
830 } else if (dent->d_offset == 1) {
831 __vfs_readdir_callback(&dctx, vfs_ddot.value, vfs_ddot.len, 0);
834 if ((errno = fd_s->file->ops.readdir(inode, &dctx))) {
846 return DO_STATUS(errno);
849 __DEFINE_LXSYSCALL3(int, read, int, fd, void*, buf, size_t, count)
853 if ((errno = __vfs_getfd(fd, &fd_s))) {
857 struct v_file* file = fd_s->file;
858 if ((file->inode->itype & VFS_IFDIR)) {
863 lock_inode(file->inode);
865 file->inode->atime = clock_unixtime();
867 __SYSCALL_INTERRUPTIBLE(
868 { errno = file->ops.read(file->inode, buf, count, file->f_pos); })
871 file->f_pos += errno;
872 unlock_inode(file->inode);
876 unlock_inode(file->inode);
879 return DO_STATUS(errno);
882 __DEFINE_LXSYSCALL3(int, write, int, fd, void*, buf, size_t, count)
886 if ((errno = __vfs_getfd(fd, &fd_s))) {
890 struct v_file* file = fd_s->file;
891 if ((file->inode->itype & VFS_IFDIR)) {
896 lock_inode(file->inode);
898 file->inode->mtime = clock_unixtime();
900 __SYSCALL_INTERRUPTIBLE(
901 { errno = file->ops.write(file->inode, buf, count, file->f_pos); })
904 file->f_pos += errno;
905 unlock_inode(file->inode);
909 unlock_inode(file->inode);
912 return DO_STATUS(errno);
915 __DEFINE_LXSYSCALL3(int, lseek, int, fd, int, offset, int, options)
919 if ((errno = __vfs_getfd(fd, &fd_s))) {
923 struct v_file* file = fd_s->file;
925 lock_inode(file->inode);
927 size_t fpos = file->f_pos;
930 fpos = (size_t)((int)file->f_pos + offset);
933 fpos = (size_t)((int)file->inode->fsize + offset);
939 if (!file->ops.seek || !(errno = file->ops.seek(file->inode, fpos))) {
943 unlock_inode(file->inode);
946 return DO_STATUS(errno);
950 vfs_get_path(struct v_dnode* dnode, char* buf, size_t size, int depth)
960 size_t len = vfs_get_path(dnode->parent, buf, size, depth + 1);
966 size_t cpy_size = MIN(dnode->name.len, size - len);
967 strncpy(buf + len, dnode->name.value, cpy_size);
971 buf[len++] = PATH_DELIM;
978 vfs_readlink(struct v_dnode* dnode, char* buf, size_t size)
981 struct v_inode* inode = dnode->inode;
982 if (inode->ops.read_symlink) {
985 int errno = inode->ops.read_symlink(inode, &link);
986 strncpy(buf, link, size);
994 __DEFINE_LXSYSCALL3(int, realpathat, int, fd, char*, buf, size_t, size)
998 if ((errno = __vfs_getfd(fd, &fd_s))) {
1002 struct v_dnode* dnode;
1003 errno = vfs_get_path(fd_s->file->dnode, buf, size, 0);
1010 return DO_STATUS(errno);
1013 __DEFINE_LXSYSCALL3(int, readlink, const char*, path, char*, buf, size_t, size)
1016 struct v_dnode* dnode;
1018 vfs_walk(__current->cwd, path, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
1019 errno = vfs_readlink(dnode, buf, size);
1026 return DO_STATUS(errno);
1029 __DEFINE_LXSYSCALL4(int,
1042 if ((errno = __vfs_getfd(dirfd, &fd_s))) {
1046 struct v_dnode* dnode;
1047 if (!(errno = vfs_walk(
1048 fd_s->file->dnode, pathname, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
1049 errno = vfs_readlink(fd_s->file->dnode, buf, size);
1057 return DO_STATUS(errno);
1062 When we perform operation that could affect the layout of
1063 directory (i.e., rename, mkdir, rmdir). We must lock the parent dir
1064 whenever possible. This will blocking any ongoing path walking to reach
1065 it hence avoid any partial state.
1068 __DEFINE_LXSYSCALL1(int, rmdir, const char*, pathname)
1071 struct v_dnode* dnode;
1072 if ((errno = vfs_walk(__current->cwd, pathname, &dnode, NULL, 0))) {
1073 return DO_STATUS(errno);
1078 if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
1083 if (dnode->ref_count > 1 || dnode->inode->open_count) {
1088 if (!llist_empty(&dnode->children)) {
1093 struct v_dnode* parent = dnode->parent;
1101 lock_inode(parent->inode);
1103 if ((dnode->inode->itype & VFS_IFDIR)) {
1104 errno = parent->inode->ops.rmdir(parent->inode, dnode);
1106 vfs_dcache_remove(dnode);
1112 unlock_inode(parent->inode);
1113 unlock_dnode(parent);
1116 unlock_dnode(dnode);
1117 return DO_STATUS(errno);
1120 __DEFINE_LXSYSCALL1(int, mkdir, const char*, path)
1123 struct v_dnode *parent, *dir = vfs_d_alloc();
1130 if ((errno = vfs_walk(
1131 __current->cwd, path, &parent, &dir->name, VFS_WALK_PARENT))) {
1136 lock_inode(parent->inode);
1138 if ((parent->super_block->fs->types & FSTYPE_ROFS)) {
1140 } else if (!parent->inode->ops.mkdir) {
1142 } else if (!(parent->inode->itype & VFS_IFDIR)) {
1144 } else if (!(errno = parent->inode->ops.mkdir(parent->inode, dir))) {
1145 llist_append(&parent->children, &dir->siblings);
1152 unlock_inode(parent->inode);
1153 unlock_dnode(parent);
1155 return DO_STATUS(errno);
1159 __vfs_do_unlink(struct v_dnode* dnode)
1161 struct v_inode* inode = dnode->inode;
1163 if (dnode->ref_count > 1) {
1170 if (inode->open_count) {
1172 } else if (!(inode->itype & VFS_IFDIR)) {
1173 // The underlying unlink implementation should handle
1175 errno = inode->ops.unlink(inode);
1177 vfs_dcache_remove(dnode);
1184 unlock_inode(inode);
1189 __DEFINE_LXSYSCALL1(int, unlink, const char*, pathname)
1192 struct v_dnode* dnode;
1193 if ((errno = vfs_walk(__current->cwd, pathname, &dnode, NULL, 0))) {
1196 if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
1201 errno = __vfs_do_unlink(dnode);
1204 return DO_STATUS(errno);
1207 __DEFINE_LXSYSCALL2(int, unlinkat, int, fd, const char*, pathname)
1211 if ((errno = __vfs_getfd(fd, &fd_s))) {
1215 struct v_dnode* dnode;
1216 if (!(errno = vfs_walk(fd_s->file->dnode, pathname, &dnode, NULL, 0))) {
1217 errno = __vfs_do_unlink(dnode);
1221 return DO_STATUS(errno);
1224 __DEFINE_LXSYSCALL2(int, link, const char*, oldpath, const char*, newpath)
1227 struct v_dnode *dentry, *to_link, *name_dentry, *name_file;
1229 errno = __vfs_try_locate_file(oldpath, &dentry, &to_link, 0);
1231 errno = __vfs_try_locate_file(
1232 newpath, &name_dentry, &name_file, FLOCATE_CREATE_EMPTY);
1235 } else if (name_file) {
1236 errno = vfs_link(to_link, name_file);
1239 return DO_STATUS(errno);
1242 __DEFINE_LXSYSCALL1(int, fsync, int, fildes)
1246 if (!(errno = __vfs_getfd(fildes, &fd_s))) {
1247 errno = vfs_fsync(fd_s->file);
1250 return DO_STATUS(errno);
1254 vfs_dup_fd(struct v_fd* old, struct v_fd** new)
1257 struct v_fd* copied = cake_grab(fd_pile);
1259 memcpy(copied, old, sizeof(struct v_fd));
1261 atomic_fetch_add(&old->file->ref_count, 1);
1269 vfs_dup2(int oldfd, int newfd)
1271 if (newfd == oldfd) {
1276 struct v_fd *oldfd_s, *newfd_s;
1277 if ((errno = __vfs_getfd(oldfd, &oldfd_s))) {
1281 if (!TEST_FD(newfd)) {
1286 newfd_s = __current->fdtable->fds[newfd];
1287 if (newfd_s && (errno = vfs_close(newfd_s->file))) {
1291 if (!(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1292 __current->fdtable->fds[newfd] = newfd_s;
1297 return DO_STATUS(errno);
1300 __DEFINE_LXSYSCALL2(int, dup2, int, oldfd, int, newfd)
1302 return vfs_dup2(oldfd, newfd);
1305 __DEFINE_LXSYSCALL1(int, dup, int, oldfd)
1308 struct v_fd *oldfd_s, *newfd_s;
1309 if ((errno = __vfs_getfd(oldfd, &oldfd_s))) {
1313 if (!(errno = vfs_alloc_fdslot(&newfd)) &&
1314 !(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1315 __current->fdtable->fds[newfd] = newfd_s;
1320 return DO_STATUS(errno);
1323 __DEFINE_LXSYSCALL2(int,
1331 struct v_dnode* dnode;
1332 if ((errno = vfs_walk(__current->cwd, pathname, &dnode, NULL, 0))) {
1335 if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
1339 if (!dnode->inode->ops.set_symlink) {
1344 lock_inode(dnode->inode);
1346 errno = dnode->inode->ops.set_symlink(dnode->inode, link_target);
1348 unlock_inode(dnode->inode);
1351 return DO_STATUS(errno);
1355 __vfs_do_chdir(struct v_dnode* dnode)
1361 if (!(dnode->inode->itype & VFS_IFDIR)) {
1366 if (__current->cwd) {
1367 atomic_fetch_add(&__current->cwd->ref_count, 1);
1370 atomic_fetch_sub(&dnode->ref_count, 1);
1371 __current->cwd = dnode;
1373 unlock_dnode(dnode);
1379 __DEFINE_LXSYSCALL1(int, chdir, const char*, path)
1381 struct v_dnode* dnode;
1384 if ((errno = vfs_walk(__current->cwd, path, &dnode, NULL, 0))) {
1388 errno = __vfs_do_chdir(dnode);
1391 return DO_STATUS(errno);
1394 __DEFINE_LXSYSCALL1(int, fchdir, int, fd)
1399 if ((errno = __vfs_getfd(fd, &fd_s))) {
1403 errno = __vfs_do_chdir(fd_s->file->dnode);
1406 return DO_STATUS(errno);
1409 __DEFINE_LXSYSCALL2(char*, getcwd, char*, buf, size_t, size)
1420 if (!__current->cwd) {
1424 len = vfs_get_path(__current->cwd, buf, size, 0);
1431 buf[len + 1] = '\0';
1436 __current->k_status = errno;
1441 vfs_do_rename(struct v_dnode* current, struct v_dnode* target)
1443 if (current->inode->id == target->inode->id) {
1448 if (current->ref_count > 1 || target->ref_count > 1) {
1452 if (current->super_block != target->super_block) {
1458 struct v_dnode* oldparent = current->parent;
1459 struct v_dnode* newparent = target->parent;
1461 lock_dnode(current);
1464 lock_dnode(oldparent);
1466 lock_dnode(newparent);
1468 if (!llist_empty(&target->children)) {
1470 unlock_dnode(target);
1474 if ((errno = current->inode->ops.rename(current->inode, current, target))) {
1475 unlock_dnode(target);
1479 // re-position current
1480 hstrcpy(¤t->name, &target->name);
1481 vfs_dcache_rehash(newparent, current);
1484 vfs_dcache_remove(target);
1486 unlock_dnode(target);
1489 unlock_dnode(current);
1491 unlock_dnode(oldparent);
1493 unlock_dnode(newparent);
1498 __DEFINE_LXSYSCALL2(int, rename, const char*, oldpath, const char*, newpath)
1500 struct v_dnode *cur, *target_parent, *target;
1501 struct hstr name = HSTR(valloc(VFS_NAME_MAXLEN), 0);
1504 if ((errno = vfs_walk(__current->cwd, oldpath, &cur, NULL, 0))) {
1508 if ((errno = vfs_walk(
1509 __current->cwd, newpath, &target_parent, &name, VFS_WALK_PARENT))) {
1513 errno = vfs_walk(target_parent, name.value, &target, NULL, 0);
1514 if (errno == ENOENT) {
1515 target = vfs_d_alloc();
1525 hstrcpy(&target->name, &name);
1527 if (!(errno = vfs_do_rename(cur, target))) {
1533 return DO_STATUS(errno);
1536 __DEFINE_LXSYSCALL3(int,
1545 struct v_dnode *dev, *mnt;
1548 if ((errno = vfs_walk(__current->cwd, source, &dev, NULL, 0))) {
1552 if ((errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
1556 if (!(dev->inode->itype & VFS_IFVOLDEV)) {
1561 if (mnt->ref_count > 1) {
1566 // FIXME should not touch the underlying fs!
1567 struct device* device =
1568 (struct device*)((struct twifs_node*)dev->inode->data)->data;
1570 errno = vfs_mount_at(fstype, device, mnt);
1573 return DO_STATUS(errno);
1576 __DEFINE_LXSYSCALL1(int, unmount, const char*, target)
1578 return vfs_unmount(target);