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;
278 * This happened when process is terminated while blocking on read.
279 * In that case, the process is still holding the inode lock and it
280 will never get released.
281 * The unlocking should also include ownership check.
283 * To see why, consider two process both open the same file both with
285 * Process A: busy on reading x
286 * Process B: do nothing with x
287 * Assuming that, after a very short time, process B get terminated
288 * while process A is still busy in it's reading business. By this
289 * design, the inode lock of this file x is get released by B rather
290 * than A. And this will cause a probable race condition on A if other
291 * process is writing to this file later after B exit.
294 mutex_unlock_for(&inode->lock, pid);
296 if (file->ref_count > 1) {
297 atomic_fetch_sub(&file->ref_count, 1);
301 if ((errno = file->ops->close(file))) {
305 atomic_fetch_sub(&file->dnode->ref_count, 1);
306 mnt_chillax(file->dnode->mnt);
307 cake_release(file_pile, file);
310 if the current inode is not being locked by other
311 threads that does not share same open context,
312 then we can try to do sync opportunistically
314 if (mutex_on_hold(&inode->lock)) {
320 pcache_commit_all(inode);
323 if (!inode->open_count) {
324 __sync_inode_nolock(inode);
334 vfs_close(struct v_file* file)
336 return vfs_pclose(file, __current->pid);
340 vfs_free_fd(struct v_fd* fd)
342 cake_release(fd_pile, fd);
346 vfs_isync(struct v_inode* inode)
350 int errno = __sync_inode_nolock(inode);
358 vfs_fsync(struct v_file* file)
361 if ((errno = vfs_check_writable(file->dnode))) {
365 return vfs_isync(file->inode);
369 vfs_alloc_fdslot(int* fd)
371 for (size_t i = 0; i < VFS_MAX_FD; i++) {
372 if (!__current->fdtable->fds[i]) {
383 struct v_superblock* sb = cake_grab(superblock_pile);
384 memset(sb, 0, sizeof(*sb));
385 llist_init_head(&sb->sb_list);
386 sb->i_cache = vzalloc(VFS_HASHTABLE_SIZE * sizeof(struct hbucket));
392 vfs_sb_ref(struct v_superblock* sb)
398 vfs_sb_free(struct v_superblock* sb)
400 assert(sb->ref_count);
407 if (sb->ops.release) {
412 cake_release(superblock_pile, sb);
416 __vfs_try_evict_dnode(struct lru_node* obj)
418 struct v_dnode* dnode = container_of(obj, struct v_dnode, lru);
420 if (!dnode->ref_count) {
428 __vfs_try_evict_inode(struct lru_node* obj)
430 struct v_inode* inode = container_of(obj, struct v_inode, lru);
432 if (!inode->link_count && !inode->open_count) {
440 vfs_d_alloc(struct v_dnode* parent, struct hstr* name)
442 struct v_dnode* dnode = cake_grab(dnode_pile);
444 lru_evict_half(dnode_lru);
446 if (!(dnode = cake_grab(dnode_pile))) {
451 memset(dnode, 0, sizeof(*dnode));
452 llist_init_head(&dnode->children);
453 llist_init_head(&dnode->siblings);
454 llist_init_head(&dnode->aka_list);
455 mutex_init(&dnode->lock);
457 dnode->ref_count = ATOMIC_VAR_INIT(0);
458 dnode->name = HHSTR(vzalloc(VFS_NAME_MAXLEN), 0, 0);
460 hstrcpy(&dnode->name, name);
463 vfs_d_assign_sb(dnode, parent->super_block);
464 dnode->mnt = parent->mnt;
467 lru_use_one(dnode_lru, &dnode->lru);
473 vfs_d_free(struct v_dnode* dnode)
475 assert(dnode->ref_count == 1);
478 assert(dnode->inode->link_count > 0);
479 dnode->inode->link_count--;
482 vfs_dcache_remove(dnode);
483 // Make sure the children de-referencing their parent.
484 // With lru presented, the eviction will be propagated over the entire
485 // detached subtree eventually
486 struct v_dnode *pos, *n;
487 llist_for_each(pos, n, &dnode->children, siblings)
489 vfs_dcache_remove(pos);
492 if (dnode->destruct) {
493 dnode->destruct(dnode);
496 vfs_sb_free(dnode->super_block);
497 vfree((void*)dnode->name.value);
498 cake_release(dnode_pile, dnode);
502 vfs_i_find(struct v_superblock* sb, u32_t i_id)
504 struct hbucket* slot = &sb->i_cache[i_id & VFS_HASH_MASK];
505 struct v_inode *pos, *n;
506 hashtable_bucket_foreach(slot, pos, n, hash_list)
508 if (pos->id == i_id) {
509 lru_use_one(inode_lru, &pos->lru);
518 vfs_i_addhash(struct v_inode* inode)
520 struct hbucket* slot = &inode->sb->i_cache[inode->id & VFS_HASH_MASK];
522 hlist_delete(&inode->hash_list);
523 hlist_add(&slot->head, &inode->hash_list);
527 vfs_i_alloc(struct v_superblock* sb)
529 assert(sb->ops.init_inode);
531 struct v_inode* inode;
532 if (!(inode = cake_grab(inode_pile))) {
533 lru_evict_half(inode_lru);
534 if (!(inode = cake_grab(inode_pile))) {
539 memset(inode, 0, sizeof(*inode));
540 mutex_init(&inode->lock);
541 llist_init_head(&inode->xattrs);
542 llist_init_head(&inode->aka_dnodes);
544 sb->ops.init_inode(sb, inode);
546 inode->ctime = clock_unixtime();
547 inode->atime = inode->ctime;
548 inode->mtime = inode->ctime;
550 vfs_i_assign_sb(inode, sb);
551 lru_use_one(inode_lru, &inode->lru);
556 vfs_i_free(struct v_inode* inode)
558 if (inode->pg_cache) {
559 pcache_release(inode->pg_cache);
560 vfree(inode->pg_cache);
562 // we don't need to sync inode.
563 // If an inode can be free, then it must be properly closed.
564 // Hence it must be synced already!
565 if (inode->destruct) {
566 inode->destruct(inode);
569 vfs_sb_free(inode->sb);
570 hlist_delete(&inode->hash_list);
571 cake_release(inode_pile, inode);
574 /* ---- System call definition and support ---- */
576 // make a new name when not exists
577 #define FLOC_MAYBE_MKNAME 1
579 // name must be non-exist and made.
580 #define FLOC_MKNAME 2
583 #define FLOC_NOFOLLOW 4
586 vfs_getfd(int fd, struct v_fd** fd_s)
588 if (TEST_FD(fd) && (*fd_s = __current->fdtable->fds[fd])) {
595 __vfs_mknod(struct v_inode* parent, struct v_dnode* dnode,
596 unsigned int itype, dev_t* dev)
600 errno = parent->ops->create(parent, dnode, itype);
608 struct file_locator {
610 struct v_dnode* file;
615 * @brief unlock the file locator (floc) if possible.
616 * If the file to be located if not exists, and
617 * any FLOC_*MKNAME flag is set, then the parent
618 * dnode will be locked until the file has been properly
619 * finalised by subsequent logic.
624 __floc_try_unlock(struct file_locator* floc)
628 unlock_dnode(floc->dir);
633 __vfs_try_locate_file(const char* path,
634 struct file_locator* floc,
637 char name_str[VFS_NAME_MAXLEN];
638 struct v_dnode *fdir, *file;
639 struct hstr name = HSTR(name_str, 0);
640 int errno, woption = 0;
642 if ((options & FLOC_NOFOLLOW)) {
643 woption |= VFS_WALK_NOFOLLOW;
644 options &= ~FLOC_NOFOLLOW;
649 errno = vfs_walk_proc(path, &fdir, &name, woption | VFS_WALK_PARENT);
654 errno = vfs_walk(fdir, name.value, &file, NULL, woption);
656 if (errno && errno != ENOENT) {
661 if ((options & FLOC_MKNAME)) {
672 errno = vfs_check_writable(fdir);
679 file = vfs_d_alloc(fdir, &name);
687 vfs_dcache_add(fdir, file);
697 vfs_do_open(const char* path, int options)
699 int errno, fd, loptions = 0;
700 struct v_dnode *dentry, *file;
701 struct v_file* ofile = NULL;
702 struct file_locator floc;
703 struct v_inode* inode;
705 if ((options & FO_CREATE)) {
706 loptions |= FLOC_MAYBE_MKNAME;
707 } else if ((options & FO_NOFOLLOW)) {
708 loptions |= FLOC_NOFOLLOW;
711 errno = __vfs_try_locate_file(path, &floc, loptions);
713 if (errno || (errno = vfs_alloc_fdslot(&fd))) {
721 errno = __vfs_mknod(dentry->inode, file, VFS_IFFILE, NULL);
724 __floc_try_unlock(&floc);
728 __floc_try_unlock(&floc);
732 if ((errno = vfs_open(file, &ofile))) {
736 inode = ofile->inode;
739 struct v_fd* fd_s = cake_grab(fd_pile);
740 memset(fd_s, 0, sizeof(*fd_s));
742 if ((options & O_TRUNC)) {
743 file->inode->fsize = 0;
746 if (vfs_get_dtype(inode->itype) == DT_DIR) {
751 fd_s->flags = options;
752 __current->fdtable->fds[fd] = fd_s;
759 __DEFINE_LXSYSCALL2(int, open, const char*, path, int, options)
761 int errno = vfs_do_open(path, options);
762 return DO_STATUS_OR_RETURN(errno);
765 __DEFINE_LXSYSCALL1(int, close, int, fd)
769 if ((errno = vfs_getfd(fd, &fd_s))) {
773 if ((errno = vfs_close(fd_s->file))) {
777 cake_release(fd_pile, fd_s);
778 __current->fdtable->fds[fd] = 0;
781 return DO_STATUS(errno);
785 __vfs_readdir_callback(struct dir_context* dctx,
790 struct lx_dirent* dent = (struct lx_dirent*)dctx->cb_data;
791 strncpy(dent->d_name, name, MIN(len, DIRENT_NAME_MAX_LEN));
793 dent->d_type = dtype;
796 __DEFINE_LXSYSCALL2(int, sys_readdir, int, fd, struct lx_dirent*, dent)
801 if ((errno = vfs_getfd(fd, &fd_s))) {
805 struct v_inode* inode = fd_s->file->inode;
809 if (!check_directory_node(inode)) {
814 struct dir_context dctx = (struct dir_context) {
816 .read_complete_callback = __vfs_readdir_callback
819 if ((errno = fd_s->file->ops->readdir(fd_s->file, &dctx)) != 1) {
829 return DO_STATUS_OR_RETURN(errno);
832 __DEFINE_LXSYSCALL3(int, read, int, fd, void*, buf, size_t, count)
836 if ((errno = vfs_getfd(fd, &fd_s))) {
840 struct v_file* file = fd_s->file;
841 if (check_directory_node(file->inode)) {
846 lock_inode(file->inode);
848 file->inode->atime = clock_unixtime();
850 if (check_seqdev_node(file->inode) || (fd_s->flags & FO_DIRECT)) {
851 errno = file->ops->read(file->inode, buf, count, file->f_pos);
853 errno = pcache_read(file->inode, buf, count, file->f_pos);
857 file->f_pos += errno;
858 unlock_inode(file->inode);
862 unlock_inode(file->inode);
865 return DO_STATUS(errno);
868 __DEFINE_LXSYSCALL3(int, write, int, fd, void*, buf, size_t, count)
872 if ((errno = vfs_getfd(fd, &fd_s))) {
876 struct v_inode* inode;
877 struct v_file* file = fd_s->file;
879 if ((errno = vfs_check_writable(file->dnode))) {
883 if (check_directory_node(file->inode)) {
891 inode->mtime = clock_unixtime();
892 if ((fd_s->flags & O_APPEND)) {
893 file->f_pos = inode->fsize;
896 if (check_seqdev_node(inode) || (fd_s->flags & FO_DIRECT)) {
897 errno = file->ops->write(inode, buf, count, file->f_pos);
899 errno = pcache_write(inode, buf, count, file->f_pos);
903 file->f_pos += errno;
904 inode->fsize = MAX(inode->fsize, file->f_pos);
913 return DO_STATUS(errno);
916 __DEFINE_LXSYSCALL3(int, lseek, int, fd, int, offset, int, options)
920 if ((errno = vfs_getfd(fd, &fd_s))) {
924 struct v_file* file = fd_s->file;
925 struct v_inode* inode = file->inode;
927 if (!file->ops->seek) {
935 int fpos = file->f_pos;
937 if (vfs_get_dtype(inode->itype) == DT_DIR) {
938 options = (options != FSEEK_END) ? options : FSEEK_SET;
943 overflow = sadd_of((int)file->f_pos, offset, &fpos);
946 overflow = sadd_of((int)inode->fsize, offset, &fpos);
957 errno = file->ops->seek(file, fpos);
963 return DO_STATUS(errno);
967 vfs_get_path(struct v_dnode* dnode, char* buf, size_t size, int depth)
979 if (dnode->parent != dnode) {
980 len = vfs_get_path(dnode->parent, buf, size, depth + 1);
987 if (!len || buf[len - 1] != VFS_PATH_DELIM) {
988 buf[len++] = VFS_PATH_DELIM;
991 size_t cpy_size = MIN(dnode->name.len, size - len);
992 strncpy(buf + len, dnode->name.value, cpy_size);
999 vfs_readlink(struct v_dnode* dnode, char* buf, size_t size)
1002 struct v_inode* inode = dnode->inode;
1004 if (!check_symlink_node(inode)) {
1008 if (!inode->ops->read_symlink) {
1014 int errno = inode->ops->read_symlink(inode, &link);
1016 strncpy(buf, link, MIN(size, (size_t)errno));
1019 unlock_inode(inode);
1024 vfs_get_dtype(int itype)
1026 int dtype = DT_FILE;
1027 if (check_itype(itype, VFS_IFSYMLINK)) {
1028 dtype |= DT_SYMLINK;
1031 if (check_itype(itype, VFS_IFDIR)) {
1041 __DEFINE_LXSYSCALL3(int, realpathat, int, fd, char*, buf, size_t, size)
1045 if ((errno = vfs_getfd(fd, &fd_s))) {
1049 struct v_dnode* dnode;
1050 errno = vfs_get_path(fd_s->file->dnode, buf, size, 0);
1057 return DO_STATUS(errno);
1060 __DEFINE_LXSYSCALL3(int, readlink, const char*, path, char*, buf, size_t, size)
1063 struct v_dnode* dnode;
1064 if (!(errno = vfs_walk_proc(path, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
1065 errno = vfs_readlink(dnode, buf, size);
1072 return DO_STATUS(errno);
1075 __DEFINE_LXSYSCALL4(
1076 int, readlinkat, int, dirfd, const char*, pathname, char*, buf, size_t, size)
1080 if ((errno = vfs_getfd(dirfd, &fd_s))) {
1084 pathname = pathname ? pathname : "";
1086 struct v_dnode* dnode;
1087 if (!(errno = vfs_walk(
1088 fd_s->file->dnode, pathname, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
1089 errno = vfs_readlink(fd_s->file->dnode, buf, size);
1097 return DO_STATUS(errno);
1102 When we perform operation that could affect the layout of
1103 directory (i.e., rename, mkdir, rmdir). We must lock the parent dir
1104 whenever possible. This will blocking any ongoing path walking to reach
1105 it hence avoid any partial state.
1108 __DEFINE_LXSYSCALL1(int, rmdir, const char*, pathname)
1111 struct v_dnode* dnode;
1112 if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
1113 return DO_STATUS(errno);
1118 if ((errno = vfs_check_writable(dnode))) {
1122 if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
1127 if (dnode->ref_count > 1 || dnode->inode->open_count) {
1132 if (!llist_empty(&dnode->children)) {
1137 struct v_dnode* parent = dnode->parent;
1145 lock_inode(parent->inode);
1147 if (check_directory_node(dnode->inode)) {
1148 errno = parent->inode->ops->rmdir(parent->inode, dnode);
1150 vfs_dcache_remove(dnode);
1156 unlock_inode(parent->inode);
1157 unlock_dnode(parent);
1160 unlock_dnode(dnode);
1161 return DO_STATUS(errno);
1164 __DEFINE_LXSYSCALL1(int, mkdir, const char*, path)
1167 struct v_dnode *parent, *dir;
1168 char name_value[VFS_NAME_MAXLEN];
1169 struct hstr name = HHSTR(name_value, 0, 0);
1171 if ((errno = vfs_walk_proc(path, &parent, &name, VFS_WALK_PARENT))) {
1175 if (!(errno = vfs_walk(parent, name_value, &dir, NULL, 0))) {
1180 if ((errno = vfs_check_writable(parent))) {
1184 if (!(dir = vfs_d_alloc(parent, &name))) {
1189 struct v_inode* inode = parent->inode;
1194 if ((parent->super_block->fs->types & FSTYPE_ROFS)) {
1196 } else if (!inode->ops->mkdir) {
1198 } else if (!check_directory_node(inode)) {
1200 } else if (!(errno = inode->ops->mkdir(inode, dir))) {
1201 vfs_dcache_add(parent, dir);
1208 unlock_inode(inode);
1209 unlock_dnode(parent);
1211 return DO_STATUS(errno);
1215 __vfs_do_unlink(struct v_dnode* dnode)
1218 struct v_inode* inode = dnode->inode;
1220 if (dnode->ref_count > 1) {
1224 if ((errno = vfs_check_writable(dnode))) {
1230 if (inode->open_count) {
1232 } else if (!check_directory_node(inode)) {
1233 errno = inode->ops->unlink(inode, dnode);
1241 unlock_inode(inode);
1246 __DEFINE_LXSYSCALL1(int, unlink, const char*, pathname)
1249 struct v_dnode* dnode;
1250 if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
1254 errno = __vfs_do_unlink(dnode);
1257 return DO_STATUS(errno);
1260 __DEFINE_LXSYSCALL2(int, unlinkat, int, fd, const char*, pathname)
1264 if ((errno = vfs_getfd(fd, &fd_s))) {
1268 struct v_dnode* dnode;
1269 if (!(errno = vfs_walk(fd_s->file->dnode, pathname, &dnode, NULL, 0))) {
1270 errno = __vfs_do_unlink(dnode);
1274 return DO_STATUS(errno);
1277 __DEFINE_LXSYSCALL2(int, link, const char*, oldpath, const char*, newpath)
1280 struct file_locator floc;
1281 struct v_dnode *to_link, *name_file;
1283 errno = __vfs_try_locate_file(oldpath, &floc, 0);
1288 __floc_try_unlock(&floc);
1290 to_link = floc.file;
1291 errno = __vfs_try_locate_file(newpath, &floc, FLOC_MKNAME);
1296 name_file = floc.file;
1297 errno = vfs_link(to_link, name_file);
1299 vfs_d_free(name_file);
1303 __floc_try_unlock(&floc);
1304 return DO_STATUS(errno);
1307 __DEFINE_LXSYSCALL1(int, fsync, int, fildes)
1312 if (!(errno = vfs_getfd(fildes, &fd_s))) {
1313 errno = vfs_fsync(fd_s->file);
1316 return DO_STATUS(errno);
1320 vfs_dup_fd(struct v_fd* old, struct v_fd** new)
1323 struct v_fd* copied = cake_grab(fd_pile);
1325 memcpy(copied, old, sizeof(struct v_fd));
1327 atomic_fetch_add(&old->file->ref_count, 1);
1335 vfs_dup2(int oldfd, int newfd)
1337 if (newfd == oldfd) {
1342 struct v_fd *oldfd_s, *newfd_s;
1343 if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
1347 if (!TEST_FD(newfd)) {
1352 newfd_s = __current->fdtable->fds[newfd];
1353 if (newfd_s && (errno = vfs_close(newfd_s->file))) {
1357 if (!(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1358 __current->fdtable->fds[newfd] = newfd_s;
1363 return DO_STATUS(errno);
1366 __DEFINE_LXSYSCALL2(int, dup2, int, oldfd, int, newfd)
1368 return vfs_dup2(oldfd, newfd);
1371 __DEFINE_LXSYSCALL1(int, dup, int, oldfd)
1374 struct v_fd *oldfd_s, *newfd_s;
1375 if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
1379 if (!(errno = vfs_alloc_fdslot(&newfd)) &&
1380 !(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1381 __current->fdtable->fds[newfd] = newfd_s;
1386 return DO_STATUS(errno);
1389 __DEFINE_LXSYSCALL2(
1390 int, symlink, const char*, pathname, const char*, link_target)
1393 struct file_locator floc;
1394 struct v_dnode *file;
1395 struct v_inode *f_ino;
1397 errno = __vfs_try_locate_file(pathname, &floc, FLOC_MKNAME);
1403 errno = __vfs_mknod(floc.dir->inode, file, VFS_IFSYMLINK, NULL);
1409 f_ino = file->inode;
1413 errno = vfs_check_writable(file);
1418 if (!f_ino->ops->set_symlink) {
1425 errno = f_ino->ops->set_symlink(f_ino, link_target);
1427 unlock_inode(f_ino);
1430 __floc_try_unlock(&floc);
1431 return DO_STATUS(errno);
1435 vfs_ref_file(struct v_file* file)
1437 atomic_fetch_add(&file->ref_count, 1);
1441 vfs_ref_dnode(struct v_dnode* dnode)
1443 atomic_fetch_add(&dnode->ref_count, 1);
1446 mnt_mkbusy(dnode->mnt);
1451 vfs_unref_dnode(struct v_dnode* dnode)
1453 atomic_fetch_sub(&dnode->ref_count, 1);
1455 mnt_chillax(dnode->mnt);
1460 vfs_do_chdir(struct proc_info* proc, struct v_dnode* dnode)
1466 if (!check_directory_node(dnode->inode)) {
1472 vfs_unref_dnode(proc->cwd);
1475 vfs_ref_dnode(dnode);
1478 unlock_dnode(dnode);
1484 __DEFINE_LXSYSCALL1(int, chdir, const char*, path)
1486 struct v_dnode* dnode;
1489 if ((errno = vfs_walk_proc(path, &dnode, NULL, 0))) {
1493 errno = vfs_do_chdir((struct proc_info*)__current, dnode);
1496 return DO_STATUS(errno);
1499 __DEFINE_LXSYSCALL1(int, fchdir, int, fd)
1504 if ((errno = vfs_getfd(fd, &fd_s))) {
1508 errno = vfs_do_chdir((struct proc_info*)__current, fd_s->file->dnode);
1511 return DO_STATUS(errno);
1514 __DEFINE_LXSYSCALL2(char*, getcwd, char*, buf, size_t, size)
1525 if (!__current->cwd) {
1526 *buf = VFS_PATH_DELIM;
1529 len = vfs_get_path(__current->cwd, buf, size, 0);
1541 syscall_result(errno);
1546 vfs_do_rename(struct v_dnode* current, struct v_dnode* target)
1549 if (current->inode->id == target->inode->id) {
1554 if ((errno = vfs_check_writable(current))) {
1558 if (current->ref_count > 1 || target->ref_count > 1) {
1562 if (current->super_block != target->super_block) {
1566 struct v_dnode* oldparent = current->parent;
1567 struct v_dnode* newparent = target->parent;
1569 lock_dnode(current);
1572 lock_dnode(oldparent);
1574 lock_dnode(newparent);
1576 if (!llist_empty(&target->children)) {
1578 unlock_dnode(target);
1583 current->inode->ops->rename(current->inode, current, target))) {
1584 unlock_dnode(target);
1588 // re-position current
1589 hstrcpy(¤t->name, &target->name);
1590 vfs_dcache_rehash(newparent, current);
1595 unlock_dnode(target);
1598 unlock_dnode(current);
1600 unlock_dnode(oldparent);
1602 unlock_dnode(newparent);
1607 __DEFINE_LXSYSCALL2(int, rename, const char*, oldpath, const char*, newpath)
1609 struct v_dnode *cur, *target_parent, *target;
1610 struct hstr name = HSTR(valloc(VFS_NAME_MAXLEN), 0);
1613 if ((errno = vfs_walk_proc(oldpath, &cur, NULL, 0))) {
1617 if ((errno = vfs_walk(
1618 __current->cwd, newpath, &target_parent, &name, VFS_WALK_PARENT))) {
1622 errno = vfs_walk(target_parent, name.value, &target, NULL, 0);
1623 if (errno == ENOENT) {
1624 target = vfs_d_alloc(target_parent, &name);
1625 vfs_dcache_add(target_parent, target);
1635 errno = vfs_do_rename(cur, target);
1638 vfree((void*)name.value);
1639 return DO_STATUS(errno);
1642 __DEFINE_LXSYSCALL2(int, fstat, int, fd, struct file_stat*, stat)
1647 if ((errno = vfs_getfd(fd, &fds))) {
1651 struct v_inode* vino = fds->file->inode;
1652 struct device* fdev = vino->sb->dev;
1654 *stat = (struct file_stat){.st_ino = vino->id,
1655 .st_blocks = vino->lb_usage,
1656 .st_size = vino->fsize,
1657 .mode = vino->itype,
1658 .st_ioblksize = PAGE_SIZE,
1659 .st_blksize = vino->sb->blksize};
1661 if (check_device_node(vino)) {
1662 struct device* rdev = resolve_device(vino->data);
1668 stat->st_rdev = (dev_t){.meta = rdev->ident.fn_grp,
1669 .unique = rdev->ident.unique,
1670 .index = dev_uid(rdev) };
1674 stat->st_dev = (dev_t){.meta = fdev->ident.fn_grp,
1675 .unique = fdev->ident.unique,
1676 .index = dev_uid(fdev) };
1680 return DO_STATUS(errno);