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 if (inode->pg_cache) {
562 pcache_release(inode->pg_cache);
563 vfree(inode->pg_cache);
565 // we don't need to sync inode.
566 // If an inode can be free, then it must be properly closed.
567 // Hence it must be synced already!
568 if (inode->destruct) {
569 inode->destruct(inode);
572 vfs_sb_unref(inode->sb);
573 hlist_delete(&inode->hash_list);
574 cake_release(inode_pile, inode);
577 /* ---- System call definition and support ---- */
579 // make a new name when not exists
580 #define FLOC_MAYBE_MKNAME 1
582 // name must be non-exist and made.
583 #define FLOC_MKNAME 2
586 #define FLOC_NOFOLLOW 4
589 vfs_getfd(int fd, struct v_fd** fd_s)
591 if (TEST_FD(fd) && (*fd_s = __current->fdtable->fds[fd])) {
598 __vfs_mknod(struct v_inode* parent, struct v_dnode* dnode,
599 unsigned int itype, dev_t* dev)
603 errno = parent->ops->create(parent, dnode, itype);
611 struct file_locator {
613 struct v_dnode* file;
618 * @brief unlock the file locator (floc) if possible.
619 * If the file to be located if not exists, and
620 * any FLOC_*MKNAME flag is set, then the parent
621 * dnode will be locked until the file has been properly
622 * finalised by subsequent logic.
627 __floc_try_unlock(struct file_locator* floc)
631 unlock_dnode(floc->dir);
636 __vfs_try_locate_file(const char* path,
637 struct file_locator* floc,
640 char name_str[VFS_NAME_MAXLEN];
641 struct v_dnode *fdir, *file;
642 struct hstr name = HSTR(name_str, 0);
643 int errno, woption = 0;
645 if ((options & FLOC_NOFOLLOW)) {
646 woption |= VFS_WALK_NOFOLLOW;
647 options &= ~FLOC_NOFOLLOW;
652 errno = vfs_walk_proc(path, &fdir, &name, woption | VFS_WALK_PARENT);
657 errno = vfs_walk(fdir, name.value, &file, NULL, woption);
659 if (errno && errno != ENOENT) {
664 if ((options & FLOC_MKNAME)) {
675 errno = vfs_check_writable(fdir);
682 file = vfs_d_alloc(fdir, &name);
690 vfs_dcache_add(fdir, file);
700 vfs_do_open(const char* path, int options)
702 int errno, fd, loptions = 0;
703 struct v_dnode *dentry, *file;
704 struct v_file* ofile = NULL;
705 struct file_locator floc;
706 struct v_inode* inode;
708 if ((options & FO_CREATE)) {
709 loptions |= FLOC_MAYBE_MKNAME;
710 } else if ((options & FO_NOFOLLOW)) {
711 loptions |= FLOC_NOFOLLOW;
714 errno = __vfs_try_locate_file(path, &floc, loptions);
716 if (errno || (errno = vfs_alloc_fdslot(&fd))) {
724 errno = __vfs_mknod(dentry->inode, file, VFS_IFFILE, NULL);
727 __floc_try_unlock(&floc);
731 __floc_try_unlock(&floc);
735 if ((errno = vfs_open(file, &ofile))) {
739 inode = ofile->inode;
742 struct v_fd* fd_s = cake_grab(fd_pile);
743 memset(fd_s, 0, sizeof(*fd_s));
745 if ((options & O_TRUNC)) {
746 file->inode->fsize = 0;
749 if (vfs_get_dtype(inode->itype) == DT_DIR) {
754 fd_s->flags = options;
755 __current->fdtable->fds[fd] = fd_s;
762 __DEFINE_LXSYSCALL2(int, open, const char*, path, int, options)
764 int errno = vfs_do_open(path, options);
765 return DO_STATUS_OR_RETURN(errno);
768 __DEFINE_LXSYSCALL1(int, close, int, fd)
772 if ((errno = vfs_getfd(fd, &fd_s))) {
776 if ((errno = vfs_close(fd_s->file))) {
780 cake_release(fd_pile, fd_s);
781 __current->fdtable->fds[fd] = 0;
784 return DO_STATUS(errno);
788 __vfs_readdir_callback(struct dir_context* dctx,
793 struct lx_dirent* dent = (struct lx_dirent*)dctx->cb_data;
794 strncpy(dent->d_name, name, MIN(len, DIRENT_NAME_MAX_LEN));
796 dent->d_type = dtype;
799 __DEFINE_LXSYSCALL2(int, sys_readdir, int, fd, struct lx_dirent*, dent)
804 if ((errno = vfs_getfd(fd, &fd_s))) {
808 struct v_inode* inode = fd_s->file->inode;
812 if (!check_directory_node(inode)) {
817 struct dir_context dctx = (struct dir_context) {
819 .read_complete_callback = __vfs_readdir_callback
822 if ((errno = fd_s->file->ops->readdir(fd_s->file, &dctx)) != 1) {
832 return DO_STATUS_OR_RETURN(errno);
835 __DEFINE_LXSYSCALL3(int, read, int, fd, void*, buf, size_t, count)
839 if ((errno = vfs_getfd(fd, &fd_s))) {
843 struct v_file* file = fd_s->file;
844 if (check_directory_node(file->inode)) {
849 lock_inode(file->inode);
851 file->inode->atime = clock_unixtime();
853 if (check_seqdev_node(file->inode) || (fd_s->flags & FO_DIRECT)) {
854 errno = file->ops->read(file->inode, buf, count, file->f_pos);
856 errno = pcache_read(file->inode, buf, count, file->f_pos);
860 file->f_pos += errno;
861 unlock_inode(file->inode);
865 unlock_inode(file->inode);
868 return DO_STATUS(errno);
871 __DEFINE_LXSYSCALL3(int, write, int, fd, void*, buf, size_t, count)
875 if ((errno = vfs_getfd(fd, &fd_s))) {
879 struct v_inode* inode;
880 struct v_file* file = fd_s->file;
882 if ((errno = vfs_check_writable(file->dnode))) {
886 if (check_directory_node(file->inode)) {
894 inode->mtime = clock_unixtime();
895 if ((fd_s->flags & O_APPEND)) {
896 file->f_pos = inode->fsize;
899 if (check_seqdev_node(inode) || (fd_s->flags & FO_DIRECT)) {
900 errno = file->ops->write(inode, buf, count, file->f_pos);
902 errno = pcache_write(inode, buf, count, file->f_pos);
906 file->f_pos += errno;
907 inode->fsize = MAX(inode->fsize, file->f_pos);
916 return DO_STATUS(errno);
919 __DEFINE_LXSYSCALL3(int, lseek, int, fd, int, offset, int, options)
923 if ((errno = vfs_getfd(fd, &fd_s))) {
927 struct v_file* file = fd_s->file;
928 struct v_inode* inode = file->inode;
930 if (!file->ops->seek) {
938 int fpos = file->f_pos;
940 if (vfs_get_dtype(inode->itype) == DT_DIR) {
941 options = (options != FSEEK_END) ? options : FSEEK_SET;
946 overflow = sadd_of((int)file->f_pos, offset, &fpos);
949 overflow = sadd_of((int)inode->fsize, offset, &fpos);
960 errno = file->ops->seek(file, fpos);
966 return DO_STATUS(errno);
970 vfs_get_path(struct v_dnode* dnode, char* buf, size_t size, int depth)
982 if (dnode->parent != dnode) {
983 len = vfs_get_path(dnode->parent, buf, size, depth + 1);
990 if (!len || buf[len - 1] != VFS_PATH_DELIM) {
991 buf[len++] = VFS_PATH_DELIM;
994 size_t cpy_size = MIN(dnode->name.len, size - len);
995 strncpy(buf + len, dnode->name.value, cpy_size);
1002 vfs_readlink(struct v_dnode* dnode, char* buf, size_t size)
1005 struct v_inode* inode = dnode->inode;
1007 if (!check_symlink_node(inode)) {
1011 if (!inode->ops->read_symlink) {
1017 int errno = inode->ops->read_symlink(inode, &link);
1019 strncpy(buf, link, MIN(size, (size_t)errno));
1022 unlock_inode(inode);
1027 vfs_get_dtype(int itype)
1029 int dtype = DT_FILE;
1030 if (check_itype(itype, VFS_IFSYMLINK)) {
1031 dtype |= DT_SYMLINK;
1034 if (check_itype(itype, VFS_IFDIR)) {
1044 __DEFINE_LXSYSCALL3(int, realpathat, int, fd, char*, buf, size_t, size)
1048 if ((errno = vfs_getfd(fd, &fd_s))) {
1052 struct v_dnode* dnode;
1053 errno = vfs_get_path(fd_s->file->dnode, buf, size, 0);
1060 return DO_STATUS(errno);
1063 __DEFINE_LXSYSCALL3(int, readlink, const char*, path, char*, buf, size_t, size)
1066 struct v_dnode* dnode;
1067 if (!(errno = vfs_walk_proc(path, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
1068 errno = vfs_readlink(dnode, buf, size);
1075 return DO_STATUS(errno);
1078 __DEFINE_LXSYSCALL4(
1079 int, readlinkat, int, dirfd, const char*, pathname, char*, buf, size_t, size)
1083 if ((errno = vfs_getfd(dirfd, &fd_s))) {
1087 pathname = pathname ? pathname : "";
1089 struct v_dnode* dnode;
1090 if (!(errno = vfs_walk(
1091 fd_s->file->dnode, pathname, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
1092 errno = vfs_readlink(fd_s->file->dnode, buf, size);
1100 return DO_STATUS(errno);
1105 When we perform operation that could affect the layout of
1106 directory (i.e., rename, mkdir, rmdir). We must lock the parent dir
1107 whenever possible. This will blocking any ongoing path walking to reach
1108 it hence avoid any partial state.
1111 __DEFINE_LXSYSCALL1(int, rmdir, const char*, pathname)
1114 struct v_dnode* dnode;
1115 if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
1116 return DO_STATUS(errno);
1121 if ((errno = vfs_check_writable(dnode))) {
1125 if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
1130 if (dnode->ref_count > 1 || dnode->inode->open_count) {
1135 if (!llist_empty(&dnode->children)) {
1140 struct v_dnode* parent = dnode->parent;
1148 lock_inode(parent->inode);
1150 if (check_directory_node(dnode->inode)) {
1151 errno = parent->inode->ops->rmdir(parent->inode, dnode);
1153 vfs_dcache_remove(dnode);
1159 unlock_inode(parent->inode);
1160 unlock_dnode(parent);
1163 unlock_dnode(dnode);
1164 return DO_STATUS(errno);
1167 __DEFINE_LXSYSCALL1(int, mkdir, const char*, path)
1170 struct v_dnode *parent, *dir;
1171 char name_value[VFS_NAME_MAXLEN];
1172 struct hstr name = HHSTR(name_value, 0, 0);
1174 if ((errno = vfs_walk_proc(path, &parent, &name, VFS_WALK_PARENT))) {
1178 if (!(errno = vfs_walk(parent, name_value, &dir, NULL, 0))) {
1183 if ((errno = vfs_check_writable(parent))) {
1187 if (!(dir = vfs_d_alloc(parent, &name))) {
1192 struct v_inode* inode = parent->inode;
1197 if ((parent->super_block->fs->types & FSTYPE_ROFS)) {
1199 } else if (!inode->ops->mkdir) {
1201 } else if (!check_directory_node(inode)) {
1203 } else if (!(errno = inode->ops->mkdir(inode, dir))) {
1204 vfs_dcache_add(parent, dir);
1211 unlock_inode(inode);
1212 unlock_dnode(parent);
1214 return DO_STATUS(errno);
1218 __vfs_do_unlink(struct v_dnode* dnode)
1221 struct v_inode* inode = dnode->inode;
1223 if (dnode->ref_count > 1) {
1227 if ((errno = vfs_check_writable(dnode))) {
1233 if (inode->open_count) {
1235 } else if (!check_directory_node(inode)) {
1236 errno = inode->ops->unlink(inode, dnode);
1244 unlock_inode(inode);
1249 __DEFINE_LXSYSCALL1(int, unlink, const char*, pathname)
1252 struct v_dnode* dnode;
1253 if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
1257 errno = __vfs_do_unlink(dnode);
1260 return DO_STATUS(errno);
1263 __DEFINE_LXSYSCALL2(int, unlinkat, int, fd, const char*, pathname)
1267 if ((errno = vfs_getfd(fd, &fd_s))) {
1271 struct v_dnode* dnode;
1272 if (!(errno = vfs_walk(fd_s->file->dnode, pathname, &dnode, NULL, 0))) {
1273 errno = __vfs_do_unlink(dnode);
1277 return DO_STATUS(errno);
1280 __DEFINE_LXSYSCALL2(int, link, const char*, oldpath, const char*, newpath)
1283 struct file_locator floc;
1284 struct v_dnode *to_link, *name_file;
1286 errno = __vfs_try_locate_file(oldpath, &floc, 0);
1291 __floc_try_unlock(&floc);
1293 to_link = floc.file;
1294 errno = __vfs_try_locate_file(newpath, &floc, FLOC_MKNAME);
1299 name_file = floc.file;
1300 errno = vfs_link(to_link, name_file);
1302 vfs_d_free(name_file);
1306 __floc_try_unlock(&floc);
1307 return DO_STATUS(errno);
1310 __DEFINE_LXSYSCALL1(int, fsync, int, fildes)
1315 if (!(errno = vfs_getfd(fildes, &fd_s))) {
1316 errno = vfs_fsync(fd_s->file);
1319 return DO_STATUS(errno);
1323 vfs_dup_fd(struct v_fd* old, struct v_fd** new)
1326 struct v_fd* copied = cake_grab(fd_pile);
1328 memcpy(copied, old, sizeof(struct v_fd));
1330 vfs_ref_file(old->file);
1338 vfs_dup2(int oldfd, int newfd)
1340 if (newfd == oldfd) {
1345 struct v_fd *oldfd_s, *newfd_s;
1346 if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
1350 if (!TEST_FD(newfd)) {
1355 newfd_s = __current->fdtable->fds[newfd];
1356 if (newfd_s && (errno = vfs_close(newfd_s->file))) {
1360 if (!(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1361 __current->fdtable->fds[newfd] = newfd_s;
1366 return DO_STATUS(errno);
1369 __DEFINE_LXSYSCALL2(int, dup2, int, oldfd, int, newfd)
1371 return vfs_dup2(oldfd, newfd);
1374 __DEFINE_LXSYSCALL1(int, dup, int, oldfd)
1377 struct v_fd *oldfd_s, *newfd_s;
1378 if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
1382 if (!(errno = vfs_alloc_fdslot(&newfd)) &&
1383 !(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1384 __current->fdtable->fds[newfd] = newfd_s;
1389 return DO_STATUS(errno);
1392 __DEFINE_LXSYSCALL2(
1393 int, symlink, const char*, pathname, const char*, link_target)
1396 struct file_locator floc;
1397 struct v_dnode *file;
1398 struct v_inode *f_ino;
1400 errno = __vfs_try_locate_file(pathname, &floc, FLOC_MKNAME);
1406 errno = __vfs_mknod(floc.dir->inode, file, VFS_IFSYMLINK, NULL);
1412 f_ino = file->inode;
1416 errno = vfs_check_writable(file);
1421 if (!f_ino->ops->set_symlink) {
1428 errno = f_ino->ops->set_symlink(f_ino, link_target);
1430 unlock_inode(f_ino);
1433 __floc_try_unlock(&floc);
1434 return DO_STATUS(errno);
1438 vfs_do_chdir(struct proc_info* proc, struct v_dnode* dnode)
1444 if (!check_directory_node(dnode->inode)) {
1450 vfs_unref_dnode(proc->cwd);
1453 vfs_ref_dnode(dnode);
1456 unlock_dnode(dnode);
1462 __DEFINE_LXSYSCALL1(int, chdir, const char*, path)
1464 struct v_dnode* dnode;
1467 if ((errno = vfs_walk_proc(path, &dnode, NULL, 0))) {
1471 errno = vfs_do_chdir((struct proc_info*)__current, dnode);
1474 return DO_STATUS(errno);
1477 __DEFINE_LXSYSCALL1(int, fchdir, int, fd)
1482 if ((errno = vfs_getfd(fd, &fd_s))) {
1486 errno = vfs_do_chdir((struct proc_info*)__current, fd_s->file->dnode);
1489 return DO_STATUS(errno);
1492 __DEFINE_LXSYSCALL2(char*, getcwd, char*, buf, size_t, size)
1503 if (!__current->cwd) {
1504 *buf = VFS_PATH_DELIM;
1507 len = vfs_get_path(__current->cwd, buf, size, 0);
1519 syscall_result(errno);
1524 vfs_do_rename(struct v_dnode* current, struct v_dnode* target)
1527 if (current->inode->id == target->inode->id) {
1532 if ((errno = vfs_check_writable(current))) {
1536 if (current->ref_count > 1 || target->ref_count > 1) {
1540 if (current->super_block != target->super_block) {
1544 struct v_dnode* oldparent = current->parent;
1545 struct v_dnode* newparent = target->parent;
1547 lock_dnode(current);
1550 lock_dnode(oldparent);
1552 lock_dnode(newparent);
1554 if (!llist_empty(&target->children)) {
1556 unlock_dnode(target);
1561 current->inode->ops->rename(current->inode, current, target))) {
1562 unlock_dnode(target);
1566 // re-position current
1567 hstrcpy(¤t->name, &target->name);
1568 vfs_dcache_rehash(newparent, current);
1573 unlock_dnode(target);
1576 unlock_dnode(current);
1578 unlock_dnode(oldparent);
1580 unlock_dnode(newparent);
1585 __DEFINE_LXSYSCALL2(int, rename, const char*, oldpath, const char*, newpath)
1587 struct v_dnode *cur, *target_parent, *target;
1588 struct hstr name = HSTR(valloc(VFS_NAME_MAXLEN), 0);
1591 if ((errno = vfs_walk_proc(oldpath, &cur, NULL, 0))) {
1595 if ((errno = vfs_walk(
1596 __current->cwd, newpath, &target_parent, &name, VFS_WALK_PARENT))) {
1600 errno = vfs_walk(target_parent, name.value, &target, NULL, 0);
1601 if (errno == ENOENT) {
1602 target = vfs_d_alloc(target_parent, &name);
1603 vfs_dcache_add(target_parent, target);
1613 errno = vfs_do_rename(cur, target);
1616 vfree((void*)name.value);
1617 return DO_STATUS(errno);
1620 __DEFINE_LXSYSCALL2(int, fstat, int, fd, struct file_stat*, stat)
1625 if ((errno = vfs_getfd(fd, &fds))) {
1629 struct v_inode* vino = fds->file->inode;
1630 struct device* fdev = vino->sb->dev;
1632 *stat = (struct file_stat){.st_ino = vino->id,
1633 .st_blocks = vino->lb_usage,
1634 .st_size = vino->fsize,
1635 .mode = vino->itype,
1636 .st_ioblksize = PAGE_SIZE,
1637 .st_blksize = vino->sb->blksize};
1639 if (check_device_node(vino)) {
1640 struct device* rdev = resolve_device(vino->data);
1646 stat->st_rdev = (dev_t){.meta = rdev->ident.fn_grp,
1647 .unique = rdev->ident.unique,
1648 .index = dev_uid(rdev) };
1652 stat->st_dev = (dev_t){.meta = fdev->ident.fn_grp,
1653 .unique = fdev->ident.unique,
1654 .index = dev_uid(fdev) };
1658 return DO_STATUS(errno);