Unifying External Interrupt System (#51)
[lunaix-os.git] / lunaix-os / kernel / fs / vfs.c
index 958c76197279bccee66e26b08175a86cd3c82c77..0eec6bf517cb53eb7aeab3972b7915897c95da1b 100644 (file)
 
     It is overseen by Twilight Sparkle ;)
 
 
     It is overseen by Twilight Sparkle ;)
 
- 1. Get inodes hooked into lru
- 2. Get dnodes hooked into lru
+ 1. Get inodes hooked into lru (CHECKED)
+ 2. Get dnodes hooked into lru (CHECKED)
  3. Get inodes properly hashed so they can be reused by underling fs (CHECKED)
  3. Get inodes properly hashed so they can be reused by underling fs (CHECKED)
- 4. (lru) Add a callback function (or destructor) for eviction.
+ 4. (lru) Add a callback function (or destructor) for eviction. (CHECKED)
         [good idea] or a constructor/destructor pattern in cake allocator ?
  5. (mount) Figure out a way to identify a busy mount point before unmount
             maybe a unified mount_point structure that maintain a referencing
             counter on any dnodes within the subtree? Such a counter will only
             increament if a file is opened or a dnode is being used as working
         [good idea] or a constructor/destructor pattern in cake allocator ?
  5. (mount) Figure out a way to identify a busy mount point before unmount
             maybe a unified mount_point structure that maintain a referencing
             counter on any dnodes within the subtree? Such a counter will only
             increament if a file is opened or a dnode is being used as working
-            directory and decreamenting conversely.
+            directory and decreamenting conversely. (CHECKED)
  6. (mount) Ability to track all mount points (including sub-mounts)
  6. (mount) Ability to track all mount points (including sub-mounts)
-            so we can be confident to clean up everything when we unmount.
+            so we can be confident to clean up everything when we
+            unmount. (CHECKED)
  7. (mount) Figure out a way to acquire the device represented by a dnode.
             so it can be used to mount. (e.g. we wish to get `struct device*`
             out of the dnode at /dev/sda)
             [tip] we should pay attention at twifs and add a private_data field
  7. (mount) Figure out a way to acquire the device represented by a dnode.
             so it can be used to mount. (e.g. we wish to get `struct device*`
             out of the dnode at /dev/sda)
             [tip] we should pay attention at twifs and add a private_data field
-            under struct v_dnode?
- 8. (mount) Then, we should refactor on mount/unmount mechanism.
+            under struct v_dnode? (CHECKED)
+ 8. (mount) Then, we should refactor on mount/unmount mechanism. (CHECKED)
  9. (mount) (future) Ability to mount any thing? e.g. Linux can mount a disk
                     image file using a so called "loopback" pseudo device. Maybe
                     we can do similar thing in Lunaix? A block device emulation
                     above the regular file when we mount it on.
  10. (device) device number (dev_t) allocation
  9. (mount) (future) Ability to mount any thing? e.g. Linux can mount a disk
                     image file using a so called "loopback" pseudo device. Maybe
                     we can do similar thing in Lunaix? A block device emulation
                     above the regular file when we mount it on.
  10. (device) device number (dev_t) allocation
-            [good idea] <class>:<subclass>:<uniq_id> composition
+            [good idea] <class>:<subclass>:<uniq_id> composition (CHECKED)
 */
 
 #include <klibc/string.h>
 */
 
 #include <klibc/string.h>
-#include <lunaix/dirent.h>
 #include <lunaix/foptions.h>
 #include <lunaix/fs.h>
 #include <lunaix/mm/cake.h>
 #include <lunaix/foptions.h>
 #include <lunaix/fs.h>
 #include <lunaix/mm/cake.h>
-#include <lunaix/mm/page.h>
 #include <lunaix/mm/valloc.h>
 #include <lunaix/process.h>
 #include <lunaix/spike.h>
 #include <lunaix/syscall.h>
 #include <lunaix/mm/valloc.h>
 #include <lunaix/process.h>
 #include <lunaix/spike.h>
 #include <lunaix/syscall.h>
+#include <lunaix/syscall_utils.h>
 
 #include <lunaix/fs/twifs.h>
 
 
 #include <lunaix/fs/twifs.h>
 
-#define PATH_DELIM '/'
-#define HASHTABLE_BITS 10
-#define HASHTABLE_SIZE (1 << HASHTABLE_BITS)
-#define HASH_MASK (HASHTABLE_SIZE - 1)
-#define HASHBITS (32 - HASHTABLE_BITS)
-
-#define lock_inode(inode) mutex_lock(&inode->lock)
-#define unlock_inode(inode) mutex_unlock(&inode->lock)
-
-#define lock_dnode(dnode) mutex_lock(&dnode->lock)
-#define unlock_dnode(dnode) mutex_unlock(&dnode->lock)
+#include <usr/lunaix/dirent_defs.h>
 
 static struct cake_pile* dnode_pile;
 static struct cake_pile* inode_pile;
 
 static struct cake_pile* dnode_pile;
 static struct cake_pile* inode_pile;
@@ -73,18 +63,20 @@ static struct cake_pile* file_pile;
 static struct cake_pile* superblock_pile;
 static struct cake_pile* fd_pile;
 
 static struct cake_pile* superblock_pile;
 static struct cake_pile* fd_pile;
 
-static struct v_superblock* root_sb;
-static struct hbucket *dnode_cache, *inode_cache;
+struct v_dnode* vfs_sysroot;
+static struct hbucket* dnode_cache;
+
+struct lru_zone *dnode_lru, *inode_lru;
 
 struct hstr vfs_ddot = HSTR("..", 2);
 struct hstr vfs_dot = HSTR(".", 1);
 struct hstr vfs_empty = HSTR("", 0);
 
 
 struct hstr vfs_ddot = HSTR("..", 2);
 struct hstr vfs_dot = HSTR(".", 1);
 struct hstr vfs_empty = HSTR("", 0);
 
-struct v_superblock*
-vfs_sb_alloc();
+static int
+__vfs_try_evict_dnode(struct lru_node* obj);
 
 
-void
-vfs_sb_free(struct v_superblock* sb);
+static int
+__vfs_try_evict_inode(struct lru_node* obj);
 
 void
 vfs_init()
 
 void
 vfs_init()
@@ -97,28 +89,43 @@ vfs_init()
     superblock_pile =
       cake_new_pile("sb_cache", sizeof(struct v_superblock), 1, 0);
 
     superblock_pile =
       cake_new_pile("sb_cache", sizeof(struct v_superblock), 1, 0);
 
-    dnode_cache = vzalloc(HASHTABLE_SIZE * sizeof(struct hbucket));
-    inode_cache = vzalloc(HASHTABLE_SIZE * sizeof(struct hbucket));
+    dnode_cache = vzalloc(VFS_HASHTABLE_SIZE * sizeof(struct hbucket));
+
+    dnode_lru = lru_new_zone("vfs_dnode", __vfs_try_evict_dnode);
+    inode_lru = lru_new_zone("vfs_inode", __vfs_try_evict_inode);
 
     hstr_rehash(&vfs_ddot, HSTR_FULL_HASH);
     hstr_rehash(&vfs_dot, HSTR_FULL_HASH);
 
 
     hstr_rehash(&vfs_ddot, HSTR_FULL_HASH);
     hstr_rehash(&vfs_dot, HSTR_FULL_HASH);
 
-    // 创建一个根superblock,用来蕴含我们的根目录
-    root_sb = vfs_sb_alloc();
-    root_sb->root = vfs_d_alloc();
-    root_sb->root->inode = vfs_i_alloc(root_sb, 0);
+    // 创建一个根dnode
+    vfs_sysroot = vfs_d_alloc(NULL, &vfs_empty);
+    vfs_sysroot->parent = vfs_sysroot;
+    atomic_fetch_add(&vfs_sysroot->ref_count, 1);
 }
 
 }
 
-inline struct hbucket*
-__dcache_hash(struct v_dnode* parent, uint32_t* hash)
+static inline struct hbucket*
+__dcache_hash(struct v_dnode* parent, u32_t* hash)
 {
 {
-    uint32_t _hash = *hash;
-    // 与parent的指针值做加法,来减小碰撞的可能性。
-    _hash += (uint32_t)parent;
+    u32_t _hash = *hash;
     // 确保低位更加随机
     // 确保低位更加随机
-    _hash = _hash ^ (_hash >> HASHBITS);
+    _hash = _hash ^ (_hash >> VFS_HASHBITS);
+    // 与parent的指针值做加法,来减小碰撞的可能性。
+    _hash += (u32_t)__ptr(parent);
     *hash = _hash;
     *hash = _hash;
-    return &dnode_cache[_hash & HASH_MASK];
+    return &dnode_cache[_hash & VFS_HASH_MASK];
+}
+
+static inline int
+__sync_inode_nolock(struct v_inode* inode)
+{
+    pcache_commit_all(inode);
+
+    int errno = ENOTSUP;
+    if (inode->ops->sync) {
+        errno = inode->ops->sync(inode);
+    }
+
+    return errno;
 }
 
 struct v_dnode*
 }
 
 struct v_dnode*
@@ -128,16 +135,16 @@ vfs_dcache_lookup(struct v_dnode* parent, struct hstr* str)
         return parent;
 
     if (HSTR_EQ(str, &vfs_ddot)) {
         return parent;
 
     if (HSTR_EQ(str, &vfs_ddot)) {
-        return parent->parent ? parent->parent : parent;
+        return parent->parent;
     }
 
     }
 
-    uint32_t hash = str->hash;
+    u32_t hash = str->hash;
     struct hbucket* slot = __dcache_hash(parent, &hash);
 
     struct v_dnode *pos, *n;
     hashtable_bucket_foreach(slot, pos, n, hash_list)
     {
     struct hbucket* slot = __dcache_hash(parent, &hash);
 
     struct v_dnode *pos, *n;
     hashtable_bucket_foreach(slot, pos, n, hash_list)
     {
-        if (pos->name.hash == hash) {
+        if (pos->name.hash == hash && pos->parent == parent) {
             return pos;
         }
     }
             return pos;
         }
     }
@@ -147,9 +154,12 @@ vfs_dcache_lookup(struct v_dnode* parent, struct hstr* str)
 void
 vfs_dcache_add(struct v_dnode* parent, struct v_dnode* dnode)
 {
 void
 vfs_dcache_add(struct v_dnode* parent, struct v_dnode* dnode)
 {
+    assert(parent);
+
     atomic_fetch_add(&dnode->ref_count, 1);
     dnode->parent = parent;
     llist_append(&parent->children, &dnode->siblings);
     atomic_fetch_add(&dnode->ref_count, 1);
     dnode->parent = parent;
     llist_append(&parent->children, &dnode->siblings);
+
     struct hbucket* bucket = __dcache_hash(parent, &dnode->name.hash);
     hlist_add(&bucket->head, &dnode->hash_list);
 }
     struct hbucket* bucket = __dcache_hash(parent, &dnode->name.hash);
     hlist_add(&bucket->head, &dnode->hash_list);
 }
@@ -157,9 +167,11 @@ vfs_dcache_add(struct v_dnode* parent, struct v_dnode* dnode)
 void
 vfs_dcache_remove(struct v_dnode* dnode)
 {
 void
 vfs_dcache_remove(struct v_dnode* dnode)
 {
+    assert(dnode);
     assert(dnode->ref_count == 1);
 
     llist_delete(&dnode->siblings);
     assert(dnode->ref_count == 1);
 
     llist_delete(&dnode->siblings);
+    llist_delete(&dnode->aka_list);
     hlist_delete(&dnode->hash_list);
 
     dnode->parent = NULL;
     hlist_delete(&dnode->hash_list);
 
     dnode->parent = NULL;
@@ -169,323 +181,188 @@ vfs_dcache_remove(struct v_dnode* dnode)
 void
 vfs_dcache_rehash(struct v_dnode* new_parent, struct v_dnode* dnode)
 {
 void
 vfs_dcache_rehash(struct v_dnode* new_parent, struct v_dnode* dnode)
 {
+    assert(new_parent);
+
     hstr_rehash(&dnode->name, HSTR_FULL_HASH);
     vfs_dcache_remove(dnode);
     vfs_dcache_add(new_parent, dnode);
 }
 
 int
     hstr_rehash(&dnode->name, HSTR_FULL_HASH);
     vfs_dcache_remove(dnode);
     vfs_dcache_add(new_parent, dnode);
 }
 
 int
-__vfs_walk(struct v_dnode* start,
-           const char* path,
-           struct v_dnode** dentry,
-           struct hstr* component,
-           int walk_options)
+vfs_open(struct v_dnode* dnode, struct v_file** file)
 {
 {
-    int errno = 0;
-    int i = 0, j = 0;
-
-    if (path[0] == PATH_DELIM || !start) {
-        if ((walk_options & VFS_WALK_FSRELATIVE) && start) {
-            start = start->super_block->root;
-        } else {
-            start = root_sb->root;
-        }
-        i++;
+    if (!dnode->inode || !dnode->inode->ops->open) {
+        return ENOTSUP;
     }
 
     }
 
-    struct v_dnode* dnode;
-    struct v_dnode* current_level = start;
-
-    char name_content[VFS_NAME_MAXLEN];
-    struct hstr name = HSTR(name_content, 0);
-
-    char current = path[i++], lookahead;
-    while (current) {
-        lookahead = path[i++];
-        if (current != PATH_DELIM) {
-            if (j >= VFS_NAME_MAXLEN - 1) {
-                return ENAMETOOLONG;
-            }
-            if (!VFS_VALID_CHAR(current)) {
-                return EINVAL;
-            }
-            name_content[j++] = current;
-            if (lookahead) {
-                goto cont;
-            }
-        }
-
-        // handling cases like /^.*(\/+).*$/
-        if (lookahead == PATH_DELIM) {
-            goto cont;
-        }
-
-        lock_dnode(current_level);
-
-        name_content[j] = 0;
-        name.len = j;
-        hstr_rehash(&name, HSTR_FULL_HASH);
-
-        if (!lookahead && (walk_options & VFS_WALK_PARENT)) {
-            if (component) {
-                component->hash = name.hash;
-                component->len = j;
-                strcpy(component->value, name_content);
-            }
-            unlock_dnode(current_level);
-            break;
-        }
-
-        dnode = vfs_dcache_lookup(current_level, &name);
-
-        if (!dnode) {
-            dnode = vfs_d_alloc();
-
-            hstrcpy(&dnode->name, &name);
-
-            lock_inode(current_level->inode);
-
-            errno =
-              current_level->inode->ops.dir_lookup(current_level->inode, dnode);
-
-            if (errno == ENOENT && (walk_options & VFS_WALK_MKPARENT)) {
-                if (!current_level->inode->ops.mkdir) {
-                    errno = ENOTSUP;
-                } else {
-                    errno = current_level->inode->ops.mkdir(
-                      current_level->inode, dnode);
-                }
-            }
-
-            unlock_inode(current_level->inode);
-
-            if (errno) {
-                unlock_dnode(current_level);
-                vfree(dnode->name.value);
-                goto error;
-            }
-
-            vfs_dcache_add(current_level, dnode);
-        }
+    struct v_inode* inode = dnode->inode;
 
 
-        unlock_dnode(current_level);
+    lock_inode(inode);
 
 
-        j = 0;
-        current_level = dnode;
-    cont:
-        current = lookahead;
-    };
+    struct v_file* vfile = cake_grab(file_pile);
+    memset(vfile, 0, sizeof(*vfile));
 
 
-    *dentry = current_level;
-    return 0;
+    vfile->dnode = dnode;
+    vfile->inode = inode;
+    vfile->ref_count = ATOMIC_VAR_INIT(1);
+    vfile->ops = inode->default_fops;
 
 
-error:
-    vfs_d_free(dnode);
-    *dentry = NULL;
-    return errno;
-}
+    if (check_file_node(inode) && !inode->pg_cache) {
+        struct pcache* pcache = vzalloc(sizeof(struct pcache));
+        pcache_init(pcache);
+        pcache->master = inode;
+        inode->pg_cache = pcache;
+    }
 
 
-#define VFS_MAX_SYMLINK 16
+    int errno = inode->ops->open(inode, vfile);
+    if (errno) {
+        cake_release(file_pile, vfile);
+    } else {
+        atomic_fetch_add(&dnode->ref_count, 1);
+        inode->open_count++;
+        mnt_mkbusy(dnode->mnt);
 
 
-int
-vfs_walk(struct v_dnode* start,
-         const char* path,
-         struct v_dnode** dentry,
-         struct hstr* component,
-         int options)
-{
-    struct v_dnode* interim;
-    const char* pathname = path;
-    int errno = __vfs_walk(start, path, &interim, component, options);
-    int counter = 0;
-
-    while (!errno) {
-        if (counter >= VFS_MAX_SYMLINK) {
-            errno = ELOOP;
-            continue;
-        }
-        if ((interim->inode->itype & VFS_IFSYMLINK) &&
-            !(options & VFS_WALK_NOFOLLOW) &&
-            interim->inode->ops.read_symlink) {
-            errno = interim->inode->ops.read_symlink(interim->inode, &pathname);
-            if (errno) {
-                break;
-            }
-        } else {
-            break;
-        }
-        errno = __vfs_walk(start, pathname, &interim, component, options);
-        counter++;
+        *file = vfile;
     }
 
     }
 
-    *dentry = errno ? 0 : interim;
+    unlock_inode(inode);
 
     return errno;
 }
 
 
     return errno;
 }
 
-int
-vfs_mount(const char* target, const char* fs_name, struct device* device)
+void
+vfs_assign_inode(struct v_dnode* assign_to, struct v_inode* inode)
 {
 {
-    int errno;
-    struct v_dnode* mnt;
-
-    if (!(errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
-        errno = vfs_mount_at(fs_name, device, mnt);
+    if (assign_to->inode) {
+        llist_delete(&assign_to->aka_list);
+        assign_to->inode->link_count--;
     }
 
     }
 
-    return errno;
+    llist_append(&inode->aka_dnodes, &assign_to->aka_list);
+    assign_to->inode = inode;
+    inode->link_count++;
 }
 
 int
 }
 
 int
-vfs_unmount(const char* target)
+vfs_link(struct v_dnode* to_link, struct v_dnode* name)
 {
     int errno;
 {
     int errno;
-    struct v_dnode* mnt;
-
-    if (!(errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
-        errno = vfs_unmount_at(mnt);
-    }
-
-    return errno;
-}
-
-int
-vfs_mount_at(const char* fs_name,
-             struct device* device,
-             struct v_dnode* mnt_point)
-{
-    if (!(mnt_point->inode->itype & VFS_IFDIR)) {
-        return ENOTDIR;
-    }
 
 
-    struct filesystem* fs = fsm_get(fs_name);
-    if (!fs) {
-        return ENODEV;
+    if ((errno = vfs_check_writable(to_link))) {
+        return errno;
     }
 
     }
 
-    struct v_superblock* sb = vfs_sb_alloc();
-    sb->dev = device;
-    sb->fs_id = fs->fs_id;
-
-    int errno = 0;
-    if (!(errno = fs->mount(sb, mnt_point))) {
-        sb->fs = fs;
-        sb->root = mnt_point;
-        mnt_point->super_block = sb;
-        llist_append(&root_sb->sb_list, &sb->sb_list);
+    lock_inode(to_link->inode);
+    if (to_link->super_block->root != name->super_block->root) {
+        errno = EXDEV;
+    } else if (!to_link->inode->ops->link) {
+        errno = ENOTSUP;
+    } else if (!(errno = to_link->inode->ops->link(to_link->inode, name))) {
+        vfs_assign_inode(name, to_link->inode);
     }
     }
+    unlock_inode(to_link->inode);
 
     return errno;
 }
 
 int
 
     return errno;
 }
 
 int
-vfs_unmount_at(struct v_dnode* mnt_point)
+vfs_pclose(struct v_file* file, pid_t pid)
 {
 {
-    // FIXME deal with the detached dcache subtree
+    struct v_inode* inode;
     int errno = 0;
     int errno = 0;
-    struct v_superblock* sb = mnt_point->super_block;
-    if (!sb) {
-        return EINVAL;
-    }
-
-    if (sb->root != mnt_point) {
-        return EINVAL;
-    }
-
-    if (!(errno = sb->fs->unmount(sb))) {
-        struct v_dnode* fs_root = sb->root;
-        vfs_dcache_remove(fs_root);
 
 
-        llist_delete(&sb->sb_list);
-        vfs_sb_free(sb);
-        vfs_d_free(fs_root);
+    inode = file->inode;
+
+    /*
+     * Prevent dead lock.
+     * This happened when process is terminated while blocking on read.
+     * In that case, the process is still holding the inode lock and it
+         will never get released.
+     * The unlocking should also include ownership check.
+     *
+     * To see why, consider two process both open the same file both with
+     * fd=x.
+     *      Process A: busy on reading x
+     *      Process B: do nothing with x
+     * Assuming that, after a very short time, process B get terminated
+     * while process A is still busy in it's reading business. By this
+     * design, the inode lock of this file x is get released by B rather
+     * than A. And this will cause a probable race condition on A if other
+     * process is writing to this file later after B exit.
+    */
+
+    mutex_unlock_for(&inode->lock, pid);
+    
+    if (file->ref_count > 1) {
+        atomic_fetch_sub(&file->ref_count, 1);
+        return 0;
     }
     }
-    return errno;
-}
 
 
-int
-vfs_open(struct v_dnode* dnode, struct v_file** file)
-{
-    if (!dnode->inode || !dnode->inode->ops.open) {
-        return ENOTSUP;
+    if ((errno = file->ops->close(file))) {
+        goto done;
     }
 
     }
 
-    struct v_inode* inode = dnode->inode;
-    struct v_file* vfile = cake_grab(file_pile);
-    memset(vfile, 0, sizeof(*vfile));
+    atomic_fetch_sub(&file->dnode->ref_count, 1);
+    mnt_chillax(file->dnode->mnt);
+    cake_release(file_pile, file);
 
 
-    vfile->dnode = dnode;
-    vfile->inode = inode;
-    vfile->ref_count = ATOMIC_VAR_INIT(1);
-    vfile->ops = inode->default_fops;
-
-    if ((inode->itype & VFS_IFFILE) && !inode->pg_cache) {
-        struct pcache* pcache = vzalloc(sizeof(struct pcache));
-        pcache_init(pcache);
-        pcache->master = inode;
-        inode->pg_cache = pcache;
+    /*
+        if the current inode is not being locked by other 
+        threads that does not share same open context,
+        then we can try to do sync opportunistically
+    */
+    if (mutex_on_hold(&inode->lock)) {
+        goto done;
     }
     }
+    
+    lock_inode(inode);
 
 
-    int errno = inode->ops.open(inode, vfile);
-    if (errno) {
-        cake_release(file_pile, vfile);
-    } else {
-        atomic_fetch_add(&dnode->ref_count, 1);
-        inode->open_count++;
+    pcache_commit_all(inode);
+    inode->open_count--;
 
 
-        *file = vfile;
+    if (!inode->open_count) {
+        __sync_inode_nolock(inode);
     }
 
     }
 
+    unlock_inode(inode);
+
+done:
     return errno;
 }
 
 int
     return errno;
 }
 
 int
-vfs_link(struct v_dnode* to_link, struct v_dnode* name)
+vfs_close(struct v_file* file)
 {
 {
-    int errno;
-
-    lock_inode(to_link->inode);
-    if (to_link->super_block->root != name->super_block->root) {
-        errno = EXDEV;
-    } else if (!to_link->inode->ops.link) {
-        errno = ENOTSUP;
-    } else if (!(errno = to_link->inode->ops.link(to_link->inode, name))) {
-        name->inode = to_link->inode;
-        to_link->inode->link_count++;
-    }
-    unlock_inode(to_link->inode);
+    return vfs_pclose(file, __current->pid);
+}
 
 
-    return errno;
+void
+vfs_free_fd(struct v_fd* fd)
+{
+    cake_release(fd_pile, fd);
 }
 
 int
 }
 
 int
-vfs_close(struct v_file* file)
+vfs_isync(struct v_inode* inode)
 {
 {
-    int errno = 0;
-    if (!file->ops.close || !(errno = file->ops.close(file))) {
-        atomic_fetch_sub(&file->dnode->ref_count, 1);
-        file->inode->open_count--;
+    lock_inode(inode);
+
+    int errno = __sync_inode_nolock(inode);
+
+    unlock_inode(inode);
 
 
-        pcache_commit_all(file->inode);
-        cake_release(file_pile, file);
-    }
     return errno;
 }
 
 int
 vfs_fsync(struct v_file* file)
 {
     return errno;
 }
 
 int
 vfs_fsync(struct v_file* file)
 {
-    lock_inode(file->inode);
-
-    int errno = ENOTSUP;
-    pcache_commit_all(file->inode);
-    if (file->ops.sync) {
-        errno = file->ops.sync(file->inode);
+    int errno;
+    if ((errno = vfs_check_writable(file->dnode))) {
+        return errno;
     }
 
     }
 
-    unlock_inode(file->inode);
-
-    return errno;
+    return vfs_isync(file->inode);
 }
 
 int
 }
 
 int
@@ -506,41 +383,103 @@ vfs_sb_alloc()
     struct v_superblock* sb = cake_grab(superblock_pile);
     memset(sb, 0, sizeof(*sb));
     llist_init_head(&sb->sb_list);
     struct v_superblock* sb = cake_grab(superblock_pile);
     memset(sb, 0, sizeof(*sb));
     llist_init_head(&sb->sb_list);
+    sb->i_cache = vzalloc(VFS_HASHTABLE_SIZE * sizeof(struct hbucket));
+    sb->ref_count = 1;
     return sb;
 }
 
     return sb;
 }
 
+void
+vfs_sb_ref(struct v_superblock* sb)
+{
+    sb->ref_count++;
+}
+
 void
 vfs_sb_free(struct v_superblock* sb)
 {
 void
 vfs_sb_free(struct v_superblock* sb)
 {
+    assert(sb->ref_count);
+
+    sb->ref_count--;
+    if (sb->ref_count) {
+        return;
+    }
+
+    if (sb->ops.release) {
+        sb->ops.release(sb);
+    }
+
+    vfree(sb->i_cache);
     cake_release(superblock_pile, sb);
 }
 
     cake_release(superblock_pile, sb);
 }
 
+static int
+__vfs_try_evict_dnode(struct lru_node* obj)
+{
+    struct v_dnode* dnode = container_of(obj, struct v_dnode, lru);
+
+    if (!dnode->ref_count) {
+        vfs_d_free(dnode);
+        return 1;
+    }
+    return 0;
+}
+
+static int
+__vfs_try_evict_inode(struct lru_node* obj)
+{
+    struct v_inode* inode = container_of(obj, struct v_inode, lru);
+
+    if (!inode->link_count && !inode->open_count) {
+        vfs_i_free(inode);
+        return 1;
+    }
+    return 0;
+}
+
 struct v_dnode*
 struct v_dnode*
-vfs_d_alloc()
+vfs_d_alloc(struct v_dnode* parent, struct hstr* name)
 {
     struct v_dnode* dnode = cake_grab(dnode_pile);
 {
     struct v_dnode* dnode = cake_grab(dnode_pile);
+    if (!dnode) {
+        lru_evict_half(dnode_lru);
+
+        if (!(dnode = cake_grab(dnode_pile))) {
+            return NULL;
+        }
+    }
+
     memset(dnode, 0, sizeof(*dnode));
     llist_init_head(&dnode->children);
     llist_init_head(&dnode->siblings);
     memset(dnode, 0, sizeof(*dnode));
     llist_init_head(&dnode->children);
     llist_init_head(&dnode->siblings);
+    llist_init_head(&dnode->aka_list);
     mutex_init(&dnode->lock);
 
     dnode->ref_count = ATOMIC_VAR_INIT(0);
     dnode->name = HHSTR(vzalloc(VFS_NAME_MAXLEN), 0, 0);
 
     mutex_init(&dnode->lock);
 
     dnode->ref_count = ATOMIC_VAR_INIT(0);
     dnode->name = HHSTR(vzalloc(VFS_NAME_MAXLEN), 0, 0);
 
+    hstrcpy(&dnode->name, name);
+
+    if (parent) {
+        vfs_d_assign_sb(dnode, parent->super_block);
+        dnode->mnt = parent->mnt;
+    }
+
+    lru_use_one(dnode_lru, &dnode->lru);
+
     return dnode;
 }
 
 void
 vfs_d_free(struct v_dnode* dnode)
 {
     return dnode;
 }
 
 void
 vfs_d_free(struct v_dnode* dnode)
 {
-    if (dnode->ref_count) {
-        // it can be only freed if no one is refering
-        return;
-    }
-    if (dnode->inode && dnode->inode->link_count) {
+    assert(dnode->ref_count == 1);
+
+    if (dnode->inode) {
+        assert(dnode->inode->link_count > 0);
         dnode->inode->link_count--;
     }
 
         dnode->inode->link_count--;
     }
 
+    vfs_dcache_remove(dnode);
     // Make sure the children de-referencing their parent.
     // With lru presented, the eviction will be propagated over the entire
     // detached subtree eventually
     // Make sure the children de-referencing their parent.
     // With lru presented, the eviction will be propagated over the entire
     // detached subtree eventually
@@ -550,61 +489,101 @@ vfs_d_free(struct v_dnode* dnode)
         vfs_dcache_remove(pos);
     }
 
         vfs_dcache_remove(pos);
     }
 
-    vfree(dnode->name.value);
+    if (dnode->destruct) {
+        dnode->destruct(dnode);
+    }
+
+    vfs_sb_free(dnode->super_block);
+    vfree((void*)dnode->name.value);
     cake_release(dnode_pile, dnode);
 }
 
 struct v_inode*
     cake_release(dnode_pile, dnode);
 }
 
 struct v_inode*
-vfs_i_alloc(dev_t device_id, uint32_t inode_id)
+vfs_i_find(struct v_superblock* sb, u32_t i_id)
 {
 {
-    // 我们这里假设每个文件系统与设备是一一对应(毕竟一个分区不可能有两个不同的文件系统)
-    // 而每个文件系统所产生的 v_inode 缓存必须要和其他文件系统产生的区分开来。
-    // 这也就是说,每个 v_inode 的 id
-    // 必须要由设备ID,和该虚拟inode缓存所对应的物理inode
-    // 相对于其所在的文件系统的id,进行组成!
-    inode_id = hash_32(inode_id ^ device_id, HASH_SIZE_BITS);
-    inode_id = (inode_id >> HASHBITS) ^ inode_id;
-
-    struct hbucket* slot = &inode_cache[inode_id & HASH_MASK];
+    struct hbucket* slot = &sb->i_cache[i_id & VFS_HASH_MASK];
     struct v_inode *pos, *n;
     hashtable_bucket_foreach(slot, pos, n, hash_list)
     {
     struct v_inode *pos, *n;
     hashtable_bucket_foreach(slot, pos, n, hash_list)
     {
-        if (pos->id == inode_id) {
+        if (pos->id == i_id) {
+            lru_use_one(inode_lru, &pos->lru);
             return pos;
         }
     }
 
             return pos;
         }
     }
 
-    pos = cake_grab(inode_pile);
-    memset(pos, 0, sizeof(*pos));
+    return NULL;
+}
+
+void
+vfs_i_addhash(struct v_inode* inode)
+{
+    struct hbucket* slot = &inode->sb->i_cache[inode->id & VFS_HASH_MASK];
+
+    hlist_delete(&inode->hash_list);
+    hlist_add(&slot->head, &inode->hash_list);
+}
+
+struct v_inode*
+vfs_i_alloc(struct v_superblock* sb)
+{
+    assert(sb->ops.init_inode);
+
+    struct v_inode* inode;
+    if (!(inode = cake_grab(inode_pile))) {
+        lru_evict_half(inode_lru);
+        if (!(inode = cake_grab(inode_pile))) {
+            return NULL;
+        }
+    }
 
 
-    pos->id = inode_id;
-    pos->link_count = 1;
+    memset(inode, 0, sizeof(*inode));
+    mutex_init(&inode->lock);
+    llist_init_head(&inode->xattrs);
+    llist_init_head(&inode->aka_dnodes);
 
 
-    mutex_init(&pos->lock);
+    sb->ops.init_inode(sb, inode);
 
 
-    hlist_add(&slot->head, &pos->hash_list);
+    inode->ctime = clock_unixtime();
+    inode->atime = inode->ctime;
+    inode->mtime = inode->ctime;
 
 
-    return pos;
+    vfs_i_assign_sb(inode, sb);
+    lru_use_one(inode_lru, &inode->lru);
+    return inode;
 }
 
 void
 vfs_i_free(struct v_inode* inode)
 {
 }
 
 void
 vfs_i_free(struct v_inode* inode)
 {
+    if (inode->pg_cache) {
+        pcache_release(inode->pg_cache);
+        vfree(inode->pg_cache);
+    }
+    // we don't need to sync inode.
+    // If an inode can be free, then it must be properly closed.
+    // Hence it must be synced already!
+    if (inode->destruct) {
+        inode->destruct(inode);
+    }
+
+    vfs_sb_free(inode->sb);
     hlist_delete(&inode->hash_list);
     cake_release(inode_pile, inode);
 }
 
 /* ---- System call definition and support ---- */
 
     hlist_delete(&inode->hash_list);
     cake_release(inode_pile, inode);
 }
 
 /* ---- System call definition and support ---- */
 
-#define FLOCATE_CREATE_EMPTY 1
+// make a new name when not exists
+#define FLOC_MAYBE_MKNAME 1
 
 
-#define DO_STATUS(errno) SYSCALL_ESTATUS(__current->k_status = errno)
-#define DO_STATUS_OR_RETURN(errno) ({ errno < 0 ? DO_STATUS(errno) : errno; })
+// name must be non-exist and made.
+#define FLOC_MKNAME 2
 
 
-#define TEST_FD(fd) (fd >= 0 && fd < VFS_MAX_FD)
+// no follow symlink
+#define FLOC_NOFOLLOW 4
 
 int
 
 int
-__vfs_getfd(int fd, struct v_fd** fd_s)
+vfs_getfd(int fd, struct v_fd** fd_s)
 {
     if (TEST_FD(fd) && (*fd_s = __current->fdtable->fds[fd])) {
         return 0;
 {
     if (TEST_FD(fd) && (*fd_s = __current->fdtable->fds[fd])) {
         return 0;
@@ -612,72 +591,169 @@ __vfs_getfd(int fd, struct v_fd** fd_s)
     return EBADF;
 }
 
     return EBADF;
 }
 
-int
+static int
+__vfs_mknod(struct v_inode* parent, struct v_dnode* dnode, 
+            unsigned int itype, dev_t* dev)
+{
+    int errno;
+
+    errno = parent->ops->create(parent, dnode, itype);
+    if (errno) {
+        return errno;
+    }
+
+    return 0;
+}
+
+struct file_locator {
+    struct v_dnode* dir;
+    struct v_dnode* file;
+    bool fresh;
+};
+
+/**
+ * @brief unlock the file locator (floc) if possible.
+ *        If the file to be located if not exists, and
+ *        any FLOC_*MKNAME flag is set, then the parent
+ *        dnode will be locked until the file has been properly
+ *        finalised by subsequent logic.
+ * 
+ * @param floc 
+ */
+static inline void
+__floc_try_unlock(struct file_locator* floc)
+{
+    if (floc->fresh) {
+        assert(floc->dir);
+        unlock_dnode(floc->dir);
+    }
+}
+
+static int
 __vfs_try_locate_file(const char* path,
 __vfs_try_locate_file(const char* path,
-                      struct v_dnode** fdir,
-                      struct v_dnode** file,
+                      struct file_locator* floc,
                       int options)
 {
     char name_str[VFS_NAME_MAXLEN];
                       int options)
 {
     char name_str[VFS_NAME_MAXLEN];
+    struct v_dnode *fdir, *file;
     struct hstr name = HSTR(name_str, 0);
     struct hstr name = HSTR(name_str, 0);
-    int errno;
-    if ((errno =
-           vfs_walk(__current->cwd, path, fdir, &name, VFS_WALK_PARENT))) {
-        return errno;
+    int errno, woption = 0;
+
+    if ((options & FLOC_NOFOLLOW)) {
+        woption |= VFS_WALK_NOFOLLOW;
+        options &= ~FLOC_NOFOLLOW;
     }
 
     }
 
-    errno = vfs_walk(*fdir, name.value, file, NULL, 0);
-    if (errno != ENOENT || !(options & FLOCATE_CREATE_EMPTY)) {
+    floc->fresh = false;
+    name_str[0] = 0;
+    errno = vfs_walk_proc(path, &fdir, &name, woption | VFS_WALK_PARENT);
+    if (errno) {
         return errno;
     }
 
         return errno;
     }
 
-    struct v_dnode* parent = *fdir;
-    struct v_dnode* file_new = vfs_d_alloc();
-    hstrcpy(&file_new->name, &name);
+    errno = vfs_walk(fdir, name.value, &file, NULL, woption);
 
 
-    if (!(errno = parent->inode->ops.create(parent->inode, file_new))) {
-        *file = file_new;
+    if (errno && errno != ENOENT) {
+        goto done;
+    }
+    
+    if (!errno) {
+        if ((options & FLOC_MKNAME)) {
+            errno = EEXIST;
+        }
+        goto done;
+    }
 
 
-        vfs_dcache_add(parent, file_new);
-        llist_append(&parent->children, &file_new->siblings);
-    } else {
-        vfs_d_free(file_new);
+    // errno == ENOENT
+    if (!options) {
+        goto done;
+    }
+
+    errno = vfs_check_writable(fdir);
+    if (errno) {
+        goto done;
+    }
+
+    floc->fresh = true;
+
+    file = vfs_d_alloc(fdir, &name);
+
+    if (!file) {
+        return ENOMEM;
     }
 
     }
 
+    lock_dnode(fdir);
+
+    vfs_dcache_add(fdir, file);
+
+done:
+    floc->dir   = fdir;
+    floc->file  = file;
+
     return errno;
 }
 
 int
 vfs_do_open(const char* path, int options)
 {
     return errno;
 }
 
 int
 vfs_do_open(const char* path, int options)
 {
-    int errno, fd;
+    int errno, fd, loptions = 0;
     struct v_dnode *dentry, *file;
     struct v_dnode *dentry, *file;
-    struct v_file* ofile = 0;
+    struct v_file* ofile = NULL;
+    struct file_locator floc;
+    struct v_inode* inode;
 
 
-    errno = __vfs_try_locate_file(
-      path, &dentry, &file, (options & FO_CREATE) ? FLOCATE_CREATE_EMPTY : 0);
+    if ((options & FO_CREATE)) {
+        loptions |= FLOC_MAYBE_MKNAME;
+    } else if ((options & FO_NOFOLLOW)) {
+        loptions |= FLOC_NOFOLLOW;
+    }
+
+    errno = __vfs_try_locate_file(path, &floc, loptions);
 
 
-    if (errno || (errno = vfs_open(file, &ofile))) {
+    if (errno || (errno = vfs_alloc_fdslot(&fd))) {
         return errno;
     }
 
         return errno;
     }
 
-    struct v_inode* o_inode = ofile->inode;
-    if (!(o_inode->itype & VFS_IFSEQDEV) && !(options & FO_DIRECT)) {
-        // XXX Change here accordingly when signature of pcache_r/w changed.
-        ofile->ops.read = pcache_read;
-        ofile->ops.write = pcache_write;
+    file   = floc.file;
+    dentry = floc.dir;
+
+    if (floc.fresh) {
+        errno = __vfs_mknod(dentry->inode, file, VFS_IFFILE, NULL);
+        if (errno) {
+            vfs_d_free(file);
+            __floc_try_unlock(&floc);
+            return errno;
+        }
+
+        __floc_try_unlock(&floc);
     }
 
     }
 
-    if (!errno && !(errno = vfs_alloc_fdslot(&fd))) {
-        struct v_fd* fd_s = vzalloc(sizeof(*fd_s));
-        ofile->f_pos = ofile->inode->fsize & -((options & FO_APPEND) != 0);
-        fd_s->file = ofile;
-        fd_s->flags = options;
-        __current->fdtable->fds[fd] = fd_s;
-        return fd;
+
+    if ((errno = vfs_open(file, &ofile))) {
+        return errno;
     }
 
     }
 
-    return errno;
+    inode = ofile->inode;
+    lock_inode(inode);
+
+    struct v_fd* fd_s = cake_grab(fd_pile);
+    memset(fd_s, 0, sizeof(*fd_s));
+
+    if ((options & O_TRUNC)) {
+        file->inode->fsize = 0;   
+    }
+
+    if (vfs_get_dtype(inode->itype) == DT_DIR) {
+        ofile->f_pos = 0;
+    }
+    
+    fd_s->file = ofile;
+    fd_s->flags = options;
+    __current->fdtable->fds[fd] = fd_s;
+
+    unlock_inode(inode);
+    
+    return fd;
 }
 
 __DEFINE_LXSYSCALL2(int, open, const char*, path, int, options)
 }
 
 __DEFINE_LXSYSCALL2(int, open, const char*, path, int, options)
@@ -690,17 +766,15 @@ __DEFINE_LXSYSCALL1(int, close, int, fd)
 {
     struct v_fd* fd_s;
     int errno = 0;
 {
     struct v_fd* fd_s;
     int errno = 0;
-    if ((errno = __vfs_getfd(fd, &fd_s))) {
+    if ((errno = vfs_getfd(fd, &fd_s))) {
         goto done_err;
     }
 
         goto done_err;
     }
 
-    if (fd_s->file->ref_count > 1) {
-        fd_s->file->ref_count--;
-    } else if ((errno = vfs_close(fd_s->file))) {
+    if ((errno = vfs_close(fd_s->file))) {
         goto done_err;
     }
 
         goto done_err;
     }
 
-    vfree(fd_s);
+    cake_release(fd_pile, fd_s);
     __current->fdtable->fds[fd] = 0;
 
 done_err:
     __current->fdtable->fds[fd] = 0;
 
 done_err:
@@ -713,18 +787,18 @@ __vfs_readdir_callback(struct dir_context* dctx,
                        const int len,
                        const int dtype)
 {
                        const int len,
                        const int dtype)
 {
-    struct dirent* dent = (struct dirent*)dctx->cb_data;
-    strncpy(dent->d_name, name, DIRENT_NAME_MAX_LEN);
+    struct lx_dirent* dent = (struct lx_dirent*)dctx->cb_data;
+    strncpy(dent->d_name, name, MIN(len, DIRENT_NAME_MAX_LEN));
     dent->d_nlen = len;
     dent->d_type = dtype;
 }
 
     dent->d_nlen = len;
     dent->d_type = dtype;
 }
 
-__DEFINE_LXSYSCALL2(int, readdir, int, fd, struct dirent*, dent)
+__DEFINE_LXSYSCALL2(int, sys_readdir, int, fd, struct lx_dirent*, dent)
 {
     struct v_fd* fd_s;
     int errno;
 
 {
     struct v_fd* fd_s;
     int errno;
 
-    if ((errno = __vfs_getfd(fd, &fd_s))) {
+    if ((errno = vfs_getfd(fd, &fd_s))) {
         goto done;
     }
 
         goto done;
     }
 
@@ -732,45 +806,39 @@ __DEFINE_LXSYSCALL2(int, readdir, int, fd, struct dirent*, dent)
 
     lock_inode(inode);
 
 
     lock_inode(inode);
 
-    if (!(fd_s->file->inode->itype & VFS_IFDIR)) {
+    if (!check_directory_node(inode)) {
         errno = ENOTDIR;
         errno = ENOTDIR;
-    } else {
-        struct dir_context dctx =
-          (struct dir_context){ .cb_data = dent,
-                                .index = dent->d_offset,
-                                .read_complete_callback =
-                                  __vfs_readdir_callback };
-        if (dent->d_offset == 0) {
-            __vfs_readdir_callback(&dctx, vfs_dot.value, vfs_dot.len, 0);
-        } else if (dent->d_offset == 1) {
-            __vfs_readdir_callback(&dctx, vfs_ddot.value, vfs_ddot.len, 0);
-        } else {
-            dctx.index -= 2;
-            if ((errno = fd_s->file->ops.readdir(inode, &dctx))) {
-                unlock_inode(inode);
-                goto done;
-            }
-        }
-        errno = 0;
-        dent->d_offset++;
+        goto unlock;
     }
 
     }
 
+    struct dir_context dctx = (struct dir_context) {
+        .cb_data = dent,
+        .read_complete_callback = __vfs_readdir_callback
+    };
+
+    if ((errno = fd_s->file->ops->readdir(fd_s->file, &dctx)) != 1) {
+        goto unlock;
+    }
+    dent->d_offset++;
+    fd_s->file->f_pos++;
+
+unlock:
     unlock_inode(inode);
 
 done:
     unlock_inode(inode);
 
 done:
-    return DO_STATUS(errno);
+    return DO_STATUS_OR_RETURN(errno);
 }
 
 __DEFINE_LXSYSCALL3(int, read, int, fd, void*, buf, size_t, count)
 {
     int errno = 0;
     struct v_fd* fd_s;
 }
 
 __DEFINE_LXSYSCALL3(int, read, int, fd, void*, buf, size_t, count)
 {
     int errno = 0;
     struct v_fd* fd_s;
-    if ((errno = __vfs_getfd(fd, &fd_s))) {
+    if ((errno = vfs_getfd(fd, &fd_s))) {
         goto done;
     }
 
     struct v_file* file = fd_s->file;
         goto done;
     }
 
     struct v_file* file = fd_s->file;
-    if ((file->inode->itype & VFS_IFDIR)) {
+    if (check_directory_node(file->inode)) {
         errno = EISDIR;
         goto done;
     }
         errno = EISDIR;
         goto done;
     }
@@ -779,16 +847,20 @@ __DEFINE_LXSYSCALL3(int, read, int, fd, void*, buf, size_t, count)
 
     file->inode->atime = clock_unixtime();
 
 
     file->inode->atime = clock_unixtime();
 
-    __SYSCALL_INTERRUPTIBLE(
-      { errno = file->ops.read(file->inode, buf, count, file->f_pos); })
-
-    unlock_inode(file->inode);
+    if (check_seqdev_node(file->inode) || (fd_s->flags & FO_DIRECT)) {
+        errno = file->ops->read(file->inode, buf, count, file->f_pos);
+    } else {
+        errno = pcache_read(file->inode, buf, count, file->f_pos);
+    }
 
     if (errno > 0) {
         file->f_pos += errno;
 
     if (errno > 0) {
         file->f_pos += errno;
+        unlock_inode(file->inode);
         return errno;
     }
 
         return errno;
     }
 
+    unlock_inode(file->inode);
+
 done:
     return DO_STATUS(errno);
 }
 done:
     return DO_STATUS(errno);
 }
@@ -797,30 +869,46 @@ __DEFINE_LXSYSCALL3(int, write, int, fd, void*, buf, size_t, count)
 {
     int errno = 0;
     struct v_fd* fd_s;
 {
     int errno = 0;
     struct v_fd* fd_s;
-    if ((errno = __vfs_getfd(fd, &fd_s))) {
+    if ((errno = vfs_getfd(fd, &fd_s))) {
         goto done;
     }
 
         goto done;
     }
 
+    struct v_inode* inode;
     struct v_file* file = fd_s->file;
     struct v_file* file = fd_s->file;
-    if ((file->inode->itype & VFS_IFDIR)) {
-        errno = EISDIR;
+
+    if ((errno = vfs_check_writable(file->dnode))) {
         goto done;
     }
 
         goto done;
     }
 
-    lock_inode(file->inode);
+    if (check_directory_node(file->inode)) {
+        errno = EISDIR;
+        goto done;
+    }
 
 
-    file->inode->mtime = clock_unixtime();
+    inode = file->inode;
+    lock_inode(inode);
 
 
-    __SYSCALL_INTERRUPTIBLE(
-      { errno = file->ops.write(file->inode, buf, count, file->f_pos); })
+    inode->mtime = clock_unixtime();
+    if ((fd_s->flags & O_APPEND)) {
+        file->f_pos = inode->fsize;
+    }
 
 
-    unlock_inode(file->inode);
+    if (check_seqdev_node(inode) || (fd_s->flags & FO_DIRECT)) {
+        errno = file->ops->write(inode, buf, count, file->f_pos);
+    } else {
+        errno = pcache_write(inode, buf, count, file->f_pos);
+    }
 
     if (errno > 0) {
         file->f_pos += errno;
 
     if (errno > 0) {
         file->f_pos += errno;
+        inode->fsize = MAX(inode->fsize, file->f_pos);
+
+        unlock_inode(inode);
         return errno;
     }
 
         return errno;
     }
 
+    unlock_inode(inode);
+
 done:
     return DO_STATUS(errno);
 }
 done:
     return DO_STATUS(errno);
 }
@@ -829,31 +917,47 @@ __DEFINE_LXSYSCALL3(int, lseek, int, fd, int, offset, int, options)
 {
     int errno = 0;
     struct v_fd* fd_s;
 {
     int errno = 0;
     struct v_fd* fd_s;
-    if ((errno = __vfs_getfd(fd, &fd_s))) {
+    if ((errno = vfs_getfd(fd, &fd_s))) {
         goto done;
     }
 
     struct v_file* file = fd_s->file;
         goto done;
     }
 
     struct v_file* file = fd_s->file;
+    struct v_inode* inode = file->inode;
 
 
-    lock_inode(file->inode);
+    if (!file->ops->seek) {
+        errno = ENOTSUP;
+        goto done;
+    }
+
+    lock_inode(inode);
+
+    int overflow = 0;
+    int fpos = file->f_pos;
 
 
-    size_t fpos = file->f_pos;
+    if (vfs_get_dtype(inode->itype) == DT_DIR) {
+        options = (options != FSEEK_END) ? options : FSEEK_SET;
+    }
+    
     switch (options) {
         case FSEEK_CUR:
     switch (options) {
         case FSEEK_CUR:
-            fpos = (size_t)((int)file->f_pos + offset);
+            overflow = sadd_of((int)file->f_pos, offset, &fpos);
             break;
         case FSEEK_END:
             break;
         case FSEEK_END:
-            fpos = (size_t)((int)file->inode->fsize + offset);
+            overflow = sadd_of((int)inode->fsize, offset, &fpos);
             break;
         case FSEEK_SET:
             fpos = offset;
             break;
     }
             break;
         case FSEEK_SET:
             fpos = offset;
             break;
     }
-    if (!file->ops.seek || !(errno = file->ops.seek(file->inode, fpos))) {
-        file->f_pos = fpos;
+
+    if (overflow) {
+        errno = EOVERFLOW;
+    }
+    else {
+        errno = file->ops->seek(file, fpos);
     }
 
     }
 
-    unlock_inode(file->inode);
+    unlock_inode(inode);
 
 done:
     return DO_STATUS(errno);
 
 done:
     return DO_STATUS(errno);
@@ -867,23 +971,27 @@ vfs_get_path(struct v_dnode* dnode, char* buf, size_t size, int depth)
     }
 
     if (depth > 64) {
     }
 
     if (depth > 64) {
-        return ELOOP;
+        return ENAMETOOLONG;
     }
 
     }
 
-    size_t len = vfs_get_path(dnode->parent, buf, size, depth + 1);
+    size_t len = 0;
+
+    if (dnode->parent != dnode) {
+        len = vfs_get_path(dnode->parent, buf, size, depth + 1);
+    }
 
     if (len >= size) {
         return len;
     }
 
 
     if (len >= size) {
         return len;
     }
 
+    if (!len || buf[len - 1] != VFS_PATH_DELIM) {
+        buf[len++] = VFS_PATH_DELIM;
+    }
+
     size_t cpy_size = MIN(dnode->name.len, size - len);
     strncpy(buf + len, dnode->name.value, cpy_size);
     len += cpy_size;
 
     size_t cpy_size = MIN(dnode->name.len, size - len);
     strncpy(buf + len, dnode->name.value, cpy_size);
     len += cpy_size;
 
-    if (len < size) {
-        buf[len++] = PATH_DELIM;
-    }
-
     return len;
 }
 
     return len;
 }
 
@@ -892,23 +1000,49 @@ vfs_readlink(struct v_dnode* dnode, char* buf, size_t size)
 {
     const char* link;
     struct v_inode* inode = dnode->inode;
 {
     const char* link;
     struct v_inode* inode = dnode->inode;
-    if (inode->ops.read_symlink) {
-        lock_inode(inode);
 
 
-        int errno = inode->ops.read_symlink(inode, &link);
-        strncpy(buf, link, size);
+    if (!check_symlink_node(inode)) {
+        return EINVAL;
+    }
+
+    if (!inode->ops->read_symlink) {
+        return ENOTSUP;
+    }
 
 
-        unlock_inode(inode);
-        return errno;
+    lock_inode(inode);
+
+    int errno = inode->ops->read_symlink(inode, &link);
+    if (errno >= 0) {
+        strncpy(buf, link, MIN(size, (size_t)errno));
     }
     }
-    return 0;
+
+    unlock_inode(inode);
+    return errno;
+}
+
+int
+vfs_get_dtype(int itype)
+{
+    int dtype = DT_FILE;
+    if (check_itype(itype, VFS_IFSYMLINK)) {
+        dtype |= DT_SYMLINK;
+    }
+    
+    if (check_itype(itype, VFS_IFDIR)) {
+        dtype |= DT_DIR;
+        return dtype;
+    }
+
+    // TODO other types
+    
+    return dtype;
 }
 
 __DEFINE_LXSYSCALL3(int, realpathat, int, fd, char*, buf, size_t, size)
 {
     int errno;
     struct v_fd* fd_s;
 }
 
 __DEFINE_LXSYSCALL3(int, realpathat, int, fd, char*, buf, size_t, size)
 {
     int errno;
     struct v_fd* fd_s;
-    if ((errno = __vfs_getfd(fd, &fd_s))) {
+    if ((errno = vfs_getfd(fd, &fd_s))) {
         goto done;
     }
 
         goto done;
     }
 
@@ -927,8 +1061,7 @@ __DEFINE_LXSYSCALL3(int, readlink, const char*, path, char*, buf, size_t, size)
 {
     int errno;
     struct v_dnode* dnode;
 {
     int errno;
     struct v_dnode* dnode;
-    if (!(errno =
-            vfs_walk(__current->cwd, path, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
+    if (!(errno = vfs_walk_proc(path, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
         errno = vfs_readlink(dnode, buf, size);
     }
 
         errno = vfs_readlink(dnode, buf, size);
     }
 
@@ -939,23 +1072,17 @@ __DEFINE_LXSYSCALL3(int, readlink, const char*, path, char*, buf, size_t, size)
     return DO_STATUS(errno);
 }
 
     return DO_STATUS(errno);
 }
 
-__DEFINE_LXSYSCALL4(int,
-                    readlinkat,
-                    int,
-                    dirfd,
-                    const char*,
-                    pathname,
-                    char*,
-                    buf,
-                    size_t,
-                    size)
+__DEFINE_LXSYSCALL4(
+  int, readlinkat, int, dirfd, const char*, pathname, char*, buf, size_t, size)
 {
     int errno;
     struct v_fd* fd_s;
 {
     int errno;
     struct v_fd* fd_s;
-    if ((errno = __vfs_getfd(dirfd, &fd_s))) {
+    if ((errno = vfs_getfd(dirfd, &fd_s))) {
         goto done;
     }
 
         goto done;
     }
 
+    pathname = pathname ? pathname : "";
+
     struct v_dnode* dnode;
     if (!(errno = vfs_walk(
             fd_s->file->dnode, pathname, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
     struct v_dnode* dnode;
     if (!(errno = vfs_walk(
             fd_s->file->dnode, pathname, &dnode, NULL, VFS_WALK_NOFOLLOW))) {
@@ -982,21 +1109,22 @@ __DEFINE_LXSYSCALL1(int, rmdir, const char*, pathname)
 {
     int errno;
     struct v_dnode* dnode;
 {
     int errno;
     struct v_dnode* dnode;
-    if ((errno = vfs_walk(__current->cwd, pathname, &dnode, NULL, 0))) {
+    if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
         return DO_STATUS(errno);
     }
 
     lock_dnode(dnode);
 
         return DO_STATUS(errno);
     }
 
     lock_dnode(dnode);
 
-    if (dnode->parent)
-        lock_dnode(dnode->parent);
+    if ((errno = vfs_check_writable(dnode))) {
+        goto done;
+    }
 
     if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
         errno = EROFS;
         goto done;
     }
 
 
     if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
         errno = EROFS;
         goto done;
     }
 
-    if (dnode->ref_count || dnode->inode->open_count) {
+    if (dnode->ref_count > 1 || dnode->inode->open_count) {
         errno = EBUSY;
         goto done;
     }
         errno = EBUSY;
         goto done;
     }
@@ -1006,57 +1134,78 @@ __DEFINE_LXSYSCALL1(int, rmdir, const char*, pathname)
         goto done;
     }
 
         goto done;
     }
 
-    lock_inode(dnode->inode);
+    struct v_dnode* parent = dnode->parent;
 
 
-    if ((dnode->inode->itype & VFS_IFDIR)) {
-        errno = dnode->inode->ops.rmdir(dnode->inode);
+    if (!parent) {
+        errno = EINVAL;
+        goto done;
+    }
+
+    lock_dnode(parent);
+    lock_inode(parent->inode);
+
+    if (check_directory_node(dnode->inode)) {
+        errno = parent->inode->ops->rmdir(parent->inode, dnode);
         if (!errno) {
             vfs_dcache_remove(dnode);
         if (!errno) {
             vfs_dcache_remove(dnode);
-            unlock_inode(dnode->inode);
-            vfs_d_free(dnode);
-
-            goto done;
         }
     } else {
         errno = ENOTDIR;
     }
 
         }
     } else {
         errno = ENOTDIR;
     }
 
-    unlock_inode(dnode->inode);
+    unlock_inode(parent->inode);
+    unlock_dnode(parent);
 
 done:
     unlock_dnode(dnode);
 
 done:
     unlock_dnode(dnode);
-    if (dnode->parent)
-        unlock_dnode(dnode->parent);
     return DO_STATUS(errno);
 }
 
 __DEFINE_LXSYSCALL1(int, mkdir, const char*, path)
 {
     return DO_STATUS(errno);
 }
 
 __DEFINE_LXSYSCALL1(int, mkdir, const char*, path)
 {
-    struct v_dnode *parent, *dir = vfs_d_alloc();
-    int errno =
-      vfs_walk(__current->cwd, path, &parent, &dir->name, VFS_WALK_PARENT);
-    if (errno) {
+    int errno = 0;
+    struct v_dnode *parent, *dir;
+    char name_value[VFS_NAME_MAXLEN];
+    struct hstr name = HHSTR(name_value, 0, 0);
+
+    if ((errno = vfs_walk_proc(path, &parent, &name, VFS_WALK_PARENT))) {
         goto done;
     }
 
         goto done;
     }
 
+    if (!(errno = vfs_walk(parent, name_value, &dir, NULL, 0))) {
+        errno = EEXIST;
+        goto done;
+    }
+
+    if ((errno = vfs_check_writable(parent))) {
+        goto done;
+    }
+
+    if (!(dir = vfs_d_alloc(parent, &name))) {
+        errno = ENOMEM;
+        goto done;
+    }
+
+    struct v_inode* inode = parent->inode;
+
     lock_dnode(parent);
     lock_dnode(parent);
-    lock_inode(parent->inode);
+    lock_inode(inode);
 
     if ((parent->super_block->fs->types & FSTYPE_ROFS)) {
         errno = ENOTSUP;
 
     if ((parent->super_block->fs->types & FSTYPE_ROFS)) {
         errno = ENOTSUP;
-    } else if (!parent->inode->ops.mkdir) {
+    } else if (!inode->ops->mkdir) {
         errno = ENOTSUP;
         errno = ENOTSUP;
-    } else if (!(parent->inode->itype & VFS_IFDIR)) {
+    } else if (!check_directory_node(inode)) {
         errno = ENOTDIR;
         errno = ENOTDIR;
-    } else if (!(errno = parent->inode->ops.mkdir(parent->inode, dir))) {
-        llist_append(&parent->children, &dir->siblings);
+    } else if (!(errno = inode->ops->mkdir(inode, dir))) {
+        vfs_dcache_add(parent, dir);
         goto cleanup;
     }
 
     vfs_d_free(dir);
 
 cleanup:
         goto cleanup;
     }
 
     vfs_d_free(dir);
 
 cleanup:
-    unlock_inode(parent->inode);
+    unlock_inode(inode);
     unlock_dnode(parent);
 done:
     return DO_STATUS(errno);
     unlock_dnode(parent);
 done:
     return DO_STATUS(errno);
@@ -1065,24 +1214,24 @@ done:
 int
 __vfs_do_unlink(struct v_dnode* dnode)
 {
 int
 __vfs_do_unlink(struct v_dnode* dnode)
 {
+    int errno;
     struct v_inode* inode = dnode->inode;
 
     if (dnode->ref_count > 1) {
         return EBUSY;
     }
 
     struct v_inode* inode = dnode->inode;
 
     if (dnode->ref_count > 1) {
         return EBUSY;
     }
 
+    if ((errno = vfs_check_writable(dnode))) {
+        return errno;
+    }
+
     lock_inode(inode);
 
     lock_inode(inode);
 
-    int errno;
     if (inode->open_count) {
         errno = EBUSY;
     if (inode->open_count) {
         errno = EBUSY;
-    } else if (!(inode->itype & VFS_IFDIR)) {
-        // The underlying unlink implementation should handle
-        //  symlink case
-        errno = inode->ops.unlink(inode);
+    } else if (!check_directory_node(inode)) {
+        errno = inode->ops->unlink(inode, dnode);
         if (!errno) {
         if (!errno) {
-            inode->link_count--;
-            vfs_dcache_remove(dnode);
             vfs_d_free(dnode);
         }
     } else {
             vfs_d_free(dnode);
         }
     } else {
@@ -1098,11 +1247,7 @@ __DEFINE_LXSYSCALL1(int, unlink, const char*, pathname)
 {
     int errno;
     struct v_dnode* dnode;
 {
     int errno;
     struct v_dnode* dnode;
-    if ((errno = vfs_walk(__current->cwd, pathname, &dnode, NULL, 0))) {
-        goto done;
-    }
-    if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
-        errno = EROFS;
+    if ((errno = vfs_walk_proc(pathname, &dnode, NULL, 0))) {
         goto done;
     }
 
         goto done;
     }
 
@@ -1116,7 +1261,7 @@ __DEFINE_LXSYSCALL2(int, unlinkat, int, fd, const char*, pathname)
 {
     int errno;
     struct v_fd* fd_s;
 {
     int errno;
     struct v_fd* fd_s;
-    if ((errno = __vfs_getfd(fd, &fd_s))) {
+    if ((errno = vfs_getfd(fd, &fd_s))) {
         goto done;
     }
 
         goto done;
     }
 
@@ -1132,18 +1277,30 @@ done:
 __DEFINE_LXSYSCALL2(int, link, const char*, oldpath, const char*, newpath)
 {
     int errno;
 __DEFINE_LXSYSCALL2(int, link, const char*, oldpath, const char*, newpath)
 {
     int errno;
-    struct v_dnode *dentry, *to_link, *name_dentry, *name_file;
+    struct file_locator floc;
+    struct v_dnode *to_link, *name_file;
+
+    errno = __vfs_try_locate_file(oldpath, &floc, 0);
+    if (errno) {
+        goto done;
+    }
 
 
-    errno = __vfs_try_locate_file(oldpath, &dentry, &to_link, 0);
+    __floc_try_unlock(&floc);
+
+    to_link = floc.file;
+    errno = __vfs_try_locate_file(newpath, &floc, FLOC_MKNAME);
     if (!errno) {
     if (!errno) {
-        errno = __vfs_try_locate_file(
-          newpath, &name_dentry, &name_file, FLOCATE_CREATE_EMPTY);
-        if (!errno) {
-            errno = EEXIST;
-        } else if (name_file) {
-            errno = vfs_link(to_link, name_file);
-        }
+        goto done;       
+    }
+
+    name_file = floc.file;
+    errno = vfs_link(to_link, name_file);
+    if (errno) {
+        vfs_d_free(name_file);
     }
     }
+
+done:
+    __floc_try_unlock(&floc);
     return DO_STATUS(errno);
 }
 
     return DO_STATUS(errno);
 }
 
@@ -1151,7 +1308,8 @@ __DEFINE_LXSYSCALL1(int, fsync, int, fildes)
 {
     int errno;
     struct v_fd* fd_s;
 {
     int errno;
     struct v_fd* fd_s;
-    if (!(errno = __vfs_getfd(fildes, &fd_s))) {
+
+    if (!(errno = vfs_getfd(fildes, &fd_s))) {
         errno = vfs_fsync(fd_s->file);
     }
 
         errno = vfs_fsync(fd_s->file);
     }
 
@@ -1182,7 +1340,7 @@ vfs_dup2(int oldfd, int newfd)
 
     int errno;
     struct v_fd *oldfd_s, *newfd_s;
 
     int errno;
     struct v_fd *oldfd_s, *newfd_s;
-    if ((errno = __vfs_getfd(oldfd, &oldfd_s))) {
+    if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
         goto done;
     }
 
         goto done;
     }
 
@@ -1214,7 +1372,7 @@ __DEFINE_LXSYSCALL1(int, dup, int, oldfd)
 {
     int errno, newfd;
     struct v_fd *oldfd_s, *newfd_s;
 {
     int errno, newfd;
     struct v_fd *oldfd_s, *newfd_s;
-    if ((errno = __vfs_getfd(oldfd, &oldfd_s))) {
+    if ((errno = vfs_getfd(oldfd, &oldfd_s))) {
         goto done;
     }
 
         goto done;
     }
 
@@ -1228,55 +1386,94 @@ done:
     return DO_STATUS(errno);
 }
 
     return DO_STATUS(errno);
 }
 
-__DEFINE_LXSYSCALL2(int,
-                    symlink,
-                    const char*,
-                    pathname,
-                    const char*,
-                    link_target)
+__DEFINE_LXSYSCALL2(
+  int, symlink, const char*, pathname, const char*, link_target)
 {
     int errno;
 {
     int errno;
-    struct v_dnode* dnode;
-    if ((errno = vfs_walk(__current->cwd, pathname, &dnode, NULL, 0))) {
+    struct file_locator floc;
+    struct v_dnode *file;
+    struct v_inode *f_ino;
+    
+    errno = __vfs_try_locate_file(pathname, &floc, FLOC_MKNAME);
+    if (errno) {
         goto done;
     }
         goto done;
     }
-    if ((dnode->super_block->fs->types & FSTYPE_ROFS)) {
-        errno = EROFS;
+
+    file = floc.file;
+    errno = __vfs_mknod(floc.dir->inode, file, VFS_IFSYMLINK, NULL);
+    if (errno) {
+        vfs_d_free(file);
+        goto done;
+    }
+
+    f_ino = file->inode;
+
+    assert(f_ino);
+
+    errno = vfs_check_writable(file);
+    if (errno) {
         goto done;
     }
         goto done;
     }
-    if (!dnode->inode->ops.symlink) {
+
+    if (!f_ino->ops->set_symlink) {
         errno = ENOTSUP;
         goto done;
     }
 
         errno = ENOTSUP;
         goto done;
     }
 
-    lock_inode(dnode->inode);
+    lock_inode(f_ino);
 
 
-    errno = dnode->inode->ops.symlink(dnode->inode, link_target);
+    errno = f_ino->ops->set_symlink(f_ino, link_target);
 
 
-    unlock_inode(dnode->inode);
+    unlock_inode(f_ino);
 
 done:
 
 done:
+    __floc_try_unlock(&floc);
     return DO_STATUS(errno);
 }
 
     return DO_STATUS(errno);
 }
 
+void
+vfs_ref_file(struct v_file* file)
+{
+    atomic_fetch_add(&file->ref_count, 1);
+}
+
+void
+vfs_ref_dnode(struct v_dnode* dnode)
+{
+    atomic_fetch_add(&dnode->ref_count, 1);
+    
+    if (dnode->mnt) {
+        mnt_mkbusy(dnode->mnt);
+    }
+}
+
+void
+vfs_unref_dnode(struct v_dnode* dnode)
+{
+    atomic_fetch_sub(&dnode->ref_count, 1);
+    if (dnode->mnt) {
+        mnt_chillax(dnode->mnt);
+    }
+}
+
 int
 int
-__vfs_do_chdir(struct v_dnode* dnode)
+vfs_do_chdir(struct proc_info* proc, struct v_dnode* dnode)
 {
     int errno = 0;
 
     lock_dnode(dnode);
 
 {
     int errno = 0;
 
     lock_dnode(dnode);
 
-    if (!(dnode->inode->itype & VFS_IFDIR)) {
+    if (!check_directory_node(dnode->inode)) {
         errno = ENOTDIR;
         goto done;
     }
 
         errno = ENOTDIR;
         goto done;
     }
 
-    if (__current->cwd) {
-        atomic_fetch_add(&__current->cwd->ref_count, 1);
+    if (proc->cwd) {
+        vfs_unref_dnode(proc->cwd);
     }
 
     }
 
-    atomic_fetch_sub(&dnode->ref_count, 1);
-    __current->cwd = dnode;
+    vfs_ref_dnode(dnode);
+    proc->cwd = dnode;
 
     unlock_dnode(dnode);
 
 
     unlock_dnode(dnode);
 
@@ -1289,11 +1486,11 @@ __DEFINE_LXSYSCALL1(int, chdir, const char*, path)
     struct v_dnode* dnode;
     int errno = 0;
 
     struct v_dnode* dnode;
     int errno = 0;
 
-    if ((errno = vfs_walk(__current->cwd, path, &dnode, NULL, 0))) {
+    if ((errno = vfs_walk_proc(path, &dnode, NULL, 0))) {
         goto done;
     }
 
         goto done;
     }
 
-    errno = __vfs_do_chdir(dnode);
+    errno = vfs_do_chdir((struct proc_info*)__current, dnode);
 
 done:
     return DO_STATUS(errno);
 
 done:
     return DO_STATUS(errno);
@@ -1304,11 +1501,11 @@ __DEFINE_LXSYSCALL1(int, fchdir, int, fd)
     struct v_fd* fd_s;
     int errno = 0;
 
     struct v_fd* fd_s;
     int errno = 0;
 
-    if ((errno = __vfs_getfd(fd, &fd_s))) {
+    if ((errno = vfs_getfd(fd, &fd_s))) {
         goto done;
     }
 
         goto done;
     }
 
-    errno = __vfs_do_chdir(fd_s->file->dnode);
+    errno = vfs_do_chdir((struct proc_info*)__current, fd_s->file->dnode);
 
 done:
     return DO_STATUS(errno);
 
 done:
     return DO_STATUS(errno);
@@ -1326,7 +1523,7 @@ __DEFINE_LXSYSCALL2(char*, getcwd, char*, buf, size_t, size)
     size_t len = 0;
 
     if (!__current->cwd) {
     size_t len = 0;
 
     if (!__current->cwd) {
-        *buf = PATH_DELIM;
+        *buf = VFS_PATH_DELIM;
         len = 1;
     } else {
         len = vfs_get_path(__current->cwd, buf, size, 0);
         len = 1;
     } else {
         len = vfs_get_path(__current->cwd, buf, size, 0);
@@ -1336,23 +1533,28 @@ __DEFINE_LXSYSCALL2(char*, getcwd, char*, buf, size_t, size)
         }
     }
 
         }
     }
 
-    buf[len + 1] = '\0';
+    buf[len] = '\0';
 
     ret_ptr = buf;
 
 done:
 
     ret_ptr = buf;
 
 done:
-    __current->k_status = errno;
+    syscall_result(errno);
     return ret_ptr;
 }
 
 int
 vfs_do_rename(struct v_dnode* current, struct v_dnode* target)
 {
     return ret_ptr;
 }
 
 int
 vfs_do_rename(struct v_dnode* current, struct v_dnode* target)
 {
+    int errno = 0;
     if (current->inode->id == target->inode->id) {
         // hard link
         return 0;
     }
 
     if (current->inode->id == target->inode->id) {
         // hard link
         return 0;
     }
 
+    if ((errno = vfs_check_writable(current))) {
+        return errno;
+    }
+
     if (current->ref_count > 1 || target->ref_count > 1) {
         return EBUSY;
     }
     if (current->ref_count > 1 || target->ref_count > 1) {
         return EBUSY;
     }
@@ -1361,8 +1563,6 @@ vfs_do_rename(struct v_dnode* current, struct v_dnode* target)
         return EXDEV;
     }
 
         return EXDEV;
     }
 
-    int errno = 0;
-
     struct v_dnode* oldparent = current->parent;
     struct v_dnode* newparent = target->parent;
 
     struct v_dnode* oldparent = current->parent;
     struct v_dnode* newparent = target->parent;
 
@@ -1379,7 +1579,8 @@ vfs_do_rename(struct v_dnode* current, struct v_dnode* target)
         goto cleanup;
     }
 
         goto cleanup;
     }
 
-    if ((errno = current->inode->ops.rename(current->inode, current, target))) {
+    if ((errno =
+           current->inode->ops->rename(current->inode, current, target))) {
         unlock_dnode(target);
         goto cleanup;
     }
         unlock_dnode(target);
         goto cleanup;
     }
@@ -1389,7 +1590,7 @@ vfs_do_rename(struct v_dnode* current, struct v_dnode* target)
     vfs_dcache_rehash(newparent, current);
 
     // detach target
     vfs_dcache_rehash(newparent, current);
 
     // detach target
-    vfs_dcache_remove(target);
+    vfs_d_free(target);
 
     unlock_dnode(target);
 
 
     unlock_dnode(target);
 
@@ -1409,7 +1610,7 @@ __DEFINE_LXSYSCALL2(int, rename, const char*, oldpath, const char*, newpath)
     struct hstr name = HSTR(valloc(VFS_NAME_MAXLEN), 0);
     int errno = 0;
 
     struct hstr name = HSTR(valloc(VFS_NAME_MAXLEN), 0);
     int errno = 0;
 
-    if ((errno = vfs_walk(__current->cwd, oldpath, &cur, NULL, 0))) {
+    if ((errno = vfs_walk_proc(oldpath, &cur, NULL, 0))) {
         goto done;
     }
 
         goto done;
     }
 
@@ -1420,62 +1621,61 @@ __DEFINE_LXSYSCALL2(int, rename, const char*, oldpath, const char*, newpath)
 
     errno = vfs_walk(target_parent, name.value, &target, NULL, 0);
     if (errno == ENOENT) {
 
     errno = vfs_walk(target_parent, name.value, &target, NULL, 0);
     if (errno == ENOENT) {
-        target = vfs_d_alloc();
-        hstrcpy(&target->name, &name);
+        target = vfs_d_alloc(target_parent, &name);
+        vfs_dcache_add(target_parent, target);
     } else if (errno) {
         goto done;
     }
 
     } else if (errno) {
         goto done;
     }
 
-    if (!(errno = vfs_do_rename(cur, target))) {
-        vfs_d_free(target);
+    if (!target) {
+        errno = ENOMEM;
+        goto done;
     }
 
     }
 
+    errno = vfs_do_rename(cur, target);
+
 done:
 done:
-    vfree(name.value);
+    vfree((void*)name.value);
     return DO_STATUS(errno);
 }
 
     return DO_STATUS(errno);
 }
 
-__DEFINE_LXSYSCALL3(int,
-                    mount,
-                    const char*,
-                    source,
-                    const char*,
-                    target,
-                    const char*,
-                    fstype)
+__DEFINE_LXSYSCALL2(int, fstat, int, fd, struct file_stat*, stat)
 {
 {
-    struct v_dnode *dev, *mnt;
     int errno = 0;
     int errno = 0;
+    struct v_fd* fds;
 
 
-    if ((errno = vfs_walk(__current->cwd, source, &dev, NULL, 0))) {
+    if ((errno = vfs_getfd(fd, &fds))) {
         goto done;
     }
 
         goto done;
     }
 
-    if ((errno = vfs_walk(__current->cwd, target, &mnt, NULL, 0))) {
-        goto done;
-    }
+    struct v_inode* vino = fds->file->inode;
+    struct device* fdev = vino->sb->dev;
 
 
-    if (!(dev->inode->itype & VFS_IFVOLDEV)) {
-        errno = ENOTDEV;
-        goto done;
-    }
+    *stat = (struct file_stat){.st_ino = vino->id,
+                               .st_blocks = vino->lb_usage,
+                               .st_size = vino->fsize,
+                               .mode = vino->itype,
+                               .st_ioblksize = PAGE_SIZE,
+                               .st_blksize = vino->sb->blksize};
 
 
-    if (mnt->ref_count > 1) {
-        errno = EBUSY;
-        goto done;
-    }
+    if (check_device_node(vino)) {
+        struct device* rdev = resolve_device(vino->data);
+        if (!rdev) {
+            errno = EINVAL;
+            goto done;
+        }
 
 
-    // FIXME should not touch the underlying fs!
-    struct device* device =
-      (struct device*)((struct twifs_node*)dev->inode->data)->data;
+        stat->st_rdev = (dev_t){.meta = rdev->ident.fn_grp,
+                                .unique = rdev->ident.unique,
+                                .index = dev_uid(rdev) };
+    }
 
 
-    errno = vfs_mount_at(fstype, device, mnt);
+    if (fdev) {
+        stat->st_dev = (dev_t){.meta = fdev->ident.fn_grp,
+                               .unique = fdev->ident.unique,
+                               .index = dev_uid(fdev) };
+    }
 
 done:
     return DO_STATUS(errno);
 
 done:
     return DO_STATUS(errno);
-}
-
-__DEFINE_LXSYSCALL1(int, unmount, const char*, target)
-{
-    return vfs_unmount(target);
 }
\ No newline at end of file
 }
\ No newline at end of file