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;
68 struct lru_zone *dnode_lru, *inode_lru;
70 struct hstr vfs_ddot = HSTR("..", 2);
71 struct hstr vfs_dot = HSTR(".", 1);
72 struct hstr vfs_empty = HSTR("", 0);
75 __vfs_try_evict_dnode(struct lru_node* obj);
78 __vfs_try_evict_inode(struct lru_node* obj);
83 // 为他们专门创建一个蛋糕堆,而不使用valloc,这样我们可以最小化内碎片的产生
84 dnode_pile = cake_new_pile("dnode_cache", sizeof(struct v_dnode), 1, 0);
85 inode_pile = cake_new_pile("inode_cache", sizeof(struct v_inode), 1, 0);
86 file_pile = cake_new_pile("file_cache", sizeof(struct v_file), 1, 0);
87 fd_pile = cake_new_pile("fd_cache", sizeof(struct v_fd), 1, 0);
89 cake_new_pile("sb_cache", sizeof(struct v_superblock), 1, 0);
91 dnode_lru = lru_new_zone("vfs_dnode", __vfs_try_evict_dnode);
92 inode_lru = lru_new_zone("vfs_inode", __vfs_try_evict_inode);
94 hstr_rehash(&vfs_ddot, HSTR_FULL_HASH);
95 hstr_rehash(&vfs_dot, HSTR_FULL_HASH);
98 vfs_sysroot = vfs_d_alloc(NULL, &vfs_empty);
99 vfs_sysroot->parent = vfs_sysroot;
101 vfs_ref_dnode(vfs_sysroot);
104 static inline struct hbucket*
105 __dcache_hash(struct v_dnode* parent, u32_t* hash)
107 struct hbucket* d_cache;
110 d_cache = parent->super_block->d_cache;
112 _hash = _hash ^ (_hash >> VFS_HASHBITS);
113 _hash += (u32_t)__ptr(parent);
116 return &d_cache[_hash & VFS_HASH_MASK];
120 __sync_inode_nolock(struct v_inode* inode)
122 pcache_commit_all(inode);
125 if (inode->ops->sync) {
126 errno = inode->ops->sync(inode);
133 vfs_dcache_lookup(struct v_dnode* parent, struct hstr* str)
135 if (!str->len || HSTR_EQ(str, &vfs_dot))
138 if (HSTR_EQ(str, &vfs_ddot)) {
139 return parent->parent;
142 u32_t hash = str->hash;
143 struct hbucket* slot = __dcache_hash(parent, &hash);
145 struct v_dnode *pos, *n;
146 hashtable_bucket_foreach(slot, pos, n, hash_list)
148 if (pos->name.hash == hash && pos->parent == parent) {
156 vfs_dcache_add(struct v_dnode* parent, struct v_dnode* dnode)
160 dnode->ref_count = 1;
161 dnode->parent = parent;
162 llist_append(&parent->children, &dnode->siblings);
164 struct hbucket* bucket = __dcache_hash(parent, &dnode->name.hash);
165 hlist_add(&bucket->head, &dnode->hash_list);
169 vfs_dcache_remove(struct v_dnode* dnode)
172 assert(dnode->ref_count == 1);
174 llist_delete(&dnode->siblings);
175 llist_delete(&dnode->aka_list);
176 hlist_delete(&dnode->hash_list);
178 dnode->parent = NULL;
179 dnode->ref_count = 0;
183 vfs_dcache_rehash(struct v_dnode* new_parent, struct v_dnode* dnode)
187 hstr_rehash(&dnode->name, HSTR_FULL_HASH);
188 vfs_dcache_remove(dnode);
189 vfs_dcache_add(new_parent, dnode);
193 vfs_open(struct v_dnode* dnode, struct v_file** file)
195 if (!dnode->inode || !dnode->inode->ops->open) {
199 struct v_inode* inode = dnode->inode;
203 struct v_file* vfile = cake_grab(file_pile);
204 memset(vfile, 0, sizeof(*vfile));
206 vfile->dnode = dnode;
207 vfile->inode = inode;
208 vfile->ref_count = 1;
209 vfile->ops = inode->default_fops;
211 if (check_regfile_node(inode) && !inode->pg_cache) {
212 struct pcache* pcache = vzalloc(sizeof(struct pcache));
214 pcache->master = inode;
215 inode->pg_cache = pcache;
218 int errno = inode->ops->open(inode, vfile);
220 cake_release(file_pile, vfile);
222 vfs_ref_dnode(dnode);
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 (vfs_check_duped_file(file)) {
297 vfs_unref_file(file);
301 if ((errno = file->ops->close(file))) {
305 vfs_unref_dnode(file->dnode);
306 cake_release(file_pile, file);
309 if the current inode is not being locked by other
310 threads that does not share same open context,
311 then we can try to do sync opportunistically
313 if (mutex_on_hold(&inode->lock)) {
319 pcache_commit_all(inode);
322 if (!inode->open_count) {
323 __sync_inode_nolock(inode);
333 vfs_close(struct v_file* file)
335 return vfs_pclose(file, __current->pid);
339 vfs_free_fd(struct v_fd* fd)
341 cake_release(fd_pile, fd);
345 vfs_isync(struct v_inode* inode)
349 int errno = __sync_inode_nolock(inode);
357 vfs_fsync(struct v_file* file)
360 if ((errno = vfs_check_writable(file->dnode))) {
364 return vfs_isync(file->inode);
368 vfs_alloc_fdslot(int* fd)
370 for (size_t i = 0; i < VFS_MAX_FD; i++) {
371 if (!__current->fdtable->fds[i]) {
382 struct v_superblock* sb = cake_grab(superblock_pile);
383 memset(sb, 0, sizeof(*sb));
384 llist_init_head(&sb->sb_list);
386 sb->i_cache = vzalloc(VFS_HASHTABLE_SIZE * sizeof(struct hbucket));
387 sb->d_cache = vzalloc(VFS_HASHTABLE_SIZE * sizeof(struct hbucket));
394 vfs_sb_ref(struct v_superblock* sb)
400 vfs_sb_unref(struct v_superblock* sb)
402 assert(sb->ref_count);
405 if (likely(sb->ref_count)) {
409 if (sb->ops.release) {
416 cake_release(superblock_pile, sb);
420 __vfs_try_evict_dnode(struct lru_node* obj)
422 struct v_dnode* dnode = container_of(obj, struct v_dnode, lru);
424 if (!dnode->ref_count) {
432 __vfs_try_evict_inode(struct lru_node* obj)
434 struct v_inode* inode = container_of(obj, struct v_inode, lru);
436 if (!inode->link_count && !inode->open_count) {
444 vfs_d_alloc(struct v_dnode* parent, struct hstr* name)
446 struct v_dnode* dnode = cake_grab(dnode_pile);
448 lru_evict_half(dnode_lru);
450 if (!(dnode = cake_grab(dnode_pile))) {
455 memset(dnode, 0, sizeof(*dnode));
456 llist_init_head(&dnode->children);
457 llist_init_head(&dnode->siblings);
458 llist_init_head(&dnode->aka_list);
459 mutex_init(&dnode->lock);
461 dnode->name = HHSTR(vzalloc(VFS_NAME_MAXLEN), 0, 0);
463 hstrcpy(&dnode->name, name);
466 vfs_d_assign_sb(dnode, parent->super_block);
467 dnode->mnt = parent->mnt;
470 lru_use_one(dnode_lru, &dnode->lru);
476 vfs_d_free(struct v_dnode* dnode)
478 assert(dnode->ref_count == 1);
481 assert(dnode->inode->link_count > 0);
482 dnode->inode->link_count--;
485 vfs_dcache_remove(dnode);
486 // Make sure the children de-referencing their parent.
487 // With lru presented, the eviction will be propagated over the entire
488 // detached subtree eventually
489 struct v_dnode *pos, *n;
490 llist_for_each(pos, n, &dnode->children, siblings)
492 vfs_dcache_remove(pos);
495 if (dnode->destruct) {
496 dnode->destruct(dnode);
499 vfs_sb_unref(dnode->super_block);
500 vfree((void*)dnode->name.value);
501 cake_release(dnode_pile, dnode);
505 vfs_i_find(struct v_superblock* sb, u32_t i_id)
507 struct hbucket* slot = &sb->i_cache[i_id & VFS_HASH_MASK];
508 struct v_inode *pos, *n;
509 hashtable_bucket_foreach(slot, pos, n, hash_list)
511 if (pos->id == i_id) {
512 lru_use_one(inode_lru, &pos->lru);
521 vfs_i_addhash(struct v_inode* inode)
523 struct hbucket* slot = &inode->sb->i_cache[inode->id & VFS_HASH_MASK];
525 hlist_delete(&inode->hash_list);
526 hlist_add(&slot->head, &inode->hash_list);
530 vfs_i_alloc(struct v_superblock* sb)
532 assert(sb->ops.init_inode);
534 struct v_inode* inode;
535 if (!(inode = cake_grab(inode_pile))) {
536 lru_evict_half(inode_lru);
537 if (!(inode = cake_grab(inode_pile))) {
542 memset(inode, 0, sizeof(*inode));
543 mutex_init(&inode->lock);
544 llist_init_head(&inode->xattrs);
545 llist_init_head(&inode->aka_dnodes);
547 sb->ops.init_inode(sb, inode);
549 inode->ctime = clock_unixtime();
550 inode->atime = inode->ctime;
551 inode->mtime = inode->ctime;
553 vfs_i_assign_sb(inode, sb);
554 lru_use_one(inode_lru, &inode->lru);
559 vfs_i_free(struct v_inode* inode)
561 assert(inode->link_count == 0);
563 if (inode->pg_cache) {
564 pcache_release(inode->pg_cache);
565 vfree(inode->pg_cache);
567 // we don't need to sync inode.
568 // If an inode can be free, then it must be properly closed.
569 // Hence it must be synced already!
570 if (inode->destruct) {
571 inode->destruct(inode);
574 vfs_sb_unref(inode->sb);
575 hlist_delete(&inode->hash_list);
576 cake_release(inode_pile, inode);
579 /* ---- System call definition and support ---- */
581 // make a new name when not exists
582 #define FLOC_MAYBE_MKNAME 1
584 // name must be non-exist and made.
585 #define FLOC_MKNAME 2
588 #define FLOC_NOFOLLOW 4
591 vfs_getfd(int fd, struct v_fd** fd_s)
593 if (TEST_FD(fd) && (*fd_s = __current->fdtable->fds[fd])) {
600 __vfs_mknod(struct v_inode* parent, struct v_dnode* dnode,
601 unsigned int itype, dev_t* dev)
605 errno = parent->ops->create(parent, dnode, itype);
613 struct file_locator {
615 struct v_dnode* file;
620 * @brief unlock the file locator (floc) if possible.
621 * If the file to be located if not exists, and
622 * any FLOC_*MKNAME flag is set, then the parent
623 * dnode will be locked until the file has been properly
624 * finalised by subsequent logic.
629 __floc_try_unlock(struct file_locator* floc)
633 unlock_dnode(floc->dir);
638 __vfs_try_locate_file(const char* path,
639 struct file_locator* floc,
642 char name_str[VFS_NAME_MAXLEN];
643 struct v_dnode *fdir, *file;
644 struct hstr name = HSTR(name_str, 0);
645 int errno, woption = 0;
647 if ((options & FLOC_NOFOLLOW)) {
648 woption |= VFS_WALK_NOFOLLOW;
649 options &= ~FLOC_NOFOLLOW;
654 errno = vfs_walk_proc(path, &fdir, &name, woption | VFS_WALK_PARENT);
659 errno = vfs_walk(fdir, name.value, &file, NULL, woption);
661 if (errno && errno != ENOENT) {
666 if ((options & FLOC_MKNAME)) {
677 errno = vfs_check_writable(fdir);
684 file = vfs_d_alloc(fdir, &name);
692 vfs_dcache_add(fdir, file);
702 vfs_do_open(const char* path, int options)
704 int errno, fd, loptions = 0;
705 struct v_dnode *dentry, *file;
706 struct v_file* ofile = NULL;
707 struct file_locator floc;
708 struct v_inode* inode;
710 if ((options & FO_CREATE)) {
711 loptions |= FLOC_MAYBE_MKNAME;
712 } else if ((options & FO_NOFOLLOW)) {
713 loptions |= FLOC_NOFOLLOW;
716 errno = __vfs_try_locate_file(path, &floc, loptions);
718 if (errno || (errno = vfs_alloc_fdslot(&fd))) {
726 errno = __vfs_mknod(dentry->inode, file, VFS_IFFILE, NULL);
729 __floc_try_unlock(&floc);
733 __floc_try_unlock(&floc);
737 if ((errno = vfs_open(file, &ofile))) {
741 inode = ofile->inode;
744 struct v_fd* fd_s = cake_grab(fd_pile);
745 memset(fd_s, 0, sizeof(*fd_s));
747 if ((options & O_TRUNC)) {
748 file->inode->fsize = 0;
751 if (vfs_get_dtype(inode->itype) == DT_DIR) {
756 fd_s->flags = options;
757 __current->fdtable->fds[fd] = fd_s;
764 __DEFINE_LXSYSCALL2(int, open, const char*, path, int, options)
766 int errno = vfs_do_open(path, options);
767 return DO_STATUS_OR_RETURN(errno);
770 __DEFINE_LXSYSCALL1(int, close, int, fd)
774 if ((errno = vfs_getfd(fd, &fd_s))) {
778 if ((errno = vfs_close(fd_s->file))) {
782 cake_release(fd_pile, fd_s);
783 __current->fdtable->fds[fd] = 0;
786 return DO_STATUS(errno);
790 __vfs_readdir_callback(struct dir_context* dctx,
795 struct lx_dirent* dent = (struct lx_dirent*)dctx->cb_data;
796 int len_ = MIN(len, DIRENT_NAME_MAX_LEN - 1);
798 strncpy(dent->d_name, name, len_);
799 dent->d_name[len_] = 0;
802 dent->d_type = dtype;
805 __DEFINE_LXSYSCALL2(int, sys_readdir, int, fd, struct lx_dirent*, dent)
810 if ((errno = vfs_getfd(fd, &fd_s))) {
814 struct v_inode* inode = fd_s->file->inode;
818 if (!check_directory_node(inode)) {
823 struct dir_context dctx = (struct dir_context) {
825 .read_complete_callback = __vfs_readdir_callback
828 if ((errno = fd_s->file->ops->readdir(fd_s->file, &dctx)) != 1) {
838 return DO_STATUS_OR_RETURN(errno);
842 check_pcache_eligibility(struct v_fd* fd_s)
844 struct v_inode* inode;
846 inode = fd_s->file->inode;
847 return !check_seqdev_node(inode) \
848 && !fsm_check_pseudo_fs(inode->sb->fs) \
849 && !(fd_s->flags & FO_DIRECT);
852 __DEFINE_LXSYSCALL3(int, read, int, fd, void*, buf, size_t, count)
856 if ((errno = vfs_getfd(fd, &fd_s))) {
860 struct v_file* file = fd_s->file;
861 if (check_directory_node(file->inode)) {
866 lock_inode(file->inode);
868 file->inode->atime = clock_unixtime();
870 if (!check_pcache_eligibility(fd_s)) {
871 errno = file->ops->read(file->inode, buf, count, file->f_pos);
873 errno = pcache_read(file->inode, buf, count, file->f_pos);
877 file->f_pos += errno;
878 unlock_inode(file->inode);
882 unlock_inode(file->inode);
885 return DO_STATUS(errno);
888 __DEFINE_LXSYSCALL3(int, write, int, fd, void*, buf, size_t, count)
892 if ((errno = vfs_getfd(fd, &fd_s))) {
896 struct v_inode* inode;
897 struct v_file* file = fd_s->file;
899 if ((errno = vfs_check_writable(file->dnode))) {
903 if (check_directory_node(file->inode)) {
911 inode->mtime = clock_unixtime();
912 if ((fd_s->flags & O_APPEND)) {
913 file->f_pos = inode->fsize;
916 if (!check_pcache_eligibility(fd_s)) {
917 errno = file->ops->write(inode, buf, count, file->f_pos);
919 errno = pcache_write(inode, buf, count, file->f_pos);
923 file->f_pos += errno;
924 inode->fsize = MAX(inode->fsize, file->f_pos);
933 return DO_STATUS(errno);
936 __DEFINE_LXSYSCALL3(int, lseek, int, fd, int, offset, int, options)
940 if ((errno = vfs_getfd(fd, &fd_s))) {
944 struct v_file* file = fd_s->file;
945 struct v_inode* inode = file->inode;
947 if (!file->ops->seek) {
955 int fpos = file->f_pos;
957 if (vfs_get_dtype(inode->itype) == DT_DIR) {
958 options = (options != FSEEK_END) ? options : FSEEK_SET;
963 overflow = sadd_of((int)file->f_pos, offset, &fpos);
966 overflow = sadd_of((int)inode->fsize, offset, &fpos);
977 errno = file->ops->seek(file, fpos);
983 return DO_STATUS(errno);
987 vfs_get_path(struct v_dnode* dnode, char* buf, size_t size, int depth)
999 if (dnode->parent != dnode) {
1000 len = vfs_get_path(dnode->parent, buf, size, depth + 1);
1007 if (!len || buf[len - 1] != VFS_PATH_DELIM) {
1008 buf[len++] = VFS_PATH_DELIM;
1011 size_t cpy_size = MIN(dnode->name.len, size - len);
1012 strncpy(buf + len, dnode->name.value, cpy_size);
1013 buf[len + cpy_size] = 0;
1021 vfs_readlink(struct v_dnode* dnode, char* buf, size_t size)
1024 struct v_inode* inode = dnode->inode;
1026 if (!check_symlink_node(inode)) {
1030 if (!inode->ops->read_symlink) {
1036 int errno = inode->ops->read_symlink(inode, &link);
1038 strncpy(buf, link, MIN(size, (size_t)errno));
1041 unlock_inode(inode);
1046 vfs_get_dtype(int itype)
1048 int dtype = DT_FILE;
1049 if (check_itype(itype, VFS_IFSYMLINK)) {
1050 dtype |= DT_SYMLINK;
1053 if (check_itype(itype, VFS_IFDIR)) {
1063 __DEFINE_LXSYSCALL3(int, realpathat, int, fd, char*, buf, size_t, size)
1067 if ((errno = vfs_getfd(fd, &fd_s))) {
1071 struct v_dnode* dnode;
1072 errno = vfs_get_path(fd_s->file->dnode, buf, size, 0);
1079 return DO_STATUS(errno);
1082 __DEFINE_LXSYSCALL3(int, readlink, const char*, path, char*, buf, size_t, size)
1085 struct v_dnode* dnode;
1086 if (!(errno = vfs_walk_proc(path, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
1087 errno = vfs_readlink(dnode, buf, size);
1094 return DO_STATUS(errno);
1097 __DEFINE_LXSYSCALL4(
1098 int, readlinkat, int, dirfd, const char*, pathname, char*, buf, size_t, size)
1102 if ((errno = vfs_getfd(dirfd, &fd_s))) {
1106 pathname = pathname ? pathname : "";
1108 struct v_dnode* dnode;
1109 if (!(errno = vfs_walk(
1110 fd_s->file->dnode, pathname, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
1111 errno = vfs_readlink(fd_s->file->dnode, buf, size);
1119 return DO_STATUS(errno);
1124 When we perform operation that could affect the layout of
1125 directory (i.e., rename, mkdir, rmdir). We must lock the parent dir
1126 whenever possible. This will blocking any ongoing path walking to reach
1127 it hence avoid any partial state.
1130 __DEFINE_LXSYSCALL1(int, rmdir, const char*, pathname)
1133 struct v_dnode* dnode;
1134 if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
1135 return DO_STATUS(errno);
1140 if ((errno = vfs_check_writable(dnode))) {
1144 if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
1149 if (dnode->ref_count > 1 || dnode->inode->open_count) {
1154 if (!llist_empty(&dnode->children)) {
1159 struct v_dnode* parent = dnode->parent;
1167 lock_inode(parent->inode);
1169 if (check_directory_node(dnode->inode)) {
1170 errno = parent->inode->ops->rmdir(parent->inode, dnode);
1172 vfs_dcache_remove(dnode);
1178 unlock_inode(parent->inode);
1179 unlock_dnode(parent);
1182 unlock_dnode(dnode);
1183 return DO_STATUS(errno);
1186 __DEFINE_LXSYSCALL1(int, mkdir, const char*, path)
1189 struct v_dnode *parent, *dir;
1190 char name_value[VFS_NAME_MAXLEN];
1191 struct hstr name = HHSTR(name_value, 0, 0);
1193 if ((errno = vfs_walk_proc(path, &parent, &name, VFS_WALK_PARENT))) {
1197 if (!(errno = vfs_walk(parent, name_value, &dir, NULL, 0))) {
1202 if ((errno = vfs_check_writable(parent))) {
1206 if (!(dir = vfs_d_alloc(parent, &name))) {
1211 struct v_inode* inode = parent->inode;
1216 if ((parent->super_block->fs->types & FSTYPE_ROFS)) {
1218 } else if (!inode->ops->mkdir) {
1220 } else if (!check_directory_node(inode)) {
1222 } else if (!(errno = inode->ops->mkdir(inode, dir))) {
1223 vfs_dcache_add(parent, dir);
1230 unlock_inode(inode);
1231 unlock_dnode(parent);
1233 return DO_STATUS(errno);
1237 __vfs_do_unlink(struct v_dnode* dnode)
1240 struct v_inode* inode = dnode->inode;
1242 if (dnode->ref_count > 1) {
1246 if ((errno = vfs_check_writable(dnode))) {
1252 if (inode->open_count) {
1254 } else if (!check_directory_node(inode)) {
1255 errno = inode->ops->unlink(inode, dnode);
1263 unlock_inode(inode);
1268 __DEFINE_LXSYSCALL1(int, unlink, const char*, pathname)
1271 struct v_dnode* dnode;
1272 if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
1276 errno = __vfs_do_unlink(dnode);
1279 return DO_STATUS(errno);
1282 __DEFINE_LXSYSCALL2(int, unlinkat, int, fd, const char*, pathname)
1286 if ((errno = vfs_getfd(fd, &fd_s))) {
1290 struct v_dnode* dnode;
1291 if (!(errno = vfs_walk(fd_s->file->dnode, pathname, &dnode, NULL, 0))) {
1292 errno = __vfs_do_unlink(dnode);
1296 return DO_STATUS(errno);
1299 __DEFINE_LXSYSCALL2(int, link, const char*, oldpath, const char*, newpath)
1302 struct file_locator floc;
1303 struct v_dnode *to_link, *name_file;
1305 errno = __vfs_try_locate_file(oldpath, &floc, 0);
1310 __floc_try_unlock(&floc);
1312 to_link = floc.file;
1313 errno = __vfs_try_locate_file(newpath, &floc, FLOC_MKNAME);
1318 name_file = floc.file;
1319 errno = vfs_link(to_link, name_file);
1321 vfs_d_free(name_file);
1325 __floc_try_unlock(&floc);
1326 return DO_STATUS(errno);
1329 __DEFINE_LXSYSCALL1(int, fsync, int, fildes)
1334 if (!(errno = vfs_getfd(fildes, &fd_s))) {
1335 errno = vfs_fsync(fd_s->file);
1338 return DO_STATUS(errno);
1342 vfs_dup_fd(struct v_fd* old, struct v_fd** new)
1345 struct v_fd* copied = cake_grab(fd_pile);
1347 memcpy(copied, old, sizeof(struct v_fd));
1349 vfs_ref_file(old->file);
1357 vfs_dup2(int oldfd, int newfd)
1359 if (newfd == oldfd) {
1364 struct v_fd *oldfd_s, *newfd_s;
1365 if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
1369 if (!TEST_FD(newfd)) {
1374 newfd_s = __current->fdtable->fds[newfd];
1375 if (newfd_s && (errno = vfs_close(newfd_s->file))) {
1379 if (!(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1380 __current->fdtable->fds[newfd] = newfd_s;
1385 return DO_STATUS(errno);
1388 __DEFINE_LXSYSCALL2(int, dup2, int, oldfd, int, newfd)
1390 return vfs_dup2(oldfd, newfd);
1393 __DEFINE_LXSYSCALL1(int, dup, int, oldfd)
1396 struct v_fd *oldfd_s, *newfd_s;
1397 if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
1401 if (!(errno = vfs_alloc_fdslot(&newfd)) &&
1402 !(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1403 __current->fdtable->fds[newfd] = newfd_s;
1408 return DO_STATUS(errno);
1411 __DEFINE_LXSYSCALL2(
1412 int, symlink, const char*, pathname, const char*, link_target)
1415 struct file_locator floc;
1416 struct v_dnode *file;
1417 struct v_inode *f_ino;
1419 errno = __vfs_try_locate_file(pathname, &floc, FLOC_MKNAME);
1425 errno = __vfs_mknod(floc.dir->inode, file, VFS_IFSYMLINK, NULL);
1431 f_ino = file->inode;
1435 errno = vfs_check_writable(file);
1440 if (!f_ino->ops->set_symlink) {
1447 errno = f_ino->ops->set_symlink(f_ino, link_target);
1449 unlock_inode(f_ino);
1452 __floc_try_unlock(&floc);
1453 return DO_STATUS(errno);
1457 vfs_do_chdir(struct proc_info* proc, struct v_dnode* dnode)
1463 if (!check_directory_node(dnode->inode)) {
1469 vfs_unref_dnode(proc->cwd);
1472 vfs_ref_dnode(dnode);
1476 unlock_dnode(dnode);
1481 __DEFINE_LXSYSCALL1(int, chdir, const char*, path)
1483 struct v_dnode* dnode;
1486 if ((errno = vfs_walk_proc(path, &dnode, NULL, 0))) {
1490 errno = vfs_do_chdir((struct proc_info*)__current, dnode);
1493 return DO_STATUS(errno);
1496 __DEFINE_LXSYSCALL1(int, fchdir, int, fd)
1501 if ((errno = vfs_getfd(fd, &fd_s))) {
1505 errno = vfs_do_chdir((struct proc_info*)__current, fd_s->file->dnode);
1508 return DO_STATUS(errno);
1511 __DEFINE_LXSYSCALL2(char*, getcwd, char*, buf, size_t, size)
1522 if (!__current->cwd) {
1523 *buf = VFS_PATH_DELIM;
1526 len = vfs_get_path(__current->cwd, buf, size, 0);
1538 syscall_result(errno);
1543 vfs_do_rename(struct v_dnode* current, struct v_dnode* target)
1546 if (current->inode->id == target->inode->id) {
1551 if ((errno = vfs_check_writable(current))) {
1555 if (current->ref_count > 1 || target->ref_count > 1) {
1559 if (current->super_block != target->super_block) {
1563 struct v_dnode* oldparent = current->parent;
1564 struct v_dnode* newparent = target->parent;
1566 lock_dnode(current);
1569 lock_dnode(oldparent);
1571 lock_dnode(newparent);
1573 if (!llist_empty(&target->children)) {
1575 unlock_dnode(target);
1580 current->inode->ops->rename(current->inode, current, target))) {
1581 unlock_dnode(target);
1585 // re-position current
1586 hstrcpy(¤t->name, &target->name);
1587 vfs_dcache_rehash(newparent, current);
1592 unlock_dnode(target);
1595 unlock_dnode(current);
1597 unlock_dnode(oldparent);
1599 unlock_dnode(newparent);
1604 __DEFINE_LXSYSCALL2(int, rename, const char*, oldpath, const char*, newpath)
1606 struct v_dnode *cur, *target_parent, *target;
1607 struct hstr name = HSTR(valloc(VFS_NAME_MAXLEN), 0);
1610 if ((errno = vfs_walk_proc(oldpath, &cur, NULL, 0))) {
1614 if ((errno = vfs_walk(
1615 __current->cwd, newpath, &target_parent, &name, VFS_WALK_PARENT))) {
1619 errno = vfs_walk(target_parent, name.value, &target, NULL, 0);
1620 if (errno == ENOENT) {
1621 target = vfs_d_alloc(target_parent, &name);
1622 vfs_dcache_add(target_parent, target);
1632 errno = vfs_do_rename(cur, target);
1635 vfree((void*)name.value);
1636 return DO_STATUS(errno);
1639 __DEFINE_LXSYSCALL2(int, fstat, int, fd, struct file_stat*, stat)
1644 if ((errno = vfs_getfd(fd, &fds))) {
1648 struct v_inode* vino = fds->file->inode;
1649 struct device* fdev = vino->sb->dev;
1651 *stat = (struct file_stat){.st_ino = vino->id,
1652 .st_blocks = vino->lb_usage,
1653 .st_size = vino->fsize,
1654 .mode = vino->itype,
1655 .st_ioblksize = PAGE_SIZE,
1656 .st_blksize = vino->sb->blksize};
1658 if (check_device_node(vino)) {
1659 struct device* rdev = resolve_device(vino->data);
1665 stat->st_rdev = (dev_t){.meta = rdev->ident.fn_grp,
1666 .unique = rdev->ident.unique,
1667 .index = dev_uid(rdev) };
1671 stat->st_dev = (dev_t){.meta = fdev->ident.fn_grp,
1672 .unique = fdev->ident.unique,
1673 .index = dev_uid(fdev) };
1677 return DO_STATUS(errno);