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 #include <klibc/string.h>
13 #include <lunaix/dirent.h>
14 #include <lunaix/foptions.h>
15 #include <lunaix/fs.h>
16 #include <lunaix/mm/cake.h>
17 #include <lunaix/mm/page.h>
18 #include <lunaix/mm/valloc.h>
19 #include <lunaix/process.h>
20 #include <lunaix/spike.h>
21 #include <lunaix/syscall.h>
23 #define PATH_DELIM '/'
24 #define DNODE_HASHTABLE_BITS 10
25 #define DNODE_HASHTABLE_SIZE (1 << DNODE_HASHTABLE_BITS)
26 #define DNODE_HASH_MASK (DNODE_HASHTABLE_SIZE - 1)
27 #define DNODE_HASHBITS (32 - DNODE_HASHTABLE_BITS)
29 #define lock_inode(inode) mutex_lock(&inode->lock)
30 #define unlock_inode(inode) mutex_unlock(&inode->lock)
32 #define lock_dnode(dnode) mutex_lock(&dnode->lock)
33 #define unlock_dnode(dnode) mutex_unlock(&dnode->lock)
35 static struct cake_pile* dnode_pile;
36 static struct cake_pile* inode_pile;
37 static struct cake_pile* file_pile;
38 static struct cake_pile* superblock_pile;
39 static struct cake_pile* fd_pile;
41 static struct v_superblock* root_sb;
42 static struct hbucket* dnode_cache;
46 struct hstr vfs_ddot = HSTR("..", 2);
47 struct hstr vfs_dot = HSTR(".", 1);
48 struct hstr vfs_empty = HSTR("", 0);
54 vfs_d_free(struct v_dnode* dnode);
60 vfs_sb_free(struct v_superblock* sb);
65 // 为他们专门创建一个蛋糕堆,而不使用valloc,这样我们可以最小化内碎片的产生
66 dnode_pile = cake_new_pile("dnode_cache", sizeof(struct v_dnode), 1, 0);
67 inode_pile = cake_new_pile("inode_cache", sizeof(struct v_inode), 1, 0);
68 file_pile = cake_new_pile("file_cache", sizeof(struct v_file), 1, 0);
69 fd_pile = cake_new_pile("fd_cache", sizeof(struct v_fd), 1, 0);
71 cake_new_pile("sb_cache", sizeof(struct v_superblock), 1, 0);
73 dnode_cache = vzalloc(DNODE_HASHTABLE_SIZE * sizeof(struct hbucket));
75 hstr_rehash(&vfs_ddot, HSTR_FULL_HASH);
76 hstr_rehash(&vfs_dot, HSTR_FULL_HASH);
78 // 创建一个根superblock,用来蕴含我们的根目录。
79 root_sb = vfs_sb_alloc();
80 root_sb->root = vfs_d_alloc();
81 root_sb->root->inode = vfs_i_alloc();
84 inline struct hbucket*
85 __dcache_get_bucket(struct v_dnode* parent, unsigned int hash)
87 // 与parent的指针值做加法,来减小碰撞的可能性。
88 hash += (uint32_t)parent;
90 hash = hash ^ (hash >> DNODE_HASHBITS);
91 return &dnode_cache[hash & DNODE_HASH_MASK];
95 vfs_dcache_lookup(struct v_dnode* parent, struct hstr* str)
97 if (!str->len || HSTR_EQ(str, &vfs_dot))
100 if (HSTR_EQ(str, &vfs_ddot)) {
101 return parent->parent ? parent->parent : parent;
104 struct hbucket* slot = __dcache_get_bucket(parent, str->hash);
106 struct v_dnode *pos, *n;
107 hashtable_bucket_foreach(slot, pos, n, hash_list)
109 if (pos->name.hash == str->hash) {
117 vfs_dcache_add(struct v_dnode* parent, struct v_dnode* dnode)
119 struct hbucket* bucket = __dcache_get_bucket(parent, dnode->name.hash);
120 hlist_add(&bucket->head, &dnode->hash_list);
124 __vfs_walk(struct v_dnode* start,
126 struct v_dnode** dentry,
127 struct hstr* component,
133 if (path[0] == PATH_DELIM || !start) {
134 if ((walk_options & VFS_WALK_FSRELATIVE) && start) {
135 start = start->super_block->root;
137 start = root_sb->root;
142 struct v_dnode* dnode;
143 struct v_dnode* current_level = start;
145 char name_content[VFS_NAME_MAXLEN];
146 struct hstr name = HSTR(name_content, 0);
148 char current = path[i++], lookahead;
150 lookahead = path[i++];
151 if (current != PATH_DELIM) {
152 if (j >= VFS_NAME_MAXLEN - 1) {
155 if (!VFS_VALID_CHAR(current)) {
158 name_content[j++] = current;
164 // handling cases like /^.*(\/+).*$/
165 if (lookahead == PATH_DELIM) {
169 lock_dnode(current_level);
173 hstr_rehash(&name, HSTR_FULL_HASH);
175 if (!lookahead && (walk_options & VFS_WALK_PARENT)) {
177 component->hash = name.hash;
179 strcpy(component->value, name_content);
181 unlock_dnode(current_level);
185 dnode = vfs_dcache_lookup(current_level, &name);
188 dnode = vfs_d_alloc();
189 dnode->name = HHSTR(valloc(VFS_NAME_MAXLEN), j, name.hash);
191 strcpy(dnode->name.value, name_content);
193 lock_inode(current_level->inode);
196 current_level->inode->ops.dir_lookup(current_level->inode, dnode);
198 if (errno == ENOENT && (walk_options & VFS_WALK_MKPARENT)) {
199 if (!current_level->inode->ops.mkdir) {
202 errno = current_level->inode->ops.mkdir(
203 current_level->inode, dnode);
207 unlock_inode(current_level->inode);
210 unlock_dnode(current_level);
211 vfree(dnode->name.value);
215 vfs_dcache_add(current_level, dnode);
217 dnode->parent = current_level;
218 llist_append(¤t_level->children, &dnode->siblings);
221 unlock_dnode(current_level);
224 current_level = dnode;
229 *dentry = current_level;
233 vfree(dnode->name.value);
239 #define VFS_MAX_SYMLINK 16
242 vfs_walk(struct v_dnode* start,
244 struct v_dnode** dentry,
245 struct hstr* component,
248 struct v_dnode* interim;
249 const char* pathname = path;
250 int errno = __vfs_walk(start, path, &interim, component, options);
254 if (counter >= VFS_MAX_SYMLINK) {
258 if ((interim->inode->itype & VFS_IFSYMLINK) &&
259 !(options & VFS_WALK_NOFOLLOW) &&
260 interim->inode->ops.read_symlink) {
261 errno = interim->inode->ops.read_symlink(interim->inode, &pathname);
268 errno = __vfs_walk(start, pathname, &interim, component, options);
272 *dentry = errno ? 0 : interim;
278 vfs_mount(const char* target, const char* fs_name, bdev_t device)
283 if (!(errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
284 errno = vfs_mount_at(fs_name, device, mnt);
291 vfs_unmount(const char* target)
296 if (!(errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
297 errno = vfs_unmount_at(mnt);
304 vfs_mount_at(const char* fs_name, bdev_t device, struct v_dnode* mnt_point)
306 struct filesystem* fs = fsm_get(fs_name);
309 struct v_superblock* sb = vfs_sb_alloc();
314 if (!(errno = fs->mount(sb, mnt_point))) {
316 sb->root = mnt_point;
317 mnt_point->super_block = sb;
318 llist_append(&root_sb->sb_list, &sb->sb_list);
325 vfs_unmount_at(struct v_dnode* mnt_point)
328 struct v_superblock* sb = mnt_point->super_block;
332 if (!(errno = sb->fs->unmount(sb))) {
333 struct v_dnode* fs_root = sb->root;
334 llist_delete(&fs_root->siblings);
335 llist_delete(&sb->sb_list);
342 vfs_open(struct v_dnode* dnode, struct v_file** file)
344 if (!dnode->inode || !dnode->inode->ops.open) {
348 struct v_inode* inode = dnode->inode;
349 struct v_file* vfile = cake_grab(file_pile);
350 memset(vfile, 0, sizeof(*vfile));
352 vfile->dnode = dnode;
353 vfile->inode = inode;
354 vfile->ref_count = ATOMIC_VAR_INIT(1);
355 vfile->ops = inode->default_fops;
357 if ((inode->itype & VFS_IFFILE) && !inode->pg_cache) {
358 struct pcache* pcache = vzalloc(sizeof(struct pcache));
360 pcache->master = inode;
361 inode->pg_cache = pcache;
364 int errno = inode->ops.open(inode, vfile);
366 cake_release(file_pile, vfile);
368 atomic_fetch_add(&dnode->ref_count, 1);
378 vfs_link(struct v_dnode* to_link, struct v_dnode* name)
382 lock_inode(to_link->inode);
383 if (to_link->super_block->root != name->super_block->root) {
385 } else if (!to_link->inode->ops.link) {
387 } else if (!(errno = to_link->inode->ops.link(to_link->inode, name))) {
388 name->inode = to_link->inode;
389 atomic_fetch_add(&to_link->inode->link_count, 1);
391 unlock_inode(to_link->inode);
397 vfs_close(struct v_file* file)
400 if (!file->ops.close || !(errno = file->ops.close(file))) {
401 atomic_fetch_sub(&file->dnode->ref_count, 1);
402 file->inode->open_count--;
404 pcache_commit_all(file->inode);
405 cake_release(file_pile, file);
411 vfs_fsync(struct v_file* file)
413 lock_inode(file->inode);
416 pcache_commit_all(file->inode);
417 if (file->ops.sync) {
418 errno = file->ops.sync(file->inode);
421 unlock_inode(file->inode);
427 vfs_alloc_fdslot(int* fd)
429 for (size_t i = 0; i < VFS_MAX_FD; i++) {
430 if (!__current->fdtable->fds[i]) {
441 struct v_superblock* sb = cake_grab(superblock_pile);
442 memset(sb, 0, sizeof(*sb));
443 llist_init_head(&sb->sb_list);
448 vfs_sb_free(struct v_superblock* sb)
450 cake_release(superblock_pile, sb);
456 struct v_dnode* dnode = cake_grab(dnode_pile);
457 memset(dnode, 0, sizeof(*dnode));
458 llist_init_head(&dnode->children);
459 mutex_init(&dnode->lock);
461 dnode->ref_count = ATOMIC_VAR_INIT(0);
463 dnode->name = vfs_empty;
468 vfs_d_free(struct v_dnode* dnode)
470 if (dnode->ops.destruct) {
471 dnode->ops.destruct(dnode);
473 cake_release(dnode_pile, dnode);
479 struct v_inode* inode = cake_grab(inode_pile);
480 memset(inode, 0, sizeof(*inode));
481 mutex_init(&inode->lock);
487 vfs_i_free(struct v_inode* inode)
489 cake_release(inode_pile, inode);
492 /* ---- System call definition and support ---- */
494 #define FLOCATE_CREATE_EMPTY 1
496 #define DO_STATUS(errno) SYSCALL_ESTATUS(__current->k_status = errno)
497 #define DO_STATUS_OR_RETURN(errno) ({ errno < 0 ? DO_STATUS(errno) : errno; })
499 #define TEST_FD(fd) (fd >= 0 && fd < VFS_MAX_FD)
502 __vfs_getfd(int fd, struct v_fd** fd_s)
504 if (TEST_FD(fd) && (*fd_s = __current->fdtable->fds[fd])) {
511 __vfs_try_locate_file(const char* path,
512 struct v_dnode** fdir,
513 struct v_dnode** file,
516 char name_str[VFS_NAME_MAXLEN];
517 struct hstr name = HSTR(name_str, 0);
520 vfs_walk(__current->cwd, path, fdir, &name, VFS_WALK_PARENT))) {
524 errno = vfs_walk(*fdir, name.value, file, NULL, 0);
525 if (errno != ENOENT || !(options & FLOCATE_CREATE_EMPTY)) {
529 struct v_dnode* parent = *fdir;
530 struct v_dnode* file_new = vfs_d_alloc();
531 file_new->name = HHSTR(valloc(VFS_NAME_MAXLEN), name.len, name.hash);
532 strcpy(file_new->name.value, name_str);
534 if (!(errno = parent->inode->ops.create(parent->inode, file_new))) {
537 vfs_dcache_add(parent, file_new);
538 llist_append(&parent->children, &file_new->siblings);
540 vfree(file_new->name.value);
541 vfs_d_free(file_new);
548 vfs_do_open(const char* path, int options)
551 struct v_dnode *dentry, *file;
552 struct v_file* ofile = 0;
554 errno = __vfs_try_locate_file(
555 path, &dentry, &file, (options & FO_CREATE) ? FLOCATE_CREATE_EMPTY : 0);
557 if (errno || (errno = vfs_open(file, &ofile))) {
561 struct v_inode* o_inode = ofile->inode;
562 if (!(o_inode->itype & VFS_IFSEQDEV) && !(options & FO_DIRECT)) {
563 // XXX Change here accordingly when signature of pcache_r/w changed.
564 ofile->ops.read = pcache_read;
565 ofile->ops.write = pcache_write;
568 if (!errno && !(errno = vfs_alloc_fdslot(&fd))) {
569 struct v_fd* fd_s = vzalloc(sizeof(*fd_s));
570 ofile->f_pos = ofile->inode->fsize & -((options & FO_APPEND) != 0);
572 fd_s->flags = options;
573 __current->fdtable->fds[fd] = fd_s;
580 __DEFINE_LXSYSCALL2(int, open, const char*, path, int, options)
582 int errno = vfs_do_open(path, options);
583 return DO_STATUS_OR_RETURN(errno);
586 __DEFINE_LXSYSCALL1(int, close, int, fd)
590 if ((errno = __vfs_getfd(fd, &fd_s))) {
594 if (fd_s->file->ref_count > 1) {
595 fd_s->file->ref_count--;
596 } else if ((errno = vfs_close(fd_s->file))) {
601 __current->fdtable->fds[fd] = 0;
604 return DO_STATUS(errno);
608 __vfs_readdir_callback(struct dir_context* dctx,
613 struct dirent* dent = (struct dirent*)dctx->cb_data;
614 strncpy(dent->d_name, name, DIRENT_NAME_MAX_LEN);
616 dent->d_type = dtype;
619 __DEFINE_LXSYSCALL2(int, readdir, int, fd, struct dirent*, dent)
624 if ((errno = __vfs_getfd(fd, &fd_s))) {
628 struct v_inode* inode = fd_s->file->inode;
632 if (!(fd_s->file->inode->itype & VFS_IFDIR)) {
635 struct dir_context dctx =
636 (struct dir_context){ .cb_data = dent,
637 .index = dent->d_offset,
638 .read_complete_callback =
639 __vfs_readdir_callback };
640 if (dent->d_offset == 0) {
641 __vfs_readdir_callback(&dctx, vfs_dot.value, vfs_dot.len, 0);
642 } else if (dent->d_offset == 1) {
643 __vfs_readdir_callback(&dctx, vfs_ddot.value, vfs_ddot.len, 0);
646 if ((errno = fd_s->file->ops.readdir(inode, &dctx))) {
658 return DO_STATUS(errno);
661 __DEFINE_LXSYSCALL3(int, read, int, fd, void*, buf, size_t, count)
665 if ((errno = __vfs_getfd(fd, &fd_s))) {
669 struct v_file* file = fd_s->file;
670 if ((file->inode->itype & VFS_IFDIR)) {
675 lock_inode(file->inode);
677 file->inode->atime = clock_unixtime();
679 __SYSCALL_INTERRUPTIBLE(
680 { errno = file->ops.read(file->inode, buf, count, file->f_pos); })
682 unlock_inode(file->inode);
685 file->f_pos += errno;
690 return DO_STATUS(errno);
693 __DEFINE_LXSYSCALL3(int, write, int, fd, void*, buf, size_t, count)
697 if ((errno = __vfs_getfd(fd, &fd_s))) {
701 struct v_file* file = fd_s->file;
702 if ((file->inode->itype & VFS_IFDIR)) {
707 lock_inode(file->inode);
709 file->inode->mtime = clock_unixtime();
711 __SYSCALL_INTERRUPTIBLE(
712 { errno = file->ops.write(file->inode, buf, count, file->f_pos); })
714 unlock_inode(file->inode);
717 file->f_pos += errno;
722 return DO_STATUS(errno);
725 __DEFINE_LXSYSCALL3(int, lseek, int, fd, int, offset, int, options)
729 if ((errno = __vfs_getfd(fd, &fd_s))) {
733 struct v_file* file = fd_s->file;
735 lock_inode(file->inode);
737 size_t fpos = file->f_pos;
740 fpos = (size_t)((int)file->f_pos + offset);
743 fpos = (size_t)((int)file->inode->fsize + offset);
749 if (!file->ops.seek || !(errno = file->ops.seek(file->inode, fpos))) {
753 unlock_inode(file->inode);
756 return DO_STATUS(errno);
760 vfs_get_path(struct v_dnode* dnode, char* buf, size_t size, int depth)
770 size_t len = vfs_get_path(dnode->parent, buf, size, depth + 1);
776 size_t cpy_size = MIN(dnode->name.len, size - len);
777 strncpy(buf + len, dnode->name.value, cpy_size);
781 buf[len++] = PATH_DELIM;
788 vfs_readlink(struct v_dnode* dnode, char* buf, size_t size)
791 struct v_inode* inode = dnode->inode;
792 if (inode->ops.read_symlink) {
795 int errno = inode->ops.read_symlink(inode, &link);
796 strncpy(buf, link, size);
804 __DEFINE_LXSYSCALL3(int, realpathat, int, fd, char*, buf, size_t, size)
808 if ((errno = __vfs_getfd(fd, &fd_s))) {
812 struct v_dnode* dnode;
813 errno = vfs_get_path(fd_s->file->dnode, buf, size, 0);
820 return DO_STATUS(errno);
823 __DEFINE_LXSYSCALL3(int, readlink, const char*, path, char*, buf, size_t, size)
826 struct v_dnode* dnode;
828 vfs_walk(__current->cwd, path, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
829 errno = vfs_readlink(dnode, buf, size);
836 return DO_STATUS(errno);
839 __DEFINE_LXSYSCALL4(int,
852 if ((errno = __vfs_getfd(dirfd, &fd_s))) {
856 struct v_dnode* dnode;
857 if (!(errno = vfs_walk(
858 fd_s->file->dnode, pathname, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
859 errno = vfs_readlink(fd_s->file->dnode, buf, size);
867 return DO_STATUS(errno);
872 When we perform operation that could affect the layout of
873 directory (i.e., rename, mkdir, rmdir). We must lock the parent dir
874 whenever possible. This will blocking any ongoing path walking to reach
875 it hence avoid any partial state.
878 __DEFINE_LXSYSCALL1(int, rmdir, const char*, pathname)
881 struct v_dnode* dnode;
882 if ((errno = vfs_walk(__current->cwd, pathname, &dnode, NULL, 0))) {
883 return DO_STATUS(errno);
889 lock_dnode(dnode->parent);
891 if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
896 if (dnode->ref_count || dnode->inode->open_count) {
901 if (!llist_empty(&dnode->children)) {
906 lock_inode(dnode->inode);
908 if ((dnode->inode->itype & VFS_IFDIR)) {
909 errno = dnode->inode->ops.rmdir(dnode->inode);
910 // FIXME remove the dnode from cache & parent.
915 unlock_inode(dnode->inode);
920 unlock_dnode(dnode->parent);
921 return DO_STATUS(errno);
924 __DEFINE_LXSYSCALL1(int, mkdir, const char*, path)
926 struct v_dnode *parent, *dir;
927 struct hstr component = HSTR(valloc(VFS_NAME_MAXLEN), 0);
929 vfs_walk(__current->cwd, path, &parent, &component, VFS_WALK_PARENT);
935 lock_inode(parent->inode);
937 if ((parent->super_block->fs->types & FSTYPE_ROFS)) {
939 } else if (!parent->inode->ops.mkdir) {
941 } else if (!(parent->inode->itype & VFS_IFDIR)) {
945 dir->name = component;
946 if (!(errno = parent->inode->ops.mkdir(parent->inode, dir))) {
947 llist_append(&parent->children, &dir->siblings);
950 vfree(component.value);
954 unlock_inode(parent->inode);
955 unlock_dnode(parent);
958 return DO_STATUS(errno);
962 __vfs_do_unlink(struct v_dnode* dnode)
964 struct v_inode* inode = dnode->inode;
966 if (dnode->ref_count) {
973 if (inode->open_count) {
975 } else if (!(inode->itype & VFS_IFDIR)) {
976 // The underlying unlink implementation should handle
978 errno = inode->ops.unlink(inode);
981 // FIXME remove the dnode from cache & parent
992 __DEFINE_LXSYSCALL1(int, unlink, const char*, pathname)
995 struct v_dnode* dnode;
996 if ((errno = vfs_walk(__current->cwd, pathname, &dnode, NULL, 0))) {
999 if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
1004 errno = __vfs_do_unlink(dnode);
1007 return DO_STATUS(errno);
1010 __DEFINE_LXSYSCALL2(int, unlinkat, int, fd, const char*, pathname)
1014 if ((errno = __vfs_getfd(fd, &fd_s))) {
1018 struct v_dnode* dnode;
1019 if (!(errno = vfs_walk(fd_s->file->dnode, pathname, &dnode, NULL, 0))) {
1020 errno = __vfs_do_unlink(dnode);
1024 return DO_STATUS(errno);
1027 __DEFINE_LXSYSCALL2(int, link, const char*, oldpath, const char*, newpath)
1030 struct v_dnode *dentry, *to_link, *name_dentry, *name_file;
1032 errno = __vfs_try_locate_file(oldpath, &dentry, &to_link, 0);
1034 errno = __vfs_try_locate_file(
1035 newpath, &name_dentry, &name_file, FLOCATE_CREATE_EMPTY);
1038 } else if (name_file) {
1039 errno = vfs_link(to_link, name_file);
1042 return DO_STATUS(errno);
1045 __DEFINE_LXSYSCALL1(int, fsync, int, fildes)
1049 if (!(errno = __vfs_getfd(fildes, &fd_s))) {
1050 errno = vfs_fsync(fd_s->file);
1053 return DO_STATUS(errno);
1057 vfs_dup_fd(struct v_fd* old, struct v_fd** new)
1060 struct v_fd* copied = cake_grab(fd_pile);
1062 memcpy(copied, old, sizeof(struct v_fd));
1064 atomic_fetch_add(&old->file->ref_count, 1);
1072 vfs_dup2(int oldfd, int newfd)
1074 if (newfd == oldfd) {
1079 struct v_fd *oldfd_s, *newfd_s;
1080 if ((errno = __vfs_getfd(oldfd, &oldfd_s))) {
1084 if (!TEST_FD(newfd)) {
1089 newfd_s = __current->fdtable->fds[newfd];
1090 if (newfd_s && (errno = vfs_close(newfd_s->file))) {
1094 if (!(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1095 __current->fdtable->fds[newfd] = newfd_s;
1100 return DO_STATUS(errno);
1103 __DEFINE_LXSYSCALL2(int, dup2, int, oldfd, int, newfd)
1105 return vfs_dup2(oldfd, newfd);
1108 __DEFINE_LXSYSCALL1(int, dup, int, oldfd)
1111 struct v_fd *oldfd_s, *newfd_s;
1112 if ((errno = __vfs_getfd(oldfd, &oldfd_s))) {
1116 if (!(errno = vfs_alloc_fdslot(&newfd)) &&
1117 !(errno = vfs_dup_fd(oldfd_s, &newfd_s))) {
1118 __current->fdtable->fds[newfd] = newfd_s;
1123 return DO_STATUS(errno);
1126 __DEFINE_LXSYSCALL2(int,
1134 struct v_dnode* dnode;
1135 if ((errno = vfs_walk(__current->cwd, pathname, &dnode, NULL, 0))) {
1138 if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
1142 if (!dnode->inode->ops.symlink) {
1147 lock_inode(dnode->inode);
1149 errno = dnode->inode->ops.symlink(dnode->inode, link_target);
1151 unlock_inode(dnode->inode);
1154 return DO_STATUS(errno);
1158 __vfs_do_chdir(struct v_dnode* dnode)
1164 if (!(dnode->inode->itype & VFS_IFDIR)) {
1169 if (__current->cwd) {
1170 __current->cwd->ref_count--;
1174 __current->cwd = dnode;
1176 unlock_dnode(dnode);
1182 __DEFINE_LXSYSCALL1(int, chdir, const char*, path)
1184 struct v_dnode* dnode;
1187 if ((errno = vfs_walk(__current->cwd, path, &dnode, NULL, 0))) {
1191 errno = __vfs_do_chdir(dnode);
1194 return DO_STATUS(errno);
1197 __DEFINE_LXSYSCALL1(int, fchdir, int, fd)
1202 if ((errno = __vfs_getfd(fd, &fd_s))) {
1206 errno = __vfs_do_chdir(fd_s->file->dnode);
1209 return DO_STATUS(errno);
1212 __DEFINE_LXSYSCALL2(char*, getcwd, char*, buf, size_t, size)
1223 if (!__current->cwd) {
1227 len = vfs_get_path(__current->cwd, buf, size, 0);
1234 buf[len + 1] = '\0';
1239 __current->k_status = errno;
1243 // TODO rename syscall