[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Minios-devel] [UNIKRAFT PATCH 04/22] lib/vfscore: Initial import of OSv vfs



The code is imported as is.

Commit f1f42915a33bebe120e70af1f32c1a4d92bac780

Signed-off-by: Yuri Volchkov <yuri.volchkov@xxxxxxxxx>
---
 lib/vfscore/dentry.c                 |  234 +++
 lib/vfscore/fops.c                   |  189 ++
 lib/vfscore/include/vfscore/dentry.h |   45 +
 lib/vfscore/include/vfscore/mount.h  |  171 ++
 lib/vfscore/include/vfscore/prex.h   |   34 +
 lib/vfscore/include/vfscore/uio.h    |   89 +
 lib/vfscore/include/vfscore/vnode.h  |  246 +++
 lib/vfscore/lookup.c                 |  375 ++++
 lib/vfscore/main.c                   | 2413 ++++++++++++++++++++++++++
 lib/vfscore/mount.c                  |  491 ++++++
 lib/vfscore/subr_uio.c               |   73 +
 lib/vfscore/syscalls.c               | 1486 ++++++++++++++++
 lib/vfscore/task.c                   |  167 ++
 lib/vfscore/vfs.h                    |  189 ++
 lib/vfscore/vnode.c                  |  522 ++++++
 15 files changed, 6724 insertions(+)
 create mode 100644 lib/vfscore/dentry.c
 create mode 100644 lib/vfscore/fops.c
 create mode 100644 lib/vfscore/include/vfscore/dentry.h
 create mode 100644 lib/vfscore/include/vfscore/mount.h
 create mode 100644 lib/vfscore/include/vfscore/prex.h
 create mode 100644 lib/vfscore/include/vfscore/uio.h
 create mode 100644 lib/vfscore/include/vfscore/vnode.h
 create mode 100644 lib/vfscore/lookup.c
 create mode 100644 lib/vfscore/main.c
 create mode 100644 lib/vfscore/mount.c
 create mode 100644 lib/vfscore/subr_uio.c
 create mode 100644 lib/vfscore/syscalls.c
 create mode 100644 lib/vfscore/task.c
 create mode 100644 lib/vfscore/vfs.h
 create mode 100644 lib/vfscore/vnode.c

diff --git a/lib/vfscore/dentry.c b/lib/vfscore/dentry.c
new file mode 100644
index 00000000..facd9eaa
--- /dev/null
+++ b/lib/vfscore/dentry.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2014 Cloudius Systems, Ltd.
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+/*
+ * Copyright (c) 2005-2007, Kohsuke Ohtani
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/param.h>
+
+#include <osv/dentry.h>
+#include <osv/vnode.h>
+#include "vfs.h"
+
+#define DENTRY_BUCKETS 32
+
+static LIST_HEAD(dentry_hash_head, dentry) dentry_hash_table[DENTRY_BUCKETS];
+static LIST_HEAD(fake, dentry) fake;
+static mutex dentry_hash_lock;
+
+/*
+ * Get the hash value from the mount point and path name.
+ * XXX: replace with a better hash for 64-bit pointers.
+ */
+static u_int
+dentry_hash(struct mount *mp, const char *path)
+{
+    u_int val = 0;
+
+    if (path) {
+        while (*path) {
+            val = ((val << 5) + val) + *path++;
+        }
+    }
+    return (val ^ (unsigned long) mp) & (DENTRY_BUCKETS - 1);
+}
+
+
+struct dentry *
+dentry_alloc(struct dentry *parent_dp, struct vnode *vp, const char *path)
+{
+    struct mount *mp = vp->v_mount;
+    struct dentry *dp = (dentry*)calloc(sizeof(*dp), 1);
+
+    if (!dp) {
+        return nullptr;
+    }
+
+    vref(vp);
+
+    dp->d_refcnt = 1;
+    dp->d_vnode = vp;
+    dp->d_mount = mp;
+    dp->d_path = strdup(path);
+    LIST_INIT(&dp->d_children);
+
+    if (parent_dp) {
+        dref(parent_dp);
+        WITH_LOCK(parent_dp->d_lock) {
+            // Insert dp into its parent's children list.
+            LIST_INSERT_HEAD(&parent_dp->d_children, dp, d_children_link);
+        }
+    }
+    dp->d_parent = parent_dp;
+
+    vn_add_name(vp, dp);
+
+    mutex_lock(&dentry_hash_lock);
+    LIST_INSERT_HEAD(&dentry_hash_table[dentry_hash(mp, path)], dp, d_link);
+    mutex_unlock(&dentry_hash_lock);
+    return dp;
+};
+
+struct dentry *
+dentry_lookup(struct mount *mp, char *path)
+{
+    struct dentry *dp;
+
+    mutex_lock(&dentry_hash_lock);
+    LIST_FOREACH(dp, &dentry_hash_table[dentry_hash(mp, path)], d_link) {
+        if (dp->d_mount == mp && !strncmp(dp->d_path, path, PATH_MAX)) {
+            dp->d_refcnt++;
+            mutex_unlock(&dentry_hash_lock);
+            return dp;
+        }
+    }
+    mutex_unlock(&dentry_hash_lock);
+    return nullptr;                /* not found */
+}
+
+static void dentry_children_remove(struct dentry *dp)
+{
+    struct dentry *entry = nullptr;
+
+    WITH_LOCK(dp->d_lock) {
+        LIST_FOREACH(entry, &dp->d_children, d_children_link) {
+            ASSERT(entry);
+            ASSERT(entry->d_refcnt > 0);
+            LIST_REMOVE(entry, d_link);
+        }
+    }
+}
+
+void
+dentry_move(struct dentry *dp, struct dentry *parent_dp, char *path)
+{
+    struct dentry *old_pdp = dp->d_parent;
+    char *old_path = dp->d_path;
+
+    if (old_pdp) {
+        WITH_LOCK(old_pdp->d_lock) {
+            // Remove dp from its old parent's children list.
+            LIST_REMOVE(dp, d_children_link);
+        }
+    }
+
+    if (parent_dp) {
+        dref(parent_dp);
+        WITH_LOCK(parent_dp->d_lock) {
+            // Insert dp into its new parent's children list.
+            LIST_INSERT_HEAD(&parent_dp->d_children, dp, d_children_link);
+        }
+    }
+
+    WITH_LOCK(dentry_hash_lock) {
+        // Remove all dp's child dentries from the hashtable.
+        dentry_children_remove(dp);
+        // Remove dp with outdated hash info from the hashtable.
+        LIST_REMOVE(dp, d_link);
+        // Update dp.
+        dp->d_path = strdup(path);
+        dp->d_parent = parent_dp;
+        // Insert dp updated hash info into the hashtable.
+        LIST_INSERT_HEAD(&dentry_hash_table[dentry_hash(dp->d_mount, path)],
+            dp, d_link);
+    }
+
+    if (old_pdp) {
+        drele(old_pdp);
+    }
+
+    free(old_path);
+}
+
+void
+dentry_remove(struct dentry *dp)
+{
+    mutex_lock(&dentry_hash_lock);
+    LIST_REMOVE(dp, d_link);
+    /* put it on a fake list for drele() to work*/
+    LIST_INSERT_HEAD(&fake, dp, d_link);
+    mutex_unlock(&dentry_hash_lock);
+}
+
+void
+dref(struct dentry *dp)
+{
+    ASSERT(dp);
+    ASSERT(dp->d_refcnt > 0);
+
+    mutex_lock(&dentry_hash_lock);
+    dp->d_refcnt++;
+    mutex_unlock(&dentry_hash_lock);
+}
+
+void
+drele(struct dentry *dp)
+{
+    ASSERT(dp);
+    ASSERT(dp->d_refcnt > 0);
+
+    mutex_lock(&dentry_hash_lock);
+    if (--dp->d_refcnt) {
+        mutex_unlock(&dentry_hash_lock);
+        return;
+    }
+    LIST_REMOVE(dp, d_link);
+    vn_del_name(dp->d_vnode, dp);
+
+    mutex_unlock(&dentry_hash_lock);
+
+    if (dp->d_parent) {
+        WITH_LOCK(dp->d_parent->d_lock) {
+            // Remove dp from its parent's children list.
+            LIST_REMOVE(dp, d_children_link);
+        }
+        drele(dp->d_parent);
+    }
+
+    vrele(dp->d_vnode);
+
+    free(dp->d_path);
+    free(dp);
+}
+
+void
+dentry_init(void)
+{
+    int i;
+
+    for (i = 0; i < DENTRY_BUCKETS; i++) {
+        LIST_INIT(&dentry_hash_table[i]);
+    }
+}
diff --git a/lib/vfscore/fops.c b/lib/vfscore/fops.c
new file mode 100644
index 00000000..3a8f98b4
--- /dev/null
+++ b/lib/vfscore/fops.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2013 Cloudius Systems, Ltd.
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <osv/file.h>
+#include <osv/poll.h>
+#include <fs/vfs/vfs.h>
+#include <osv/vfs_file.hh>
+#include <osv/mmu.hh>
+#include <osv/pagecache.hh>
+
+vfs_file::vfs_file(unsigned flags)
+       : file(flags, DTYPE_VNODE)
+{
+}
+
+int vfs_file::close()
+{
+       auto fp = this;
+       struct vnode *vp = fp->f_dentry->d_vnode;
+       int error;
+
+       vn_lock(vp);
+       error = VOP_CLOSE(vp, fp);
+       vn_unlock(vp);
+
+       if (error)
+               return error;
+
+       fp->f_dentry.reset();
+       return 0;
+}
+
+int vfs_file::read(struct uio *uio, int flags)
+{
+       auto fp = this;
+       struct vnode *vp = fp->f_dentry->d_vnode;
+       int error;
+       size_t count;
+       ssize_t bytes;
+
+       bytes = uio->uio_resid;
+
+       vn_lock(vp);
+       if ((flags & FOF_OFFSET) == 0)
+               uio->uio_offset = fp->f_offset;
+
+       error = VOP_READ(vp, fp, uio, 0);
+       if (!error) {
+               count = bytes - uio->uio_resid;
+               if ((flags & FOF_OFFSET) == 0)
+                       fp->f_offset += count;
+       }
+       vn_unlock(vp);
+
+       return error;
+}
+
+
+int vfs_file::write(struct uio *uio, int flags)
+{
+       auto fp = this;
+       struct vnode *vp = fp->f_dentry->d_vnode;
+       int ioflags = 0;
+       int error;
+       size_t count;
+       ssize_t bytes;
+
+       bytes = uio->uio_resid;
+
+       vn_lock(vp);
+
+       if (fp->f_flags & O_APPEND)
+               ioflags |= IO_APPEND;
+       if (fp->f_flags & (O_DSYNC|O_SYNC))
+               ioflags |= IO_SYNC;
+
+       if ((flags & FOF_OFFSET) == 0)
+               uio->uio_offset = fp->f_offset;
+
+       error = VOP_WRITE(vp, uio, ioflags);
+       if (!error) {
+               count = bytes - uio->uio_resid;
+               if ((flags & FOF_OFFSET) == 0)
+                       fp->f_offset += count;
+       }
+
+       vn_unlock(vp);
+       return error;
+}
+
+int vfs_file::ioctl(u_long com, void *data)
+{
+       auto fp = this;
+       struct vnode *vp = fp->f_dentry->d_vnode;
+       int error;
+
+       vn_lock(vp);
+       error = VOP_IOCTL(vp, fp, com, data);
+       vn_unlock(vp);
+
+       return error;
+}
+
+int vfs_file::stat(struct stat *st)
+{
+       auto fp = this;
+       struct vnode *vp = fp->f_dentry->d_vnode;
+       int error;
+
+       vn_lock(vp);
+       error = vn_stat(vp, st);
+       vn_unlock(vp);
+
+       return error;
+}
+
+int vfs_file::poll(int events)
+{
+       return poll_no_poll(events);
+}
+
+int vfs_file::truncate(off_t len)
+{
+       // somehow this is handled outside file ops
+       abort();
+}
+
+int vfs_file::chmod(mode_t mode)
+{
+       // somehow this is handled outside file ops
+       abort();
+}
+
+bool vfs_file::map_page(uintptr_t off, mmu::hw_ptep<0> ptep, 
mmu::pt_element<0> pte, bool write, bool shared)
+{
+    return pagecache::get(this, off, ptep, pte, write, shared);
+}
+
+bool vfs_file::put_page(void *addr, uintptr_t off, mmu::hw_ptep<0> ptep)
+{
+    return pagecache::release(this, addr, off, ptep);
+}
+
+void vfs_file::sync(off_t start, off_t end)
+{
+    pagecache::sync(this, start, end);
+}
+
+// Locking: VOP_CACHE will call into the filesystem, and that can trigger an
+// eviction that will hold the mmu-side lock that protects the mappings
+// Always follow that order. We however can't just get rid of the mmu-side 
lock,
+// because not all invalidations will be synchronous.
+int vfs_file::get_arcbuf(void* key, off_t offset)
+{
+    struct vnode *vp = f_dentry->d_vnode;
+
+    iovec io[1];
+
+    io[0].iov_base = key;
+    uio data;
+    data.uio_iov = io;
+    data.uio_iovcnt = 1;
+    data.uio_offset = offset;
+    data.uio_resid = mmu::page_size;
+    data.uio_rw = UIO_READ;
+
+    vn_lock(vp);
+    assert(VOP_CACHE(vp, this, &data) == 0);
+    vn_unlock(vp);
+
+    return (data.uio_resid != 0) ? -1 : 0;
+}
+
+std::unique_ptr<mmu::file_vma> vfs_file::mmap(addr_range range, unsigned 
flags, unsigned perm, off_t offset)
+{
+       auto fp = this;
+       struct vnode *vp = fp->f_dentry->d_vnode;
+       if (!vp->v_op->vop_cache || (vp->v_size < (off_t)mmu::page_size)) {
+               return mmu::default_file_mmap(this, range, flags, perm, offset);
+       }
+       return mmu::map_file_mmap(this, range, flags, perm, offset);
+}
diff --git a/lib/vfscore/include/vfscore/dentry.h 
b/lib/vfscore/include/vfscore/dentry.h
new file mode 100644
index 00000000..a2545af8
--- /dev/null
+++ b/lib/vfscore/include/vfscore/dentry.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 Cloudius Systems, Ltd.
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#ifndef _OSV_DENTRY_H
+#define _OSV_DENTRY_H 1
+
+#include <osv/mutex.h>
+#include <bsd/sys/sys/queue.h>
+
+struct vnode;
+
+struct dentry {
+       LIST_ENTRY(dentry) d_link;      /* link for hash list */
+       int             d_refcnt;       /* reference count */
+       char            *d_path;        /* pointer to path in fs */
+       struct vnode    *d_vnode;
+       struct mount    *d_mount;
+       struct dentry   *d_parent; /* pointer to parent */
+       LIST_ENTRY(dentry) d_names_link; /* link fo vnode::d_names */
+       mutex_t         d_lock;
+       LIST_HEAD(, dentry) d_children;
+       LIST_ENTRY(dentry) d_children_link;
+};
+
+#ifdef __cplusplus
+
+#include <boost/intrusive_ptr.hpp>
+
+using dentry_ref = boost::intrusive_ptr<dentry>;
+
+extern "C" {
+    void dref(struct dentry* dp);
+    void drele(struct dentry* dp);
+};
+
+inline void intrusive_ptr_add_ref(dentry* dp) { dref(dp); }
+inline void intrusive_ptr_release(dentry* dp) { drele(dp); }
+
+#endif
+
+#endif /* _OSV_DENTRY_H */
diff --git a/lib/vfscore/include/vfscore/mount.h 
b/lib/vfscore/include/vfscore/mount.h
new file mode 100644
index 00000000..7268d8ce
--- /dev/null
+++ b/lib/vfscore/include/vfscore/mount.h
@@ -0,0 +1,171 @@
+/*-
+ * Copyright (c) 1989, 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)mount.h     8.21 (Berkeley) 5/20/95
+ */
+
+#ifndef _SYS_MOUNT_H_
+#define _SYS_MOUNT_H_
+
+#include <sys/cdefs.h>
+#include <sys/statfs.h>
+#include <osv/vnode.h>
+#include <bsd/sys/sys/queue.h>
+
+__BEGIN_DECLS
+
+#ifdef _KERNEL
+
+/*
+ * Mount data
+ */
+struct mount {
+       struct vfsops   *m_op;          /* pointer to vfs operation */
+       int             m_flags;        /* mount flag */
+       int             m_count;        /* reference count */
+       char            m_path[PATH_MAX]; /* mounted path */
+       char            m_special[PATH_MAX]; /* resource */
+       struct device   *m_dev;         /* mounted device */
+       struct dentry   *m_root;        /* root vnode */
+       struct dentry   *m_covered;     /* vnode covered on parent fs */
+       void            *m_data;        /* private data for fs */
+       fsid_t          m_fsid;         /* id that uniquely identifies the fs */
+};
+
+#endif
+
+/*
+ * Mount flags.
+ */
+#define        MNT_RDONLY      0x00000001      /* read only filesystem */
+#define        MNT_SYNCHRONOUS 0x00000002      /* file system written 
synchronously */
+#define        MNT_NOEXEC      0x00000004      /* can't exec from filesystem */
+#define        MNT_NOSUID      0x00000008      /* don't honor setuid bits on 
fs */
+#define        MNT_NODEV       0x00000010      /* don't interpret special 
files */
+#define        MNT_UNION       0x00000020      /* union with underlying 
filesystem */
+#define        MNT_ASYNC       0x00000040      /* file system written 
asynchronously */
+
+/*
+ * Unmount flags.
+ */
+#define MNT_FORCE      0x00000001      /* forced unmount */
+
+/*
+ * exported mount flags.
+ */
+#define        MNT_EXRDONLY    0x00000080      /* exported read only */
+#define        MNT_EXPORTED    0x00000100      /* file system is exported */
+#define        MNT_DEFEXPORTED 0x00000200      /* exported to the world */
+#define        MNT_EXPORTANON  0x00000400      /* use anon uid mapping for 
everyone */
+#define        MNT_EXKERB      0x00000800      /* exported with Kerberos uid 
mapping */
+
+/*
+ * Flags set by internal operations.
+ */
+#define        MNT_LOCAL       0x00001000      /* filesystem is stored locally 
*/
+#define        MNT_QUOTA       0x00002000      /* quotas are enabled on 
filesystem */
+#define        MNT_ROOTFS      0x00004000      /* identifies the root 
filesystem */
+
+/*
+ * Mask of flags that are visible to statfs()
+ */
+#define        MNT_VISFLAGMASK 0x0000ffff
+
+#ifdef _KERNEL
+
+/*
+ * Filesystem type switch table.
+ */
+struct vfssw {
+       const char      *vs_name;       /* name of file system */
+       int             (*vs_init)(void); /* initialize routine */
+       struct vfsops   *vs_op;         /* pointer to vfs operation */
+};
+
+/*
+ * Operations supported on virtual file system.
+ */
+struct vfsops {
+       int (*vfs_mount)        (struct mount *, const char *, int, const void 
*);
+       int (*vfs_unmount)      (struct mount *, int flags);
+       int (*vfs_sync)         (struct mount *);
+       int (*vfs_vget)         (struct mount *, struct vnode *);
+       int (*vfs_statfs)       (struct mount *, struct statfs *);
+       struct vnops    *vfs_vnops;
+};
+
+typedef int (*vfsop_mount_t)(struct mount *, const char *, int, const void *);
+typedef int (*vfsop_umount_t)(struct mount *, int flags);
+typedef int (*vfsop_sync_t)(struct mount *);
+typedef int (*vfsop_vget_t)(struct mount *, struct vnode *);
+typedef int (*vfsop_statfs_t)(struct mount *, struct statfs *);
+
+/*
+ * VFS interface
+ */
+#define VFS_MOUNT(MP, DEV, FL, DAT) ((MP)->m_op->vfs_mount)(MP, DEV, FL, DAT)
+#define VFS_UNMOUNT(MP, FL)         ((MP)->m_op->vfs_unmount)(MP, FL)
+#define VFS_SYNC(MP)                ((MP)->m_op->vfs_sync)(MP)
+#define VFS_VGET(MP, VP)            ((MP)->m_op->vfs_vget)(MP, VP)
+#define VFS_STATFS(MP, SFP)         ((MP)->m_op->vfs_statfs)(MP, SFP)
+
+#define VFS_NULL                   ((void *)vfs_null)
+
+int    vfs_nullop(void);
+int    vfs_einval(void);
+
+void    vfs_busy(struct mount *mp);
+void    vfs_unbusy(struct mount *mp);
+
+void    release_mp_dentries(struct mount *mp);
+
+#endif
+
+__END_DECLS
+
+#ifdef __cplusplus
+
+#include <vector>
+#include <string>
+
+namespace osv {
+
+struct mount_desc {
+    std::string special;
+    std::string path;
+    std::string type;
+    std::string options;
+};
+
+std::vector<mount_desc> current_mounts();
+
+}
+
+#endif
+
+#endif /* !_SYS_MOUNT_H_ */
diff --git a/lib/vfscore/include/vfscore/prex.h 
b/lib/vfscore/include/vfscore/prex.h
new file mode 100644
index 00000000..43650340
--- /dev/null
+++ b/lib/vfscore/include/vfscore/prex.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2013 Cloudius Systems, Ltd.
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#ifndef _OSV_PREX_H
+#define _OSV_PREX_H 1
+
+
+#include <unistd.h>
+#include <osv/fcntl.h>
+
+__BEGIN_DECLS
+
+#define __packed        __attribute__((__packed__))
+
+#define        BSIZE   512             /* size of secondary block (bytes) */
+
+#define DO_RDWR                0x2
+
+#define PAGE_SIZE      4096
+#define PAGE_MASK      (PAGE_SIZE-1)
+#define round_page(x)  (((x) + PAGE_MASK) & ~PAGE_MASK)
+
+size_t strlcat(char *dst, const char *src, size_t siz);
+size_t strlcpy(char *dst, const char *src, size_t siz);
+
+void sys_panic(const char *);
+
+__END_DECLS
+
+#endif /* _OSV_PREX_H */
diff --git a/lib/vfscore/include/vfscore/uio.h 
b/lib/vfscore/include/vfscore/uio.h
new file mode 100644
index 00000000..696b01cf
--- /dev/null
+++ b/lib/vfscore/include/vfscore/uio.h
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)uio.h       8.5 (Berkeley) 2/22/94
+ * $FreeBSD$
+ */
+
+#ifndef _UIO_H_
+#define        _UIO_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <limits.h>
+
+__BEGIN_DECLS
+
+enum   uio_rw { UIO_READ, UIO_WRITE };
+
+/*
+ * Safe default to prevent possible overflows in user code, otherwise could
+ * be SSIZE_T_MAX.
+ */
+#define IOSIZE_MAX      INT_MAX
+
+#define UIO_MAXIOV 1024
+
+#define UIO_SYSSPACE 0
+
+struct uio {
+       struct iovec *uio_iov;          /* scatter/gather list */
+       int     uio_iovcnt;             /* length of scatter/gather list */
+       off_t   uio_offset;             /* offset in target object */
+       ssize_t uio_resid;              /* remaining bytes to process */
+       enum    uio_rw uio_rw;          /* operation */
+};
+
+int    uiomove(void *cp, int n, struct uio *uio);
+
+__END_DECLS
+
+#ifdef __cplusplus
+
+template <typename F>
+static inline void linearize_uio_write(struct uio *uio, int ioflag, F f)
+{
+    while (uio->uio_resid > 0) {
+        struct iovec *iov = uio->uio_iov;
+
+        if (iov->iov_len) {
+            f(reinterpret_cast<const char *>(iov->iov_base),
+                iov->iov_len);
+        }
+
+        uio->uio_iov++;
+        uio->uio_iovcnt--;
+        uio->uio_resid -= iov->iov_len;
+        uio->uio_offset += iov->iov_len;
+    }
+}
+
+#endif
+
+#endif /* !_UIO_H_ */
diff --git a/lib/vfscore/include/vfscore/vnode.h 
b/lib/vfscore/include/vfscore/vnode.h
new file mode 100644
index 00000000..e35aa830
--- /dev/null
+++ b/lib/vfscore/include/vfscore/vnode.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2005-2007, Kohsuke Ohtani
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _SYS_VNODE_H_
+#define _SYS_VNODE_H_
+
+#ifdef _KERNEL
+
+#include <sys/cdefs.h>
+#include <sys/stat.h>
+#include <osv/prex.h>
+#include <osv/uio.h>
+#include <osv/mutex.h>
+#include "file.h"
+#include "dirent.h"
+
+__BEGIN_DECLS
+
+struct vfsops;
+struct vnops;
+struct vnode;
+struct file;
+
+/*
+ * Vnode types.
+ */
+enum vtype {
+       VNON,       /* no type */
+       VREG,       /* regular file  */
+       VDIR,       /* directory */
+       VBLK,       /* block device */
+       VCHR,       /* character device */
+       VLNK,       /* symbolic link */
+       VSOCK,      /* socks */
+       VFIFO,      /* FIFO */
+       VBAD
+};
+
+/*
+ * Reading or writing any of these items requires holding the
+ * appropriate lock.
+ */
+struct vnode {
+       uint64_t        v_ino;          /* inode number */
+       LIST_ENTRY(vnode) v_link;       /* link for hash list */
+       struct mount    *v_mount;       /* mounted vfs pointer */
+       struct vnops    *v_op;          /* vnode operations */
+       int             v_refcnt;       /* reference count */
+       int             v_type;         /* vnode type */
+       int             v_flags;        /* vnode flag */
+       mode_t          v_mode;         /* file mode */
+       off_t           v_size;         /* file size */
+       mutex_t         v_lock;         /* lock for this vnode */
+       LIST_HEAD(, dentry) v_names;    /* directory entries pointing at this */
+       int             v_nrlocks;      /* lock count (for debug) */
+       void            *v_data;        /* private data for fs */
+};
+
+/* flags for vnode */
+#define VROOT          0x0001          /* root of its file system */
+#define VISTTY         0x0002          /* device is tty */
+#define VPROTDEV       0x0004          /* protected device */
+
+/*
+ * Vnode attribute
+ */
+struct vattr {
+       unsigned int    va_mask;
+       enum vtype      va_type;        /* vnode type */
+       mode_t          va_mode;        /* file access mode */
+       nlink_t         va_nlink;
+       uid_t           va_uid;
+       gid_t           va_gid;
+       dev_t           va_fsid;        /* id of the underlying filesystem */
+       ino_t           va_nodeid;
+       struct timespec va_atime;
+       struct timespec va_mtime;
+       struct timespec va_ctime;
+       dev_t           va_rdev;
+       uint64_t        va_nblocks;
+       off_t           va_size;
+};
+
+/*
+ *  Modes.
+ */
+#define VAPPEND 00010
+#define        VREAD   00004           /* read, write, execute permissions */
+#define        VWRITE  00002
+#define        VEXEC   00001
+
+#define IO_APPEND      0x0001
+#define IO_SYNC                0x0002
+
+/*
+ * ARC actions
+ */
+#define ARC_ACTION_QUERY    0
+#define ARC_ACTION_HOLD     1
+#define ARC_ACTION_RELEASE  2
+
+typedef        int (*vnop_open_t)      (struct file *);
+typedef        int (*vnop_close_t)     (struct vnode *, struct file *);
+typedef        int (*vnop_read_t)      (struct vnode *, struct file *, struct 
uio *, int);
+typedef        int (*vnop_write_t)     (struct vnode *, struct uio *, int);
+typedef        int (*vnop_seek_t)      (struct vnode *, struct file *, off_t, 
off_t);
+typedef        int (*vnop_ioctl_t)     (struct vnode *, struct file *, u_long, 
void *);
+typedef        int (*vnop_fsync_t)     (struct vnode *, struct file *);
+typedef        int (*vnop_readdir_t)   (struct vnode *, struct file *, struct 
dirent *);
+typedef        int (*vnop_lookup_t)    (struct vnode *, char *, struct vnode 
**);
+typedef        int (*vnop_create_t)    (struct vnode *, char *, mode_t);
+typedef        int (*vnop_remove_t)    (struct vnode *, struct vnode *, char 
*);
+typedef        int (*vnop_rename_t)    (struct vnode *, struct vnode *, char *,
+                                struct vnode *, struct vnode *, char *);
+typedef        int (*vnop_mkdir_t)     (struct vnode *, char *, mode_t);
+typedef        int (*vnop_rmdir_t)     (struct vnode *, struct vnode *, char 
*);
+typedef        int (*vnop_getattr_t)   (struct vnode *, struct vattr *);
+typedef        int (*vnop_setattr_t)   (struct vnode *, struct vattr *);
+typedef        int (*vnop_inactive_t)  (struct vnode *);
+typedef        int (*vnop_truncate_t)  (struct vnode *, off_t);
+typedef        int (*vnop_link_t)      (struct vnode *, struct vnode *, char 
*);
+typedef int (*vnop_cache_t) (struct vnode *, struct file *, struct uio *);
+typedef int (*vnop_fallocate_t) (struct vnode *, int, loff_t, loff_t);
+typedef int (*vnop_readlink_t)  (struct vnode *, struct uio *);
+typedef int (*vnop_symlink_t)   (struct vnode *, char *, char *);
+
+/*
+ * vnode operations
+ */
+struct vnops {
+       vnop_open_t             vop_open;
+       vnop_close_t            vop_close;
+       vnop_read_t             vop_read;
+       vnop_write_t            vop_write;
+       vnop_seek_t             vop_seek;
+       vnop_ioctl_t            vop_ioctl;
+       vnop_fsync_t            vop_fsync;
+       vnop_readdir_t          vop_readdir;
+       vnop_lookup_t           vop_lookup;
+       vnop_create_t           vop_create;
+       vnop_remove_t           vop_remove;
+       vnop_rename_t           vop_rename;
+       vnop_mkdir_t            vop_mkdir;
+       vnop_rmdir_t            vop_rmdir;
+       vnop_getattr_t          vop_getattr;
+       vnop_setattr_t          vop_setattr;
+       vnop_inactive_t         vop_inactive;
+       vnop_truncate_t         vop_truncate;
+       vnop_link_t             vop_link;
+       vnop_cache_t            vop_cache;
+       vnop_fallocate_t        vop_fallocate;
+       vnop_readlink_t         vop_readlink;
+       vnop_symlink_t          vop_symlink;
+};
+
+/*
+ * vnode interface
+ */
+#define VOP_OPEN(VP, FP)          ((VP)->v_op->vop_open)(FP)
+#define VOP_CLOSE(VP, FP)         ((VP)->v_op->vop_close)(VP, FP)
+#define VOP_READ(VP, FP, U, F)    ((VP)->v_op->vop_read)(VP, FP, U, F)
+#define VOP_CACHE(VP, FP, U)      ((VP)->v_op->vop_cache)(VP, FP, U)
+#define VOP_WRITE(VP, U, F)       ((VP)->v_op->vop_write)(VP, U, F)
+#define VOP_SEEK(VP, FP, OLD, NEW) ((VP)->v_op->vop_seek)(VP, FP, OLD, NEW)
+#define VOP_IOCTL(VP, FP, C, A)           ((VP)->v_op->vop_ioctl)(VP, FP, C, A)
+#define VOP_FSYNC(VP, FP)         ((VP)->v_op->vop_fsync)(VP, FP)
+#define VOP_READDIR(VP, FP, DIR)   ((VP)->v_op->vop_readdir)(VP, FP, DIR)
+#define VOP_LOOKUP(DVP, N, VP)    ((DVP)->v_op->vop_lookup)(DVP, N, VP)
+#define VOP_CREATE(DVP, N, M)     ((DVP)->v_op->vop_create)(DVP, N, M)
+#define VOP_REMOVE(DVP, VP, N)    ((DVP)->v_op->vop_remove)(DVP, VP, N)
+#define VOP_RENAME(DVP1, VP1, N1, DVP2, VP2, N2) \
+                          ((DVP1)->v_op->vop_rename)(DVP1, VP1, N1, DVP2, VP2, 
N2)
+#define VOP_MKDIR(DVP, N, M)      ((DVP)->v_op->vop_mkdir)(DVP, N, M)
+#define VOP_RMDIR(DVP, VP, N)     ((DVP)->v_op->vop_rmdir)(DVP, VP, N)
+#define VOP_GETATTR(VP, VAP)      ((VP)->v_op->vop_getattr)(VP, VAP)
+#define VOP_SETATTR(VP, VAP)      ((VP)->v_op->vop_setattr)(VP, VAP)
+#define VOP_INACTIVE(VP)          ((VP)->v_op->vop_inactive)(VP)
+#define VOP_TRUNCATE(VP, N)       ((VP)->v_op->vop_truncate)(VP, N)
+#define VOP_LINK(DVP, SVP, N)     ((DVP)->v_op->vop_link)(DVP, SVP, N)
+#define VOP_FALLOCATE(VP, M, OFF, LEN) ((VP)->v_op->vop_fallocate)(VP, M, OFF, 
LEN)
+#define VOP_READLINK(VP, U)        ((VP)->v_op->vop_readlink)(VP, U)
+#define VOP_SYMLINK(DVP, OP, NP)   ((DVP)->v_op->vop_symlink)(DVP, OP, NP)
+
+int     vop_nullop(void);
+int     vop_einval(void);
+int     vop_eperm(void);
+int     vop_erofs(void);
+struct vnode *vn_lookup(struct mount *, uint64_t);
+void    vn_lock(struct vnode *);
+void    vn_unlock(struct vnode *);
+int     vn_stat(struct vnode *, struct stat *);
+int     vn_settimes(struct vnode *, struct timespec[2]);
+int     vn_setmode(struct vnode *, mode_t mode);
+int     vn_access(struct vnode *, int);
+int     vget(struct mount *, uint64_t ino, struct vnode **vpp);
+void    vput(struct vnode *);
+void    vref(struct vnode *);
+void    vrele(struct vnode *);
+void    vflush(struct mount *);
+void vn_add_name(struct vnode *, struct dentry *);
+void vn_del_name(struct vnode *, struct dentry *);
+
+extern enum vtype iftovt_tab[];
+extern int vttoif_tab[];
+#define IFTOVT(mode)    (iftovt_tab[((mode) & S_IFMT) >> 12])
+#define VTTOIF(indx)   (vttoif_tab[(int)(indx)])
+#define MAKEIMODE(indx, mode)   (int)(VTTOIF(indx) | (mode))
+
+#define VATTR_NULL(vp) (*(vp) = (vattr_t){})
+
+static inline void vnode_pager_setsize(struct vnode *vp, off_t size)
+{
+       vp->v_size = size;
+}
+
+__END_DECLS
+
+#endif
+
+#endif /* !_SYS_VNODE_H_ */
diff --git a/lib/vfscore/lookup.c b/lib/vfscore/lookup.c
new file mode 100644
index 00000000..ad03fe25
--- /dev/null
+++ b/lib/vfscore/lookup.c
@@ -0,0 +1,375 @@
+/*
+ * Copyright (c) 2005-2007, Kohsuke Ohtani
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/param.h>
+
+#include <osv/dentry.h>
+#include <osv/vnode.h>
+#include "vfs.h"
+
+static ssize_t
+read_link(struct vnode *vp, char *buf, size_t bufsz, ssize_t *sz)
+{
+    struct iovec iov = {buf, bufsz};
+    struct uio   uio = {&iov, 1, 0, (ssize_t) bufsz, UIO_READ};
+    int rc;
+
+    *sz = 0;
+    vn_lock(vp);
+    rc  = VOP_READLINK(vp, &uio);
+    vn_unlock(vp);
+
+    if (rc != 0) {
+        return (rc);
+    }
+
+    *sz = bufsz - uio.uio_resid;
+    return (0);
+}
+
+int
+namei_follow_link(struct dentry *dp, char *node, char *name, char *fp, size_t 
mountpoint_len)
+{
+    std::unique_ptr<char []> link (new char[PATH_MAX]);
+    std::unique_ptr<char []> t (new char[PATH_MAX]);
+    char    *lp;
+    int     error;
+    ssize_t sz;
+    char    *p;
+    int     c;
+
+    lp    = link.get();
+    error = read_link(dp->d_vnode, lp, PATH_MAX, &sz);
+    if (error != 0) {
+        return (error);
+    }
+    lp[sz] = 0;
+
+    p = fp + mountpoint_len + strlen(node);
+    c = strlen(node) - strlen(name) - 1;
+    node[c] = 0;
+
+    if (lp[0] == '/') {
+        strlcat(lp, p, PATH_MAX);
+        strlcpy(fp, lp, PATH_MAX);
+    } else {
+        strlcpy(t.get(), p, PATH_MAX);
+        strlcpy(node, fp, mountpoint_len + c + 1);
+        path_conv(node, lp, fp);
+        strlcat(fp, t.get(), PATH_MAX);
+    }
+    node[0] = 0;
+    name[0] = 0;
+    return (0);
+}
+/*
+ * Convert a pathname into a pointer to a dentry
+ *
+ * @path: full path name.
+ * @dpp:  dentry to be returned.
+ */
+int
+namei(const char *path, struct dentry **dpp)
+{
+    char *p;
+    char node[PATH_MAX];
+    char name[PATH_MAX];
+    std::unique_ptr<char []> fp (new char [PATH_MAX]);
+    std::unique_ptr<char []> t (new char [PATH_MAX]);
+    struct mount *mp;
+    struct dentry *dp, *ddp;
+    struct vnode *dvp, *vp;
+    int error, i;
+    int links_followed;
+    bool need_continue;
+
+    DPRINTF(VFSDB_VNODE, ("namei: path=%s\n", path));
+
+    links_followed = 0;
+    strlcpy(fp.get(), path, PATH_MAX);
+
+    do {
+        need_continue = false;
+        /*
+         * Convert a full path name to its mount point and
+         * the local node in the file system.
+         */
+        if (vfs_findroot(fp.get(), &mp, &p)) {
+            return ENOTDIR;
+        }
+        int mountpoint_len = p - fp.get() - 1;
+        strlcpy(node, "/", sizeof(node));
+        strlcat(node, p, sizeof(node));
+        dp = dentry_lookup(mp, node);
+        if (dp) {
+            /* vnode is already active. */
+            *dpp = dp;
+            return 0;
+        }
+        /*
+         * Find target vnode, started from root directory.
+         * This is done to attach the fs specific data to
+         * the target vnode.
+         */
+        ddp = mp->m_root;
+        if (!ddp) {
+            sys_panic("VFS: no root");
+        }
+        dref(ddp);
+
+        node[0] = '\0';
+
+        while (*p != '\0') {
+            /*
+             * Get lower directory/file name.
+             */
+            while (*p == '/') {
+                p++;
+            }
+
+            if (*p == '\0') {
+                break;
+            }
+
+            for (i = 0; i < PATH_MAX; i++) {
+                if (*p == '\0' || *p == '/') {
+                    break;
+                }
+                name[i] = *p++;
+            }
+            name[i] = '\0';
+
+            /*
+             * Get a vnode for the target.
+             */
+            strlcat(node, "/", sizeof(node));
+            strlcat(node, name, sizeof(node));
+            dvp = ddp->d_vnode;
+            vn_lock(dvp);
+            dp = dentry_lookup(mp, node);
+            if (dp == nullptr) {
+                /* Find a vnode in this directory. */
+                error = VOP_LOOKUP(dvp, name, &vp);
+                if (error) {
+                    vn_unlock(dvp);
+                    drele(ddp);
+                    return error;
+                }
+
+                dp = dentry_alloc(ddp, vp, node);
+                vput(vp);
+
+                if (!dp) {
+                    vn_unlock(dvp);
+                    drele(ddp);
+                    return ENOMEM;
+                }
+            }
+            vn_unlock(dvp);
+            drele(ddp);
+            ddp = dp;
+
+            if (dp->d_vnode->v_type == VLNK) {
+                error = namei_follow_link(dp, node, name, fp.get(), 
mountpoint_len);
+                if (error) {
+                    drele(dp);
+                    return (error);
+                }
+
+                drele(dp);
+
+                p       = fp.get();
+                dp      = nullptr;
+                ddp     = nullptr;
+                vp      = nullptr;
+                dvp     = nullptr;
+                name[0] = 0;
+                node[0] = 0;
+
+                if (++links_followed >= MAXSYMLINKS) {
+                    return (ELOOP);
+                }
+                need_continue = true;
+                break;
+            }
+
+            if (*p == '/' && ddp->d_vnode->v_type != VDIR) {
+                drele(ddp);
+                return ENOTDIR;
+            }
+        }
+    } while (need_continue == true);
+
+    *dpp = dp;
+    return 0;
+}
+
+/*
+ * Convert last component in the path to pointer to dentry
+ *
+ * @path: full path name
+ * @ddp : pointer to dentry of parent
+ * @dpp : dentry to be returned
+ */
+int
+namei_last_nofollow(char *path, struct dentry *ddp, struct dentry **dpp)
+{
+    char          *name;
+    int           error;
+    struct mount  *mp;
+    char          *p;
+    struct dentry *dp;
+    struct vnode  *dvp;
+    struct vnode  *vp;
+    std::unique_ptr<char []> node (new char[PATH_MAX]);
+
+    dvp  = nullptr;
+
+    if (path[0] != '/') {
+        return (ENOTDIR);
+    }
+
+    name = strrchr(path, '/');
+    if (name == nullptr) {
+        return (ENOENT);
+    }
+    name++;
+
+    error = vfs_findroot(path, &mp, &p);
+    if (error != 0) {
+        return (ENOTDIR);
+    }
+
+    strlcpy(node.get(), "/", PATH_MAX);
+    strlcat(node.get(), p, PATH_MAX);
+
+    // We want to treat things like /tmp/ the same as /tmp. Best way to do that
+    // is to ignore the last character, except when we're stating the root.
+    auto l = strlen(node.get()) - 1;
+    if (l && node.get()[l] == '/') {
+        node.get()[l] = '\0';
+    }
+
+    dvp = ddp->d_vnode;
+    vn_lock(dvp);
+    dp = dentry_lookup(mp, node.get());
+    if (dp == nullptr) {
+        error = VOP_LOOKUP(dvp, name, &vp);
+        if (error != 0) {
+            goto out;
+        }
+
+        dp = dentry_alloc(ddp, vp, node.get());
+        vput(vp);
+
+        if (dp == nullptr) {
+            error = ENOMEM;
+            goto out;
+        }
+    }
+
+    *dpp  = dp;
+    error = 0;
+out:
+    if (dvp != nullptr) {
+        vn_unlock(dvp);
+    }
+    return (error);
+}
+
+/*
+ * Search a pathname.
+ * This is a very central but not so complicated routine. ;-P
+ *
+ * @path: full path.
+ * @dpp:  pointer to dentry for directory.
+ * @name: if non-null, pointer to file name in path.
+ *
+ * This routine returns a locked directory vnode and file name.
+ */
+int
+lookup(char *path, struct dentry **dpp, char **name)
+{
+    char buf[PATH_MAX];
+    char root[] = "/";
+    char *file, *dir;
+    struct dentry *dp;
+    int error;
+
+    DPRINTF(VFSDB_VNODE, ("lookup: path=%s\n", path));
+
+    /*
+     * Get the path for directory.
+     */
+    strlcpy(buf, path, sizeof(buf));
+    file = strrchr(buf, '/');
+    if (!buf[0]) {
+        return ENOTDIR;
+    }
+    if (file == buf) {
+        dir = root;
+    } else {
+        *file = '\0';
+        dir = buf;
+    }
+    /*
+     * Get the vnode for directory
+     */
+    if ((error = namei(dir, &dp)) != 0) {
+        return error;
+    }
+    if (dp->d_vnode->v_type != VDIR) {
+        drele(dp);
+        return ENOTDIR;
+    }
+
+    *dpp = dp;
+
+    if (name) {
+        /*
+         * Get the file name
+         */
+        *name = strrchr(path, '/') + 1;
+    }
+    return 0;
+}
+
+/*
+ * vnode_init() is called once (from vfs_init)
+ * in initialization.
+ */
+void
+lookup_init(void)
+{
+    dentry_init();
+}
diff --git a/lib/vfscore/main.c b/lib/vfscore/main.c
new file mode 100644
index 00000000..cd141117
--- /dev/null
+++ b/lib/vfscore/main.c
@@ -0,0 +1,2413 @@
+/*
+ * Copyright (C) 2013 Cloudius Systems, Ltd.
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+/*
+ * Copyright (c) 2005-2007, Kohsuke Ohtani
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/statvfs.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/sendfile.h>
+
+#include <limits.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#define open __open_variadic
+#define fcntl __fcntl_variadic
+#include <fcntl.h>
+#undef open
+#undef fcntl
+
+#include <osv/prex.h>
+#include <osv/vnode.h>
+#include <osv/stubbing.hh>
+#include <osv/ioctl.h>
+#include <osv/trace.hh>
+#include <osv/run.hh>
+#include <drivers/console.hh>
+
+#include "vfs.h"
+
+#include "libc/internal/libc.h"
+
+#include <algorithm>
+#include <unordered_map>
+
+#include <sys/file.h>
+
+#include "fs/fs.hh"
+#include "libc/libc.hh"
+
+#include <mntent.h>
+#include <sys/mman.h>
+
+#include <osv/clock.hh>
+#include <api/utime.h>
+#include <chrono>
+
+using namespace std;
+
+
+#ifdef DEBUG_VFS
+int    vfs_debug = VFSDB_FLAGS;
+#endif
+
+std::atomic<mode_t> global_umask{S_IWGRP | S_IWOTH};
+
+static inline mode_t apply_umask(mode_t mode)
+{
+    return mode & ~global_umask.load(std::memory_order_relaxed);
+}
+
+TRACEPOINT(trace_vfs_open, "\"%s\" 0x%x 0%0o", const char*, int, mode_t);
+TRACEPOINT(trace_vfs_open_ret, "%d", int);
+TRACEPOINT(trace_vfs_open_err, "%d", int);
+
+struct task *main_task;        /* we only have a single process */
+
+extern "C"
+int open(const char *pathname, int flags, ...)
+{
+    mode_t mode = 0;
+    if (flags & O_CREAT) {
+        va_list ap;
+        va_start(ap, flags);
+        mode = apply_umask(va_arg(ap, mode_t));
+        va_end(ap);
+    }
+
+    trace_vfs_open(pathname, flags, mode);
+
+    struct task *t = main_task;
+    char path[PATH_MAX];
+    struct file *fp;
+    int fd, error;
+    int acc;
+
+    acc = 0;
+    switch (flags & O_ACCMODE) {
+    case O_RDONLY:
+        acc = VREAD;
+        break;
+    case O_WRONLY:
+        acc = VWRITE;
+        break;
+    case O_RDWR:
+        acc = VREAD | VWRITE;
+        break;
+    }
+
+    error = task_conv(t, pathname, acc, path);
+    if (error)
+        goto out_errno;
+
+    error = sys_open(path, flags, mode, &fp);
+    if (error)
+        goto out_errno;
+
+    error = fdalloc(fp, &fd);
+    if (error)
+        goto out_fput;
+    fdrop(fp);
+    trace_vfs_open_ret(fd);
+    return fd;
+
+    out_fput:
+    fdrop(fp);
+    out_errno:
+    errno = error;
+    trace_vfs_open_err(error);
+    return -1;
+}
+
+LFS64(open);
+
+int openat(int dirfd, const char *pathname, int flags, ...)
+{
+    mode_t mode = 0;
+    if (flags & O_CREAT) {
+        va_list ap;
+        va_start(ap, flags);
+        mode = apply_umask(va_arg(ap, mode_t));
+        va_end(ap);
+    }
+
+    if (pathname[0] == '/' || dirfd == AT_FDCWD) {
+        return open(pathname, flags, mode);
+    }
+
+    struct file *fp;
+    int error = fget(dirfd, &fp);
+    if (error) {
+        errno = error;
+        return -1;
+    }
+
+    struct vnode *vp = fp->f_dentry->d_vnode;
+    vn_lock(vp);
+
+    std::unique_ptr<char []> up (new char[PATH_MAX]);
+    char *p = up.get();
+
+    /* build absolute path */
+    strlcpy(p, fp->f_dentry->d_mount->m_path, PATH_MAX);
+    strlcat(p, fp->f_dentry->d_path, PATH_MAX);
+    strlcat(p, "/", PATH_MAX);
+    strlcat(p, pathname, PATH_MAX);
+
+    error = open(p, flags, mode);
+
+    vn_unlock(vp);
+    fdrop(fp);
+
+    return error;
+}
+LFS64(openat);
+
+// open() has an optional third argument, "mode", which is only needed in
+// some cases (when the O_CREAT mode is used). As a safety feature, recent
+// versions of Glibc add a feature where open() with two arguments is replaced
+// by a call to __open_2(), which verifies it isn't called with O_CREATE.
+extern "C" int __open_2(const char *pathname, int flags)
+{
+    assert(!(flags & O_CREAT));
+    return open(pathname, flags, 0);
+}
+
+extern "C" int __open64_2(const char *file, int flags)
+{
+    if (flags & O_CREAT) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    return open64(file, flags);
+}
+
+int creat(const char *pathname, mode_t mode)
+{
+    return open(pathname, O_CREAT|O_WRONLY|O_TRUNC, mode);
+}
+LFS64(creat);
+
+TRACEPOINT(trace_vfs_close, "%d", int);
+TRACEPOINT(trace_vfs_close_ret, "");
+TRACEPOINT(trace_vfs_close_err, "%d", int);
+
+int close(int fd)
+{
+    int error;
+
+    trace_vfs_close(fd);
+    error = fdclose(fd);
+    if (error)
+        goto out_errno;
+
+    trace_vfs_close_ret();
+    return 0;
+
+    out_errno:
+    trace_vfs_close_err(error);
+    errno = error;
+    return -1;
+}
+
+TRACEPOINT(trace_vfs_mknod, "\"%s\" 0%0o 0x%x", const char*, mode_t, dev_t);
+TRACEPOINT(trace_vfs_mknod_ret, "");
+TRACEPOINT(trace_vfs_mknod_err, "%d", int);
+
+
+extern "C"
+int __xmknod(int ver, const char *pathname, mode_t mode, dev_t *dev)
+{
+    assert(ver == 0); // On x86-64 Linux, _MKNOD_VER_LINUX is 0.
+    struct task *t = main_task;
+    char path[PATH_MAX];
+    int error;
+
+    trace_vfs_mknod(pathname, mode, *dev);
+    if ((error = task_conv(t, pathname, VWRITE, path)) != 0)
+        goto out_errno;
+
+    error = sys_mknod(path, mode);
+    if (error)
+        goto out_errno;
+
+    trace_vfs_mknod_ret();
+    return 0;
+
+    out_errno:
+    trace_vfs_mknod_err(error);
+    errno = error;
+    return -1;
+}
+
+int mknod(const char *pathname, mode_t mode, dev_t dev)
+{
+    return __xmknod(0, pathname, mode, &dev);
+}
+
+
+TRACEPOINT(trace_vfs_lseek, "%d 0x%x %d", int, off_t, int);
+TRACEPOINT(trace_vfs_lseek_ret, "0x%x", off_t);
+TRACEPOINT(trace_vfs_lseek_err, "%d", int);
+
+off_t lseek(int fd, off_t offset, int whence)
+{
+    struct file *fp;
+    off_t org;
+    int error;
+
+    trace_vfs_lseek(fd, offset, whence);
+    error = fget(fd, &fp);
+    if (error)
+        goto out_errno;
+
+    error = sys_lseek(fp, offset, whence, &org);
+    fdrop(fp);
+
+    if (error)
+        goto out_errno;
+    trace_vfs_lseek_ret(org);
+    return org;
+
+    out_errno:
+    trace_vfs_lseek_err(error);
+    errno = error;
+    return -1;
+}
+
+LFS64(lseek);
+
+TRACEPOINT(trace_vfs_pread, "%d %p 0x%x 0x%x", int, void*, size_t, off_t);
+TRACEPOINT(trace_vfs_pread_ret, "0x%x", ssize_t);
+TRACEPOINT(trace_vfs_pread_err, "%d", int);
+
+// In BSD's internal implementation of read() and write() code, for example
+// sosend_generic(), a partial read or write returns both an EWOULDBLOCK error
+// *and* a non-zero number of written bytes. In that case, we need to zero the
+// error, so the system call appear a successful partial read/write.
+// In FreeBSD, dofilewrite() and dofileread() (sys_generic.c) do this too.
+static inline bool has_error(int error, int bytes)
+{
+    return error && (
+            (bytes == 0) ||
+            (error != EWOULDBLOCK && error != EINTR && error != ERESTART));
+}
+
+
+ssize_t pread(int fd, void *buf, size_t count, off_t offset)
+{
+    trace_vfs_pread(fd, buf, count, offset);
+    struct iovec iov = {
+            .iov_base  = buf,
+            .iov_len   = count,
+    };
+    struct file *fp;
+    size_t bytes;
+    int error;
+
+    error = fget(fd, &fp);
+    if (error)
+        goto out_errno;
+
+    error = sys_read(fp, &iov, 1, offset, &bytes);
+    fdrop(fp);
+
+    if (has_error(error, bytes))
+        goto out_errno;
+    trace_vfs_pread_ret(bytes);
+    return bytes;
+
+    out_errno:
+    trace_vfs_pread_err(error);
+    errno = error;
+    return -1;
+}
+
+LFS64(pread);
+
+ssize_t read(int fd, void *buf, size_t count)
+{
+    return pread(fd, buf, count, -1);
+}
+
+TRACEPOINT(trace_vfs_pwrite, "%d %p 0x%x 0x%x", int, const void*, size_t, 
off_t);
+TRACEPOINT(trace_vfs_pwrite_ret, "0x%x", ssize_t);
+TRACEPOINT(trace_vfs_pwrite_err, "%d", int);
+
+ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset)
+{
+    trace_vfs_pwrite(fd, buf, count, offset);
+    struct iovec iov = {
+            .iov_base  = (void *)buf,
+            .iov_len   = count,
+    };
+    struct file *fp;
+    size_t bytes;
+    int error;
+
+    error = fget(fd, &fp);
+    if (error)
+        goto out_errno;
+
+    error = sys_write(fp, &iov, 1, offset, &bytes);
+    fdrop(fp);
+
+    if (has_error(error, bytes))
+        goto out_errno;
+    trace_vfs_pwrite_ret(bytes);
+    return bytes;
+
+    out_errno:
+    trace_vfs_pwrite_err(error);
+    errno = error;
+    return -1;
+}
+
+LFS64(pwrite);
+
+ssize_t write(int fd, const void *buf, size_t count)
+{
+    return pwrite(fd, buf, count, -1);
+}
+
+ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset)
+{
+    struct file *fp;
+    size_t bytes;
+    int error;
+
+    error = fget(fd, &fp);
+    if (error)
+        goto out_errno;
+
+    error = sys_read(fp, iov, iovcnt, offset, &bytes);
+    fdrop(fp);
+
+    if (has_error(error, bytes))
+        goto out_errno;
+    return bytes;
+
+    out_errno:
+    errno = error;
+    return -1;
+}
+
+LFS64(preadv);
+
+ssize_t readv(int fd, const struct iovec *iov, int iovcnt)
+{
+    return preadv(fd, iov, iovcnt, -1);
+}
+
+TRACEPOINT(trace_vfs_pwritev, "%d %p 0x%x 0x%x", int, const struct iovec*, 
int, off_t);
+TRACEPOINT(trace_vfs_pwritev_ret, "0x%x", ssize_t);
+TRACEPOINT(trace_vfs_pwritev_err, "%d", int);
+
+ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset)
+{
+    struct file *fp;
+    size_t bytes;
+    int error;
+
+    trace_vfs_pwritev(fd, iov, iovcnt, offset);
+    error = fget(fd, &fp);
+    if (error)
+        goto out_errno;
+
+    error = sys_write(fp, iov, iovcnt, offset, &bytes);
+    fdrop(fp);
+
+    if (has_error(error, bytes))
+        goto out_errno;
+    trace_vfs_pwritev_ret(bytes);
+    return bytes;
+
+    out_errno:
+    trace_vfs_pwritev_err(error);
+    errno = error;
+    return -1;
+}
+LFS64(pwritev);
+
+ssize_t writev(int fd, const struct iovec *iov, int iovcnt)
+{
+    return pwritev(fd, iov, iovcnt, -1);
+}
+
+TRACEPOINT(trace_vfs_ioctl, "%d 0x%x", int, unsigned long);
+TRACEPOINT(trace_vfs_ioctl_ret, "");
+TRACEPOINT(trace_vfs_ioctl_err, "%d", int);
+
+int ioctl(int fd, unsigned long int request, ...)
+{
+    struct file *fp;
+    int error;
+    va_list ap;
+    void* arg;
+
+    trace_vfs_ioctl(fd, request);
+    /* glibc ABI provides a variadic prototype for ioctl so we need to agree
+     * with it, since we now include sys/ioctl.h
+     * read the first argument and pass it to sys_ioctl() */
+    va_start(ap, request);
+    arg = va_arg(ap, void*);
+    va_end(ap);
+
+    error = fget(fd, &fp);
+    if (error)
+        goto out_errno;
+
+    error = sys_ioctl(fp, request, arg);
+    fdrop(fp);
+
+    if (error)
+        goto out_errno;
+    trace_vfs_ioctl_ret();
+    return 0;
+
+    out_errno:
+    trace_vfs_ioctl_err(error);
+    errno = error;
+    return -1;
+}
+
+TRACEPOINT(trace_vfs_fsync, "%d", int);
+TRACEPOINT(trace_vfs_fsync_ret, "");
+TRACEPOINT(trace_vfs_fsync_err, "%d", int);
+
+int fsync(int fd)
+{
+    struct file *fp;
+    int error;
+
+    trace_vfs_fsync(fd);
+    error = fget(fd, &fp);
+    if (error)
+        goto out_errno;
+
+    error = sys_fsync(fp);
+    fdrop(fp);
+
+    if (error)
+        goto out_errno;
+    trace_vfs_fsync_ret();
+    return 0;
+
+    out_errno:
+    trace_vfs_fsync_err(error);
+    errno = error;
+    return -1;
+}
+
+int fdatasync(int fd)
+{
+    // TODO: See if we can do less than fsync().
+    return fsync(fd);
+}
+
+TRACEPOINT(trace_vfs_fstat, "%d %p", int, struct stat*);
+TRACEPOINT(trace_vfs_fstat_ret, "");
+TRACEPOINT(trace_vfs_fstat_err, "%d", int);
+
+extern "C"
+int __fxstat(int ver, int fd, struct stat *st)
+{
+    struct file *fp;
+    int error;
+
+    trace_vfs_fstat(fd, st);
+
+    error = fget(fd, &fp);
+    if (error)
+        goto out_errno;
+
+    error = sys_fstat(fp, st);
+    fdrop(fp);
+
+    if (error)
+        goto out_errno;
+    trace_vfs_fstat_ret();
+    return 0;
+
+    out_errno:
+    trace_vfs_fstat_err(error);
+    errno = error;
+    return -1;
+}
+
+LFS64(__fxstat);
+
+extern "C"
+int fstat(int fd, struct stat *st)
+{
+    return __fxstat(1, fd, st);
+}
+
+LFS64(fstat);
+
+extern "C"
+int __fxstatat(int ver, int dirfd, const char *pathname, struct stat *st,
+        int flags)
+{
+    if (flags & AT_SYMLINK_NOFOLLOW) {
+        UNIMPLEMENTED("fstatat() with AT_SYMLINK_NOFOLLOW");
+    }
+
+    if (pathname[0] == '/' || dirfd == AT_FDCWD) {
+        return stat(pathname, st);
+    }
+    // If AT_EMPTY_PATH and pathname is an empty string, fstatat() operates on
+    // dirfd itself, and in that case it doesn't have to be a directory.
+    if ((flags & AT_EMPTY_PATH) && !pathname[0]) {
+        return fstat(dirfd, st);
+    }
+
+    struct file *fp;
+    int error = fget(dirfd, &fp);
+    if (error) {
+        errno = error;
+        return -1;
+    }
+
+    struct vnode *vp = fp->f_dentry->d_vnode;
+    vn_lock(vp);
+
+    std::unique_ptr<char []> up (new char[PATH_MAX]);
+    char *p = up.get();
+    /* build absolute path */
+    strlcpy(p, fp->f_dentry->d_mount->m_path, PATH_MAX);
+    strlcat(p, fp->f_dentry->d_path, PATH_MAX);
+    strlcat(p, "/", PATH_MAX);
+    strlcat(p, pathname, PATH_MAX);
+
+    error = stat(p, st);
+
+    vn_unlock(vp);
+    fdrop(fp);
+
+    return error;
+}
+
+LFS64(__fxstatat);
+
+extern "C"
+int fstatat(int dirfd, const char *path, struct stat *st, int flags)
+{
+    return __fxstatat(1, dirfd, path, st, flags);
+}
+
+LFS64(fstatat);
+
+extern "C" int flock(int fd, int operation)
+{
+    if (!fileref_from_fd(fd)) {
+        return libc_error(EBADF);
+    }
+
+    switch (operation) {
+    case LOCK_SH:
+    case LOCK_SH | LOCK_NB:
+    case LOCK_EX:
+    case LOCK_EX | LOCK_NB:
+    case LOCK_UN:
+        break;
+    default:
+        return libc_error(EINVAL);
+    }
+
+    return 0;
+}
+
+TRACEPOINT(trace_vfs_readdir, "%d %p", int, dirent*);
+TRACEPOINT(trace_vfs_readdir_ret, "");
+TRACEPOINT(trace_vfs_readdir_err, "%d", int);
+
+struct __dirstream
+{
+    int fd;
+};
+
+DIR *opendir(const char *path)
+{
+    DIR *dir = new DIR;
+
+    if (!dir)
+        return libc_error_ptr<DIR>(ENOMEM);
+
+    dir->fd = open(path, O_RDONLY);
+    if (dir->fd < 0) {
+        delete dir;
+        return nullptr;
+    }
+    return dir;
+}
+
+DIR *fdopendir(int fd)
+{
+    DIR *dir;
+    struct stat st;
+    if (fstat(fd, &st) < 0) {
+        return nullptr;
+    }
+    if (!S_ISDIR(st.st_mode)) {
+        errno = ENOTDIR;
+        return nullptr;
+    }
+    dir = new DIR;
+    dir->fd = fd;
+    return dir;
+
+}
+
+int dirfd(DIR *dirp)
+{
+    if (!dirp) {
+        return libc_error(EINVAL);
+    }
+
+    return dirp->fd;
+}
+
+int closedir(DIR *dir)
+{
+    close(dir->fd);
+    delete dir;
+    return 0;
+}
+
+struct dirent *readdir(DIR *dir)
+{
+    static __thread struct dirent entry, *result;
+    int ret;
+
+    ret = readdir_r(dir, &entry, &result);
+    if (ret)
+        return libc_error_ptr<struct dirent>(ret);
+
+    errno = 0;
+    return result;
+}
+
+int readdir_r(DIR *dir, struct dirent *entry, struct dirent **result)
+{
+    int error;
+    struct file *fp;
+
+    trace_vfs_readdir(dir->fd, entry);
+    error = fget(dir->fd, &fp);
+    if (error) {
+        trace_vfs_readdir_err(error);
+    } else {
+        error = sys_readdir(fp, entry);
+        fdrop(fp);
+        if (error) {
+            trace_vfs_readdir_err(error);
+        } else {
+            trace_vfs_readdir_ret();
+        }
+    }
+    // Our dirent has (like Linux) a d_reclen field, but a constant size.
+    entry->d_reclen = sizeof(*entry);
+
+    if (error) {
+        *result = nullptr;
+    } else {
+        *result = entry;
+    }
+    return error == ENOENT ? 0 : error;
+}
+
+// FIXME: in 64bit dirent64 and dirent are identical, so it's safe to alias
+#undef readdir64_r
+extern "C" int readdir64_r(DIR *dir, struct dirent64 *entry,
+        struct dirent64 **result)
+        __attribute__((alias("readdir_r")));
+
+#undef readdir64
+extern "C" struct dirent *readdir64(DIR *dir) 
__attribute__((alias("readdir")));
+
+void rewinddir(DIR *dirp)
+{
+    struct file *fp;
+
+    auto error = fget(dirp->fd, &fp);
+    if (error) {
+        // POSIX specifies that what rewinddir() does in the case of error
+        // is undefined...
+        return;
+    }
+
+    sys_rewinddir(fp);
+    // Again, error code from sys_rewinddir() is ignored.
+    fdrop(fp);
+}
+
+long telldir(DIR *dirp)
+{
+    struct file *fp;
+    int error = fget(dirp->fd, &fp);
+    if (error) {
+        return libc_error(error);
+    }
+
+    long loc;
+    error = sys_telldir(fp, &loc);
+    fdrop(fp);
+    if (error) {
+        return libc_error(error);
+    }
+    return loc;
+}
+
+void seekdir(DIR *dirp, long loc)
+{
+    struct file *fp;
+    int error = fget(dirp->fd, &fp);
+    if (error) {
+        // POSIX specifies seekdir() cannot return errors.
+        return;
+    }
+    sys_seekdir(fp, loc);
+    // Again, error code from sys_seekdir() is ignored.
+    fdrop(fp);
+}
+
+TRACEPOINT(trace_vfs_mkdir, "\"%s\" 0%0o", const char*, mode_t);
+TRACEPOINT(trace_vfs_mkdir_ret, "");
+TRACEPOINT(trace_vfs_mkdir_err, "%d", int);
+
+int
+mkdir(const char *pathname, mode_t mode)
+{
+    struct task *t = main_task;
+    char path[PATH_MAX];
+    int error;
+
+    mode = apply_umask(mode);
+
+    trace_vfs_mkdir(pathname, mode);
+    if ((error = task_conv(t, pathname, VWRITE, path)) != 0)
+        goto out_errno;
+
+    error = sys_mkdir(path, mode);
+    if (error)
+        goto out_errno;
+    trace_vfs_mkdir_ret();
+    return 0;
+    out_errno:
+    trace_vfs_mkdir_err(error);
+    errno = error;
+    return -1;
+}
+
+TRACEPOINT(trace_vfs_rmdir, "\"%s\"", const char*);
+TRACEPOINT(trace_vfs_rmdir_ret, "");
+TRACEPOINT(trace_vfs_rmdir_err, "%d", int);
+
+int rmdir(const char *pathname)
+{
+    struct task *t = main_task;
+    char path[PATH_MAX];
+    int error;
+
+    trace_vfs_rmdir(pathname);
+    error = ENOENT;
+    if (pathname == nullptr)
+        goto out_errno;
+    if ((error = task_conv(t, pathname, VWRITE, path)) != 0)
+        goto out_errno;
+
+    error = sys_rmdir(path);
+    if (error)
+        goto out_errno;
+    trace_vfs_rmdir_ret();
+    return 0;
+    out_errno:
+    trace_vfs_rmdir_err(error);
+    errno = error;
+    return -1;
+}
+
+static void
+get_last_component(const char *path, char *dst)
+{
+    int pos = strlen(path) - 1;
+
+    while (pos >= 0 && path[pos] == '/')
+        pos--;
+
+    int component_end = pos;
+
+    while (pos >= 0 && path[pos] != '/')
+        pos--;
+
+    int component_start = pos + 1;
+
+    int len = component_end - component_start + 1;
+    memcpy(dst, path + component_start, len);
+    dst[len] = 0;
+}
+
+static bool null_or_empty(const char *str)
+{
+    return str == nullptr || *str == '\0';
+}
+
+TRACEPOINT(trace_vfs_rename, "\"%s\" \"%s\"", const char*, const char*);
+TRACEPOINT(trace_vfs_rename_ret, "");
+TRACEPOINT(trace_vfs_rename_err, "%d", int);
+
+int rename(const char *oldpath, const char *newpath)
+{
+    trace_vfs_rename(oldpath, newpath);
+    struct task *t = main_task;
+    char src[PATH_MAX];
+    char dest[PATH_MAX];
+    int error;
+
+    error = ENOENT;
+    if (null_or_empty(oldpath) || null_or_empty(newpath))
+        goto out_errno;
+
+    get_last_component(oldpath, src);
+    if (!strcmp(src, ".") || !strcmp(src, "..")) {
+        error = EINVAL;
+        goto out_errno;
+    }
+
+    get_last_component(newpath, dest);
+    if (!strcmp(dest, ".") || !strcmp(dest, "..")) {
+        error = EINVAL;
+        goto out_errno;
+    }
+
+    if ((error = task_conv(t, oldpath, VREAD, src)) != 0)
+        goto out_errno;
+
+    if ((error = task_conv(t, newpath, VWRITE, dest)) != 0)
+        goto out_errno;
+
+    error = sys_rename(src, dest);
+    if (error)
+        goto out_errno;
+    trace_vfs_rename_ret();
+    return 0;
+    out_errno:
+    trace_vfs_rename_err(error);
+    errno = error;
+    return -1;
+}
+
+TRACEPOINT(trace_vfs_chdir, "\"%s\"", const char*);
+TRACEPOINT(trace_vfs_chdir_ret, "");
+TRACEPOINT(trace_vfs_chdir_err, "%d", int);
+
+static int replace_cwd(struct task *t, struct file *new_cwdfp,
+                       std::function<int (void)> chdir_func)
+{
+    struct file *old = nullptr;
+
+    if (!t) {
+        return 0;
+    }
+
+    if (t->t_cwdfp) {
+        old = t->t_cwdfp;
+    }
+
+    /* Do the actual chdir operation here */
+    int error = chdir_func();
+
+    t->t_cwdfp = new_cwdfp;
+    if (old) {
+        fdrop(old);
+    }
+
+    return error;
+}
+
+int chdir(const char *pathname)
+{
+    trace_vfs_chdir(pathname);
+    struct task *t = main_task;
+    char path[PATH_MAX];
+    struct file *fp;
+    int error;
+
+    error = ENOENT;
+    if (pathname == nullptr)
+        goto out_errno;
+
+    if ((error = task_conv(t, pathname, VREAD, path)) != 0)
+        goto out_errno;
+
+    /* Check if directory exits */
+    error = sys_open(path, O_DIRECTORY, 0, &fp);
+    if (error) {
+        goto out_errno;
+    }
+
+    replace_cwd(t, fp, [&]() { strlcpy(t->t_cwd, path, sizeof(t->t_cwd)); 
return 0; });
+
+    trace_vfs_chdir_ret();
+    return 0;
+    out_errno:
+    errno = error;
+    trace_vfs_chdir_err(errno);
+    return -1;
+}
+
+TRACEPOINT(trace_vfs_fchdir, "%d", int);
+TRACEPOINT(trace_vfs_fchdir_ret, "");
+TRACEPOINT(trace_vfs_fchdir_err, "%d", int);
+
+int fchdir(int fd)
+{
+    trace_vfs_fchdir(fd);
+    struct task *t = main_task;
+    struct file *fp;
+    int error;
+
+    error = fget(fd, &fp);
+    if (error)
+        goto out_errno;
+
+    error = replace_cwd(t, fp, [&]() { return sys_fchdir(fp, t->t_cwd); });
+    if (error) {
+        fdrop(fp);
+        goto out_errno;
+    }
+
+    trace_vfs_fchdir_ret();
+    return 0;
+
+    out_errno:
+    trace_vfs_fchdir_err(error);
+    errno = error;
+    return -1;
+}
+
+TRACEPOINT(trace_vfs_link, "\"%s\" \"%s\"", const char*, const char*);
+TRACEPOINT(trace_vfs_link_ret, "");
+TRACEPOINT(trace_vfs_link_err, "%d", int);
+
+int link(const char *oldpath, const char *newpath)
+{
+    struct task *t = main_task;
+    char path1[PATH_MAX];
+    char path2[PATH_MAX];
+    int error;
+
+    trace_vfs_link(oldpath, newpath);
+
+    error = ENOENT;
+    if (oldpath == nullptr || newpath == nullptr)
+        goto out_errno;
+    if ((error = task_conv(t, oldpath, VWRITE, path1)) != 0)
+        goto out_errno;
+    if ((error = task_conv(t, newpath, VWRITE, path2)) != 0)
+        goto out_errno;
+
+    error = sys_link(path1, path2);
+    if (error)
+        goto out_errno;
+    trace_vfs_link_ret();
+    return 0;
+    out_errno:
+    trace_vfs_link_err(error);
+    errno = error;
+    return -1;
+}
+
+
+TRACEPOINT(trace_vfs_symlink, "oldpath=%s, newpath=%s", const char*, const 
char*);
+TRACEPOINT(trace_vfs_symlink_ret, "");
+TRACEPOINT(trace_vfs_symlink_err, "errno=%d", int);
+
+int symlink(const char *oldpath, const char *newpath)
+{
+    int error;
+
+    trace_vfs_symlink(oldpath, newpath);
+
+    error = ENOENT;
+    if (oldpath == nullptr || newpath == nullptr) {
+        errno = ENOENT;
+        trace_vfs_symlink_err(error);
+        return (-1);
+    }
+
+    error = sys_symlink(oldpath, newpath);
+    if (error) {
+        errno = error;
+        trace_vfs_symlink_err(error);
+        return (-1);
+    }
+
+    trace_vfs_symlink_ret();
+    return 0;
+}
+
+TRACEPOINT(trace_vfs_unlink, "\"%s\"", const char*);
+TRACEPOINT(trace_vfs_unlink_ret, "");
+TRACEPOINT(trace_vfs_unlink_err, "%d", int);
+
+int unlink(const char *pathname)
+{
+    trace_vfs_unlink(pathname);
+    struct task *t = main_task;
+    char path[PATH_MAX];
+    int error;
+
+    error = ENOENT;
+    if (pathname == nullptr)
+        goto out_errno;
+    if ((error = task_conv(t, pathname, VWRITE, path)) != 0)
+        goto out_errno;
+
+    error = sys_unlink(path);
+    if (error)
+        goto out_errno;
+    trace_vfs_unlink_ret();
+    return 0;
+    out_errno:
+    trace_vfs_unlink_err(error);
+    errno = error;
+    return -1;
+}
+
+TRACEPOINT(trace_vfs_stat, "\"%s\" %p", const char*, struct stat*);
+TRACEPOINT(trace_vfs_stat_ret, "");
+TRACEPOINT(trace_vfs_stat_err, "%d", int);
+
+extern "C"
+int __xstat(int ver, const char *pathname, struct stat *st)
+{
+    struct task *t = main_task;
+    char path[PATH_MAX];
+    int error;
+
+    trace_vfs_stat(pathname, st);
+
+    error = task_conv(t, pathname, 0, path);
+    if (error)
+        goto out_errno;
+
+    error = sys_stat(path, st);
+    if (error)
+        goto out_errno;
+    trace_vfs_stat_ret();
+    return 0;
+
+    out_errno:
+    trace_vfs_stat_err(error);
+    errno = error;
+    return -1;
+}
+
+LFS64(__xstat);
+
+int stat(const char *pathname, struct stat *st)
+{
+    return __xstat(1, pathname, st);
+}
+
+LFS64(stat);
+
+TRACEPOINT(trace_vfs_lstat, "pathname=%s, stat=%p", const char*, struct stat*);
+TRACEPOINT(trace_vfs_lstat_ret, "");
+TRACEPOINT(trace_vfs_lstat_err, "errno=%d", int);
+extern "C"
+int __lxstat(int ver, const char *pathname, struct stat *st)
+{
+    struct task *t = main_task;
+    char path[PATH_MAX];
+    int error;
+
+    trace_vfs_lstat(pathname, st);
+
+    error = task_conv(t, pathname, 0, path);
+    if (error) {
+        errno = error;
+        trace_vfs_lstat_err(error);
+        return (-1);
+    }
+
+    error = sys_lstat(path, st);
+    if (error) {
+        errno = error;
+        trace_vfs_lstat_err(error);
+        return (-1);
+    }
+
+    trace_vfs_lstat_ret();
+    return 0;
+}
+
+LFS64(__lxstat);
+
+int lstat(const char *pathname, struct stat *st)
+{
+    return __lxstat(1, pathname, st);
+}
+
+LFS64(lstat);
+
+TRACEPOINT(trace_vfs_statfs, "\"%s\" %p", const char*, struct statfs*);
+TRACEPOINT(trace_vfs_statfs_ret, "");
+TRACEPOINT(trace_vfs_statfs_err, "%d", int);
+
+extern "C"
+int __statfs(const char *pathname, struct statfs *buf)
+{
+    trace_vfs_statfs(pathname, buf);
+    struct task *t = main_task;
+    char path[PATH_MAX];
+    int error;
+
+    error = task_conv(t, pathname, 0, path);
+    if (error)
+        goto out_errno;
+
+    error = sys_statfs(path, buf);
+    if (error)
+        goto out_errno;
+    trace_vfs_statfs_ret();
+    return 0;
+    out_errno:
+    trace_vfs_statfs_err(error);
+    errno = error;
+    return -1;
+}
+weak_alias(__statfs, statfs);
+
+LFS64(statfs);
+
+TRACEPOINT(trace_vfs_fstatfs, "\"%s\" %p", int, struct statfs*);
+TRACEPOINT(trace_vfs_fstatfs_ret, "");
+TRACEPOINT(trace_vfs_fstatfs_err, "%d", int);
+
+extern "C"
+int __fstatfs(int fd, struct statfs *buf)
+{
+    struct file *fp;
+    int error;
+
+    trace_vfs_fstatfs(fd, buf);
+    error = fget(fd, &fp);
+    if (error)
+        goto out_errno;
+
+    error = sys_fstatfs(fp, buf);
+    fdrop(fp);
+
+    if (error)
+        goto out_errno;
+    trace_vfs_fstatfs_ret();
+    return 0;
+
+    out_errno:
+    trace_vfs_fstatfs_err(error);
+    errno = error;
+    return -1;
+}
+weak_alias(__fstatfs, fstatfs);
+
+LFS64(fstatfs);
+
+static int
+statfs_to_statvfs(struct statvfs *dst, struct statfs *src)
+{
+    dst->f_bsize = src->f_bsize;
+    dst->f_frsize = src->f_bsize;
+    dst->f_blocks = src->f_blocks;
+    dst->f_bfree = src->f_bfree;
+    dst->f_bavail = src->f_bavail;
+    dst->f_files = src->f_files;
+    dst->f_ffree = src->f_ffree;
+    dst->f_favail = 0;
+    dst->f_fsid = src->f_fsid.__val[0];
+    dst->f_flag = src->f_flags;
+    dst->f_namemax = src->f_namelen;
+    return 0;
+}
+
+int
+statvfs(const char *pathname, struct statvfs *buf)
+{
+    struct statfs st;
+
+    if (__statfs(pathname, &st) < 0)
+        return -1;
+    return statfs_to_statvfs(buf, &st);
+}
+
+LFS64(statvfs);
+
+int
+fstatvfs(int fd, struct statvfs *buf)
+{
+    struct statfs st;
+
+    if (__fstatfs(fd, &st) < 0)
+        return -1;
+    return statfs_to_statvfs(buf, &st);
+}
+
+LFS64(fstatvfs);
+
+
+TRACEPOINT(trace_vfs_getcwd, "%p %d", char*, size_t);
+TRACEPOINT(trace_vfs_getcwd_ret, "\"%s\"", const char*);
+TRACEPOINT(trace_vfs_getcwd_err, "%d", int);
+
+char *getcwd(char *path, size_t size)
+{
+    trace_vfs_getcwd(path, size);
+    struct task *t = main_task;
+    int len = strlen(t->t_cwd) + 1;
+    int error;
+
+    if (!path) {
+        if (!size)
+            size = len;
+        path = (char*)malloc(size);
+        if (!path) {
+            error = ENOMEM;
+            goto out_errno;
+        }
+    } else {
+        if (!size) {
+            error = EINVAL;
+            goto out_errno;
+        }
+    }
+
+    if (size < len) {
+        error = ERANGE;
+        goto out_errno;
+    }
+
+    memcpy(path, t->t_cwd, len);
+    trace_vfs_getcwd_ret(path);
+    return path;
+
+    out_errno:
+    trace_vfs_getcwd_err(error);
+    errno = error;
+    return nullptr;
+}
+
+TRACEPOINT(trace_vfs_dup, "%d", int);
+TRACEPOINT(trace_vfs_dup_ret, "\"%s\"", int);
+TRACEPOINT(trace_vfs_dup_err, "%d", int);
+/*
+ * Duplicate a file descriptor
+ */
+int dup(int oldfd)
+{
+    struct file *fp;
+    int newfd;
+    int error;
+
+    trace_vfs_dup(oldfd);
+    error = fget(oldfd, &fp);
+    if (error)
+        goto out_errno;
+
+    error = fdalloc(fp, &newfd);
+    if (error)
+        goto out_fdrop;
+
+    fdrop(fp);
+    trace_vfs_dup_ret(newfd);
+    return newfd;
+
+    out_fdrop:
+    fdrop(fp);
+    out_errno:
+    trace_vfs_dup_err(error);
+    errno = error;
+    return -1;
+}
+
+TRACEPOINT(trace_vfs_dup3, "%d %d 0x%x", int, int, int);
+TRACEPOINT(trace_vfs_dup3_ret, "%d", int);
+TRACEPOINT(trace_vfs_dup3_err, "%d", int);
+/*
+ * Duplicate a file descriptor to a particular value.
+ */
+int dup3(int oldfd, int newfd, int flags)
+{
+    struct file *fp;
+    int error;
+
+    trace_vfs_dup3(oldfd, newfd, flags);
+    /*
+     * Don't allow any argument but O_CLOEXEC.  But we even ignore
+     * that as we don't support exec() and thus don't care.
+     */
+    if ((flags & ~O_CLOEXEC) != 0) {
+        error = EINVAL;
+        goto out_errno;
+    }
+
+    if (oldfd == newfd) {
+        error = EINVAL;
+        goto out_errno;
+    }
+
+    error = fget(oldfd, &fp);
+    if (error)
+        goto out_errno;
+
+    error = fdset(newfd, fp);
+    if (error) {
+        fdrop(fp);
+        goto out_errno;
+    }
+
+    fdrop(fp);
+    trace_vfs_dup3_ret(newfd);
+    return newfd;
+
+    out_errno:
+    trace_vfs_dup3_err(error);
+    errno = error;
+    return -1;
+}
+
+int dup2(int oldfd, int newfd)
+{
+    if (oldfd == newfd)
+        return newfd;
+
+    return dup3(oldfd, newfd, 0);
+}
+
+/*
+ * The file control system call.
+ */
+#define SETFL (O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | O_NONBLOCK)
+
+TRACEPOINT(trace_vfs_fcntl, "%d %d 0x%x", int, int, int);
+TRACEPOINT(trace_vfs_fcntl_ret, "\"%s\"", int);
+TRACEPOINT(trace_vfs_fcntl_err, "%d", int);
+
+extern "C"
+int fcntl(int fd, int cmd, int arg)
+{
+    struct file *fp;
+    int ret = 0, error;
+    int tmp;
+
+    trace_vfs_fcntl(fd, cmd, arg);
+    error = fget(fd, &fp);
+    if (error)
+        goto out_errno;
+
+    // An important note about our handling of FD_CLOEXEC / O_CLOEXEC:
+    // close-on-exec shouldn't have been a file flag (fp->f_flags) - it is a
+    // file descriptor flag, meaning that that two dup()ed file descriptors
+    // could have different values for FD_CLOEXEC. Our current implementation
+    // *wrongly* makes close-on-exec an f_flag (using the bit O_CLOEXEC).
+    // There is little practical difference, though, because this flag is
+    // ignored in OSv anyway, as it doesn't support exec().
+    switch (cmd) {
+    case F_DUPFD:
+        error = _fdalloc(fp, &ret, arg);
+        if (error)
+            goto out_errno;
+        break;
+    case F_GETFD:
+        ret = (fp->f_flags & O_CLOEXEC) ? FD_CLOEXEC : 0;
+        break;
+    case F_SETFD:
+        FD_LOCK(fp);
+        fp->f_flags = (fp->f_flags & ~O_CLOEXEC) |
+                ((arg & FD_CLOEXEC) ? O_CLOEXEC : 0);
+        FD_UNLOCK(fp);
+        break;
+    case F_GETFL:
+        // As explained above, the O_CLOEXEC should have been in f_flags,
+        // and shouldn't be returned. Linux always returns 0100000 ("the
+        // flag formerly known as O_LARGEFILE) so let's do it too.
+        ret = (oflags(fp->f_flags) & ~O_CLOEXEC) | 0100000;
+        break;
+    case F_SETFL:
+        FD_LOCK(fp);
+        fp->f_flags = fflags((oflags(fp->f_flags) & ~SETFL) |
+                (arg & SETFL));
+        FD_UNLOCK(fp);
+
+        /* Sync nonblocking/async state with file flags */
+        tmp = fp->f_flags & FNONBLOCK;
+        fp->ioctl(FIONBIO, &tmp);
+        tmp = fp->f_flags & FASYNC;
+        fp->ioctl(FIOASYNC, &tmp);
+
+        break;
+    case F_SETLK:
+        WARN_ONCE("fcntl(F_SETLK) stubbed\n");
+        break;
+    case F_GETLK:
+        WARN_ONCE("fcntl(F_GETLK) stubbed\n");
+        break;
+    case F_SETLKW:
+        WARN_ONCE("fcntl(F_SETLKW) stubbed\n");
+        break;
+    case F_SETOWN:
+        WARN_ONCE("fcntl(F_SETOWN) stubbed\n");
+        break;
+    default:
+        kprintf("unsupported fcntl cmd 0x%x\n", cmd);
+        error = EINVAL;
+    }
+
+    fdrop(fp);
+    if (error)
+        goto out_errno;
+    trace_vfs_fcntl_ret(ret);
+    return ret;
+
+    out_errno:
+    trace_vfs_fcntl_err(error);
+    errno = error;
+    return -1;
+}
+
+TRACEPOINT(trace_vfs_access, "\"%s\" 0%0o", const char*, int);
+TRACEPOINT(trace_vfs_access_ret, "");
+TRACEPOINT(trace_vfs_access_err, "%d", int);
+
+/*
+ * Check permission for file access
+ */
+int access(const char *pathname, int mode)
+{
+    trace_vfs_access(pathname, mode);
+    struct task *t = main_task;
+    char path[PATH_MAX];
+    int acc, error = 0;
+
+    acc = 0;
+    if (mode & R_OK)
+        acc |= VREAD;
+    if (mode & W_OK)
+        acc |= VWRITE;
+
+    if ((error = task_conv(t, pathname, acc, path)) != 0)
+        goto out_errno;
+
+    error = sys_access(path, mode);
+    if (error)
+        goto out_errno;
+    trace_vfs_access_ret();
+    return 0;
+    out_errno:
+    errno = error;
+    trace_vfs_access_err(error);
+    return -1;
+}
+
+int faccessat(int dirfd, const char *pathname, int mode, int flags)
+{
+    if (flags & AT_SYMLINK_NOFOLLOW) {
+        UNIMPLEMENTED("faccessat() with AT_SYMLINK_NOFOLLOW");
+    }
+
+    if (pathname[0] == '/' || dirfd == AT_FDCWD) {
+        return access(pathname, mode);
+    }
+
+    struct file *fp;
+    int error = fget(dirfd, &fp);
+    if (error) {
+        errno = error;
+        return -1;
+    }
+
+    struct vnode *vp = fp->f_dentry->d_vnode;
+    vn_lock(vp);
+
+    std::unique_ptr<char []> up (new char[PATH_MAX]);
+    char *p = up.get();
+
+    /* build absolute path */
+    strlcpy(p, fp->f_dentry->d_mount->m_path, PATH_MAX);
+    strlcat(p, fp->f_dentry->d_path, PATH_MAX);
+    strlcat(p, "/", PATH_MAX);
+    strlcat(p, pathname, PATH_MAX);
+
+    error = access(p, mode);
+
+    vn_unlock(vp);
+    fdrop(fp);
+
+    return error;
+}
+
+extern "C" 
+int euidaccess(const char *pathname, int mode)
+{
+    return access(pathname, mode);
+}
+
+weak_alias(euidaccess,eaccess);
+
+#if 0
+static int
+fs_pipe(struct task *t, struct msg *msg)
+{
+#ifdef CONFIG_FIFOFS
+    char path[PATH_MAX];
+    file_t rfp, wfp;
+    int error, rfd, wfd;
+
+    DPRINTF(VFSDB_CORE, ("fs_pipe\n"));
+
+    if ((rfd = task_newfd(t)) == -1)
+        return EMFILE;
+    t->t_ofile[rfd] = (file_t)1; /* temp */
+
+    if ((wfd = task_newfd(t)) == -1) {
+        t->t_ofile[rfd] = nullptr;
+        return EMFILE;
+    }
+    sprintf(path, "/mnt/fifo/pipe-%x-%d", (u_int)t->t_taskid, rfd);
+
+    if ((error = sys_mknod(path, S_IFIFO)) != 0)
+        goto out;
+    if ((error = sys_open(path, O_RDONLY | O_NONBLOCK, 0, &rfp)) != 0) {
+        goto out;
+    }
+    if ((error = sys_open(path, O_WRONLY | O_NONBLOCK, 0, &wfp)) != 0) {
+        goto out;
+    }
+    t->t_ofile[rfd] = rfp;
+    t->t_ofile[wfd] = wfp;
+    t->t_nopens += 2;
+    msg->data[0] = rfd;
+    msg->data[1] = wfd;
+    return 0;
+    out:
+    t->t_ofile[rfd] = nullptr;
+    t->t_ofile[wfd] = nullptr;
+    return error;
+#else
+    return ENOSYS;
+#endif
+}
+#endif
+
+TRACEPOINT(trace_vfs_isatty, "%d", int);
+TRACEPOINT(trace_vfs_isatty_ret, "%d", int);
+TRACEPOINT(trace_vfs_isatty_err, "%d", int);
+
+/*
+ * Return if specified file is a tty
+ */
+int isatty(int fd)
+{
+    struct file *fp;
+    int istty = 0;
+
+    trace_vfs_isatty(fd);
+    fileref f(fileref_from_fd(fd));
+    if (!f) {
+        errno = EBADF;
+        trace_vfs_isatty_err(errno);
+        return -1;
+    }
+
+    fp = f.get();
+    if (dynamic_cast<tty_file*>(fp) ||
+        (fp->f_dentry && fp->f_dentry->d_vnode->v_flags & VISTTY)) {
+        istty = 1;
+    }
+
+    trace_vfs_isatty_ret(istty);
+    return istty;
+}
+
+TRACEPOINT(trace_vfs_truncate, "\"%s\" 0x%x", const char*, off_t);
+TRACEPOINT(trace_vfs_truncate_ret, "");
+TRACEPOINT(trace_vfs_truncate_err, "%d", int);
+
+int truncate(const char *pathname, off_t length)
+{
+    trace_vfs_truncate(pathname, length);
+    struct task *t = main_task;
+    char path[PATH_MAX];
+    int error;
+
+    error = ENOENT;
+    if (pathname == nullptr)
+        goto out_errno;
+    if ((error = task_conv(t, pathname, VWRITE, path)) != 0)
+        goto out_errno;
+
+    error = sys_truncate(path, length);
+    if (error)
+        goto out_errno;
+    trace_vfs_truncate_ret();
+    return 0;
+    out_errno:
+    errno = error;
+    trace_vfs_truncate_err(error);
+    return -1;
+}
+
+LFS64(truncate);
+
+TRACEPOINT(trace_vfs_ftruncate, "%d 0x%x", int, off_t);
+TRACEPOINT(trace_vfs_ftruncate_ret, "");
+TRACEPOINT(trace_vfs_ftruncate_err, "%d", int);
+
+int ftruncate(int fd, off_t length)
+{
+    trace_vfs_ftruncate(fd, length);
+    struct file *fp;
+    int error;
+
+    error = fget(fd, &fp);
+    if (error)
+        goto out_errno;
+
+    error = sys_ftruncate(fp, length);
+    fdrop(fp);
+
+    if (error)
+        goto out_errno;
+    trace_vfs_ftruncate_ret();
+    return 0;
+
+    out_errno:
+    errno = error;
+    trace_vfs_ftruncate_err(error);
+    return -1;
+}
+
+LFS64(ftruncate);
+
+ssize_t readlink(const char *pathname, char *buf, size_t bufsize)
+{
+    struct task *t = main_task;
+    char path[PATH_MAX];
+    int error;
+    ssize_t size;
+
+    error = -EINVAL;
+    if (bufsize <= 0)
+        goto out_errno;
+
+    error = ENOENT;
+    if (pathname == nullptr)
+        goto out_errno;
+    error = task_conv(t, pathname, VWRITE, path);
+    if (error)
+        goto out_errno;
+
+    size  = 0;
+    error = sys_readlink(path, buf, bufsize, &size);
+
+    if (error != 0)
+        goto out_errno;
+
+    return size;
+    out_errno:
+    errno = error;
+    return -1;
+}
+
+TRACEPOINT(trace_vfs_fallocate, "%d %d 0x%x 0x%x", int, int, loff_t, loff_t);
+TRACEPOINT(trace_vfs_fallocate_ret, "");
+TRACEPOINT(trace_vfs_fallocate_err, "%d", int);
+
+int fallocate(int fd, int mode, loff_t offset, loff_t len)
+{
+    struct file *fp;
+    int error;
+
+    trace_vfs_fallocate(fd, mode, offset, len);
+    error = fget(fd, &fp);
+    if (error)
+        goto out_errno;
+
+    error = sys_fallocate(fp, mode, offset, len);
+    fdrop(fp);
+
+    if (error)
+        goto out_errno;
+    trace_vfs_fallocate_ret();
+    return 0;
+
+    out_errno:
+    trace_vfs_fallocate_err(error);
+    errno = error;
+    return -1;
+}
+
+LFS64(fallocate);
+
+TRACEPOINT(trace_vfs_utimes, "\"%s\"", const char*);
+TRACEPOINT(trace_vfs_utimes_ret, "");
+TRACEPOINT(trace_vfs_utimes_err, "%d", int);
+
+int futimes(int fd, const struct timeval times[2])
+{
+    return futimesat(fd, nullptr, times);
+}
+
+int futimesat(int dirfd, const char *pathname, const struct timeval times[2])
+{
+    struct stat st;
+    struct file *fp;
+    int error;
+    char *absolute_path;
+
+    if ((pathname && pathname[0] == '/') || dirfd == AT_FDCWD)
+        return utimes(pathname, times);
+
+    // Note: if pathname == nullptr, futimesat operates on dirfd itself, and in
+    // that case it doesn't have to be a directory.
+    if (pathname) {
+        error = fstat(dirfd, &st);
+        if (error) {
+            error = errno;
+            goto out_errno;
+        }
+
+        if (!S_ISDIR(st.st_mode)){
+            error = ENOTDIR;
+            goto out_errno;
+        }
+    }
+
+    error = fget(dirfd, &fp);
+    if (error)
+        goto out_errno;
+
+    /* build absolute path */
+    absolute_path = (char*)malloc(PATH_MAX);
+    strlcpy(absolute_path, fp->f_dentry->d_mount->m_path, PATH_MAX);
+    strlcat(absolute_path, fp->f_dentry->d_path, PATH_MAX);
+
+    if (pathname) {
+        strlcat(absolute_path, "/", PATH_MAX);
+        strlcat(absolute_path, pathname, PATH_MAX);
+    }
+
+    error = utimes(absolute_path, times);
+    free(absolute_path);
+
+    fdrop(fp);
+
+    if (error)
+        goto out_errno;
+    return 0;
+
+    out_errno:
+    errno = error;
+    return -1;
+}
+
+TRACEPOINT(trace_vfs_utimensat, "\"%s\"", const char*);
+TRACEPOINT(trace_vfs_utimensat_ret, "");
+TRACEPOINT(trace_vfs_utimensat_err, "%d", int);
+
+extern "C"
+int utimensat(int dirfd, const char *pathname, const struct timespec times[2], 
int flags)
+{
+    trace_vfs_utimensat(pathname);
+
+    auto error = sys_utimensat(dirfd, pathname, times, flags);
+    if (error) {
+        trace_vfs_utimensat_err(error);
+        errno = error;
+        return -1;
+    }
+
+    trace_vfs_utimensat_ret();
+    return 0;
+}
+
+TRACEPOINT(trace_vfs_futimens, "%d", int);
+TRACEPOINT(trace_vfs_futimens_ret, "");
+TRACEPOINT(trace_vfs_futimens_err, "%d", int);
+
+extern "C"
+int futimens(int fd, const struct timespec times[2])
+{
+    trace_vfs_futimens(fd);
+
+    auto error = sys_futimens(fd, times);
+    if (error) {
+        trace_vfs_futimens_err(error);
+        errno = error;
+        return -1;
+    }
+
+    trace_vfs_futimens_ret();
+    return 0;
+}
+
+static int do_utimes(const char *pathname, const struct timeval times[2], int 
flags)
+{
+    struct task *t = main_task;
+    char path[PATH_MAX];
+    int error;
+
+    trace_vfs_utimes(pathname);
+
+    error = task_conv(t, pathname, 0, path);
+    if (error) {
+        trace_vfs_utimes_err(error);
+        return libc_error(error);
+    }
+
+    error = sys_utimes(path, times, flags);
+    if (error) {
+        trace_vfs_utimes_err(error);
+        return libc_error(error);
+    }
+
+    trace_vfs_utimes_ret();
+    return 0;
+}
+
+extern "C"
+int utimes(const char *pathname, const struct timeval times[2])
+{
+    return do_utimes(pathname, times, 0);
+}
+
+extern "C"
+int lutimes(const char *pathname, const struct timeval times[2])
+{
+    return do_utimes(pathname, times, AT_SYMLINK_NOFOLLOW);
+}
+
+extern "C"
+int utime(const char *pathname, const struct utimbuf *t)
+{
+    using namespace std::chrono;
+
+    struct timeval times[2];
+    times[0].tv_usec = 0;
+    times[1].tv_usec = 0;
+    if (!t) {
+        long int tsec = 
duration_cast<seconds>(osv::clock::wall::now().time_since_epoch()).count();
+        times[0].tv_sec = tsec;
+        times[1].tv_sec = tsec;
+    } else {
+        times[0].tv_sec = t->actime;
+        times[1].tv_sec = t->modtime;
+    }
+
+    return utimes(pathname, times);
+}
+
+TRACEPOINT(trace_vfs_chmod, "\"%s\" 0%0o", const char*, mode_t);
+TRACEPOINT(trace_vfs_chmod_ret, "");
+TRACEPOINT(trace_vfs_chmod_err, "%d", int);
+
+int chmod(const char *pathname, mode_t mode)
+{
+    trace_vfs_chmod(pathname, mode);
+    struct task *t = main_task;
+    char path[PATH_MAX];
+    int error = ENOENT;
+    if (pathname == nullptr)
+        goto out_errno;
+    if ((error = task_conv(t, pathname, VWRITE, path)) != 0)
+        goto out_errno;
+    error = sys_chmod(path, mode & ALLPERMS);
+    if (error)
+        goto out_errno;
+    trace_vfs_chmod_ret();
+    return 0;
+out_errno:
+    trace_vfs_chmod_err(error);
+    errno = error;
+    return -1;
+}
+
+TRACEPOINT(trace_vfs_fchmod, "\"%d\" 0%0o", int, mode_t);
+TRACEPOINT(trace_vfs_fchmod_ret, "");
+
+int fchmod(int fd, mode_t mode)
+{
+    trace_vfs_fchmod(fd, mode);
+    auto error = sys_fchmod(fd, mode & ALLPERMS);
+    trace_vfs_fchmod_ret();
+    if (error) {
+        errno = error;
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
+TRACEPOINT(trace_vfs_fchown, "\"%d\" %d %d", int, uid_t, gid_t);
+TRACEPOINT(trace_vfs_fchown_ret, "");
+
+int fchown(int fd, uid_t owner, gid_t group)
+{
+    trace_vfs_fchown(fd, owner, group);
+    WARN_STUBBED();
+    trace_vfs_fchown_ret();
+    return 0;
+}
+
+int chown(const char *path, uid_t owner, gid_t group)
+{
+    WARN_STUBBED();
+    return 0;
+}
+
+int lchown(const char *path, uid_t owner, gid_t group)
+{
+    WARN_STUBBED();
+    return 0;
+}
+
+
+ssize_t sendfile(int out_fd, int in_fd, off_t *_offset, size_t count)
+{
+    struct file *in_fp;
+    struct file *out_fp;
+    fileref in_f{fileref_from_fd(in_fd)};
+    fileref out_f{fileref_from_fd(out_fd)};
+
+    if (!in_f || !out_f) {
+        return libc_error(EBADF);
+    }
+
+    in_fp = in_f.get();
+    out_fp = out_f.get();
+
+    if (!in_fp->f_dentry) {
+        return libc_error(EBADF);
+    }
+
+    if (!(in_fp->f_flags & FREAD)) {
+        return libc_error(EBADF);
+    }
+
+    if (out_fp->f_type & DTYPE_VNODE) {
+        if (!out_fp->f_dentry) {
+            return libc_error(EBADF);
+       } else if (!(out_fp->f_flags & FWRITE)) {
+            return libc_error(EBADF);
+       }
+    }
+
+    off_t offset ;
+
+    if (_offset != nullptr) {
+        offset = *_offset;
+    } else {
+        /* if _offset is nullptr, we need to read from the present position of 
in_fd */
+        offset = lseek(in_fd, 0, SEEK_CUR);
+    }
+
+    // Constrain count to the extent of the file...
+    struct stat st;
+    if (fstat(in_fd, &st) < 0) {
+        return -1;
+    } else {
+        if (offset >= st.st_size) {
+            return 0;
+        } else if ((offset + count) >= st.st_size) {
+            count = st.st_size - offset;
+            if (count == 0) {
+                return 0;
+            }
+        }
+    }
+
+    size_t bytes_to_mmap = count + (offset % mmu::page_size);
+    off_t offset_for_mmap =  align_down(offset, (off_t)mmu::page_size);
+
+    char *src = static_cast<char *>(mmap(nullptr, bytes_to_mmap, PROT_READ, 
MAP_SHARED, in_fd, offset_for_mmap));
+
+    if (src == MAP_FAILED) {
+        return -1;
+    }
+
+    auto ret = write(out_fd, src + (offset % PAGESIZE), count);
+
+    if (ret < 0) {
+        return libc_error(errno);
+    } else if(_offset == nullptr) {
+        lseek(in_fd, ret, SEEK_CUR);
+    } else {
+        *_offset += ret;
+    }
+
+    assert(munmap(src, count) == 0);
+
+    return ret;
+}
+
+#undef sendfile64
+LFS64(sendfile);
+
+NO_SYS(int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags));
+
+mode_t umask(mode_t newmask)
+{
+    return global_umask.exchange(newmask, std::memory_order_relaxed);
+}
+
+int
+fs_noop(void)
+{
+    return 0;
+}
+
+int chroot(const char *path)
+{
+    WARN_STUBBED();
+    errno = ENOSYS;
+    return -1;
+}
+
+// unpack_bootfs() unpacks a collection of files stored as part of the OSv
+// executable (in memory location "bootfs_start") into the file system,
+// normally the in-memory filesystem ramfs.
+// The files are packed in the executable in an ad-hoc format defined here.
+// Code in scripts/mkbootfs.py packs files into this format.
+#define BOOTFS_PATH_MAX 111
+enum class bootfs_file_type : char { other = 0, symlink = 1 };
+struct bootfs_metadata {
+    uint64_t size;
+    uint64_t offset;
+    // The file's type. Can be "symlink" or "other". A directory is an "other"
+    // file with its name ending with a "/" (and no content).
+    bootfs_file_type type;
+    // name must end with a null. For symlink files, the content must end
+    // with a null as well.
+    char name[BOOTFS_PATH_MAX];
+};
+
+extern char bootfs_start;
+
+int ramfs_set_file_data(struct vnode *vp, const void *data, size_t size);
+void unpack_bootfs(void)
+{
+    struct bootfs_metadata *md = (struct bootfs_metadata *)&bootfs_start;
+    int fd, i;
+
+    for (i = 0; md[i].name[0]; i++) {
+        int ret;
+        char *p;
+
+        // mkdir() directories needed for this path name, as necessary
+        char tmp[BOOTFS_PATH_MAX];
+        strlcpy(tmp, md[i].name, BOOTFS_PATH_MAX);
+        for (p = tmp; *p; ++p) {
+            if (*p == '/') {
+                *p = '\0';
+                mkdir(tmp, 0666);  // silently ignore errors and existing dirs
+                *p = '/';
+            }
+        }
+
+        if (md[i].type == bootfs_file_type::symlink) {
+            // This is a symbolic link record. The file's content is the
+            // target path, and we assume ends with a null.
+            if (symlink(&bootfs_start + md[i].offset, md[i].name) != 0) {
+                kprintf("couldn't symlink %s: %d\n", md[i].name, errno);
+                sys_panic("unpack_bootfs failed");
+            }
+            continue;
+        }
+        if (*(p-1) == '/' && md[i].size == 0) {
+            // This is directory record. Nothing else to do
+            continue;
+        }
+
+        fd = creat(md[i].name, 0666);
+        if (fd < 0) {
+            kprintf("couldn't create %s: %d\n",
+                    md[i].name, errno);
+            sys_panic("unpack_bootfs failed");
+        }
+
+        struct file *fp;
+        int error = fget(fd, &fp);
+        if (error) {
+            kprintf("couldn't fget %s: %d\n",
+                    md[i].name, error);
+            sys_panic("unpack_bootfs failed");
+        }
+
+        struct vnode *vp = fp->f_dentry->d_vnode;
+        ret = ramfs_set_file_data(vp, &bootfs_start + md[i].offset, 
md[i].size);
+        if (ret) {
+            kprintf("ramfs_set_file_data failed, ret = %d\n", ret);
+            sys_panic("unpack_bootfs failed");
+        }
+
+        fdrop(fp);
+        close(fd);
+    }
+}
+
+void mount_rootfs(void)
+{
+    int ret;
+
+    ret = sys_mount("", "/", "ramfs", 0, nullptr);
+    if (ret)
+        kprintf("failed to mount rootfs, error = %s\n", strerror(ret));
+
+    if (mkdir("/dev", 0755) < 0)
+        kprintf("failed to create /dev, error = %s\n", strerror(errno));
+
+    ret = sys_mount("", "/dev", "devfs", 0, nullptr);
+    if (ret)
+        kprintf("failed to mount devfs, error = %s\n", strerror(ret));
+}
+
+extern "C"
+int nmount(struct iovec *iov, unsigned niov, int flags)
+{
+    struct args {
+        char* fstype = nullptr;
+        char* fspath = nullptr;
+        char* from = nullptr;
+    };
+    static unordered_map<string, char* args::*> argmap {
+        { "fstype", &args::fstype },
+        { "fspath", &args::fspath },
+        { "from", &args::from },
+    };
+    args a;
+    for (size_t i = 0; i < niov; i += 2) {
+        std::string s(static_cast<const char*>(iov[i].iov_base));
+        if (argmap.count(s)) {
+            a.*(argmap[s]) = static_cast<char*>(iov[i+1].iov_base);
+        }
+    }
+    return sys_mount(a.from, a.fspath, a.fstype, flags, nullptr);
+}
+
+static void import_extra_zfs_pools(void)
+{
+    struct stat st;
+    int ret;
+
+    // The file '/etc/mnttab' is a LibZFS requirement and will not
+    // exist during cpiod phase. The functionality provided by this
+    // function isn't needed during that phase, so let's skip it.
+    if (stat("/etc/mnttab" , &st) != 0) {
+        return;
+    }
+
+    // Import extra pools mounting datasets there contained.
+    // Datasets from osv pool will not be mounted here.
+    if (access("zpool.so", X_OK) != 0) {
+        return;
+    }
+    vector<string> zpool_args = {"zpool", "import", "-f", "-a" };
+    auto ok = osv::run("zpool.so", zpool_args, &ret);
+    assert(ok);
+
+    if (!ret) {
+        debug("zfs: extra ZFS pool(s) found.\n");
+    }
+}
+
+void pivot_rootfs(const char* path)
+{
+    int ret = sys_pivot_root(path, "/");
+    if (ret)
+        kprintf("failed to pivot root, error = %s\n", strerror(ret));
+
+    auto ent = setmntent("/etc/fstab", "r");
+    if (!ent) {
+        return;
+    }
+
+    struct mntent *m = nullptr;
+    while ((m = getmntent(ent)) != nullptr) {
+        if (!strcmp(m->mnt_dir, "/")) {
+            continue;
+        }
+
+        if ((m->mnt_opts != nullptr) && strcmp(m->mnt_opts, MNTOPT_DEFAULTS)) {
+            printf("Warning: opts %s, ignored for fs %s\n", m->mnt_opts, 
m->mnt_type);
+        }
+
+        // FIXME: Right now, ignoring mntops. In the future we may have an 
option parser
+        ret = sys_mount(m->mnt_fsname, m->mnt_dir, m->mnt_type, 0, nullptr);
+        if (ret) {
+            printf("failed to mount %s, error = %s\n", m->mnt_type, 
strerror(ret));
+        }
+    }
+    endmntent(ent);
+}
+
+extern "C" void unmount_devfs()
+{
+    int ret = sys_umount("/dev");
+    if (ret)
+        kprintf("failed to unmount /dev, error = %s\n", strerror(ret));
+}
+
+extern "C" int mount_rofs_rootfs(bool pivot_root)
+{
+    int ret;
+
+    if (mkdir("/rofs", 0755) < 0)
+        kprintf("failed to create /rofs, error = %s\n", strerror(errno));
+
+    ret = sys_mount("/dev/vblk0.1", "/rofs", "rofs", MNT_RDONLY, 0);
+
+    if (ret) {
+        kprintf("failed to mount /rofs, error = %s\n", strerror(ret));
+        rmdir("/rofs");
+        return ret;
+    }
+
+    if (pivot_root) {
+        pivot_rootfs("/rofs");
+    }
+
+    return 0;
+}
+
+extern "C" void mount_zfs_rootfs(bool pivot_root)
+{
+    if (mkdir("/zfs", 0755) < 0)
+        kprintf("failed to create /zfs, error = %s\n", strerror(errno));
+
+    int ret = sys_mount("/dev/vblk0.1", "/zfs", "zfs", 0, (void *)"osv/zfs");
+
+    if (ret)
+        kprintf("failed to mount /zfs, error = %s\n", strerror(ret));
+
+    if (!pivot_root) {
+        return;
+    }
+
+    pivot_rootfs("/zfs");
+
+    import_extra_zfs_pools();
+}
+
+extern "C" void unmount_rootfs(void)
+{
+    int ret;
+
+    sys_umount("/dev");
+
+    ret = sys_umount("/proc");
+    if (ret) {
+        kprintf("Warning: unmount_rootfs: failed to unmount /proc, "
+            "error = %s\n", strerror(ret));
+    }
+
+    ret = sys_umount2("/", MNT_FORCE);
+    if (ret) {
+        kprintf("Warning: unmount_rootfs: failed to unmount /, "
+            "error = %s\n", strerror(ret));
+    }
+}
+
+extern "C" void bio_init(void);
+extern "C" void bio_sync(void);
+
+int vfs_initialized;
+
+extern "C"
+void
+vfs_init(void)
+{
+    const struct vfssw *fs;
+
+    bio_init();
+    lookup_init();
+    vnode_init();
+    task_alloc(&main_task);
+
+    /*
+     * Initialize each file system.
+     */
+    for (fs = vfssw; fs->vs_name; fs++) {
+        if (fs->vs_init) {
+            DPRINTF(VFSDB_CORE, ("VFS: initializing %s\n",
+                    fs->vs_name));
+            fs->vs_init();
+        }
+    }
+
+    mount_rootfs();
+    unpack_bootfs();
+
+    // if (open("/dev/console", O_RDWR, 0) != 0)
+    if (console::open() != 0)
+        kprintf("failed to open console, error = %d\n", errno);
+    if (dup(0) != 1)
+        kprintf("failed to dup console (1)\n");
+    if (dup(0) != 2)
+        kprintf("failed to dup console (2)\n");
+    vfs_initialized = 1;
+}
+
+void vfs_exit(void)
+{
+    /* Free up main_task (stores cwd data) resources */
+    replace_cwd(main_task, nullptr, []() { return 0; });
+    /* Unmount all file systems */
+    unmount_rootfs();
+    /* Finish with the bio layer */
+    bio_sync();
+}
+
+void sys_panic(const char *str)
+{
+    abort("panic: %s", str);
+}
+
diff --git a/lib/vfscore/mount.c b/lib/vfscore/mount.c
new file mode 100644
index 00000000..dac4d09c
--- /dev/null
+++ b/lib/vfscore/mount.c
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 2005-2007, Kohsuke Ohtani
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * vfs_mount.c - mount operations
+ */
+
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <dirent.h>
+
+#include <limits.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <osv/prex.h>
+#include <osv/vnode.h>
+#include <osv/device.h>
+#include <osv/debug.h>
+#include <osv/mutex.h>
+#include "vfs.h"
+
+#include <memory>
+#include <list>
+
+/*
+ * List for VFS mount points.
+ */
+static std::list<mount*> mount_list;
+
+/*
+ * Global lock to access mount point.
+ */
+static mutex mount_lock;
+
+/*
+ * Lookup file system.
+ */
+static const struct vfssw *
+fs_getfs(const char *name)
+{
+    const struct vfssw *fs;
+
+    for (fs = vfssw; fs->vs_name; fs++) {
+        if (!strncmp(name, fs->vs_name, FSMAXNAMES))
+            break;
+    }
+    if (!fs->vs_name)
+        return nullptr;
+    return fs;
+}
+
+const char*
+fs_getfsname(vfsops* ops)
+{
+    for (auto fs = vfssw; fs->vs_name; fs++) {
+        if (fs->vs_op == ops) {
+            return fs->vs_name;
+        }
+    }
+    abort();
+}
+
+int
+sys_mount(const char *dev, const char *dir, const char *fsname, int flags, 
const void *data)
+{
+    const struct vfssw *fs;
+    struct mount *mp;
+    struct device *device;
+    struct dentry *dp_covered;
+    struct vnode *vp;
+    int error;
+
+    kprintf("VFS: mounting %s at %s\n", fsname, dir);
+
+    if (!dir || *dir == '\0')
+        return ENOENT;
+
+    /* Find a file system. */
+    if (!(fs = fs_getfs(fsname)))
+        return ENODEV;  /* No such file system */
+
+    /* Open device. nullptr can be specified as a device. */
+    // Allow device_open() to fail, in which case dev is interpreted
+    // by the file system mount routine (e.g zfs pools)
+    device = 0;
+    if (dev && strncmp(dev, "/dev/", 5) == 0)
+        device_open(dev + 5, DO_RDWR, &device);
+
+    /* Check if device or directory has already been mounted. */
+    // We need to avoid the situation where after we already verified that
+    // the mount point is free, but before we actually add it to mount_list,
+    // another concurrent mount adds it. So we use a new mutex to ensure
+    // that only one sys_mount() runs at a time. We cannot reuse the existing
+    // mount_lock for this purpose: If we take mount_lock and then do
+    // lookups, this is lock order inversion and can result in deadlock.
+    static mutex sys_mount_lock;
+    SCOPE_LOCK(sys_mount_lock);
+    WITH_LOCK(mount_lock) {
+        for (auto&& mp : mount_list) {
+            if (!strcmp(mp->m_path, dir) ||
+                (device && mp->m_dev == device)) {
+                error = EBUSY;  /* Already mounted */
+                goto err1;
+            }
+        }
+    }
+    /*
+     * Create VFS mount entry.
+     */
+    if (!(mp = new mount)) {
+        error = ENOMEM;
+        goto err1;
+    }
+    mp->m_count = 0;
+    mp->m_op = fs->vs_op;
+    mp->m_flags = flags;
+    mp->m_dev = device;
+    mp->m_data = nullptr;
+    strlcpy(mp->m_path, dir, sizeof(mp->m_path));
+    strlcpy(mp->m_special, dev, sizeof(mp->m_special));
+
+    /*
+     * Get vnode to be covered in the upper file system.
+     */
+    if (*dir == '/' && *(dir + 1) == '\0') {
+        /* Ignore if it mounts to global root directory. */
+        dp_covered = nullptr;
+    } else {
+        if ((error = namei(dir, &dp_covered)) != 0) {
+
+            error = ENOENT;
+            goto err2;
+        }
+        if (dp_covered->d_vnode->v_type != VDIR) {
+            error = ENOTDIR;
+            goto err3;
+        }
+    }
+    mp->m_covered = dp_covered;
+
+    /*
+     * Create a root vnode for this file system.
+     */
+    vget(mp, 0, &vp);
+    if (vp == nullptr) {
+        error = ENOMEM;
+        goto err3;
+    }
+    vp->v_type = VDIR;
+    vp->v_flags = VROOT;
+    vp->v_mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR;
+
+    mp->m_root = dentry_alloc(nullptr, vp, "/");
+    if (!mp->m_root) {
+        vput(vp);
+        goto err3;
+    }
+    vput(vp);
+
+    /*
+     * Call a file system specific routine.
+     */
+    if ((error = VFS_MOUNT(mp, dev, flags, data)) != 0)
+        goto err4;
+
+    if (mp->m_flags & MNT_RDONLY)
+        vp->v_mode &=~S_IWUSR;
+
+    /*
+     * Insert to mount list
+     */
+    WITH_LOCK(mount_lock) {
+        mount_list.push_back(mp);
+    }
+
+    return 0;   /* success */
+ err4:
+    drele(mp->m_root);
+ err3:
+    if (dp_covered)
+        drele(dp_covered);
+ err2:
+    delete mp;
+ err1:
+    if (device)
+        device_close(device);
+
+    return error;
+}
+
+void
+release_mp_dentries(struct mount *mp)
+{
+    /* Decrement referece count of root vnode */
+    if (mp->m_covered) {
+        drele(mp->m_covered);
+    }
+
+    /* Release root dentry */
+    drele(mp->m_root);
+}
+
+int
+sys_umount2(const char *path, int flags)
+{
+    struct mount *mp;
+    int error, pathlen;
+
+    kprintf("VFS: unmounting %s\n", path);
+
+    SCOPE_LOCK(mount_lock);
+
+    pathlen = strlen(path);
+    if (pathlen >= MAXPATHLEN) {
+        error = ENAMETOOLONG;
+        goto out;
+    }
+
+    /* Get mount entry */
+    for (auto&& tmp : mount_list) {
+        if (!strcmp(path, tmp->m_path)) {
+            mp = tmp;
+            goto found;
+        }
+    }
+
+    error = EINVAL;
+    goto out;
+
+found:
+    /*
+     * Root fs can not be unmounted.
+     */
+    if (mp->m_covered == nullptr && !(flags & MNT_FORCE)) {
+        error = EINVAL;
+        goto out;
+    }
+
+    if ((error = VFS_UNMOUNT(mp, flags)) != 0)
+        goto out;
+    mount_list.remove(mp);
+
+#ifdef HAVE_BUFFERS
+    /* Flush all buffers */
+    binval(mp->m_dev);
+#endif
+
+    if (mp->m_dev)
+        device_close(mp->m_dev);
+    delete mp;
+ out:
+    return error;
+}
+
+int
+sys_umount(const char *path)
+{
+    return sys_umount2(path, 0);
+}
+
+int
+sys_pivot_root(const char *new_root, const char *put_old)
+{
+    struct mount *newmp = nullptr, *oldmp = nullptr;
+    int error;
+
+    WITH_LOCK(mount_lock) {
+        for (auto&& mp : mount_list) {
+            if (!strcmp(mp->m_path, new_root)) {
+                newmp = mp;
+            }
+            if (!strcmp(mp->m_path, put_old)) {
+                oldmp = mp;
+            }
+        }
+        if (!newmp || !oldmp || newmp == oldmp) {
+            return EINVAL;
+        }
+        for (auto&& mp : mount_list) {
+            if (mp == newmp || mp == oldmp) {
+                continue;
+            }
+            if (!strncmp(mp->m_path, put_old, strlen(put_old))) {
+                return EBUSY;
+            }
+        }
+        if ((error = VFS_UNMOUNT(oldmp, 0)) != 0) {
+            return error;
+        }
+        mount_list.remove(oldmp);
+
+        newmp->m_root->d_vnode->v_mount = newmp;
+
+        if (newmp->m_covered) {
+            drele(newmp->m_covered);
+        }
+        newmp->m_covered = nullptr;
+
+        if (newmp->m_root->d_parent) {
+            drele(newmp->m_root->d_parent);
+        }
+        newmp->m_root->d_parent = nullptr;
+
+        strlcpy(newmp->m_path, "/", sizeof(newmp->m_path));
+    }
+    return 0;
+}
+
+int
+sys_sync(void)
+{
+    /* Call each mounted file system. */
+    WITH_LOCK(mount_lock) {
+        for (auto&& mp : mount_list) {
+            VFS_SYNC(mp);
+        }
+    }
+#ifdef HAVE_BUFFERS
+    bio_sync();
+#endif
+    return 0;
+}
+
+/*
+ * Compare two path strings. Return matched length.
+ * @path: target path.
+ * @root: vfs root path as mount point.
+ */
+static size_t
+count_match(const char *path, char *mount_root)
+{
+    size_t len = 0;
+
+    while (*path && *mount_root) {
+        if (*path != *mount_root)
+            break;
+
+        path++;
+        mount_root++;
+        len++;
+    }
+    if (*mount_root != '\0')
+        return 0;
+
+    if (len == 1 && *(path - 1) == '/')
+        return 1;
+
+    if (*path == '\0' || *path == '/')
+        return len;
+    return 0;
+}
+
+/*
+ * Get the root directory and mount point for specified path.
+ * @path: full path.
+ * @mp: mount point to return.
+ * @root: pointer to root directory in path.
+ */
+int
+vfs_findroot(const char *path, struct mount **mp, char **root)
+{
+    struct mount *m = nullptr;
+    size_t len, max_len = 0;
+
+    if (!path)
+        return -1;
+
+    /* Find mount point from nearest path */
+    SCOPE_LOCK(mount_lock);
+    for (auto&& tmp : mount_list) {
+        len = count_match(path, tmp->m_path);
+        if (len > max_len) {
+            max_len = len;
+            m = tmp;
+        }
+    }
+    if (m == nullptr)
+        return -1;
+    *root = (char *)(path + max_len);
+    if (**root == '/')
+        (*root)++;
+    *mp = m;
+    return 0;
+}
+
+/*
+ * Mark a mount point as busy.
+ */
+void
+vfs_busy(struct mount *mp)
+{
+    SCOPE_LOCK(mount_lock);
+    mp->m_count++;
+}
+
+
+/*
+ * Mark a mount point as busy.
+ */
+void
+vfs_unbusy(struct mount *mp)
+{
+    SCOPE_LOCK(mount_lock);
+    mp->m_count--;
+}
+
+int
+vfs_nullop(void)
+{
+    return 0;
+}
+
+int
+vfs_einval(void)
+{
+    return EINVAL;
+}
+
+namespace osv {
+
+mount_desc to_mount_desc(mount* m)
+{
+    mount_desc ret;
+    ret.special = m->m_special;
+    ret.path = m->m_path;
+    ret.type = fs_getfsname(m->m_op);
+    // FIXME: record options
+    ret.options = "";
+    return ret;
+}
+
+std::vector<mount_desc>
+current_mounts()
+{
+    WITH_LOCK(mount_lock) {
+        std::vector<mount_desc> ret;
+        for (auto&& mp : mount_list) {
+            ret.push_back(to_mount_desc(mp));
+        }
+        return ret;
+    }
+}
+
+}
+
+#ifdef DEBUG_VFS
+void
+mount_dump(void)
+{
+    SCOPE_LOCK(mount_lock);
+
+    kprintf("mount_dump\n");
+    kprintf("dev      count root\n");
+    kprintf("-------- ----- --------\n");
+
+    for (auto&& mp : mount_list) {
+        kprintf("%8x %5d %s\n", mp->m_dev, mp->m_count, mp->m_path);
+    }
+}
+#endif
diff --git a/lib/vfscore/subr_uio.c b/lib/vfscore/subr_uio.c
new file mode 100644
index 00000000..bf138b8e
--- /dev/null
+++ b/lib/vfscore/subr_uio.c
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 1982, 1986, 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)kern_subr.c 8.3 (Berkeley) 1/21/94
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <osv/uio.h>
+
+int
+uiomove(void *cp, int n, struct uio *uio)
+{
+       assert(uio->uio_rw == UIO_READ || uio->uio_rw == UIO_WRITE);
+
+       while (n > 0 && uio->uio_resid) {
+               struct iovec *iov = uio->uio_iov;
+               int cnt = iov->iov_len;
+               if (cnt == 0) {
+                       uio->uio_iov++;
+                       uio->uio_iovcnt--;
+                       continue;
+               }
+               if (cnt > n)
+                       cnt = n;
+
+               if (uio->uio_rw == UIO_READ)
+                       memcpy(iov->iov_base, cp, cnt);
+               else
+                       memcpy(cp, iov->iov_base, cnt);
+
+               iov->iov_base = (char *)iov->iov_base + cnt;
+               iov->iov_len -= cnt;
+               uio->uio_resid -= cnt;
+               uio->uio_offset += cnt;
+               cp = (char *)cp + cnt;
+               n -= cnt;
+       }
+
+       return 0;
+}
diff --git a/lib/vfscore/syscalls.c b/lib/vfscore/syscalls.c
new file mode 100644
index 00000000..487d5729
--- /dev/null
+++ b/lib/vfscore/syscalls.c
@@ -0,0 +1,1486 @@
+/*
+ * Copyright (C) 2013 Cloudius Systems, Ltd.
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+/*
+ * Copyright (c) 2005-2007, Kohsuke Ohtani
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * vfs_syscalls.c - everything in this file is a routine implementing
+ *                  a VFS system call.
+ */
+
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include <limits.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <osv/prex.h>
+#include <osv/vnode.h>
+#include <osv/vfs_file.hh>
+#include "vfs.h"
+#include <fs/fs.hh>
+
+extern struct task *main_task;
+
+static int
+open_no_follow_chk(char *path)
+{
+       int           error;
+       struct dentry *ddp;
+       char          *name;
+       struct dentry *dp;
+       struct vnode  *vp;
+
+       ddp = nullptr;
+       dp  = nullptr;
+       vp  = nullptr;
+
+       error = lookup(path, &ddp, &name);
+       if (error) {
+               return (error);
+       }
+
+       error = namei_last_nofollow(path, ddp, &dp);
+       if (error) {
+               goto out;
+       }
+
+       vp = dp->d_vnode;
+       vn_lock(vp);
+       if (vp->v_type == VLNK) {
+               error = ELOOP;
+               goto out;
+       }
+
+       error = 0;
+out:
+       if (vp != nullptr) {
+               vn_unlock(vp);
+       }
+
+       if (dp != nullptr) {
+               drele(dp);
+       }
+
+       if (ddp != nullptr) {
+               drele(ddp);
+       }
+
+       return (error);
+}
+
+int
+sys_open(char *path, int flags, mode_t mode, struct file **fpp)
+{
+       file *fp;
+       struct dentry *dp, *ddp;
+       struct vnode *vp;
+       char *filename;
+       int error;
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_open: path=%s flags=%x mode=%x\n",
+                               path, flags, mode));
+
+       flags = fflags(flags);
+       if (flags & O_CREAT) {
+               error = namei(path, &dp);
+               if (error == ENOENT) {
+                       /* Create new file. */
+                       if ((error = lookup(path, &ddp, &filename)) != 0)
+                               return error;
+
+                       vn_lock(ddp->d_vnode);
+                       if ((error = vn_access(ddp->d_vnode, VWRITE)) != 0) {
+                               vn_unlock(ddp->d_vnode);
+                               drele(ddp);
+                               return error;
+                       }
+                       mode &= ~S_IFMT;
+                       mode |= S_IFREG;
+                       error = VOP_CREATE(ddp->d_vnode, filename, mode);
+                       vn_unlock(ddp->d_vnode);
+                       drele(ddp);
+
+                       if (error)
+                               return error;
+                       if ((error = namei(path, &dp)) != 0)
+                               return error;
+
+                       vp = dp->d_vnode;
+                       flags &= ~O_TRUNC;
+               } else if (error) {
+                       return error;
+               } else {
+                       /* File already exits */
+                       if (flags & O_EXCL) {
+                               error = EEXIST;
+                               goto out_drele;
+                       }
+               }
+
+               vp = dp->d_vnode;
+               flags &= ~O_CREAT;
+       } else {
+               /* Open */
+               if (flags & O_NOFOLLOW) {
+                       error = open_no_follow_chk(path);
+                       if (error != 0) {
+                               return (error);
+                       }
+               }
+               error = namei(path, &dp);
+               if (error)
+                       return error;
+
+               vp = dp->d_vnode;
+
+               if (flags & FWRITE || flags & O_TRUNC) {
+                       error = vn_access(vp, VWRITE);
+                       if (error)
+                               goto out_drele;
+
+                       error = EISDIR;
+                       if (vp->v_type == VDIR)
+                               goto out_drele;
+               }
+               if (flags & O_DIRECTORY) {
+                   if (vp->v_type != VDIR) {
+                       error = ENOTDIR;
+                       goto out_drele;
+                   }
+               }
+       }
+
+       vn_lock(vp);
+       /* Process truncate request */
+       if (flags & O_TRUNC) {
+               error = EINVAL;
+               if (!(flags & FWRITE) || vp->v_type == VDIR)
+                       goto out_vn_unlock;
+
+               error = VOP_TRUNCATE(vp, 0);
+               if (error)
+                       goto out_vn_unlock;
+       }
+
+       try {
+           fileref f = make_file<vfs_file>(flags);
+           fp = f.get();
+           fhold(fp);
+       } catch (int err) {
+           error = err;
+           goto out_vn_unlock;
+       }
+       // change to std::move once dp is a dentry_ref
+       fp->f_dentry = dentry_ref(dp, false);
+       dp = nullptr;
+
+       error = VOP_OPEN(vp, fp);
+       if (error) {
+               vn_unlock(vp);
+               // Note direct delete of fp instead of fdrop(fp). fp was never
+               // returned so cannot be in use, and because it wasn't opened
+               // it cannot be close()ed.
+               delete fp;
+               return error;
+       }
+       vn_unlock(vp);
+
+       *fpp = fp;
+       return 0;
+
+out_vn_unlock:
+       vn_unlock(vp);
+out_drele:
+       if (dp) {
+               drele(dp);
+       }
+       return error;
+}
+
+int
+sys_close(struct file *fp)
+{
+
+       return 0;
+}
+
+int
+sys_read(struct file *fp, const struct iovec *iov, size_t niov,
+               off_t offset, size_t *count)
+{
+    if ((fp->f_flags & FREAD) == 0)
+        return EBADF;
+
+    size_t bytes = 0;
+    auto iovp = iov;
+    for (unsigned i = 0; i < niov; i++) {
+        if (iovp->iov_len > IOSIZE_MAX - bytes) {
+            return EINVAL;
+        }
+        bytes += iovp->iov_len;
+        iovp++;
+    }
+
+    if (bytes == 0) {
+        *count = 0;
+        return 0;
+    }
+
+    struct uio uio;
+    // Unfortunately, the current implementation of fp->read zeros the
+    // iov_len fields when it reads from disk, so we have to copy iov.
+    std::vector<iovec> copy_iov(iov, iov + niov);
+    uio.uio_iov = copy_iov.data();
+    uio.uio_iovcnt = niov;
+    uio.uio_offset = offset;
+    uio.uio_resid = bytes;
+    uio.uio_rw = UIO_READ;
+    auto error = fp->read(&uio, (offset == -1) ? 0 : FOF_OFFSET);
+    *count = bytes - uio.uio_resid;
+    return error;
+}
+
+int
+sys_write(struct file *fp, const struct iovec *iov, size_t niov,
+               off_t offset, size_t *count)
+{
+    if ((fp->f_flags & FWRITE) == 0)
+        return EBADF;
+
+    size_t bytes = 0;
+    auto iovp = iov;
+    for (unsigned i = 0; i < niov; i++) {
+        if (iovp->iov_len > IOSIZE_MAX - bytes) {
+            return EINVAL;
+        }
+        bytes += iovp->iov_len;
+        iovp++;
+    }
+
+    if (bytes == 0) {
+        *count = 0;
+        return 0;
+    }
+
+    struct uio uio;
+    // Unfortunately, the current implementation of fp->write zeros the
+    // iov_len fields when it writes to disk, so we have to copy iov.
+    std::vector<iovec> copy_iov(iov, iov + niov);
+    uio.uio_iov = copy_iov.data();
+    uio.uio_iovcnt = niov;
+    uio.uio_offset = offset;
+    uio.uio_resid = bytes;
+    uio.uio_rw = UIO_WRITE;
+    auto error = fp->write(&uio, (offset == -1) ? 0 : FOF_OFFSET);
+    *count = bytes - uio.uio_resid;
+    return error;
+}
+
+int
+sys_lseek(struct file *fp, off_t off, int type, off_t *origin)
+{
+       struct vnode *vp;
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_seek: fp=%x off=%d type=%d\n",
+                               (u_long)fp, (u_int)off, type));
+
+       if (!fp->f_dentry) {
+           // Linux doesn't implement lseek() on pipes, sockets, or ttys.
+           // In OSV, we only implement lseek() on regular files, backed by 
vnode
+           return ESPIPE;
+       }
+
+       vp = fp->f_dentry->d_vnode;
+       int error = EINVAL;
+       vn_lock(vp);
+       switch (type) {
+       case SEEK_CUR:
+               off = fp->f_offset + off;
+               break;
+       case SEEK_END:
+               off = vp->v_size + off;
+               break;
+       }
+       if (off >= 0) {
+               error = VOP_SEEK(vp, fp, fp->f_offset, off);
+               if (!error) {
+                       *origin      = off;
+                       fp->f_offset = off;
+               }
+       }
+       vn_unlock(vp);
+       return error;
+}
+
+int
+sys_ioctl(struct file *fp, u_long request, void *buf)
+{
+       int error;
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_ioctl: fp=%x request=%x\n", fp, request));
+
+       if ((fp->f_flags & (FREAD | FWRITE)) == 0)
+               return EBADF;
+
+       error = fp->ioctl(request, buf);
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_ioctl: comp error=%d\n", error));
+       return error;
+}
+
+int
+sys_fsync(struct file *fp)
+{
+       struct vnode *vp;
+       int error;
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_fsync: fp=%x\n", fp));
+
+       if (!fp->f_dentry)
+               return EINVAL;
+
+       vp = fp->f_dentry->d_vnode;
+       vn_lock(vp);
+       error = VOP_FSYNC(vp, fp);
+       vn_unlock(vp);
+       return error;
+}
+
+int
+sys_fstat(struct file *fp, struct stat *st)
+{
+       int error = 0;
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_fstat: fp=%x\n", fp));
+
+       error = fp->stat(st);
+
+       return error;
+}
+
+/*
+ * Return 0 if directory is empty
+ */
+static int
+check_dir_empty(char *path)
+{
+       int error;
+       struct file *fp;
+       struct dirent dir;
+
+       DPRINTF(VFSDB_SYSCALL, ("check_dir_empty\n"));
+
+       error = sys_open(path, O_RDONLY, 0, &fp);
+       if (error)
+               goto out_error;
+
+       do {
+               error = sys_readdir(fp, &dir);
+               if (error != 0 && error != EACCES)
+                       break;
+       } while (!strcmp(dir.d_name, ".") || !strcmp(dir.d_name, ".."));
+
+       if (error == ENOENT)
+               error = 0;
+       else if (error == 0) {
+           // Posix specifies to return EEXIST in this case (rmdir of non-empty
+           // directory, but Linux actually returns ENOTEMPTY).
+               error = ENOTEMPTY;
+       }
+       fdrop(fp);
+out_error:
+       return error;
+}
+
+int
+sys_readdir(struct file *fp, struct dirent *dir)
+{
+       struct vnode *dvp;
+       int error;
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_readdir: fp=%x\n", fp));
+
+       if (!fp->f_dentry)
+               return ENOTDIR;
+
+       dvp = fp->f_dentry->d_vnode;
+       vn_lock(dvp);
+       if (dvp->v_type != VDIR) {
+               vn_unlock(dvp);
+               return ENOTDIR;
+       }
+       error = VOP_READDIR(dvp, fp, dir);
+       DPRINTF(VFSDB_SYSCALL, ("sys_readdir: error=%d path=%s\n",
+                               error, dir->d_name));
+       vn_unlock(dvp);
+       return error;
+}
+
+int
+sys_rewinddir(struct file *fp)
+{
+       struct vnode *dvp;
+
+       if (!fp->f_dentry)
+               return ENOTDIR;
+
+       dvp = fp->f_dentry->d_vnode;
+       vn_lock(dvp);
+       if (dvp->v_type != VDIR) {
+               vn_unlock(dvp);
+               return EBADF;
+       }
+       fp->f_offset = 0;
+       vn_unlock(dvp);
+       return 0;
+}
+
+int
+sys_seekdir(struct file *fp, long loc)
+{
+       struct vnode *dvp;
+
+       if (!fp->f_dentry)
+               return ENOTDIR;
+
+       dvp = fp->f_dentry->d_vnode;
+       vn_lock(dvp);
+       if (dvp->v_type != VDIR) {
+               vn_unlock(dvp);
+               return EBADF;
+       }
+       fp->f_offset = (off_t)loc;
+       vn_unlock(dvp);
+       return 0;
+}
+
+int
+sys_telldir(struct file *fp, long *loc)
+{
+       struct vnode *dvp;
+
+       if (!fp->f_dentry)
+               return ENOTDIR;
+
+       dvp = fp->f_dentry->d_vnode;
+       vn_lock(dvp);
+       if (dvp->v_type != VDIR) {
+               vn_unlock(dvp);
+               return EBADF;
+       }
+       *loc = (long)fp->f_offset;
+       vn_unlock(dvp);
+       return 0;
+}
+
+int
+sys_mkdir(char *path, mode_t mode)
+{
+       char *name;
+       struct dentry *dp, *ddp;
+       int error;
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_mkdir: path=%s mode=%d\n", path, mode));
+
+       error = namei(path, &dp);
+       if (!error) {
+               /* File already exists */
+               drele(dp);
+               return EEXIST;
+       }
+
+       if ((error = lookup(path, &ddp, &name)) != 0) {
+               /* Directory already exists */
+               return error;
+       }
+
+       vn_lock(ddp->d_vnode);
+       if ((error = vn_access(ddp->d_vnode, VWRITE)) != 0)
+               goto out;
+       mode &= ~S_IFMT;
+       mode |= S_IFDIR;
+
+       error = VOP_MKDIR(ddp->d_vnode, name, mode);
+ out:
+       vn_unlock(ddp->d_vnode);
+       drele(ddp);
+       return error;
+}
+
+int
+sys_rmdir(char *path)
+{
+       struct dentry *dp, *ddp;
+       struct vnode *vp;
+       int error;
+       char *name;
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_rmdir: path=%s\n", path));
+
+       if ((error = check_dir_empty(path)) != 0)
+               return error;
+       error = namei(path, &dp);
+       if (error)
+               return error;
+
+       vp = dp->d_vnode;
+       vn_lock(vp);
+       if ((error = vn_access(vp, VWRITE)) != 0)
+               goto out;
+       if (vp->v_type != VDIR) {
+               error = ENOTDIR;
+               goto out;
+       }
+       if (vp->v_flags & VROOT || vp->v_refcnt >= 2) {
+               error = EBUSY;
+               goto out;
+       }
+       if ((error = lookup(path, &ddp, &name)) != 0)
+               goto out;
+
+       vn_lock(ddp->d_vnode);
+       error = VOP_RMDIR(ddp->d_vnode, vp, name);
+       vn_unlock(ddp->d_vnode);
+
+       vn_unlock(vp);
+       dentry_remove(dp);
+       drele(ddp);
+       drele(dp);
+       return error;
+
+ out:
+       vn_unlock(vp);
+       drele(dp);
+       return error;
+}
+
+int
+sys_mknod(char *path, mode_t mode)
+{
+       char *name;
+       struct dentry *dp, *ddp;
+       int error;
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_mknod: path=%s mode=%d\n", path, mode));
+
+       switch (mode & S_IFMT) {
+       case S_IFREG:
+       case S_IFDIR:
+       case S_IFIFO:
+       case S_IFSOCK:
+               /* OK */
+               break;
+       default:
+               return EINVAL;
+       }
+
+       error = namei(path, &dp);
+       if (!error) {
+               drele(dp);
+               return EEXIST;
+       }
+
+       if ((error = lookup(path, &ddp, &name)) != 0)
+               return error;
+
+       vn_lock(ddp->d_vnode);
+       if ((error = vn_access(ddp->d_vnode, VWRITE)) != 0)
+               goto out;
+       if (S_ISDIR(mode))
+               error = VOP_MKDIR(ddp->d_vnode, name, mode);
+       else
+               error = VOP_CREATE(ddp->d_vnode, name, mode);
+ out:
+       vn_unlock(ddp->d_vnode);
+       drele(ddp);
+       return error;
+}
+
+/*
+ * Returns true when @parent path could represent parent directory
+ * of a file or directory represented by @child path.
+ *
+ * Assumes both paths do not have trailing slashes.
+ */
+static bool
+is_parent(const char *parent, const char *child)
+{
+       size_t p_len = strlen(parent);
+       return !strncmp(parent, child, p_len) && (parent[p_len-1] == '/' || 
child[p_len] == '/');
+}
+
+static bool
+has_trailing(const char *path, char ch)
+{
+       size_t len = strlen(path);
+       return len && path[len - 1] == ch;
+}
+
+static void
+strip_trailing(char *path, char ch)
+{
+       size_t len = strlen(path);
+
+       while (len && path[len - 1] == ch)
+               len--;
+
+       path[len] = '\0';
+}
+
+int
+sys_rename(char *src, char *dest)
+{
+       struct dentry *dp1, *dp2 = 0, *ddp1, *ddp2;
+       struct vnode *vp1, *vp2 = 0, *dvp1, *dvp2;
+       char *sname, *dname;
+       int error;
+       char root[] = "/";
+       bool ts; /* trailing slash */
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_rename: src=%s dest=%s\n", src, dest));
+
+       ts = false;
+       if (has_trailing(src, '/') == true) {
+               if (strlen(src) != 1) {
+                       /* remove trailing slash iff path is none root */
+                       strip_trailing(src, '/');
+                       ts = true;
+               }
+       }
+
+       error = lookup(src, &ddp1, &sname);
+       if (error != 0) {
+               return (error);
+       }
+
+       error = namei_last_nofollow(src, ddp1, &dp1);
+       if (error != 0) {
+               drele(ddp1);
+               return (error);
+       }
+
+       vp1 = dp1->d_vnode;
+       vn_lock(vp1);
+
+       if (vp1->v_type != VDIR && ts == true) {
+               error = ENOTDIR;
+               goto err1;
+       }
+
+       ts = false;
+       if (has_trailing(dest, '/') == true) {
+               if (strlen(dest) != 1) {
+                       /* remove trailing slash iff path is none root */
+                       strip_trailing(dest, '/');
+                       ts = true;
+               }
+       }
+
+       error = lookup(dest, &ddp2, &dname);
+       if (error != 0) {
+               goto err1;
+       }
+
+       error = namei_last_nofollow(dest, ddp2, &dp2);
+       if (error == 0) {
+               /* target exists */
+
+               vp2 = dp2->d_vnode;
+               vn_lock(vp2);
+
+               if (vp2->v_type != VDIR && vp2->v_type != VLNK) {
+                       if (vp1->v_type == VDIR || ts == true) {
+                               error = ENOTDIR;
+                               goto err2;
+                       }
+               } else if (vp1->v_type != VDIR && vp2->v_type == VDIR) {
+                       error = EISDIR;
+                       goto err2;
+               }
+               if (vp2->v_type == VDIR && check_dir_empty(dest)) {
+                       error = EEXIST;
+                       goto err2;
+               }
+       } else if (error == ENOENT) {
+               if (vp1->v_type != VDIR && ts == true) {
+                       error = ENOTDIR;
+                       goto err2;
+               }
+       } else {
+               goto err2;
+       }
+
+       if (strcmp(dest, "/"))
+               strip_trailing(dest, '/');
+
+       if (strcmp(src, "/"))
+               strip_trailing(src, '/');
+
+       /* If source and dest are the same, do nothing */
+       if (!strncmp(src, dest, PATH_MAX))
+               goto err2;
+
+       /* Check if target is directory of source */
+       if (is_parent(src, dest)) {
+               error = EINVAL;
+               goto err2;
+       }
+
+       dname = strrchr(dest, '/');
+       if (dname == nullptr) {
+               error = ENOTDIR;
+               goto err2;
+       }
+       if (dname == dest)
+               dest = root;
+
+       *dname = 0;
+       dname++;
+
+       dvp1 = ddp1->d_vnode;
+       vn_lock(dvp1);
+
+       dvp2 = ddp2->d_vnode;
+       vn_lock(dvp2);
+
+       /* Source and destination directions should be writable) */
+       if ((error = vn_access(dvp1, VWRITE)) != 0)
+           goto err3;
+       if ((error = vn_access(dvp2, VWRITE)) != 0)
+           goto err3;
+
+       /* The source and dest must be same file system */
+       if (dvp1->v_mount != dvp2->v_mount) {
+               error = EXDEV;
+               goto err3;
+       }
+
+       error = VOP_RENAME(dvp1, vp1, sname, dvp2, vp2, dname);
+
+       dentry_move(dp1, ddp2, dname);
+       if (dp2)
+               dentry_remove(dp2);
+
+ err3:
+       vn_unlock(dvp2);
+       vn_unlock(dvp1);
+ err2:
+       if (vp2) {
+               vn_unlock(vp2);
+               drele(dp2);
+       }
+       drele(ddp2);
+ err1:
+       vn_unlock(vp1);
+       drele(dp1);
+       drele(ddp1);
+       return error;
+}
+
+int
+sys_symlink(const char *oldpath, const char *newpath)
+{
+       struct task     *t = main_task;
+       int             error;
+       std::unique_ptr<char []> up_op (new char[PATH_MAX]);
+       char            *op = up_op.get();
+       std::unique_ptr<char []> up_np (new char[PATH_MAX]);
+       char            *np = up_np.get();
+       struct dentry   *newdp;
+       struct dentry   *newdirdp;
+       char            *name;
+
+       if (oldpath == nullptr || newpath == nullptr) {
+               return (EFAULT);
+       }
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_link: oldpath=%s newpath=%s\n",
+                               oldpath, newpath));
+
+       newdp           = nullptr;
+       newdirdp        = nullptr;
+
+       error = task_conv(t, newpath, VWRITE, np);
+       if (error != 0) {
+               return (error);
+       }
+
+       /* parent directory for new path must exist */
+       if ((error = lookup(np, &newdirdp, &name)) != 0) {
+               error = ENOENT;
+               goto out;
+       }
+       vn_lock(newdirdp->d_vnode);
+
+       /* newpath should not already exist */
+       if (namei_last_nofollow(np, newdirdp, &newdp) == 0) {
+               drele(newdp);
+               error = EEXIST;
+               goto out;
+       }
+
+       /* check for write access at newpath */
+       if ((error = vn_access(newdirdp->d_vnode, VWRITE)) != 0) {
+               goto out;
+       }
+
+       /* oldpath may not be const char * to VOP_SYMLINK - need to copy */
+       size_t tocopy;
+       tocopy = strlcpy(op, oldpath, PATH_MAX);
+       if (tocopy >= PATH_MAX - 1) {
+               error = ENAMETOOLONG;
+               goto out;
+       }
+       error = VOP_SYMLINK(newdirdp->d_vnode, name, op);
+
+out:
+       if (newdirdp != nullptr) {
+               vn_unlock(newdirdp->d_vnode);
+               drele(newdirdp);
+       }
+
+       return (error);
+}
+
+int
+sys_link(char *oldpath, char *newpath)
+{
+       struct dentry *olddp, *newdp, *newdirdp;
+       struct vnode *vp;
+       char *name;
+       int error;
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_link: oldpath=%s newpath=%s\n",
+                               oldpath, newpath));
+
+       /* File from oldpath must exist */
+       if ((error = namei(oldpath, &olddp)) != 0)
+               return error;
+
+       vp = olddp->d_vnode;
+       vn_lock(vp);
+
+       if (vp->v_type == VDIR) {
+               error = EPERM;
+               goto out;
+       }
+
+       /* If newpath exists, it shouldn't be overwritten */
+       if (!namei(newpath, &newdp)) {
+               error = EEXIST;
+               goto out;
+       }
+
+       /* Get pointer to the parent dentry of newpath */
+       if ((error = lookup(newpath, &newdirdp, &name)) != 0)
+               goto out;
+
+       vn_lock(newdirdp->d_vnode);
+
+       /* Both files must reside on the same mounted file system */
+       if (olddp->d_mount != newdirdp->d_mount) {
+               error = EXDEV;
+               goto out1;
+       }
+
+       /* Write access to the dir containing newpath is required */
+       if ((error = vn_access(newdirdp->d_vnode, VWRITE)) != 0)
+               goto out1;
+
+       /* Map newpath into dentry hash with the same vnode as oldpath */
+       if (!(newdp = dentry_alloc(newdirdp, vp, newpath))) {
+               error = ENOMEM;
+               goto out1;
+       }
+
+       error = VOP_LINK(newdirdp->d_vnode, vp, name);
+ out1:
+       vn_unlock(newdirdp->d_vnode);
+       drele(newdirdp);
+ out:
+       vn_unlock(vp);
+       drele(olddp);
+       drele(newdp);
+       return error;
+}
+
+int
+sys_unlink(char *path)
+{
+       char *name;
+       struct dentry *dp, *ddp;
+       struct vnode *vp;
+       int error;
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_unlink: path=%s\n", path));
+
+       ddp   = nullptr;
+       dp    = nullptr;
+       vp    = nullptr;
+
+       error = lookup(path, &ddp, &name);
+       if (error != 0) {
+               return (error);
+       }
+
+       error = namei_last_nofollow(path, ddp, &dp);
+       if (error != 0) {
+               goto out;
+       }
+
+       vp = dp->d_vnode;
+       vn_lock(vp);
+       if (vp->v_type == VDIR) {
+           // Posix specifies that we should return EPERM here, but Linux
+           // actually returns EISDIR.
+               error = EISDIR;
+               goto out;
+       }
+       if (vp->v_flags & VROOT) {
+               error = EBUSY;
+               goto out;
+       }
+
+       vn_lock(ddp->d_vnode);
+       if ((error = vn_access(ddp->d_vnode, VWRITE)) != 0) {
+           vn_unlock(ddp->d_vnode);
+           goto out;
+       }
+       error = VOP_REMOVE(ddp->d_vnode, vp, name);
+       vn_unlock(ddp->d_vnode);
+
+       vn_unlock(vp);
+       dentry_remove(dp);
+       drele(ddp);
+       drele(dp);
+       return error;
+ out:
+       if (vp != nullptr) {
+               vn_unlock(vp);
+       }
+
+       if (dp != nullptr) {
+               drele(dp);
+       }
+
+       if (ddp != nullptr) {
+               drele(ddp);
+       }
+       return error;
+}
+
+int
+sys_access(char *path, int mode)
+{
+       struct dentry *dp;
+       int error, flags;
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_access: path=%s mode=%x\n", path, mode));
+
+       /* If F_OK is set, we return here if file is not found. */
+       error = namei(path, &dp);
+       if (error)
+               return error;
+
+       flags = 0;
+       if (mode & R_OK)
+               flags |= VREAD;
+       if (mode & W_OK)
+               flags |= VWRITE;
+       if (mode & X_OK)
+               flags |= VEXEC;
+
+       error = vn_access(dp->d_vnode, flags);
+
+       drele(dp);
+       return error;
+}
+
+int
+sys_stat(char *path, struct stat *st)
+{
+       DPRINTF(VFSDB_SYSCALL, ("sys_stat: path=%s\n", path));
+
+       try {
+               dentry_ref dp = namei(path);
+               if (!dp) {
+                       return ENOENT;
+               }
+               return vn_stat(dp->d_vnode, st);
+       } catch (error e) {
+               return e.get();
+       }
+}
+
+int sys_lstat(char *path, struct stat *st)
+{
+       int           error;
+       struct dentry *ddp;
+       char          *name;
+       struct dentry *dp;
+
+       DPRINTF(VFSDB_SYSCALL, ("sys_lstat: path=%s\n", path));
+
+       error = lookup(path, &ddp, &name);
+       if (error) {
+               return (error);
+       }
+
+       error = namei_last_nofollow(path, ddp, &dp);
+       if (error) {
+               drele(ddp);
+               return error;
+       }
+
+       error = vn_stat(dp->d_vnode, st);
+       drele(dp);
+       drele(ddp);
+       return error;
+}
+
+int
+sys_statfs(char *path, struct statfs *buf)
+{
+       memset(buf, 0, sizeof(*buf));
+       try {
+               dentry_ref dp = namei(path);
+               if (!dp) {
+                       return ENOENT;
+               }
+               return VFS_STATFS(dp->d_mount, buf);
+       } catch (error e) {
+               return e.get();
+       }
+}
+
+int
+sys_fstatfs(struct file *fp, struct statfs *buf)
+{
+       struct vnode *vp;
+       int error = 0;
+
+       if (!fp->f_dentry)
+               return EBADF;
+
+       vp = fp->f_dentry->d_vnode;
+       memset(buf, 0, sizeof(*buf));
+
+       vn_lock(vp);
+       error = VFS_STATFS(vp->v_mount, buf);
+       vn_unlock(vp);
+
+       return error;
+}
+
+int
+sys_truncate(char *path, off_t length)
+{
+       struct dentry *dp;
+       int error;
+
+       error = namei(path, &dp);
+       if (error)
+               return error;
+
+       vn_lock(dp->d_vnode);
+       error = VOP_TRUNCATE(dp->d_vnode, length);
+       vn_unlock(dp->d_vnode);
+
+       drele(dp);
+       return error;
+}
+
+int
+sys_ftruncate(struct file *fp, off_t length)
+{
+       struct vnode *vp;
+       int error;
+
+       if (!fp->f_dentry)
+               return EBADF;
+
+       vp = fp->f_dentry->d_vnode;
+       vn_lock(vp);
+       error = VOP_TRUNCATE(vp, length);
+       vn_unlock(vp);
+
+       return error;
+}
+
+int
+sys_fchdir(struct file *fp, char *cwd)
+{
+       struct vnode *dvp;
+
+       if (!fp->f_dentry)
+               return EBADF;
+
+       dvp = fp->f_dentry->d_vnode;
+       vn_lock(dvp);
+       if (dvp->v_type != VDIR) {
+               vn_unlock(dvp);
+               return EBADF;
+       }
+       strlcpy(cwd, fp->f_dentry->d_path, PATH_MAX);
+       vn_unlock(dvp);
+       return 0;
+}
+
+int
+sys_readlink(char *path, char *buf, size_t bufsize, ssize_t *size)
+{
+       int             error;
+       struct dentry   *ddp;
+       char            *name;
+       struct dentry   *dp;
+       struct vnode    *vp;
+       struct iovec    vec;
+       struct uio      uio;
+
+       *size = 0;
+       error = lookup(path, &ddp, &name);
+       if (error) {
+               return (error);
+       }
+
+       error = namei_last_nofollow(path, ddp, &dp);
+       if (error) {
+               drele(ddp);
+               return (error);
+       }
+
+       if (dp->d_vnode->v_type != VLNK) {
+               drele(dp);
+               drele(ddp);
+               return (EINVAL);
+       }
+       vec.iov_base    = buf;
+       vec.iov_len     = bufsize;
+
+       uio.uio_iov     = &vec;
+       uio.uio_iovcnt  = 1;
+       uio.uio_offset  = 0;
+       uio.uio_resid   = bufsize;
+       uio.uio_rw      = UIO_READ;
+
+       vp = dp->d_vnode;
+       vn_lock(vp);
+       error = VOP_READLINK(vp, &uio);
+       vn_unlock(vp);
+
+       drele(dp);
+       drele(ddp);
+
+       if (error) {
+               return (error);
+       }
+
+       *size = bufsize - uio.uio_resid;
+       return (0);
+}
+
+/*
+ * Check the validity of the members of a struct timeval.
+ */
+static bool is_timeval_valid(const struct timeval *time)
+{
+    return (time->tv_sec >= 0) &&
+           (time->tv_usec >= 0 && time->tv_usec < 1000000);
+}
+
+/*
+ * Convert a timeval struct to a timespec one.
+ */
+static void convert_timeval(struct timespec &to, const struct timeval *from)
+{
+    if (from) {
+        to.tv_sec = from->tv_sec;
+        to.tv_nsec = from->tv_usec * 1000; // Convert microseconds to 
nanoseconds
+    } else {
+        clock_gettime(CLOCK_REALTIME, &to);
+    }
+}
+
+int
+sys_utimes(char *path, const struct timeval times[2], int flags)
+{
+    int error;
+    struct dentry *dp;
+    struct timespec timespec_times[2];
+
+    DPRINTF(VFSDB_SYSCALL, ("sys_utimes: path=%s\n", path));
+
+    if (times && (!is_timeval_valid(&times[0]) || 
!is_timeval_valid(&times[1])))
+        return EINVAL;
+
+    // Convert each element of timeval array to the timespec type
+    convert_timeval(timespec_times[0], times ? times + 0 : nullptr);
+    convert_timeval(timespec_times[1], times ? times + 1 : nullptr);
+
+    if (flags & AT_SYMLINK_NOFOLLOW) {
+        struct dentry *ddp;
+        error = lookup(path, &ddp, nullptr);
+        if (error) {
+            return error;
+        }
+
+        error = namei_last_nofollow(path, ddp, &dp);
+        if (ddp != nullptr) {
+            drele(ddp);
+        }
+        if (error) {
+            return error;
+        }
+    } else {
+        error = namei(path, &dp);
+        if (error)
+            return error;
+    }
+
+    if (dp->d_mount->m_flags & MNT_RDONLY) {
+        error = EROFS;
+    } else {
+        error = vn_settimes(dp->d_vnode, timespec_times);
+    }
+
+    drele(dp);
+    return error;
+}
+
+/*
+ * Check the validity of members of a struct timespec
+ */
+static bool is_timespec_valid(const struct timespec &time)
+{
+    return (time.tv_sec >= 0) &&
+          ((time.tv_nsec >= 0 && time.tv_nsec <= 999999999) ||
+           time.tv_nsec == UTIME_NOW ||
+           time.tv_nsec == UTIME_OMIT);
+}
+
+void init_timespec(struct timespec &_times, const struct timespec *times)
+{
+    if (times == nullptr || times->tv_nsec == UTIME_NOW) {
+        clock_gettime(CLOCK_REALTIME, &_times);
+    } else {
+        _times.tv_sec = times->tv_sec;
+        _times.tv_nsec = times->tv_nsec;
+    }
+    return;
+}
+
+int
+sys_utimensat(int dirfd, const char *pathname, const struct timespec times[2], 
int flags)
+{
+    int error;
+    std::string ap;
+    struct timespec timespec_times[2];
+    extern struct task *main_task;
+    struct dentry *dp;
+
+    /* utimensat should return ENOENT when pathname is empty */
+    if(pathname && pathname[0] == 0)
+        return ENOENT;
+
+    if (flags && !(flags & AT_SYMLINK_NOFOLLOW))
+        return EINVAL;
+
+    if (times && (!is_timespec_valid(times[0]) || 
!is_timespec_valid(times[1])))
+        return EINVAL;
+
+    init_timespec(timespec_times[0], times ? times + 0 : nullptr);
+    init_timespec(timespec_times[1], times ? times + 1 : nullptr);
+
+    if (pathname && pathname[0] == '/') {
+       ap = pathname;
+    } else if (dirfd == AT_FDCWD) {
+       if (!pathname)
+           return EFAULT;
+       ap = std::string(main_task->t_cwd) + "/" + pathname;
+    } else {
+        struct file *fp;
+        fileref f(fileref_from_fd(dirfd));
+
+        if (!f)
+           return EBADF;
+
+       fp = f.get();
+
+       if(!fp->f_dentry)
+           return EBADF;
+
+       if (!(fp->f_dentry->d_vnode->v_type & VDIR))
+           return ENOTDIR;
+
+       if (pathname)
+           ap = std::string(fp->f_dentry->d_path) + "/" + pathname;
+       else
+           ap = fp->f_dentry->d_path;
+
+       ap = std::string(fp->f_dentry->d_mount->m_path) + "/" + ap;
+    }
+
+    /* FIXME: Add support for AT_SYMLINK_NOFOLLOW */
+
+    error = namei(ap.c_str(), &dp);
+
+    if (error)
+        return error;
+
+    if (dp->d_mount->m_flags & MNT_RDONLY) {
+        error = EROFS;
+    } else {
+        if (vn_access(dp->d_vnode, VWRITE)) {
+            return EACCES;
+        }
+           if (times &&
+               (times[0].tv_nsec != UTIME_NOW || times[1].tv_nsec != 
UTIME_NOW) &&
+               (times[0].tv_nsec != UTIME_OMIT || times[1].tv_nsec != 
UTIME_OMIT) &&
+              (!(dp->d_vnode->v_mode & ~VAPPEND)))
+               return EPERM;
+        error = vn_settimes(dp->d_vnode, timespec_times);
+    }
+
+    drele(dp);
+    return error;
+}
+
+int
+sys_futimens(int fd, const struct timespec times[2])
+{
+    struct file *fp;
+
+    fileref f(fileref_from_fd(fd));
+    if (!f)
+        return EBADF;
+
+    fp = f.get();
+
+    if (!fp->f_dentry)
+        return EBADF;
+
+    std::string pathname = fp->f_dentry->d_path;
+    auto error = sys_utimensat(AT_FDCWD, pathname.c_str(), times, 0);
+    return error;
+}
+
+int
+sys_fallocate(struct file *fp, int mode, loff_t offset, loff_t len)
+{
+    int error;
+    struct vnode *vp;
+
+    DPRINTF(VFSDB_SYSCALL, ("sys_fallocate: fp=%x", fp));
+
+    if (!fp->f_dentry || !(fp->f_flags & FWRITE)) {
+        return EBADF;
+    }
+
+    if (offset < 0 || len <= 0) {
+        return EINVAL;
+    }
+
+    // Strange, but that's what Linux returns.
+    if ((mode & FALLOC_FL_PUNCH_HOLE) && !(mode & FALLOC_FL_KEEP_SIZE)) {
+        return ENOTSUP;
+    }
+
+    vp = fp->f_dentry->d_vnode;
+    vn_lock(vp);
+
+    // NOTE: It's not detected here whether or not the device underlying
+    // the fs is a block device. It's up to the fs itself tell us whether
+    // or not fallocate is supported. See below:
+    if (vp->v_type != VREG && vp->v_type != VDIR) {
+        error = ENODEV;
+        goto ret;
+    }
+
+    // EOPNOTSUPP here means that the underlying file system
+    // referred by vp doesn't support fallocate.
+    if (!vp->v_op->vop_fallocate) {
+        error = EOPNOTSUPP;
+        goto ret;
+    }
+
+    error = VOP_FALLOCATE(vp, mode, offset, len);
+ret:
+    vn_unlock(vp);
+    return error;
+}
+
+int
+sys_chmod(const char *path, mode_t mode)
+{
+    int error;
+    struct dentry *dp;
+    DPRINTF(VFSDB_SYSCALL, ("sys_chmod: path=%s\n", path));
+    error = namei(path, &dp);
+    if (error)
+        return error;
+    if (dp->d_mount->m_flags & MNT_RDONLY) {
+        error = EROFS;
+    } else {
+        error = vn_setmode(dp->d_vnode, mode);
+    }
+    drele(dp);
+    return error;
+}
+
+int
+sys_fchmod(int fd, mode_t mode)
+{
+    fileref f(fileref_from_fd(fd));
+    if (!f)
+        return EBADF;
+    // Posix is ambivalent on what fchmod() should do on an fd that does not
+    // refer to a real file. It suggests an implementation may (but not must)
+    // fail EINVAL on a pipe, can behave in an "unspecified" manner on a
+    // socket, and for a STREAM, it must succeed and do nothing. Linux seems
+    // to just do the last thing (do nothing and succeed).
+    if (!f->f_dentry) {
+        return 0;
+    }
+    if (f->f_dentry->d_mount->m_flags & MNT_RDONLY) {
+        return EROFS;
+    } else {
+        return vn_setmode(f->f_dentry->d_vnode, mode);
+    }
+}
diff --git a/lib/vfscore/task.c b/lib/vfscore/task.c
new file mode 100644
index 00000000..7a355034
--- /dev/null
+++ b/lib/vfscore/task.c
@@ -0,0 +1,167 @@
+/*-
+ * Copyright (c) 2007, Kohsuke Ohtani All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * vfs_task.c - Routines to manage the per task data.
+ */
+
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <osv/prex.h>
+#include "vfs.h"
+
+/*
+ * Allocate new task.
+ */
+int
+task_alloc(struct task **pt)
+{
+       struct task *t;
+
+    // FIXME: where do we free task ?
+       if (!(t = new task))
+               return ENOMEM;
+       memset(t, 0, sizeof(struct task));
+       strlcpy(t->t_cwd, "/", sizeof(t->t_cwd));
+
+       *pt = t;
+       return 0;
+}
+
+/*
+ * Convert to full path from the cwd of task and path.
+ * @wd:   working directory
+ * @path: target path
+ * @full: full path to be returned
+ */
+int
+path_conv(char *wd, const char *cpath, char *full)
+{
+       char path[PATH_MAX];
+       char *src, *tgt, *p, *end;
+       size_t len = 0;
+
+       strlcpy(path, cpath, PATH_MAX);
+       path[PATH_MAX - 1] = '\0';
+
+       len = strlen(path);
+       if (len >= PATH_MAX)
+               return ENAMETOOLONG;
+       if (strlen(wd) + len >= PATH_MAX)
+               return ENAMETOOLONG;
+       src = path;
+       tgt = full;
+       end = src + len;
+       if (path[0] == '/') {
+               *tgt++ = *src++;
+               len = 1;
+       } else {
+               strlcpy(full, wd, PATH_MAX);
+               len = strlen(wd);
+               tgt += len;
+               if (len > 1 && path[0] != '.') {
+                       *tgt = '/';
+                       tgt++;
+                       len++;
+               }
+       }
+       while (*src) {
+               p = src;
+               while (*p != '/' && *p != '\0')
+                       p++;
+               *p = '\0';
+               if (!strcmp(src, "..")) {
+                       if (len >= 2) {
+                               len -= 2;
+                               tgt -= 2;       /* skip previous '/' */
+                               while (*tgt != '/') {
+                                       tgt--;
+                                       len--;
+                               }
+                               if (len == 0) {
+                                       tgt++;
+                                       len++;
+                               }
+                       }
+               } else if (!strcmp(src, ".")) {
+                       /* Ignore "." */
+               } else {
+                       while (*src != '\0') {
+                               *tgt++ = *src++;
+                               len++;
+                       }
+               }
+               if (p == end)
+                       break;
+               if (len > 0 && *(tgt - 1) != '/') {
+                       *tgt++ = '/';
+                       len++;
+               }
+               src = p + 1;
+       }
+       *tgt = '\0';
+
+       return (0);
+}
+
+/*
+ * Convert to full path from the cwd of task and path.
+ * @t:    task structure
+ * @path: target path
+ * @full: full path to be returned
+ * @acc: access mode
+ */
+int
+task_conv(struct task *t, const char *cpath, int acc, char *full)
+{
+       int rc;
+
+       rc = path_conv(t->t_cwd, cpath, full);
+       if (rc != 0) {
+               return (rc);
+       }
+
+       /* Check if the client task has required permission */
+       return (0); //sec_file_permission(t->t_taskid, full, acc);
+}
+
+/*
+ * Safe copying function that checks for overflow.
+ */
+int vfs_dname_copy(char *dest, const char *src, size_t size)
+{
+    if (strlcpy(dest, src, size) >= size) {
+        return -1;
+    }
+    return 0;
+}
diff --git a/lib/vfscore/vfs.h b/lib/vfscore/vfs.h
new file mode 100644
index 00000000..d86ef957
--- /dev/null
+++ b/lib/vfscore/vfs.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2005-2007, Kohsuke Ohtani
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _VFS_H
+#define _VFS_H
+
+#include <sys/cdefs.h>
+#include <assert.h>
+#include <dirent.h>
+#include <limits.h>
+
+#include <osv/prex.h>
+#include <osv/file.h>
+#include <osv/mount.h>
+#include <osv/vnode.h>
+#include <osv/dentry.h>
+#include <osv/error.h>
+
+/*
+ * Import vnode attributes flags
+ */
+#include <osv/vnode_attr.h>
+
+/* #define DEBUG_VFS 1 */
+
+/*
+ * Tunable parameters
+ */
+#define FSMAXNAMES     16              /* max length of 'file system' name */
+
+#ifdef DEBUG_VFS
+#include <osv/debug.h>
+
+extern int vfs_debug;
+
+#define        VFSDB_CORE      0x00000001
+#define        VFSDB_SYSCALL   0x00000002
+#define        VFSDB_VNODE     0x00000004
+#define        VFSDB_BIO       0x00000008
+#define        VFSDB_CAP       0x00000010
+
+#define VFSDB_FLAGS    0x00000013
+
+#define        DPRINTF(_m,X)   if (vfs_debug & (_m)) kprintf X
+#else
+#define        DPRINTF(_m, X)
+#endif
+
+#define ASSERT(e)      assert(e)
+
+#define OPEN_MAX       256
+
+/*
+ * per task data
+ */
+struct task {
+       char        t_cwd[PATH_MAX];    /* current working directory */
+       struct file *t_cwdfp;           /* directory for cwd */
+};
+
+extern const struct vfssw vfssw[];
+
+__BEGIN_DECLS
+int     sys_open(char *path, int flags, mode_t mode, struct file **fp);
+int     sys_read(struct file *fp, const struct iovec *iov, size_t niov,
+               off_t offset, size_t *count);
+int     sys_write(struct file *fp, const struct iovec *iov, size_t niov,
+               off_t offset, size_t *count);
+int     sys_lseek(struct file *fp, off_t off, int type, off_t * cur_off);
+int     sys_ioctl(struct file *fp, u_long request, void *buf);
+int     sys_fstat(struct file *fp, struct stat *st);
+int     sys_fstatfs(struct file *fp, struct statfs *buf);
+int     sys_fsync(struct file *fp);
+int     sys_ftruncate(struct file *fp, off_t length);
+
+int     sys_readdir(struct file *fp, struct dirent *dirent);
+int     sys_rewinddir(struct file *fp);
+int     sys_seekdir(struct file *fp, long loc);
+int     sys_telldir(struct file *fp, long *loc);
+int     sys_fchdir(struct file *fp, char *path);
+
+int     sys_mkdir(char *path, mode_t mode);
+int     sys_rmdir(char *path);
+int     sys_mknod(char *path, mode_t mode);
+int     sys_rename(char *src, char *dest);
+int     sys_link(char *oldpath, char *newpath);
+int     sys_unlink(char *path);
+int     sys_symlink(const char *oldpath, const char *newpath);
+int     sys_access(char *path, int mode);
+int     sys_stat(char *path, struct stat *st);
+int     sys_lstat(char *path, struct stat *st);
+int     sys_statfs(char *path, struct statfs *buf);
+int     sys_truncate(char *path, off_t length);
+int     sys_readlink(char *path, char *buf, size_t bufsize, ssize_t *size);
+int  sys_utimes(char *path, const struct timeval times[2], int flags);
+int  sys_utimensat(int dirfd, const char *pathname,
+                   const struct timespec times[2], int flags);
+int  sys_futimens(int fd, const struct timespec times[2]);
+int  sys_fallocate(struct file *fp, int mode, loff_t offset, loff_t len);
+
+int     sys_mount(const char *dev, const char *dir, const char *fsname, int 
flags, const void *data);
+int     sys_umount2(const char *path, int flags);
+int     sys_umount(const char *path);
+int     sys_pivot_root(const char *new_root, const char *old_put);
+int     sys_sync(void);
+int     sys_chmod(const char *path, mode_t mode);
+int     sys_fchmod(int fd, mode_t mode);
+
+
+int     task_alloc(struct task **pt);
+int     task_conv(struct task *t, const char *path, int mode, char *full);
+int     path_conv(char *wd, const char *cpath, char *full);
+
+//int   sec_file_permission(task_t task, char *path, int mode);
+int     sec_vnode_permission(char *path);
+
+int     namei(const char *path, struct dentry **dpp);
+int     namei_last_nofollow(char *path, struct dentry *ddp, struct dentry 
**dp);
+int     lookup(char *path, struct dentry **dpp, char **name);
+void    vnode_init(void);
+void    lookup_init(void);
+
+int     vfs_findroot(const char *path, struct mount **mp, char **root);
+int     vfs_dname_copy(char *dest, const char *src, size_t size);
+
+int     fs_noop(void);
+
+struct dentry *dentry_alloc(struct dentry *parent_dp, struct vnode *vp, const 
char *path);
+struct dentry *dentry_lookup(struct mount *mp, char *path);
+void dentry_move(struct dentry *dp, struct dentry *parent_dp, char *path);
+void dentry_remove(struct dentry *dp);
+void dref(struct dentry *dp);
+void drele(struct dentry *dp);
+void dentry_init(void);
+
+#ifdef DEBUG_VFS
+void    vnode_dump(void);
+void    mount_dump(void);
+#endif
+
+__END_DECLS
+
+#ifdef __cplusplus
+
+// Convert a path to a dentry_ref.  Returns an empty
+// reference if not found (ENOENT) for efficiency, throws
+// an error on other errors.
+inline dentry_ref namei(char* path)
+{
+       dentry* dp;
+       auto err = namei(path, &dp);
+       if (err == ENOENT) {
+               return dentry_ref();
+       } else if (err) {
+               throw make_error(err);
+       } else {
+               return dentry_ref(dp, false);
+       }
+}
+
+#endif
+
+#endif /* !_VFS_H */
diff --git a/lib/vfscore/vnode.c b/lib/vfscore/vnode.c
new file mode 100644
index 00000000..a292344f
--- /dev/null
+++ b/lib/vfscore/vnode.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright (c) 2005-2008, Kohsuke Ohtani
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * vfs_vnode.c - vnode service
+ */
+
+#include <limits.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <osv/prex.h>
+#include <osv/vnode.h>
+#include "vfs.h"
+
+enum vtype iftovt_tab[16] = {
+       VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON,
+       VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD,
+};
+int vttoif_tab[10] = {
+       0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK,
+       S_IFSOCK, S_IFIFO, S_IFMT, S_IFMT
+};
+
+/*
+ * Memo:
+ *
+ * Function   Ref count Lock
+ * ---------- --------- ----------
+ * vn_lock     *        Lock
+ * vn_unlock   *        Unlock
+ * vget        1        Lock
+ * vput       -1        Unlock
+ * vref       +1        *
+ * vrele      -1        *
+ */
+
+#define VNODE_BUCKETS 32               /* size of vnode hash table */
+
+/*
+ * vnode table.
+ * All active (opened) vnodes are stored on this hash table.
+ * They can be accessed by its path name.
+ */
+static LIST_HEAD(vnode_hash_head, vnode) vnode_table[VNODE_BUCKETS];
+
+/*
+ * Global lock to access all vnodes and vnode table.
+ * If a vnode is already locked, there is no need to
+ * lock this global lock to access internal data.
+ */
+static mutex_t vnode_lock = MUTEX_INITIALIZER;
+#define VNODE_LOCK()   mutex_lock(&vnode_lock)
+#define VNODE_UNLOCK() mutex_unlock(&vnode_lock)
+#define VNODE_OWNED()  mutex_owned(&vnode_lock)
+
+/*
+ * Get the hash value from the mount point and path name.
+ * XXX(hch): replace with a better hash for 64-bit pointers.
+ */
+static u_int
+vn_hash(struct mount *mp, uint64_t ino)
+{
+       return (ino ^ (unsigned long)mp) & (VNODE_BUCKETS - 1);
+}
+
+/*
+ * Returns locked vnode for specified mount point and path.
+ * vn_lock() will increment the reference count of vnode.
+ *
+ * Locking: VNODE_LOCK must be held.
+ */
+struct vnode *
+vn_lookup(struct mount *mp, uint64_t ino)
+{
+       struct vnode *vp;
+
+       assert(VNODE_OWNED());
+       LIST_FOREACH(vp, &vnode_table[vn_hash(mp, ino)], v_link) {
+               if (vp->v_mount == mp && vp->v_ino == ino) {
+                       vp->v_refcnt++;
+                       mutex_lock(&vp->v_lock);
+                       vp->v_nrlocks++;
+                       return vp;
+               }
+       }
+       return nullptr;         /* not found */
+}
+
+#ifdef DEBUG_VFS
+static const char *
+vn_path(struct vnode *vp)
+{
+       struct dentry *dp;
+
+       if (LIST_EMPTY(&vp->v_names) == 1) {
+               return (" ");
+       }
+       dp = LIST_FIRST(&vp->v_names);
+       return (dp->d_path);
+}
+#endif
+
+/*
+ * Lock vnode
+ */
+void
+vn_lock(struct vnode *vp)
+{
+       ASSERT(vp);
+       ASSERT(vp->v_refcnt > 0);
+
+       mutex_lock(&vp->v_lock);
+       vp->v_nrlocks++;
+       DPRINTF(VFSDB_VNODE, ("vn_lock:   %s\n", vn_path(vp)));
+}
+
+/*
+ * Unlock vnode
+ */
+void
+vn_unlock(struct vnode *vp)
+{
+       ASSERT(vp);
+       ASSERT(vp->v_refcnt > 0);
+       ASSERT(vp->v_nrlocks > 0);
+
+       vp->v_nrlocks--;
+       mutex_unlock(&vp->v_lock);
+       DPRINTF(VFSDB_VNODE, ("vn_lock:   %s\n", vn_path(vp)));
+}
+
+/*
+ * Allocate new vnode for specified path.
+ * Increment its reference count and lock it.
+ * Returns 1 if vnode was found in cache; otherwise returns 0.
+ */
+int
+vget(struct mount *mp, uint64_t ino, struct vnode **vpp)
+{
+       struct vnode *vp;
+       int error;
+
+       *vpp = nullptr;
+
+       DPRINTF(VFSDB_VNODE, ("vget %LLu\n", ino));
+
+       VNODE_LOCK();
+
+       vp = vn_lookup(mp, ino);
+       if (vp) {
+               VNODE_UNLOCK();
+               *vpp = vp;
+               return 1;
+       }
+
+       if (!(vp = new vnode())) {
+               VNODE_UNLOCK();
+               return 0;
+       }
+
+       LIST_INIT(&vp->v_names);
+       vp->v_ino = ino;
+       vp->v_mount = mp;
+       vp->v_refcnt = 1;
+       vp->v_op = mp->m_op->vfs_vnops;
+       vp->v_nrlocks = 0;
+
+       /*
+        * Request to allocate fs specific data for vnode.
+        */
+       if ((error = VFS_VGET(mp, vp)) != 0) {
+               VNODE_UNLOCK();
+               delete vp;
+               return error;
+       }
+       vfs_busy(vp->v_mount);
+       mutex_lock(&vp->v_lock);
+       vp->v_nrlocks++;
+
+       LIST_INSERT_HEAD(&vnode_table[vn_hash(mp, ino)], vp, v_link);
+       VNODE_UNLOCK();
+
+       *vpp = vp;
+
+       return 0;
+}
+
+/*
+ * Unlock vnode and decrement its reference count.
+ */
+void
+vput(struct vnode *vp)
+{
+       ASSERT(vp);
+       ASSERT(vp->v_nrlocks > 0);
+       ASSERT(vp->v_refcnt > 0);
+       DPRINTF(VFSDB_VNODE, ("vput: ref=%d %s\n", vp->v_refcnt, vn_path(vp)));
+
+       VNODE_LOCK();
+       vp->v_refcnt--;
+       if (vp->v_refcnt > 0) {
+           VNODE_UNLOCK();
+               vn_unlock(vp);
+               return;
+       }
+       LIST_REMOVE(vp, v_link);
+       VNODE_UNLOCK();
+
+       /*
+        * Deallocate fs specific vnode data
+        */
+       if (vp->v_op->vop_inactive)
+               VOP_INACTIVE(vp);
+       vfs_unbusy(vp->v_mount);
+       vp->v_nrlocks--;
+       ASSERT(vp->v_nrlocks == 0);
+       mutex_unlock(&vp->v_lock);
+       delete vp;
+}
+
+/*
+ * Increment the reference count on an active vnode.
+ */
+void
+vref(struct vnode *vp)
+{
+       ASSERT(vp);
+       ASSERT(vp->v_refcnt > 0);       /* Need vget */
+
+       VNODE_LOCK();
+       DPRINTF(VFSDB_VNODE, ("vref: ref=%d\n", vp->v_refcnt));
+       vp->v_refcnt++;
+       VNODE_UNLOCK();
+}
+
+/*
+ * Decrement the reference count of the vnode.
+ * Any code in the system which is using vnode should call vrele()
+ * when it is finished with the vnode.
+ * If count drops to zero, call inactive routine and return to freelist.
+ */
+void
+vrele(struct vnode *vp)
+{
+       ASSERT(vp);
+       ASSERT(vp->v_refcnt > 0);
+
+       VNODE_LOCK();
+       DPRINTF(VFSDB_VNODE, ("vrele: ref=%d\n", vp->v_refcnt));
+       vp->v_refcnt--;
+       if (vp->v_refcnt > 0) {
+               VNODE_UNLOCK();
+               return;
+       }
+       LIST_REMOVE(vp, v_link);
+       VNODE_UNLOCK();
+
+       /*
+        * Deallocate fs specific vnode data
+        */
+       VOP_INACTIVE(vp);
+       vfs_unbusy(vp->v_mount);
+       delete vp;
+}
+
+/*
+ * Remove all vnode in the vnode table for unmount.
+ */
+void
+vflush(struct mount *mp)
+{
+}
+
+int
+vn_stat(struct vnode *vp, struct stat *st)
+{
+       struct vattr vattr;
+       struct vattr *vap;
+       mode_t mode;
+       int error;
+
+       vap = &vattr;
+
+       memset(st, 0, sizeof(struct stat));
+
+       memset(vap, 0, sizeof(struct vattr));
+
+       error = VOP_GETATTR(vp, vap);
+       if (error)
+               return error;
+
+       st->st_ino = (ino_t)vap->va_nodeid;
+       st->st_size = vap->va_size;
+       mode = vap->va_mode;
+       switch (vp->v_type) {
+       case VREG:
+               mode |= S_IFREG;
+               break;
+       case VDIR:
+               mode |= S_IFDIR;
+               break;
+       case VBLK:
+               mode |= S_IFBLK;
+               break;
+       case VCHR:
+               mode |= S_IFCHR;
+               break;
+       case VLNK:
+               mode |= S_IFLNK;
+               break;
+       case VSOCK:
+               mode |= S_IFSOCK;
+               break;
+       case VFIFO:
+               mode |= S_IFIFO;
+               break;
+       default:
+               return EBADF;
+       };
+       st->st_mode = mode;
+       st->st_nlink = vap->va_nlink;
+       st->st_blksize = BSIZE;
+       st->st_blocks = vap->va_size / S_BLKSIZE;
+       st->st_uid = vap->va_uid;
+       st->st_gid = vap->va_gid;
+       st->st_dev = vap->va_fsid;
+       if (vp->v_type == VCHR || vp->v_type == VBLK)
+               st->st_rdev = vap->va_rdev;
+
+       st->st_atim = vap->va_atime;
+       st->st_mtim = vap->va_mtime;
+       st->st_ctim = vap->va_ctime;
+
+       return 0;
+}
+
+/*
+ * Set access and modification times of the vnode
+ */
+int
+vn_settimes(struct vnode *vp, struct timespec times[2])
+{
+    struct vattr vattr;
+    struct vattr *vap;
+    int error;
+
+    vap = &vattr;
+    memset(vap, 0, sizeof(struct vattr));
+
+    vap->va_atime = times[0];
+    vap->va_mtime = times[1];
+    vap->va_mask = ((times[0].tv_nsec == UTIME_OMIT) ? 0 : AT_ATIME)
+                    | ((times[1].tv_nsec == UTIME_OMIT) ? 0 : AT_MTIME);
+    vn_lock(vp);
+    error = VOP_SETATTR(vp, vap);
+    vn_unlock(vp);
+
+    return error;
+}
+
+/*
+ * Set chmod permissions on the vnode.
+ */
+int
+vn_setmode(struct vnode *vp, mode_t new_mode)
+{
+    struct vattr vattr;
+    memset(&vattr, 0, sizeof(vattr));
+    vattr.va_mode = new_mode;
+    vattr.va_mask = AT_MODE;
+    vn_lock(vp);
+    vp->v_mode = new_mode;
+    int error = VOP_SETATTR(vp, &vattr);
+    vn_unlock(vp);
+    return error;
+}
+
+/*
+ * Check permission on vnode pointer.
+ */
+int
+vn_access(struct vnode *vp, int flags)
+{
+       int error = 0;
+
+       if ((flags & VEXEC) && (vp->v_mode & 0111) == 0) {
+               error = EACCES;
+               goto out;
+       }
+       if ((flags & VREAD) && (vp->v_mode & 0444) == 0) {
+               error = EACCES;
+               goto out;
+       }
+       if (flags & VWRITE) {
+               if (vp->v_mount->m_flags & MNT_RDONLY) {
+                       error = EROFS;
+                       goto out;
+               }
+               if ((vp->v_mode & 0222) == 0) {
+                       error = EACCES;
+                       goto out;
+               }
+       }
+ out:
+       return error;
+}
+
+#ifdef DEBUG_VFS
+/*
+ * Dump all all vnode.
+ */
+void
+vnode_dump(void)
+{
+       int i;
+       struct vnode *vp;
+       struct mount *mp;
+       char type[][6] = { "VNON ", "VREG ", "VDIR ", "VBLK ", "VCHR ",
+                          "VLNK ", "VSOCK", "VFIFO" };
+
+       VNODE_LOCK();
+       kprintf("Dump vnode\n");
+       kprintf(" vnode    mount    type  refcnt blkno    path\n");
+       kprintf(" -------- -------- ----- ------ -------- 
------------------------------\n");
+
+       for (i = 0; i < VNODE_BUCKETS; i++) {
+               LIST_FOREACH(vp, &vnode_table[i], v_link) {
+                       mp = vp->v_mount;
+
+                       kprintf(" %08x %08x %s %6d %8d %s%s\n", (u_long)vp,
+                               (u_long)mp, type[vp->v_type], vp->v_refcnt,
+                               (strlen(mp->m_path) == 1) ? "\0" : mp->m_path,
+                               vn_path(vp));
+               }
+       }
+       kprintf("\n");
+       VNODE_UNLOCK();
+}
+#endif
+
+int
+vop_nullop(void)
+{
+       return 0;
+}
+
+int
+vop_einval(void)
+{
+       return EINVAL;
+}
+
+int
+vop_eperm(void)
+{
+       return EPERM;
+}
+
+int
+vop_erofs(void)
+{
+       return EROFS;
+}
+
+/*
+ * vnode_init() is called once (from vfs_init)
+ * in initialization.
+ */
+void
+vnode_init(void)
+{
+       int i;
+
+       for (i = 0; i < VNODE_BUCKETS; i++)
+               LIST_INIT(&vnode_table[i]);
+}
+
+void vn_add_name(struct vnode *vp, struct dentry *dp)
+{
+       vn_lock(vp);
+       LIST_INSERT_HEAD(&vp->v_names, dp, d_names_link);
+       vn_unlock(vp);
+}
+
+void vn_del_name(struct vnode *vp, struct dentry *dp)
+{
+       vn_lock(vp);
+       LIST_REMOVE(dp, d_names_link);
+       vn_unlock(vp);
+}
+
-- 
2.19.2


_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.