From: Charlie Paul <cpaul.windri...@gmail.com>

Added Virtual file system to support the AXXIA 5500 board.

Signed-off-by: Charlie Paul <cpaul.windri...@gmail.com>
---
 fs/Kconfig           |   16 +
 fs/Makefile          |    1 +
 fs/namespace.c       |    1 -
 fs/vmfs/Makefile     |   39 ++
 fs/vmfs/cache.c      |  231 +++++++++++
 fs/vmfs/dir.c        |  626 +++++++++++++++++++++++++++++
 fs/vmfs/file.c       |  500 +++++++++++++++++++++++
 fs/vmfs/getopt.c     |   67 ++++
 fs/vmfs/getopt.h     |   14 +
 fs/vmfs/inode.c      |  667 +++++++++++++++++++++++++++++++
 fs/vmfs/ioctl.c      |   57 +++
 fs/vmfs/mboxtypes.h  |   31 ++
 fs/vmfs/messagebox.c |  314 +++++++++++++++
 fs/vmfs/messagebox.h |  121 ++++++
 fs/vmfs/msg.c        |  232 +++++++++++
 fs/vmfs/msg.h        |  182 +++++++++
 fs/vmfs/proc.c       | 1086 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/vmfs/proto.h      |   71 ++++
 fs/vmfs/symlink.c    |   68 ++++
 fs/vmfs/vfs.c        |  577 +++++++++++++++++++++++++++
 fs/vmfs/vfs.h        |  356 +++++++++++++++++
 fs/vmfs/vmfs.h       |   45 +++
 fs/vmfs/vmfs_debug.h |   39 ++
 fs/vmfs/vmfs_fs.h    |  111 ++++++
 fs/vmfs/vmfs_fs_i.h  |   39 ++
 fs/vmfs/vmfs_fs_sb.h |   64 +++
 fs/vmfs/vmfs_mount.h |   62 +++
 fs/vmfs/vmfsno.h     |  138 +++++++
 28 files changed, 5754 insertions(+), 1 deletion(-)
 create mode 100644 fs/vmfs/Makefile
 create mode 100644 fs/vmfs/cache.c
 create mode 100644 fs/vmfs/dir.c
 create mode 100644 fs/vmfs/file.c
 create mode 100644 fs/vmfs/getopt.c
 create mode 100644 fs/vmfs/getopt.h
 create mode 100644 fs/vmfs/inode.c
 create mode 100644 fs/vmfs/ioctl.c
 create mode 100644 fs/vmfs/mboxtypes.h
 create mode 100644 fs/vmfs/messagebox.c
 create mode 100644 fs/vmfs/messagebox.h
 create mode 100644 fs/vmfs/msg.c
 create mode 100644 fs/vmfs/msg.h
 create mode 100644 fs/vmfs/proc.c
 create mode 100644 fs/vmfs/proto.h
 create mode 100644 fs/vmfs/symlink.c
 create mode 100644 fs/vmfs/vfs.c
 create mode 100644 fs/vmfs/vfs.h
 create mode 100644 fs/vmfs/vmfs.h
 create mode 100644 fs/vmfs/vmfs_debug.h
 create mode 100644 fs/vmfs/vmfs_fs.h
 create mode 100644 fs/vmfs/vmfs_fs_i.h
 create mode 100644 fs/vmfs/vmfs_fs_sb.h
 create mode 100644 fs/vmfs/vmfs_mount.h
 create mode 100644 fs/vmfs/vmfsno.h

diff --git a/fs/Kconfig b/fs/Kconfig
index 69004c6..22a642a 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -266,4 +266,20 @@ endif # NETWORK_FILESYSTEMS
 source "fs/nls/Kconfig"
 source "fs/dlm/Kconfig"
 
+config VMFS_FS
+       tristate "VMFS file system support (to mount host directories etc.)"
+       select NLS
+       help
+         Say Y here to enable support for accessing the host filesystem
+         when running the kernel in a virtual platform built with the Fast
+         Models product from ARM.
+
+config VMFS_DEV_BASE
+       hex "VMFS base address"
+       depends on VMFS_FS
+
+config VMFS_IRQ
+       int "VMFS IRQ"
+       depends on VMFS_FS
+
 endmenu
diff --git a/fs/Makefile b/fs/Makefile
index 18eaee0..afa1e52 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -128,3 +128,4 @@ obj-$(CONFIG_PSTORE)                += pstore/
 obj-$(CONFIG_EFIVAR_FS)                += efivarfs/
 obj-$(CONFIG_YAFFS_FS)         += yaffs2/
 obj-$(CONFIG_AUFS_FS)           += aufs/
+obj-$(CONFIG_VMFS_FS)           += vmfs/
diff --git a/fs/namespace.c b/fs/namespace.c
index 778cb1b..af94aa3 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2465,7 +2465,6 @@ long do_mount(const char *dev_name, const char *dir_name,
                flags &= ~MS_MGC_MSK;
 
        /* Basic sanity checks */
-
        if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))
                return -EINVAL;
 
diff --git a/fs/vmfs/Makefile b/fs/vmfs/Makefile
new file mode 100644
index 0000000..e74d18d
--- /dev/null
+++ b/fs/vmfs/Makefile
@@ -0,0 +1,39 @@
+#
+# Makefile for the linux vmfs-filesystem routines.
+#
+
+obj-$(CONFIG_VMFS_FS) += vmfsfs.o
+
+vmfsfs-objs := proc.o dir.o cache.o inode.o file.o ioctl.o getopt.o \
+               symlink.o messagebox.o msg.o vfs.o
+
+# If you want debugging output, you may add these flags to the EXTRA_CFLAGS
+# VMFSFS_PARANOIA should normally be enabled.
+
+#EXTRA_CFLAGS += -DVMFSFS_PARANOIA
+#EXTRA_CFLAGS += -DVMFSFS_DEBUG
+#EXTRA_CFLAGS += -DVMFSFS_DEBUG_VERBOSE
+#EXTRA_CFLAGS += -DMESSAGEBOX_DEBUG
+#EXTRA_CFLAGS += -DDEBUG_VMFS_TIMESTAMP
+#EXTRA_CFLAGS += -Werror
+
+#
+# Maintainer rules
+#
+
+# getopt.c not included. It is intentionally separate
+SRC = proc.c dir.c cache.c inode.c file.c ioctl.c \
+       symlink.c
+
+proto:
+       -rm -f proto.h
+       @echo >  proto2.h "/*"
+       @echo >> proto2.h " *  Autogenerated with cproto on: " `date`
+       @echo >> proto2.h " */"
+       @echo >> proto2.h ""
+       @echo >> proto2.h "struct vmfs_request;"
+       @echo >> proto2.h "struct sock;"
+       @echo >> proto2.h "struct statfs;"
+       @echo >> proto2.h ""
+       cproto -E "gcc -E" -e -v -I $(TOPDIR)/include -DMAKING_PROTO 
-D__KERNEL__ $(SRC) >> proto2.h
+       mv proto2.h proto.h
diff --git a/fs/vmfs/cache.c b/fs/vmfs/cache.c
new file mode 100644
index 0000000..23fbf36
--- /dev/null
+++ b/fs/vmfs/cache.c
@@ -0,0 +1,231 @@
+/*
+ *  cache.c
+ *
+ * Copyright (C) 1997 by Bill Hawes
+ *
+ * Routines to support directory cacheing using the page cache.
+ * This cache code is almost directly taken from ncpfs.
+ *
+ * Please add a note about your changes to vmfs_ in the ChangeLog file.
+ */
+
+#include <linux/time.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/dirent.h>
+#include "vmfs_fs.h"
+#include <linux/pagemap.h>
+#include <linux/net.h>
+#include <linux/version.h>
+
+#include <asm/page.h>
+
+#include "vmfs_debug.h"
+#include "proto.h"
+
+/*
+ * Force the next attempt to use the cache to be a timeout.
+ * If we can't find the page that's fine, it will cause a refresh.
+ */
+void vmfs_invalid_dir_cache(struct inode *dir)
+{
+       struct vmfs_sb_info *server = server_from_inode(dir);
+       union vmfs_dir_cache *cache = NULL;
+       struct page *page = NULL;
+
+       page = grab_cache_page(&dir->i_data, 0);
+       if (!page)
+               goto out;
+
+       if (!PageUptodate(page))
+               goto out_unlock;
+
+       cache = kmap(page);
+       cache->head.time = jiffies - VMFS_MAX_AGE(server);
+
+       kunmap(page);
+       SetPageUptodate(page);
+out_unlock:
+       unlock_page(page);
+       page_cache_release(page);
+out:
+       return;
+}
+
+/*
+ * Mark all dentries for 'parent' as invalid, forcing them to be re-read
+ */
+void vmfs_invalidate_dircache_entries(struct dentry *parent)
+{
+       struct vmfs_sb_info *server = server_from_dentry(parent);
+       struct list_head *next;
+       struct dentry *dentry;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+       spin_lock(&dcache_lock);
+#else
+       spin_lock(&parent->d_lock);
+#endif
+       next = parent->d_subdirs.next;
+       while (next != &parent->d_subdirs) {
+               dentry = list_entry(next, struct dentry, d_u.d_child);
+               dentry->d_fsdata = NULL;
+               vmfs_age_dentry(server, dentry);
+               next = next->next;
+       }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+       spin_unlock(&dcache_lock);
+#else
+       spin_unlock(&parent->d_lock);
+#endif
+}
+
+/*
+ * dget, but require that fpos and parent matches what the dentry contains.
+ * dentry is not known to be a valid pointer at entry.
+ */
+struct dentry *vmfs_dget_fpos(struct dentry *dentry, struct dentry *parent,
+                             unsigned long fpos)
+{
+       struct dentry *dent = dentry;
+       struct list_head *next;
+
+       if (d_validate(dent, parent)) {
+               if (dent->d_name.len <= VMFS_MAXNAMELEN &&
+                   (unsigned long)dent->d_fsdata == fpos) {
+                       if (!dent->d_inode) {
+                               dput(dent);
+                               dent = NULL;
+                       }
+                       return dent;
+               }
+               dput(dent);
+       }
+
+       /* If a pointer is invalid, we search the dentry. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+       spin_lock(&dcache_lock);
+#else
+       spin_lock(&parent->d_lock);
+#endif
+       next = parent->d_subdirs.next;
+       while (next != &parent->d_subdirs) {
+               dent = list_entry(next, struct dentry, d_u.d_child);
+               if ((unsigned long)dent->d_fsdata == fpos) {
+                       if (dent->d_inode) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+                               dget_locked(dent);
+#else
+                               dget(dent);
+#endif
+                       } else {
+                               dent = NULL;
+                       }
+                       goto out_unlock;
+               }
+               next = next->next;
+       }
+       dent = NULL;
+out_unlock:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+       spin_unlock(&dcache_lock);
+#else
+       spin_unlock(&parent->d_lock);
+#endif
+       return dent;
+}
+
+/*
+ * Create dentry/inode for this file and add it to the dircache.
+ */
+int
+vmfs_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
+               struct vmfs_cache_control *ctrl, struct qstr *qname,
+               struct vmfs_fattr *entry)
+{
+       struct dentry *newdent, *dentry = filp->f_path.dentry;
+       struct inode *newino, *inode = dentry->d_inode;
+       struct vmfs_cache_control ctl = *ctrl;
+       int valid = 0;
+       int hashed = 0;
+       ino_t ino = 0;
+
+       DEBUG1("name=%s\n", qname->name);
+
+       qname->hash = full_name_hash(qname->name, qname->len);
+
+       if (dentry->d_op && dentry->d_op->d_hash) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+               if (dentry->d_op->d_hash(dentry, qname) != 0)
+#endif
+                       goto end_advance;
+       }
+
+       newdent = d_lookup(dentry, qname);
+
+       if (!newdent) {
+               newdent = d_alloc(dentry, qname);
+               if (!newdent)
+                       goto end_advance;
+       } else {
+               hashed = 1;
+               memcpy((char *)newdent->d_name.name, qname->name,
+                      newdent->d_name.len);
+       }
+
+       if (!newdent->d_inode) {
+               vmfs_renew_times(newdent);
+               entry->f_ino = iunique(inode->i_sb, 2);
+               newino = vmfs_iget(inode->i_sb, entry);
+               if (newino) {
+                       vmfs_new_dentry(newdent);
+                       d_instantiate(newdent, newino);
+                       if (!hashed)
+                               d_rehash(newdent);
+               }
+       } else
+               vmfs_set_inode_attr(newdent->d_inode, entry);
+
+       if (newdent->d_inode) {
+               ino = newdent->d_inode->i_ino;
+               newdent->d_fsdata = (void *)ctl.fpos;
+               vmfs_new_dentry(newdent);
+       }
+
+       if (ctl.idx >= VMFS_DIRCACHE_SIZE) {
+               if (ctl.page) {
+                       kunmap(ctl.page);
+                       SetPageUptodate(ctl.page);
+                       unlock_page(ctl.page);
+                       page_cache_release(ctl.page);
+               }
+               ctl.cache = NULL;
+               ctl.idx -= VMFS_DIRCACHE_SIZE;
+               ctl.ofs += 1;
+               ctl.page = grab_cache_page(&inode->i_data, ctl.ofs);
+               if (ctl.page)
+                       ctl.cache = kmap(ctl.page);
+       }
+       if (ctl.cache) {
+               ctl.cache->dentry[ctl.idx] = newdent;
+               valid = 1;
+       }
+       dput(newdent);
+
+end_advance:
+       if (!valid)
+               ctl.valid = 0;
+       if (!ctl.filled && (ctl.fpos == filp->f_pos)) {
+               if (!ino)
+                       ino = iunique(inode->i_sb, 2);
+               ctl.filled = filldir(dirent, qname->name, qname->len,
+                                    filp->f_pos, ino, DT_UNKNOWN);
+               if (!ctl.filled)
+                       filp->f_pos += 1;
+       }
+       ctl.fpos += 1;
+       ctl.idx += 1;
+       *ctrl = ctl;
+       return ctl.valid || !ctl.filled;
+}
diff --git a/fs/vmfs/dir.c b/fs/vmfs/dir.c
new file mode 100644
index 0000000..ccda867
--- /dev/null
+++ b/fs/vmfs/dir.c
@@ -0,0 +1,626 @@
+/*
+ *  dir.c
+ *
+ *  Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *  Copyright (C) 1997 by Volker Lendecke
+ *
+ *  Copyright (C) 2008-2009 ARM Limited
+ *
+ *  Please add a note about your changes to vmfs_ in the ChangeLog file.
+ */
+
+#include <linux/time.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/net.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+
+#include "vmfs_fs.h"
+#include "vmfs_mount.h"
+#include "vmfsno.h"
+
+#include "vmfs_debug.h"
+#include "proto.h"
+
+// ORIG - TBD static int vmfs_readdir(struct file *, void *, filldir_t);
+static int vmfs_readdir(struct file *, struct dir_context *ctx);
+static int vmfs_dir_open(struct inode *, struct file *);
+
+static struct dentry *vmfs_lookup(struct inode *, struct dentry *,
+                                 unsigned int flags);
+static int vmfs_create(struct inode *, struct dentry *, umode_t,
+                      bool excl);
+static int vmfs_mkdir(struct inode *, struct dentry *, umode_t);
+static int vmfs_rmdir(struct inode *, struct dentry *);
+static int vmfs_unlink(struct inode *, struct dentry *);
+static int vmfs_rename(struct inode *, struct dentry *,
+                      struct inode *, struct dentry *);
+static int vmfs_make_node(struct inode *, struct dentry *, umode_t, dev_t);
+static int vmfs_link(struct dentry *, struct inode *, struct dentry *);
+
+const struct file_operations vmfs_dir_operations = {
+       .read = generic_read_dir,
+       .iterate = vmfs_readdir,
+       .unlocked_ioctl = vmfs_unlocked_ioctl,
+       .open = vmfs_dir_open,
+};
+
+const struct inode_operations vmfs_dir_inode_operations = {
+       .create = vmfs_create,
+       .lookup = vmfs_lookup,
+       .unlink = vmfs_unlink,
+       .mkdir = vmfs_mkdir,
+       .rmdir = vmfs_rmdir,
+       .rename = vmfs_rename,
+       .getattr = vmfs_getattr,
+       .setattr = vmfs_notify_change,
+};
+
+const struct inode_operations vmfs_dir_inode_operations_unix = {
+       .create = vmfs_create,
+       .lookup = vmfs_lookup,
+       .unlink = vmfs_unlink,
+       .mkdir = vmfs_mkdir,
+       .rmdir = vmfs_rmdir,
+       .rename = vmfs_rename,
+       .getattr = vmfs_getattr,
+       .setattr = vmfs_notify_change,
+       .symlink = vmfs_symlink,
+       .mknod = vmfs_make_node,
+       .link = vmfs_link,
+};
+
+/*
+ * Read a directory, using filldir to fill the dirent memory.
+ * vmfs_proc_readdir does the actual reading from the vmfs server.
+ *
+ * The cache code is almost directly taken from ncpfs
+ */
+// ORIG - TBD static int vmfs_readdir(struct file *filp, void *dirent, 
filldir_t filldir)
+static int vmfs_readdir(struct file *filp, struct dir_context *ctx)
+{
+       int result;
+#if 0 // Needs to be ported
+       struct dentry *dentry = filp->f_path.dentry;
+       struct inode *dir = dentry->d_inode;
+       struct vmfs_sb_info *server = server_from_dentry(dentry);
+       union vmfs_dir_cache *cache = NULL;
+       struct vmfs_cache_control ctl;
+       struct page *page = NULL;
+
+       ctl.page = NULL;
+       ctl.cache = NULL;
+
+       VERBOSE("reading %s/%s, f_pos=%d\n",
+               DENTRY_PATH(dentry), (int)filp->f_pos);
+
+       result = 0;
+
+       mutex_lock(&vmfs_mutex);
+
+       switch ((unsigned int)filp->f_pos) {
+       case 0:
+               if (filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR) < 0)
+                       goto out;
+               filp->f_pos = 1;
+               /* fallthrough */
+       case 1:
+              if (filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR) < 0)
+                       goto out;
+               filp->f_pos = 2;
+       }
+
+       /*
+        * Make sure our inode is up-to-date.
+        */
+       result = vmfs_revalidate_inode(dentry);
+       if (result)
+               goto out;
+
+       page = grab_cache_page(&dir->i_data, 0);
+       if (!page)
+               goto read_really;
+
+       ctl.cache = cache = kmap(page);
+       ctl.head = cache->head;
+
+       if (!PageUptodate(page) || !ctl.head.eof) {
+               VERBOSE("%s/%s, page uptodate=%d, eof=%d\n",
+                       DENTRY_PATH(dentry), PageUptodate(page), ctl.head.eof);
+               goto init_cache;
+       }
+
+       if (filp->f_pos == 2) {
+               if (jiffies - ctl.head.time >= VMFS_MAX_AGE(server))
+                       goto init_cache;
+
+               /*
+                * N.B. ncpfs checks mtime of dentry too here, we don't.
+                *   1. common vmfs servers do not update mtime on dir changes
+                *   2. it requires an extra vmfs request
+                *      (revalidate has the same timeout as ctl.head.time)
+                *
+                * Instead vmfs_ invalidates its own cache on local changes
+                * and remote changes are not seen until timeout.
+                */
+       }
+
+       if (filp->f_pos > ctl.head.end)
+               goto finished;
+
+       ctl.fpos = filp->f_pos + (VMFS_DIRCACHE_START - 2);
+       ctl.ofs = ctl.fpos / VMFS_DIRCACHE_SIZE;
+       ctl.idx = ctl.fpos % VMFS_DIRCACHE_SIZE;
+
+       for (;;) {
+               if (ctl.ofs != 0) {
+                       ctl.page = find_lock_page(&dir->i_data, ctl.ofs);
+                       if (!ctl.page)
+                               goto invalid_cache;
+                       ctl.cache = kmap(ctl.page);
+                       if (!PageUptodate(ctl.page))
+                               goto invalid_cache;
+               }
+               while (ctl.idx < VMFS_DIRCACHE_SIZE) {
+                       struct dentry *dent;
+                       int res;
+
+                       dent = vmfs_dget_fpos(ctl.cache->dentry[ctl.idx],
+                                             dentry, filp->f_pos);
+                       if (!dent)
+                               goto invalid_cache;
+
+                       res = filldir(dirent, dent->d_name.name,
+                                     dent->d_name.len, filp->f_pos,
+                                     dent->d_inode->i_ino, DT_UNKNOWN);
+                       dput(dent);
+                       if (res)
+                               goto finished;
+                       filp->f_pos += 1;
+                       ctl.idx += 1;
+                       if (filp->f_pos > ctl.head.end)
+                               goto finished;
+               }
+               if (ctl.page) {
+                       kunmap(ctl.page);
+                       SetPageUptodate(ctl.page);
+                       unlock_page(ctl.page);
+                       page_cache_release(ctl.page);
+                       ctl.page = NULL;
+               }
+               ctl.idx = 0;
+               ctl.ofs += 1;
+       }
+invalid_cache:
+       if (ctl.page) {
+               kunmap(ctl.page);
+               unlock_page(ctl.page);
+               page_cache_release(ctl.page);
+               ctl.page = NULL;
+       }
+       ctl.cache = cache;
+init_cache:
+       vmfs_invalidate_dircache_entries(dentry);
+       ctl.head.time = jiffies;
+       ctl.head.eof = 0;
+       ctl.fpos = 2;
+       ctl.ofs = 0;
+       ctl.idx = VMFS_DIRCACHE_START;
+       ctl.filled = 0;
+       ctl.valid = 1;
+read_really:
+       result = server->ops->readdir(filp, dirent, filldir, &ctl);
+       if (result == -ERESTARTSYS && page)
+               ClearPageUptodate(page);
+       if (ctl.idx == -1)
+               goto invalid_cache;     /* retry */
+       ctl.head.end = ctl.fpos - 1;
+       ctl.head.eof = ctl.valid;
+finished:
+       if (page) {
+               cache->head = ctl.head;
+               kunmap(page);
+               if (result != -ERESTARTSYS)
+                       SetPageUptodate(page);
+               unlock_page(page);
+               page_cache_release(page);
+       }
+       if (ctl.page) {
+               kunmap(ctl.page);
+               SetPageUptodate(ctl.page);
+               unlock_page(ctl.page);
+               page_cache_release(ctl.page);
+       }
+out:
+       mutex_unlock(&vmfs_mutex);
+#endif
+       return result;
+}
+
+static int vmfs_dir_open(struct inode *dir, struct file *file)
+{
+       struct dentry *dentry = file->f_path.dentry;
+       int error = 0;
+
+       VERBOSE("(%s/%s)\n", dentry->d_parent->d_name.name,
+               file->f_path.dentry->d_name.name);
+
+       mutex_lock(&vmfs_mutex);
+
+
+       if (!IS_ROOT(dentry))
+               error = vmfs_revalidate_inode(dentry);
+
+       mutex_unlock(&vmfs_mutex);
+       return error;
+}
+
+/*
+ * Dentry operations routines
+ */
+static int vmfs_lookup_validate(struct dentry *, unsigned int flags);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+static int vmfs_delete_dentry(struct dentry *);
+#else
+static int vmfs_delete_dentry(const struct dentry *);
+#endif
+
+
+static const struct dentry_operations vmfs__dentry_operations_case = {
+       .d_revalidate = vmfs_lookup_validate,
+       .d_delete = vmfs_delete_dentry,
+};
+
+/*
+ * This is the callback when the dcache has a lookup hit.
+ */
+static int vmfs_lookup_validate(struct dentry *dentry, unsigned int flags)
+{
+       struct vmfs_sb_info *server = server_from_dentry(dentry);
+       struct inode *inode = dentry->d_inode;
+       unsigned long age = jiffies - dentry->d_time;
+       int valid;
+
+       /*
+        * The default validation is based on dentry age:
+        * we believe in dentries for a few seconds.  (But each
+        * successful server lookup renews the timestamp.)
+        */
+       valid = (age <= VMFS_MAX_AGE(server));
+#ifdef VMFSFS_DEBUG_VERBOSE
+       if (!valid)
+               VERBOSE("%s/%s not valid, age=%lu\n", DENTRY_PATH(dentry), age);
+#endif
+
+       if (inode) {
+               mutex_lock(&vmfs_mutex);
+               if (is_bad_inode(inode)) {
+                       PARANOIA("%s/%s has dud inode\n", DENTRY_PATH(dentry));
+                       valid = 0;
+               } else if (!valid)
+                       valid = (vmfs_revalidate_inode(dentry) == 0);
+               mutex_unlock(&vmfs_mutex);
+       } else {
+               /*
+                * What should we do for negative dentries?
+                */
+       }
+       return valid;
+}
+
+
+/*
+ * This is the callback from dput() when d_count is going to 0.
+ * We use this to unhash dentries with bad inodes.
+ */
+static int
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+vmfs_delete_dentry(struct dentry *dentry)
+#else
+vmfs_delete_dentry(const struct dentry *dentry)
+#endif
+{
+       if (dentry->d_inode) {
+               if (is_bad_inode(dentry->d_inode)) {
+                       PARANOIA("bad inode, unhashing %s/%s\n",
+                                DENTRY_PATH(dentry));
+                       return 1;
+               }
+       } else {
+               /* N.B. Unhash negative dentries? */
+       }
+       return 0;
+}
+
+/*
+ * Initialize a new dentry
+ */
+void vmfs_new_dentry(struct dentry *dentry)
+{
+       dentry->d_op = &vmfs__dentry_operations_case;
+}
+
+/*
+ * Whenever a lookup succeeds, we know the parent directories
+ * are all valid, so we want to update the dentry timestamps.
+ * N.B. Move this to dcache?
+ */
+void vmfs_renew_times(struct dentry *dentry)
+{
+       dget(dentry);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
+       spin_lock(&dentry->d_lock);
+       for (;;) {
+               struct dentry *parent;
+
+               dentry->d_time = jiffies;
+               if (IS_ROOT(dentry))
+                       break;
+               parent = dentry->d_parent;
+               dget(parent);
+               spin_unlock(&dentry->d_lock);
+               dput(dentry);
+               dentry = parent;
+               spin_lock(&dentry->d_lock);
+       }
+       spin_unlock(&dentry->d_lock);
+#else
+       dentry->d_time = jiffies;
+
+       while (!IS_ROOT(dentry)) {
+               struct dentry *parent = dget_parent(dentry);
+               dput(dentry);
+               dentry = parent;
+
+               dentry->d_time = jiffies;
+       }
+#endif
+       dput(dentry);
+}
+
+static struct dentry *vmfs_lookup(struct inode *dir, struct dentry *dentry,
+                                 unsigned int flags)
+{
+       struct vmfs_fattr finfo;
+       struct inode *inode;
+       int error;
+       struct vmfs_sb_info *server;
+
+       VERBOSE("%s\n", dentry->d_name.name);
+
+       error = -ENAMETOOLONG;
+       if (dentry->d_name.len > VMFS_MAXNAMELEN)
+               goto out;
+
+       /* Do not allow lookup of names with backslashes in */
+       error = -EINVAL;
+
+       mutex_lock(&vmfs_mutex);
+       error = vmfs_proc_getattr(dentry, &finfo);
+#ifdef VMFSFS_PARANOIA
+       if (error && error != -ENOENT)
+               PARANOIA("find %s/%s failed, error=%d\n",
+                        DENTRY_PATH(dentry), error);
+#endif
+
+       inode = NULL;
+       if (error == -ENOENT)
+               goto add_entry;
+       if (!error) {
+               error = -EACCES;
+               finfo.f_ino = iunique(dentry->d_sb, 2);
+               inode = vmfs_iget(dir->i_sb, &finfo);
+               if (inode) {
+add_entry:
+                       server = server_from_dentry(dentry);
+                       dentry->d_op = &vmfs__dentry_operations_case;
+                       d_add(dentry, inode);
+                       vmfs_renew_times(dentry);
+                       error = 0;
+               }
+       }
+       mutex_unlock(&vmfs_mutex);
+out:
+       return ERR_PTR(error);
+}
+
+/*
+ * This code is common to all routines creating a new inode.
+ */
+static int vmfs_instantiate(struct dentry *dentry, int32_t vhandle,
+                           int have_id)
+{
+       struct inode *inode;
+       int error;
+       struct vmfs_fattr fattr;
+
+       VERBOSE("file %s/%s, fileid=%u\n", DENTRY_PATH(dentry), vhandle);
+
+       error = vmfs_proc_getattr(dentry, &fattr);
+       if (error)
+               goto out_close;
+
+       vmfs_renew_times(dentry);
+       fattr.f_ino = iunique(dentry->d_sb, 2);
+       inode = vmfs_iget(dentry->d_sb, &fattr);
+       if (!inode)
+               goto out_no_inode;
+
+       if (have_id) {
+               /* this is really only for create, where there is a
+                * catch-22 between creating the file and inode */
+               struct vmfs_inode_info *ei = VMFS_I(inode);
+               ei->vhandle = vhandle;
+               ei->vopen = 1;
+               ei->vaccess = VFS_OPEN_RDWR;
+       }
+       d_instantiate(dentry, inode);
+out:
+       return error;
+
+out_no_inode:
+       error = -EACCES;
+out_close:
+       if (have_id) {
+               PARANOIA("%s/%s failed, error=%d, closing %u\n",
+                        DENTRY_PATH(dentry), error, vhandle);
+               vmfs_close_fileid(dentry, vhandle);
+       }
+       goto out;
+}
+
+/* N.B. How should the mode argument be used? */
+static int
+vmfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+           bool excl)
+{
+       int32_t fileid;
+       int error;
+
+       VERBOSE("creating %s/%s, mode=%d\n", DENTRY_PATH(dentry), mode);
+
+       mutex_lock(&vmfs_mutex);
+
+       vmfs_invalid_dir_cache(dir);
+       error = vmfs_proc_create(dentry, mode, &fileid);
+       if (!error) {
+               error = vmfs_instantiate(dentry, fileid, 1);
+       } else {
+               PARANOIA("%s/%s failed, error=%d\n",
+                        DENTRY_PATH(dentry), error);
+       }
+       mutex_unlock(&vmfs_mutex);
+       return error;
+}
+
+/* N.B. How should the mode argument be used? */
+static int vmfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+       int error;
+
+       VERBOSE("\n");
+
+       mutex_lock(&vmfs_mutex);
+       vmfs_invalid_dir_cache(dir);
+       error = vmfs_proc_mkdir(dentry);
+       if (!error)
+               error = vmfs_instantiate(dentry, 0, 0);
+       mutex_unlock(&vmfs_mutex);
+       return error;
+}
+
+static int vmfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+       struct inode *inode = dentry->d_inode;
+       int error;
+
+       VERBOSE("\n");
+       /*
+        * Close the directory if it's open.
+        */
+       mutex_lock(&vmfs_mutex);
+       vmfs_close(inode);
+
+       /*
+        * Check that nobody else is using the directory..
+        */
+       error = -EBUSY;
+       if (!d_unhashed(dentry))
+               goto out;
+
+       vmfs_invalid_dir_cache(dir);
+       error = vmfs_proc_rmdir(dentry);
+
+out:
+       mutex_unlock(&vmfs_mutex);
+       return error;
+}
+
+static int vmfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+       int error;
+
+       /*
+        * Close the file if it's open.
+        */
+       mutex_lock(&vmfs_mutex);
+       vmfs_close(dentry->d_inode);
+
+       vmfs_invalid_dir_cache(dir);
+       error = vmfs_proc_unlink(dentry);
+       if (!error)
+               vmfs_renew_times(dentry);
+       mutex_unlock(&vmfs_mutex);
+       return error;
+}
+
+static int
+vmfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+           struct inode *new_dir, struct dentry *new_dentry)
+{
+       int error;
+
+       VERBOSE("\n");
+
+       /*
+        * Close any open files, and check whether to delete the
+        * target before attempting the rename.
+        */
+       mutex_lock(&vmfs_mutex);
+       if (old_dentry->d_inode)
+               vmfs_close(old_dentry->d_inode);
+       if (new_dentry->d_inode) {
+               vmfs_close(new_dentry->d_inode);
+               error = vmfs_proc_unlink(new_dentry);
+               if (error) {
+                       VERBOSE("unlink %s/%s, error=%d\n",
+                               DENTRY_PATH(new_dentry), error);
+                       goto out;
+               }
+               /* FIXME */
+               d_delete(new_dentry);
+       }
+
+       vmfs_invalid_dir_cache(old_dir);
+       vmfs_invalid_dir_cache(new_dir);
+       error = vmfs_proc_mv(old_dentry, new_dentry);
+       if (!error) {
+               vmfs_renew_times(old_dentry);
+               vmfs_renew_times(new_dentry);
+       }
+out:
+       mutex_unlock(&vmfs_mutex);
+       return error;
+}
+
+/*
+ * FIXME: samba servers won't let you create device nodes unless uid/gid
+ * matches the connection credentials (and we don't know which those are ...)
+ */
+static int
+vmfs_make_node(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t 
dev)
+{
+       return -EINVAL;
+}
+
+/*
+ * dentry = existing file
+ * new_dentry = new file
+ */
+static int
+vmfs_link(struct dentry *dentry, struct inode *dir, struct dentry *new_dentry)
+{
+       int error;
+
+       DEBUG1("vmfs_link old=%s/%s new=%s/%s\n",
+              DENTRY_PATH(dentry), DENTRY_PATH(new_dentry));
+       vmfs_invalid_dir_cache(dir);
+       error = vmfs_proc_link(server_from_dentry(dentry), dentry, new_dentry);
+       if (!error) {
+               vmfs_renew_times(dentry);
+               error = vmfs_instantiate(new_dentry, 0, 0);
+       }
+       return error;
+}
diff --git a/fs/vmfs/file.c b/fs/vmfs/file.c
new file mode 100644
index 0000000..b80bac0
--- /dev/null
+++ b/fs/vmfs/file.c
@@ -0,0 +1,500 @@
+/*
+ *  file.c
+ *
+ *  Copyright (C) 1995, 1996, 1997 by Paal-Kr. Engstad and Volker Lendecke
+ *  Copyright (C) 1997 by Volker Lendecke
+ *
+ *  Please add a note about your changes to vmfs_ in the ChangeLog file.
+ */
+
+#include <linux/time.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/net.h>
+#include <linux/aio.h>
+
+#include <linux/uaccess.h>
+#include <asm/system.h>
+#include <linux/version.h>
+
+#include "vmfsno.h"
+#include "vmfs_fs.h"
+
+#include "vmfs_debug.h"
+#include "proto.h"
+
+static int
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+vmfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+#else
+vmfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+       struct dentry *dentry = file->f_path.dentry;
+#endif
+       struct vmfs_sb_info *server = server_from_dentry(dentry);
+       int result;
+
+       VERBOSE("sync file %s/%s\n", DENTRY_PATH(dentry));
+
+       /*
+        * The VFS will writepage() all dirty pages for us, but we
+        * should send a VMFSflush to the server, letting it know that
+        * we want things synchronized with actual storage.
+        *
+        * Note: this function requires all pages to have been written already
+        *       (should be ok with writepage_sync)
+        */
+       mutex_lock(&vmfs_mutex);
+       result = vmfs_proc_flush(server, VMFS_I(dentry->d_inode)->vhandle);
+       mutex_unlock(&vmfs_mutex);
+
+       return result;
+}
+
+/*
+ * Read a page synchronously.
+ */
+static int vmfs_readpage_sync(struct dentry *dentry, struct page *page)
+{
+       char *buffer = kmap(page);
+       loff_t offset = (loff_t) page->index << PAGE_CACHE_SHIFT;
+       struct vmfs_sb_info *server = server_from_dentry(dentry);
+       int count = PAGE_SIZE;
+       unsigned int rsize = count;
+       int result;
+
+       VERBOSE("file %s/%s, count=%d@%lld, rsize=%d\n",
+               DENTRY_PATH(dentry), count, offset, rsize);
+
+       result = vmfs_open(dentry, 0, VMFS_O_RDONLY);
+       if (result < 0)
+               goto io_error;
+
+       do {
+               if (count < rsize)
+                       rsize = count;
+
+               result =
+                   server->ops->read(dentry->d_inode, offset, rsize, buffer);
+               if (result < 0)
+                       goto io_error;
+
+               count -= result;
+               offset += result;
+               buffer += result;
+               dentry->d_inode->i_atime =
+                   current_fs_time(dentry->d_inode->i_sb);
+               if (result < rsize)
+                       break;
+       } while (count);
+
+       memset(buffer, 0, count);
+       flush_dcache_page(page);
+       SetPageUptodate(page);
+       result = 0;
+
+io_error:
+       kunmap(page);
+       unlock_page(page);
+       return result;
+}
+
+/*
+ * We are called with the page locked and we unlock it when done.
+ */
+static int vmfs_readpage(struct file *file, struct page *page)
+{
+       int error;
+       struct dentry *dentry = file->f_path.dentry;
+
+       page_cache_get(page);
+       error = vmfs_readpage_sync(dentry, page);
+       page_cache_release(page);
+       return error;
+}
+
+/*
+ * Write a page synchronously.
+ * Offset is the data offset within the page.
+ */
+static int
+vmfs_writepage_sync(struct inode *inode, struct page *page,
+                   unsigned long pageoffset, unsigned int count)
+{
+       loff_t offset;
+       char *buffer = kmap(page) + pageoffset;
+       struct vmfs_sb_info *server = server_from_inode(inode);
+       unsigned int wsize = count;
+       int ret = 0;
+
+       offset = ((loff_t) page->index << PAGE_CACHE_SHIFT) + pageoffset;
+       VERBOSE("file ino=%ld, handle=%d, count=%d@%lld, wsize=%d\n",
+               inode->i_ino, VMFS_I(inode)->vhandle, count, offset, wsize);
+
+       do {
+               int write_ret;
+
+               if (count < wsize)
+                       wsize = count;
+
+               write_ret = server->ops->write(inode, offset, wsize, buffer);
+               if (write_ret < 0) {
+                       PARANOIA("failed write, wsize=%d, write_ret=%d\n",
+                                wsize, write_ret);
+                       ret = write_ret;
+                       break;
+               }
+
+               /* N.B. what if result < wsize?? */
+#ifdef VMFSFS_PARANOIA
+               if (write_ret < wsize)
+                       PARANOIA("short write, wsize=%d, write_ret=%d\n",
+                                wsize, write_ret);
+#endif
+               buffer += wsize;
+               offset += wsize;
+               count -= wsize;
+               /*
+                * Update the inode now rather than waiting for a refresh.
+                */
+
+               inode->i_mtime = inode->i_atime = current_fs_time(inode->i_sb);
+               if (offset > inode->i_size)
+                       inode->i_size = offset;
+       } while (count);
+
+       kunmap(page);
+       return ret;
+}
+
+/*
+ * Write a page to the server. This will be used for NFS swapping only
+ * (for now), and we currently do this synchronously only.
+ *
+ * We are called with the page locked and we unlock it when done.
+ */
+static int vmfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+       struct address_space *mapping = page->mapping;
+       struct inode *inode;
+       unsigned long end_index;
+       unsigned offset = PAGE_CACHE_SIZE;
+       int err;
+
+       DEBUG1("\n");
+
+       BUG_ON(!mapping);
+       inode = mapping->host;
+       BUG_ON(!inode);
+
+       end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+
+       /* easy case */
+       if (page->index < end_index)
+               goto do_it;
+       /* things got complicated... */
+       offset = inode->i_size & (PAGE_CACHE_SIZE - 1);
+       /* OK, are we completely out? */
+       if (page->index >= end_index + 1 || !offset)
+               return 0;       /* truncated - don't care */
+do_it:
+       page_cache_get(page);
+       err = vmfs_writepage_sync(inode, page, 0, offset);
+       SetPageUptodate(page);
+       unlock_page(page);
+       page_cache_release(page);
+       return err;
+}
+
+static int
+vmfs_updatepage(struct file *file, struct page *page, unsigned long offset,
+               unsigned int count)
+{
+       struct dentry *dentry = file->f_path.dentry;
+
+       DEBUG1("(%s/%s %d@%lld)\n", DENTRY_PATH(dentry), count,
+              ((unsigned long long)page->index << PAGE_CACHE_SHIFT) + offset);
+
+       return vmfs_writepage_sync(dentry->d_inode, page, offset, count);
+}
+
+static ssize_t
+vmfs_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
+                  unsigned long nr_segs, loff_t pos)
+{
+       struct file *file = iocb->ki_filp;
+       struct dentry *dentry = file->f_path.dentry;
+       ssize_t status;
+
+       VERBOSE("file %s/%s, count=%lu@%lu\n", DENTRY_PATH(dentry),
+               (unsigned long)iocb->ki_left, (unsigned long)pos);
+
+       mutex_lock(&vmfs_mutex);
+       status = vmfs_revalidate_inode(dentry);
+       mutex_unlock(&vmfs_mutex);
+       if (status) {
+               PARANOIA("%s/%s validation failed, error=%Zd\n",
+                        DENTRY_PATH(dentry), status);
+               goto out;
+       }
+
+       VERBOSE("before read, size=%ld, flags=%x, atime=%ld\n",
+               (long)dentry->d_inode->i_size,
+               dentry->d_inode->i_flags, dentry->d_inode->i_atime.tv_sec);
+
+       status = generic_file_aio_read(iocb, iov, nr_segs, pos);
+out:
+       return status;
+}
+
+static int vmfs_file_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct dentry *dentry = file->f_path.dentry;
+       int status;
+
+       VERBOSE("file %s/%s, address %lu - %lu\n",
+               DENTRY_PATH(dentry), vma->vm_start, vma->vm_end);
+
+       mutex_lock(&vmfs_mutex);
+       status = vmfs_revalidate_inode(dentry);
+       mutex_unlock(&vmfs_mutex);
+       if (status) {
+               PARANOIA("%s/%s validation failed, error=%d\n",
+                        DENTRY_PATH(dentry), status);
+               goto out;
+       }
+       status = generic_file_mmap(file, vma);
+out:
+       return status;
+}
+
+static ssize_t
+vmfs_file_splice_read(struct file *file, loff_t *ppos,
+                     struct pipe_inode_info *pipe, size_t count,
+                     unsigned int flags)
+{
+       struct dentry *dentry = file->f_path.dentry;
+       ssize_t status;
+
+       VERBOSE("file %s/%s, pos=%lld, count=%u\n",
+               DENTRY_PATH(dentry), *ppos, count);
+
+       mutex_lock(&vmfs_mutex);
+       status = vmfs_revalidate_inode(dentry);
+       mutex_unlock(&vmfs_mutex);
+       if (status) {
+               PARANOIA("%s/%s validation failed, error=%Zd\n",
+                        DENTRY_PATH(dentry), status);
+               goto out;
+       }
+       status = generic_file_splice_read(file, ppos, pipe, count, flags);
+out:
+       return status;
+}
+
+/*
+ * This does the "real" work of the write. The generic routine has
+ * allocated the page, locked it, done all the page alignment stuff
+ * calculations etc. Now we should just copy the data from user
+ * space and write it back to the real medium..
+ *
+ * If the writer ends up delaying the write, the writer needs to
+ * increment the page use counts until he is done with the page.
+ */
+static int vmfs_write_begin(struct file *file, struct address_space *mapping,
+                           loff_t pos, unsigned len, unsigned flags,
+                           struct page **pagep, void **fsdata)
+{
+       pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+       *pagep = grab_cache_page(mapping, index);
+       if (!*pagep)
+               return -ENOMEM;
+       return 0;
+}
+
+static int vmfs_write_end(struct file *file, struct address_space *mapping,
+                         loff_t pos, unsigned len, unsigned copied,
+                         struct page *page, void *fsdata)
+{
+       int status;
+       unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
+
+       mutex_lock(&vmfs_mutex);
+       status = vmfs_updatepage(file, page, offset, copied);
+       mutex_unlock(&vmfs_mutex);
+
+       if (!status) {
+               if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE)
+                       SetPageUptodate(page);
+               status = copied;
+       }
+
+       unlock_page(page);
+       page_cache_release(page);
+
+       return status;
+}
+
+const struct address_space_operations vmfs_file_aops = {
+       .readpage = vmfs_readpage,
+       .writepage = vmfs_writepage,
+       .write_begin = vmfs_write_begin,
+       .write_end = vmfs_write_end,
+};
+
+/*
+ * Write to a file (through the page cache).
+ */
+static ssize_t
+vmfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
+                   unsigned long nr_segs, loff_t pos)
+{
+       struct file *file = iocb->ki_filp;
+       struct dentry *dentry = file->f_path.dentry;
+       ssize_t result;
+
+       VERBOSE("file %s/%s, count=%lu@%lu\n",
+               DENTRY_PATH(dentry),
+               (unsigned long)iocb->ki_left, (unsigned long)pos);
+
+       mutex_lock(&vmfs_mutex);
+       result = vmfs_revalidate_inode(dentry);
+       mutex_unlock(&vmfs_mutex);
+       if (result) {
+               PARANOIA("%s/%s validation failed, error=%Zd\n",
+                        DENTRY_PATH(dentry), result);
+               goto out;
+       }
+
+       mutex_lock(&vmfs_mutex);
+       result = vmfs_open(dentry, 0, VMFS_O_WRONLY);
+       mutex_unlock(&vmfs_mutex);
+
+       DEBUG1("1\n");
+
+       if (result)
+               goto out;
+
+       if (iocb->ki_left > 0) {
+               DEBUG1("1\n");
+
+               result = generic_file_aio_write(iocb, iov, nr_segs, pos);
+               VERBOSE("pos=%ld, size=%ld, mtime=%ld, atime=%ld\n",
+                       (long)file->f_pos, (long)dentry->d_inode->i_size,
+                       dentry->d_inode->i_mtime.tv_sec,
+                       dentry->d_inode->i_atime.tv_sec);
+
+               DEBUG1("2\n");
+       }
+out:
+       DEBUG1("return\n");
+       return result;
+}
+
+static int vmfs_file_open(struct inode *inode, struct file *file)
+{
+       int result;
+       struct dentry *dentry = file->f_path.dentry;
+       int vmfs_mode = (file->f_mode & O_ACCMODE) - 1;
+       int vmfs_flags = file->f_flags;
+
+       VERBOSE("\n");
+
+       mutex_lock(&vmfs_mutex);
+       result = vmfs_open(dentry, vmfs_flags, vmfs_mode);
+       if (result)
+               goto out;
+
+       DEBUG1("inode=%p, ei=%p\n", inode, VMFS_I(inode));
+
+       VMFS_I(inode)->openers++;
+out:
+       mutex_unlock(&vmfs_mutex);
+       DEBUG1("return\n");
+
+       return result;
+}
+
+static int vmfs_file_release(struct inode *inode, struct file *file)
+{
+       mutex_lock(&vmfs_mutex);
+       if (!--VMFS_I(inode)->openers) {
+               /* We must flush any dirty pages now as we won't be able to
+                  write anything after close. mmap can trigger this.
+                  "openers" should perhaps include mmap'ers ... */
+               filemap_write_and_wait(inode->i_mapping);
+               vmfs_close(inode);
+       }
+       mutex_unlock(&vmfs_mutex);
+       return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
+static loff_t vmfs_remote_llseek(struct file *file, loff_t offset, int origin)
+{
+       loff_t ret;
+       mutex_lock(&vmfs_mutex);
+       ret = generic_file_llseek(file, offset, origin);
+       mutex_unlock(&vmfs_mutex);
+       return ret;
+}
+#endif
+/*
+ * Check whether the required access is compatible with
+ * an inode's permission. VMFS doesn't recognize superuser
+ * privileges, so we need our own check for this.
+ */
+static int
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
+vmfs_file_permission(struct inode *inode, int mask, struct nameidata *nd)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+vmfs_file_permission(struct inode *inode, int mask)
+#else
+vmfs_file_permission(struct inode *inode, int mask)
+#endif
+{
+       int mode = inode->i_mode;
+       int error = 0;
+
+       VERBOSE("mode=%x, mask=%x\n", mode, mask);
+
+       /* Look at user permissions */
+       mode >>= 6;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
+       if ((mask & ~mode) & (MAY_READ | MAY_WRITE | MAY_EXEC))
+#else
+       if ((mode & 7 & mask) != mask)
+#endif
+               error = -EACCES;
+       return error;
+}
+
+const struct file_operations vmfs_file_operations = {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
+       .llseek = vmfs_remote_llseek,
+#else
+       .llseek = remote_llseek,
+#endif
+       .read = do_sync_read,
+       .aio_read = vmfs_file_aio_read,
+       .write = do_sync_write,
+       .aio_write = vmfs_file_aio_write,
+       .unlocked_ioctl = vmfs_unlocked_ioctl,
+       .mmap = vmfs_file_mmap,
+       .open = vmfs_file_open,
+       .release = vmfs_file_release,
+       .fsync = vmfs_fsync,
+       .splice_read = vmfs_file_splice_read,
+};
+
+const struct inode_operations vmfs_file_inode_operations = {
+       .permission = vmfs_file_permission,
+       .getattr = vmfs_getattr,
+       .setattr = vmfs_notify_change,
+};
diff --git a/fs/vmfs/getopt.c b/fs/vmfs/getopt.c
new file mode 100644
index 0000000..2ce5adc
--- /dev/null
+++ b/fs/vmfs/getopt.c
@@ -0,0 +1,67 @@
+/*
+ * getopt.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/net.h>
+
+#include "getopt.h"
+
+/**
+ *  vmfs_getopt - option parser
+ *  @caller: name of the caller, for error messages
+ *  @options: the options string
+ *  @opts: an array of &struct option entries controlling parser operations
+ *  @optopt: output; will contain the current option
+ *  @optarg: output; will contain the value (if one exists)
+ *  @flag: output; may be NULL; should point to a long for or'ing flags
+ *  @value: output; may be NULL; will be overwritten with the integer value
+ *      of the current argument.
+ *
+ *  Helper to parse options on the format used by mount ("a=b,c=d,e,f").
+ *  Returns opts->val if a matching entry in the 'opts' array is found,
+ *  0 when no more tokens are found, -1 if an error is encountered.
+ */
+int vmfs_getopt(char *caller, char **options, struct option *opts,
+               char **optopt, char **optarg, unsigned long *flag,
+               unsigned long *value)
+{
+       char *token;
+       char *val;
+       int i;
+
+       do {
+               token = strsep(options, ",");
+               if (token == NULL)
+                       return 0;
+       } while (*token == '\0');
+       *optopt = token;
+
+       *optarg = NULL;
+       val = strchr(token, '=');
+       if (val != NULL) {
+               *val++ = 0;
+               if (value)
+                       *value = kstrtoul(val, 0, NULL);
+               *optarg = val;
+       }
+
+       for (i = 0; opts[i].name != NULL; i++) {
+               if (!strcmp(opts[i].name, token)) {
+                       if (!opts[i].flag && (!val || !*val)) {
+                               printk
+                                  ("%s: the %s option requires an argument\n",
+                                   caller, token);
+                               return -1;
+                       }
+
+                       if (flag && opts[i].flag)
+                               *flag |= opts[i].flag;
+
+                       return opts[i].val;
+               }
+       }
+       pr_info("%s: Unrecognized mount option %s\n", caller, token);
+       return -1;
+}
diff --git a/fs/vmfs/getopt.h b/fs/vmfs/getopt.h
new file mode 100644
index 0000000..98dddfc
--- /dev/null
+++ b/fs/vmfs/getopt.h
@@ -0,0 +1,14 @@
+#ifndef _LINUX_GETOPT_H
+#define _LINUX_GETOPT_H
+
+struct option {
+       const char *name;
+       unsigned long flag;
+       int val;
+};
+
+extern int vmfs_getopt(char *caller, char **options, struct option *opts,
+                      char **optopt, char **optarg, unsigned long *flag,
+                      unsigned long *value);
+
+#endif /* _LINUX_GETOPT_H */
diff --git a/fs/vmfs/inode.c b/fs/vmfs/inode.c
new file mode 100644
index 0000000..492dc8a
--- /dev/null
+++ b/fs/vmfs/inode.c
@@ -0,0 +1,667 @@
+/*
+ *  inode.c
+ *
+ *  Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *  Copyright (C) 1997 by Volker Lendecke
+ *
+ *  Copyright (C) 2008-2009 ARM Limited
+ *
+ *  Please add a note about your changes to vmfs_ in the ChangeLog file.
+ */
+
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/file.h>
+#include <linux/dcache.h>
+#include <linux/seq_file.h>
+#include <linux/mount.h>
+#include <linux/vfs.h>
+#include <linux/highuid.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include "vmfs_fs.h"
+#include "vmfsno.h"
+#include "vmfs_mount.h"
+
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+
+#include "vmfs_debug.h"
+#include "getopt.h"
+#include "proto.h"
+
+#include "messagebox.h"
+#include "vmfs.h"
+
+/* gpb: no reason this has to be in linux/magic.h */
+
+#define VMFS_SUPER_MAGIC 0x564D4653    /* VMFS */
+
+/* gpb: this needs to be made configurable */
+
+#define VMFS_TTL_DEFAULT 1000
+
+DEFINE_MUTEX(vmfs_mutex);
+
+static void vmfs_delete_inode(struct inode *);
+static void vmfs_put_super(struct super_block *);
+static int vmfs_statfs(struct dentry *, struct kstatfs *);
+static int vmfs_show_options(struct seq_file *, struct dentry *);
+
+static struct kmem_cache *vmfs_inode_cachep;
+
+static MessageBox *mbox;
+static VFS *vfs;
+
+static struct inode *vmfs_alloc_inode(struct super_block *sb)
+{
+       struct vmfs_inode_info *ei;
+       ei = (struct vmfs_inode_info *)kmem_cache_alloc(vmfs_inode_cachep,
+                                                       GFP_KERNEL);
+       if (!ei)
+               return NULL;
+       return &ei->vfs_inode;
+}
+
+static void vmfs_destroy_inode(struct inode *inode)
+{
+       kmem_cache_free(vmfs_inode_cachep, VMFS_I(inode));
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
+static void init_once(void *foo)
+#else
+static void init_once(struct kmem_cache *cachep, void *foo)
+#endif
+{
+       struct vmfs_inode_info *ei = (struct vmfs_inode_info *)foo;
+
+       inode_init_once(&ei->vfs_inode);
+}
+
+static int init_inodecache(void)
+{
+       vmfs_inode_cachep = kmem_cache_create("vmfs_inode_cache",
+                                             sizeof(struct vmfs_inode_info),
+                                             0, (SLAB_RECLAIM_ACCOUNT |
+                                                 SLAB_MEM_SPREAD), init_once);
+       if (vmfs_inode_cachep == NULL)
+               return -ENOMEM;
+       return 0;
+}
+
+static void destroy_inodecache(void)
+{
+       kmem_cache_destroy(vmfs_inode_cachep);
+}
+
+static int vmfs_remount(struct super_block *sb, int *flags, char *data)
+{
+       *flags |= MS_NODIRATIME;
+       return 0;
+}
+
+static const struct super_operations vmfs_sops = {
+       .alloc_inode = vmfs_alloc_inode,
+       .destroy_inode = vmfs_destroy_inode,
+       .drop_inode = generic_delete_inode,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+       .delete_inode = vmfs_delete_inode,
+#else
+       .evict_inode = vmfs_delete_inode,
+#endif
+       .put_super = vmfs_put_super,
+       .statfs = vmfs_statfs,
+       .show_options = vmfs_show_options,
+       .remount_fs = vmfs_remount,
+};
+
+/* We are always generating a new inode here */
+struct inode *vmfs_iget(struct super_block *sb, struct vmfs_fattr *fattr)
+{
+       struct inode *result;
+
+       DEBUG1("vmfs_iget: %p\n", fattr);
+
+       result = new_inode(sb);
+       if (!result)
+               return result;
+       result->i_ino = fattr->f_ino;
+       VMFS_I(result)->open = 0;
+       VMFS_I(result)->closed = 0;
+       VMFS_I(result)->openers = 0;
+       VMFS_I(result)->vhandle = -1;
+       VMFS_I(result)->vaccess = 0;
+       VMFS_I(result)->vopen = 0;
+
+       vmfs_set_inode_attr(result, fattr);
+       if (S_ISREG(result->i_mode)) {
+               result->i_op = &vmfs_file_inode_operations;
+               result->i_fop = &vmfs_file_operations;
+               result->i_data.a_ops = &vmfs_file_aops;
+       } else if (S_ISDIR(result->i_mode)) {
+               result->i_op = &vmfs_dir_inode_operations_unix;
+               result->i_fop = &vmfs_dir_operations;
+       } else if (S_ISLNK(result->i_mode)) {
+               result->i_op = &vmfs_link_inode_operations;
+       } else {
+               init_special_inode(result, result->i_mode, fattr->f_rdev);
+       }
+       insert_inode_hash(result);
+       return result;
+}
+
+/*
+ * Copy the inode data to a vmfs_fattr structure.
+ */
+void vmfs_get_inode_attr(struct inode *inode, struct vmfs_fattr *fattr)
+{
+       memset(fattr, 0, sizeof(struct vmfs_fattr));
+       fattr->f_mode = inode->i_mode;
+       fattr->f_nlink = inode->i_nlink;
+       fattr->f_ino = inode->i_ino;
+       fattr->f_uid = inode->i_uid;
+       fattr->f_gid = inode->i_gid;
+       fattr->f_size = inode->i_size;
+       fattr->f_mtime = inode->i_mtime;
+       fattr->f_ctime = inode->i_ctime;
+       fattr->f_atime = inode->i_atime;
+       fattr->f_blocks = inode->i_blocks;
+
+}
+
+/*
+ * Update the inode, possibly causing it to invalidate its pages if mtime/size
+ * is different from last time.
+ */
+void vmfs_set_inode_attr(struct inode *inode, struct vmfs_fattr *fattr)
+{
+       struct vmfs_inode_info *ei = VMFS_I(inode);
+
+       /*
+        * A size change should have a different mtime, or same mtime
+        * but different size.
+        */
+       time_t last_time = inode->i_mtime.tv_sec;
+       loff_t last_sz = inode->i_size;
+
+       inode->i_mode = fattr->f_mode;
+       set_nlink(inode, fattr->f_nlink);
+       inode->i_uid = fattr->f_uid;
+       inode->i_gid = fattr->f_gid;
+       inode->i_ctime = fattr->f_ctime;
+       inode->i_blocks = fattr->f_blocks;
+       inode->i_size = fattr->f_size;
+       inode->i_mtime = fattr->f_mtime;
+       inode->i_atime = fattr->f_atime;
+
+
+       /*
+        * Update the "last time refreshed" field for revalidation.
+        */
+       ei->oldmtime = jiffies;
+
+       if (inode->i_mtime.tv_sec != last_time || inode->i_size != last_sz) {
+               VERBOSE("%ld changed, old=%ld, new=%ld, oz=%ld, nz=%ld\n",
+                       inode->i_ino,
+                       (long)last_time, (long)inode->i_mtime.tv_sec,
+                       (long)last_sz, (long)inode->i_size);
+
+               if (!S_ISDIR(inode->i_mode))
+                       invalidate_remote_inode(inode);
+       }
+}
+
+/*
+ * This is called if the connection has gone bad ...
+ * try to kill off all the current inodes.
+ */
+void vmfs_invalidate_inodes(struct vmfs_sb_info *server)
+{
+       VERBOSE("\n");
+       shrink_dcache_sb(SB_of(server));
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
+       invalidate_inodes(SB_of(server));
+#endif
+}
+
+/*
+ * This is called to update the inode attributes after
+ * we've made changes to a file or directory.
+ */
+static int vmfs_refresh_inode(struct dentry *dentry)
+{
+       struct inode *inode = dentry->d_inode;
+       int error;
+       struct vmfs_fattr fattr;
+
+       DEBUG1("");
+
+       error = vmfs_proc_getattr(dentry, &fattr);
+       if (!error) {
+               vmfs_renew_times(dentry);
+               /*
+                * Check whether the type part of the mode changed,
+                * and don't update the attributes if it did.
+                *
+                * And don't dick with the root inode
+                */
+               if (inode->i_ino == 2)
+                       return error;
+               if (S_ISLNK(inode->i_mode))
+                       return error;   /* VFS will deal with it */
+
+               if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) {
+                       vmfs_set_inode_attr(inode, &fattr);
+               } else {
+                       /*
+                        * Big trouble! The inode has become a new object,
+                        * so any operations attempted on it are invalid.
+                        *
+                        * To limit damage, mark the inode as bad so that
+                        * subsequent lookup validations will fail.
+                        */
+                       PARANOIA("%s/%s changed mode, %07o to %07o\n",
+                                DENTRY_PATH(dentry),
+                                inode->i_mode, fattr.f_mode);
+
+                       fattr.f_mode = inode->i_mode;   /* save mode */
+                       make_bad_inode(inode);
+                       inode->i_mode = fattr.f_mode;   /* restore mode */
+                       /*
+                        * No need to worry about unhashing the dentry: the
+                        * lookup validation will see that the inode is bad.
+                        * But we do want to invalidate the caches ...
+                        */
+                       if (!S_ISDIR(inode->i_mode))
+                               invalidate_remote_inode(inode);
+                       else
+                               vmfs_invalid_dir_cache(inode);
+                       error = -EIO;
+               }
+       }
+       return error;
+}
+
+/*
+ * This is called when we want to check whether the inode
+ * has changed on the server.  If it has changed, we must
+ * invalidate our local caches.
+ */
+int vmfs_revalidate_inode(struct dentry *dentry)
+{
+       struct vmfs_sb_info *s = server_from_dentry(dentry);
+       struct inode *inode = dentry->d_inode;
+       int error = 0;
+
+       DEBUG1("vmfs_revalidate_inode\n");
+
+       /*
+        * Check whether we've recently refreshed the inode.
+        */
+       if (time_before(jiffies, VMFS_I(inode)->oldmtime + VMFS_MAX_AGE(s))) {
+               VERBOSE("up-to-date, ino=%ld, jiffies=%lu, oldtime=%lu\n",
+                       inode->i_ino, jiffies, VMFS_I(inode)->oldmtime);
+               goto out;
+       }
+
+       error = vmfs_refresh_inode(dentry);
+out:
+       return error;
+}
+
+/*
+ * This routine is called when i_nlink == 0 and i_count goes to 0.
+ * All blocking cleanup operations need to go here to avoid races.
+ */
+static void vmfs_delete_inode(struct inode *ino)
+{
+       DEBUG1("ino=%ld\n", ino->i_ino);
+       truncate_inode_pages(&ino->i_data, 0);
+       mutex_lock(&vmfs_mutex);
+       if (vmfs_close(ino))
+               PARANOIA("could not close inode %ld\n", ino->i_ino);
+       mutex_unlock(&vmfs_mutex);
+}
+
+/*
+ * vmfs_show_options() is for displaying mount options in /proc/mounts.
+ * It tries to avoid showing settings that were not changed from their
+ * defaults.
+ */
+static int vmfs_show_options(struct seq_file *s, struct dentry *m)
+{
+
+       return 0;
+}
+
+static void vmfs_put_super(struct super_block *sb)
+{
+       struct vmfs_sb_info *server = VMFS_SB(sb);
+
+       mutex_lock(&vmfs_mutex);
+
+       kfree(server->ops);
+
+       sb->s_fs_info = NULL;
+
+       mutex_unlock(&vmfs_mutex);
+       kfree(server);
+}
+
+static int vmfs_fill_super(struct super_block *sb, void *raw_data_unused,
+                          int silent)
+{
+       struct vmfs_sb_info *server;
+       struct vmfs_mount_data_kernel *mnt;
+       struct inode *root_inode;
+       struct vmfs_fattr root;
+       void *mem;
+
+       sb->s_flags |= MS_NODIRATIME;
+       sb->s_blocksize = 1024;
+       sb->s_blocksize_bits = 10;
+       sb->s_magic = VMFS_SUPER_MAGIC;
+       sb->s_op = &vmfs_sops;
+       sb->s_time_gran = 100;
+
+       server = kzalloc(sizeof(struct vmfs_sb_info), GFP_KERNEL);
+       if (!server)
+               goto out_no_server;
+       sb->s_fs_info = server;
+
+       server->super_block = sb;
+       server->mnt = NULL;
+       server->vfs = vfs;
+
+       sema_init(&server->sem, 1);
+       INIT_LIST_HEAD(&server->entry);
+
+       /* Allocate global temp buffer and some superblock helper structs */
+       /* FIXME: move these to the vmfs_sb_info struct */
+       VERBOSE("alloc chunk = %u\n", sizeof(struct vmfs_ops) +
+               sizeof(struct vmfs_mount_data_kernel));
+       mem = kmalloc(sizeof(struct vmfs_ops) +
+                     sizeof(struct vmfs_mount_data_kernel), GFP_KERNEL);
+       if (!mem)
+               goto out_no_mem;
+
+       server->ops = mem;
+       vmfs_install_ops(server->ops);
+       server->mnt = mem + sizeof(struct vmfs_ops);
+
+       mnt = server->mnt;
+
+       memset(mnt, 0, sizeof(struct vmfs_mount_data_kernel));
+
+       mnt->ttl = VMFS_TTL_DEFAULT;
+       mnt->file_mode = S_IRWXU | S_IRGRP | S_IXGRP |
+           S_IROTH | S_IXOTH | S_IFREG;
+       mnt->dir_mode = S_IRWXU | S_IRGRP | S_IXGRP |
+           S_IROTH | S_IXOTH | S_IFDIR;
+
+       mnt->mounted_uid = current->real_cred->uid;
+
+       /*
+        * Keep the super block locked while we get the root inode.
+        */
+       vmfs_init_root_dirent(server, &root, sb);
+       root_inode = vmfs_iget(sb, &root);
+       if (!root_inode)
+               goto out_no_root;
+
+       sb->s_root = d_make_root(root_inode);
+       if (!sb->s_root)
+               goto out_no_root;
+
+       vmfs_new_dentry(sb->s_root);
+
+       return 0;
+
+out_no_root:
+       iput(root_inode);
+       kfree(mem);
+out_no_mem:
+       if (!server->mnt)
+               pr_err("vmfs_fill_super: allocation failure\n");
+       sb->s_fs_info = NULL;
+       kfree(server);
+       return -EINVAL;
+out_no_server:
+       pr_err(
+              "vmfs_fill_super: cannot allocate struct vmfs_sb_info\n");
+       return -ENOMEM;
+}
+
+static int vmfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+       int result;
+
+       mutex_lock(&vmfs_mutex);
+
+       result = vmfs_proc_dskattr(dentry, buf);
+
+       mutex_unlock(&vmfs_mutex);
+
+       buf->f_type = VMFS_SUPER_MAGIC;
+       buf->f_namelen = VMFS_MAXPATHLEN;
+       return result;
+}
+
+int vmfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
+                struct kstat *stat)
+{
+       int err = vmfs_revalidate_inode(dentry);
+       if (!err)
+               generic_fillattr(dentry->d_inode, stat);
+       return err;
+}
+
+int vmfs_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+       struct inode *inode = dentry->d_inode;
+       struct vmfs_sb_info *server = server_from_dentry(dentry);
+       unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXUGO);
+       int error, changed, refresh = 0;
+       struct vmfs_fattr fattr;
+
+       DEBUG1("\n");
+
+       mutex_lock(&vmfs_mutex);
+
+       error = vmfs_revalidate_inode(dentry);
+       if (error)
+               goto out;
+
+       error = inode_change_ok(inode, attr);
+       if (error < 0)
+               goto out;
+
+       error = -EPERM;
+#ifdef CONFIG_UIDGID_STRICT_TYPE_CHECKS
+       if ((attr->ia_valid & ATTR_UID) &&
+           (attr->ia_uid.val != (server->mnt->uid).val))
+               goto out;
+#else
+       if ((attr->ia_valid & ATTR_UID) &&
+           (attr->ia_uid != server->mnt->uid))
+               goto out;
+#endif
+
+#ifdef CONFIG_UIDGID_STRICT_TYPE_CHECKS
+       if ((attr->ia_valid & ATTR_GID) &&
+           ((attr->ia_gid).val != (server->mnt->gid).val))
+               goto out;
+#else
+       if ((attr->ia_valid & ATTR_GID) &&
+           (attr->ia_gid != server->mnt->gid))
+               goto out;
+#endif
+
+       if ((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~mask))
+               goto out;
+
+       if ((attr->ia_valid & ATTR_SIZE) != 0) {
+               VERBOSE("changing %s/%s, old size=%ld, new size=%ld\n",
+                       DENTRY_PATH(dentry),
+                       (long)inode->i_size, (long)attr->ia_size);
+
+               filemap_write_and_wait(inode->i_mapping);
+
+               error = vmfs_open(dentry, 0, O_WRONLY);
+               if (error)
+                       goto out;
+               error = server->ops->truncate(inode, attr->ia_size);
+               if (error)
+                       goto out;
+               /*error = vmtruncate(inode, attr->ia_size);
+               if (error)
+                       goto out;*/
+               refresh = 1;
+       }
+
+       /*
+        * Initialize the fattr and check for changed fields.
+        * Note: CTIME under VMFS is creation time rather than
+        * change time, so we don't attempt to change it.
+        */
+       vmfs_get_inode_attr(inode, &fattr);
+
+       changed = 0;
+       if ((attr->ia_valid & ATTR_MTIME) != 0) {
+               fattr.f_mtime = attr->ia_mtime;
+               changed = 1;
+       }
+       if ((attr->ia_valid & ATTR_ATIME) != 0) {
+               fattr.f_atime = attr->ia_atime;
+               /* Earlier protocols don't have an access time */
+       }
+       if (changed) {
+               error = vmfs_proc_settime(dentry, &fattr);
+               if (error)
+                       goto out;
+               refresh = 1;
+       }
+
+       /*
+        * Check for mode changes ... we're extremely limited in
+        * what can be set for VMFS servers: just the read-only bit.
+        */
+       if ((attr->ia_valid & ATTR_MODE) != 0) {
+               VERBOSE("%s/%s mode change, old=%x, new=%x\n",
+                       DENTRY_PATH(dentry), fattr.f_mode, attr->ia_mode);
+               changed = 0;
+               if (attr->ia_mode & S_IWUSR) {
+                       if (fattr.attr & aRONLY) {
+                               fattr.attr &= ~aRONLY;
+                               changed = 1;
+                       }
+               } else {
+                       if (!(fattr.attr & aRONLY)) {
+                               fattr.attr |= aRONLY;
+                               changed = 1;
+                       }
+               }
+               if (changed) {
+                       error = vmfs_proc_setattr(dentry, &fattr);
+                       if (error)
+                               goto out;
+                       refresh = 1;
+               }
+       }
+       error = 0;
+
+out:
+       if (refresh)
+               vmfs_refresh_inode(dentry);
+       mutex_unlock(&vmfs_mutex);
+       return error;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
+static int vmfs_get_sb(struct file_system_type *fs_type,
+                      int flags, const char *dev_name, void *data,
+                      struct vfsmount *mnt)
+{
+       return get_sb_nodev(fs_type, flags, data, vmfs_fill_super, mnt);
+}
+#else
+static struct dentry *vmfs_do_mount(struct file_system_type *fs_type,
+                                   int flags, const char *dev_name,
+                                   void *data)
+{
+       return mount_nodev(fs_type, flags, data, vmfs_fill_super);
+}
+#endif
+
+static struct file_system_type vmfs_fs_type = {
+       .owner = THIS_MODULE,
+       .name = "vmfs",
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
+       .get_sb = vmfs_get_sb,
+#else
+       .mount = vmfs_do_mount,
+#endif
+       .kill_sb = kill_anon_super,
+       .fs_flags = FS_BINARY_MOUNTDATA,
+};
+
+static int __init init_vmfs_fs(void)
+{
+       int err;
+       DEBUG1("registering ...\n");
+
+       err = init_inodecache();
+       if (err)
+               goto out_inode;
+
+       /* map the message box device into memory */
+
+       mbox = mb_new(CONFIG_VMFS_DEV_BASE, CONFIG_VMFS_IRQ);
+
+       if (mbox == NULL) {
+               err = -1;
+               goto out_inode;
+       }
+
+       vfs = vfsop_new(mbox);
+       if (vfs == NULL) {
+               err = -1;
+               goto out_mbox;
+       }
+
+       err = register_filesystem(&vmfs_fs_type);
+       if (err)
+               goto out;
+       return 0;
+out:
+       destroy_inodecache();
+       vfsop_delete(vfs);
+out_mbox:
+       mb_delete(mbox);
+out_inode:
+       return err;
+}
+
+static void __exit exit_vmfs_fs(void)
+{
+       DEBUG1("unregistering ...\n");
+       unregister_filesystem(&vmfs_fs_type);
+
+       vfsop_delete(vfs);
+       mb_delete(mbox);
+
+       destroy_inodecache();
+}
+
+module_init(init_vmfs_fs)
+       module_exit(exit_vmfs_fs)
+       MODULE_LICENSE("GPL");
diff --git a/fs/vmfs/ioctl.c b/fs/vmfs/ioctl.c
new file mode 100644
index 0000000..baec528
--- /dev/null
+++ b/fs/vmfs/ioctl.c
@@ -0,0 +1,57 @@
+/*
+ *  ioctl.c
+ *
+ *  Copyright (C) 1995, 1996 by Volker Lendecke
+ *  Copyright (C) 1997 by Volker Lendecke
+ *
+ *  Copyright (C) 2008-2009 ARM Limited
+ *
+ *  Please add a note about your changes to vmfs_ in the ChangeLog file.
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/highuid.h>
+
+#include "vmfs_fs.h"
+#include "vmfs_mount.h"
+
+#include <linux/uaccess.h>
+
+#include "proto.h"
+
+long vmfs_unlocked_ioctl(struct file *filp, unsigned int cmd,
+                        unsigned long arg)
+{
+       struct inode *inode = filp->f_path.dentry->d_inode;
+       struct vmfs_sb_info *server = server_from_inode(inode);
+       int result = -EINVAL;
+
+       switch (cmd) {
+               uid16_t uid16;
+               uid_t uid32;
+       case VMFS_IOC_GETMOUNTUID:
+#ifdef CONFIG_UIDGID_STRICT_TYPE_CHECKS
+               SET_UID(uid16, (server->mnt->mounted_uid).val);
+#else
+               SET_UID(uid16, server->mnt->mounted_uid);
+#endif
+               result = put_user(uid16, (uid16_t __user *) arg);
+               break;
+       case VMFS_IOC_GETMOUNTUID32:
+#ifdef CONFIG_UIDGID_STRICT_TYPE_CHECKS
+               SET_UID(uid32, (server->mnt->mounted_uid).val);
+#else
+               SET_UID(uid32, server->mnt->mounted_uid);
+#endif
+               result = put_user(uid32, (uid_t __user *) arg);
+               break;
+       default:
+               break;
+       }
+
+       return result;
+}
diff --git a/fs/vmfs/mboxtypes.h b/fs/vmfs/mboxtypes.h
new file mode 100644
index 0000000..54c4ebd
--- /dev/null
+++ b/fs/vmfs/mboxtypes.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2008-2009 ARM Limited. All rights reserved.
+ */
+
+/*
+ * Messagebox definitions shared between messagebox device and driver
+ */
+
+#ifndef MBOXTYPES_H
+#define MBOXTYPES_H
+
+enum MessageBoxDefs {
+       MBOX_CONTROL_START = 1, /* start sending a message */
+       MBOX_CONTROL_END = 2,   /* end sending a message */
+       MBOX_CONTROL_CANCEL = 3,        /* cancel sending a message */
+
+       MBOX_STATUS_RXEMPTY = (1 << 0), /* no more incoming message data */
+       MBOX_STATUS_TXFULL = (1 << 1),  /* no room for an outgoing message */
+       MBOX_STATUS_RXREADY = (1 << 2), /* a new incoming message is
+                                          ready to be received */
+
+       MBOX_REGISTER_BASE = 0x0,       /* base of device registers */
+       MBOX_REGISTER_SIZE = 0x1000,    /* size of register region */
+
+       MBOX_BUFFER_BASE = 0x1000,      /* base of shared buffer */
+       MBOX_BUFFER_SIZE = 0xf000,      /* size of buffer region */
+
+       MBOX_DEVICE_SIZE = MBOX_REGISTER_SIZE + MBOX_BUFFER_SIZE
+};
+
+#endif /* MBOXTYPES_H */
diff --git a/fs/vmfs/messagebox.c b/fs/vmfs/messagebox.c
new file mode 100644
index 0000000..8494493
--- /dev/null
+++ b/fs/vmfs/messagebox.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2008-2009 ARM Limited. All rights reserved.
+ */
+
+/*
+    Very simple linux 2.6 implementation of a messagebox
+    for passing messages (data) between the vm and a device.
+
+    Currently this implements just enough to satisfy the needs of VFS
+
+    There are lots of TODOs here:
+       clean up the device interface
+       add support for delayed message response (PENDING vs OK/ERROR)
+       add support for multiple users (vmfs currently does the locking)
+*/
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/semaphore.h>
+
+#include "vmfs_debug.h"
+
+#include "mboxtypes.h"
+#include "messagebox.h"
+
+
+/* Define this to make the driver use PIO rather than memory mapped access */
+/* #define USE_PIO */
+
+/* Define this to use interrupts rather than polling */
+#define USE_IRQ
+
+/* Message box device register layout in memory */
+
+struct MBRegs {
+       uint32_t id;
+       uint32_t data;
+       uint32_t control;
+       uint32_t status;
+       uint32_t start;
+       uint32_t end;
+       uint32_t irqmask;
+} MBRegs;
+
+struct MessageBox {
+       volatile struct MBRegs *dev;    /* virtual base of registers */
+
+       uint32_t dev_base;      /* physical base of registers */
+       uint32_t dev_irq;       /* irq number of device */
+       uint32_t *buffer;       /* fixed size buffer used for passing data */
+
+#ifdef USE_IRQ
+       /* if we use IRQs then the calling thread must be able to sleep */
+       /* and be woken by the IRQ handler. for this we appear to need a */
+       /* wait queue */
+       wait_queue_head_t irq_queue;
+       spinlock_t irq_lock;
+#endif
+
+       uint32_t use_irq;       /* set to true if we're using irq's
+                                * rather than polling */
+
+       struct semaphore mb_access;     /* semaphore to allow only one thread
+                                        * to access the message box */
+};
+
+#ifdef USE_IRQ
+static irqreturn_t mb_interrupt(int irq, void *dev_id)
+{
+       MessageBox *mb = (MessageBox *) dev_id;
+
+       FNENTER("");
+
+       /* should be safe to access the device here,
+        * or do we need to spinlock?
+        */
+       spin_lock(&mb->irq_lock);
+
+       /* disable all interrupts, we only use RXREADY */
+       writel(0, &mb->dev->irqmask);
+
+       /* wake up any thread waiting on the queue */
+       wake_up_interruptible(&mb->irq_queue);
+
+       spin_unlock(&mb->irq_lock);
+
+       FNEXIT("");
+
+       return IRQ_HANDLED;
+}
+#endif /* USE_IRQ */
+
+/* Initialise OS structures involved in serialising access to the messagebox */
+MessageBox *mb_new(phys_addr_t dev_base, uint32_t dev_irq)
+{
+       MessageBox *mb;
+
+       DEBUG1("initialising at 0x%x ...\n", dev_base);
+
+       mb = (MessageBox *) kmalloc(sizeof(MessageBox), GFP_KERNEL);
+
+       /* Map the messagebox registers and buffer int VM */
+
+       if (check_mem_region(dev_base, MBOX_DEVICE_SIZE)) {
+               DEBUG1("i/o space at 0x%x already in use\n", dev_base);
+               return NULL;
+       }
+
+       request_mem_region(dev_base, MBOX_DEVICE_SIZE, "messagebox");
+
+       mb->dev = ioremap_nocache(dev_base, MBOX_DEVICE_SIZE);
+
+       DEBUG1("device registers mapped at %p, size 0x%x\n", mb->dev,
+              MBOX_DEVICE_SIZE);
+
+#ifdef USE_PIO
+       mb->buffer = (uint32_t *) kmalloc(MBOX_BUFFER_SIZE, GFP_KERNEL);
+#else
+       mb->buffer = (uint32_t *) ((uint8_t *) mb->dev + MBOX_BUFFER_BASE);
+#endif
+
+       /* optionally request an interrupt source */
+
+#ifdef USE_IRQ
+       mb->dev_irq = dev_irq;
+       mb->use_irq = 1;
+       if (request_irq(dev_irq, mb_interrupt, 0, "VFS", mb)) {
+               DEBUG1("failed to register irq %d\n", dev_irq);
+               mb->use_irq = 0;
+       }
+
+       init_waitqueue_head(&mb->irq_queue);
+       spin_lock_init(&mb->irq_lock);
+#endif
+
+       /* set up a semaphore to restrict access to the message box */
+
+       sema_init(&mb->mb_access, 1);
+
+       DEBUG1("initialised %p, id=0x%x\n", mb, mb_id(mb));
+
+       return mb;
+}
+
+void mb_delete(MessageBox *mb)
+{
+#ifdef USE_IRQ
+       if (mb->use_irq)
+               free_irq(mb->dev_irq, mb);
+#endif
+
+       iounmap(mb->dev);
+
+       release_mem_region(mb->dev_base, MBOX_DEVICE_SIZE);
+
+#ifdef USE_PIO
+       kfree(mb->buffer);
+#endif
+
+       kfree(mb);
+}
+
+/* the message box should be locked by the thread
+ * during the send/receive cycle
+ */
+int mb_lock(MessageBox *mb)
+{
+       return down_interruptible(&mb->mb_access);
+}
+
+void mb_unlock(MessageBox *mb)
+{
+       up(&mb->mb_access);
+}
+
+void *mb_start(MessageBox *mb, uint32_t len)
+{
+       /* start a message
+        *
+        * Current implementation expects exclusive access to the device
+        * from mb_start to mb_end and through to mb_receive.
+        */
+       writel(MBOX_CONTROL_START, &mb->dev->control);
+
+       /* reset buffer pointers
+       */
+       writel(0, &mb->dev->start);
+       writel(0, &mb->dev->end);
+
+       return mb->buffer;
+}
+
+int mb_end(MessageBox *mb, uint32_t len)
+{
+#ifdef USE_PIO
+       uint32_t *buffer = mb->buffer;
+
+       len = len / 4;
+
+       while (len > 0) {
+               writel(*buffer++, &mb->dev->data);
+               --len;
+       }
+#else
+       writel(len, &mb->dev->end);
+#endif
+
+       /* Indicate to the device that all the buffered data is now written
+        */
+       writel(MBOX_CONTROL_END, &mb->dev->control);
+
+       /* current implementation will set RXREADY to true to indicate
+        * that the return data is available
+        */
+       return mb_ready(mb);
+}
+
+/* Indicate whether there is receive data ready to read */
+int mb_ready(MessageBox *mb)
+{
+       return (readl(&mb->dev->status) & MBOX_STATUS_RXREADY) != 0;
+}
+
+/* Wait for the reply to become ready */
+int mb_wait(MessageBox *mb)
+{
+#ifdef USE_IRQ
+       if (mb->use_irq) {
+               /* add our thread to the irq wait queue */
+               DECLARE_WAITQUEUE(wait, current);
+
+               add_wait_queue(&mb->irq_queue, &wait);
+               do {
+                       /* make ourself sleep interruptible */
+                       set_current_state(TASK_INTERRUPTIBLE);
+
+                       /* enable RXREADY interrupt */
+                       spin_lock_irq(&mb->irq_lock);
+                       writel(MBOX_STATUS_RXREADY, &mb->dev->irqmask);
+                       spin_unlock_irq(&mb->irq_lock);
+
+                       DEBUG1("sleeping");
+
+                       /* sleep */
+                       schedule();
+
+                       DEBUG1("waking");
+
+                       /* once there is data ready, break out */
+                       if (mb_ready(mb))
+                               break;
+
+                       /* if we were interrupted also break out */
+                       if (signal_pending(current))
+                               break;
+               } while (1);
+
+               /* back to normal */
+               remove_wait_queue(&mb->irq_queue, &wait);
+
+               set_current_state(TASK_RUNNING);
+
+               /* ensure interrupts are masked out */
+               spin_lock_irq(&mb->irq_lock);
+               writel(0, &mb->dev->irqmask);
+               spin_unlock_irq(&mb->irq_lock);
+
+               if (!mb_ready(mb))
+                       return -EINTR;
+       }
+#endif
+
+       while (!mb_ready(mb))
+               schedule();
+
+       return 0;
+}
+
+void *mb_receive(MessageBox *mb, uint32_t *len)
+{
+#ifdef USE_PIO
+       uint32_t *buffer = mb->buffer;
+       uint32_t bidx = 0;
+
+       /* read data from the device until there is no more to receive
+        */
+
+       while ((readl(&mb->dev->status) & MBOX_STATUS_RXEMPTY) == 0)
+               buffer[bidx++] = readl(&mb->dev->data);
+
+       *len = bidx * 4;
+
+       return mb->buffer;
+#else
+       uint32_t start = readl(&mb->dev->start);
+       uint32_t end = readl(&mb->dev->end);
+
+       *len = end - start;
+
+       return mb->buffer + start;
+#endif
+}
+
+uint32_t mb_id(MessageBox *mb)
+{
+       return readl(&mb->dev->id);
+}
diff --git a/fs/vmfs/messagebox.h b/fs/vmfs/messagebox.h
new file mode 100644
index 0000000..cadb097
--- /dev/null
+++ b/fs/vmfs/messagebox.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2008-2009 ARM Limited. All rights reserved.
+ */
+
+/*!
+ * \file    messagebox.h
+ * \brief   driver for simple messagebox device
+ *
+ */
+
+/*! Defines the interface to a simple messagebox device driver
+ *
+ * The intention with the messagebox device is to provide a very simple
+ * interface for sending and receiving packets of information to the host
+ * side device. It should be possible to encapsulate all target OS locking
+ * and barriers within the messagebox.
+ *
+ * The current implementation is very basic, supporting enough functionality
+ * for the VFS blocking model to work correctly.
+ *
+ * TODO:
+ * split the interface so that the buffer is allocated/freed
+ * separate to send/receive?
+ */
+
+#ifndef MESSAGEBOX_H
+#define MESSAGEBOX_H
+
+/*! Opaque handle for message box operations */
+typedef struct MessageBox MessageBox;
+
+/*! Instantiate a new messagebox driver
+ *
+ * \param dev_base physical base address of device registers+buffer
+ * \param dev_irq  irq number of device
+ *
+ * \return opaque message box structure for use in other calls
+ */
+MessageBox *mb_new(phys_addr_t dev_base, uint32_t dev_irq);
+
+/*! free resources assocuated with the messagebox handle
+ *
+ * \param mb    messagebox handle
+ */
+void mb_delete(MessageBox *mb);
+
+/*! Reserve resources for sending a message
+ *
+ * \param mb    messagebox handle
+ * \param len   maximum length of message that will be sent
+ *
+ * \returns     pointer to buffer to fill with message
+ */
+void *mb_start(MessageBox *mb, uint32_t len);
+
+/*! Send and release the buffer obtained with mb_start
+ *
+ * \param mb    messagebox handle
+ * \param len   actual length of message in buffer
+ *
+ * \return      non-zero if a reply message is ready to be received
+ */
+int mb_end(MessageBox *mb, uint32_t len);
+
+/*! Check whether receive data is available
+ *
+ * \param mb    messagebox handle
+ *
+ * \return      1 for data available. 0 for none available
+ *
+ * Allows drivers to poll for receive data (rather than using interrupts)
+ */
+int mb_ready(MessageBox *mb);
+
+/*! Wait for receive data to become available
+ *
+ * \param mb    messagebox handle
+ *
+ * \return      0 for data available. -ve for error (e.g. -EINTR)
+ *
+ * This function will block until there is receive data available
+ */
+int mb_wait(MessageBox *mb);
+
+/*! lock the messagebox for exclusive access by a thread
+ *
+ * \param mb    messagebox handle
+ *
+ * \return      0 for lockable, -ve for error (e.g. -EINTR)
+ *
+ * This function will block until the message box is free and locked,
+ * or until the thread is interrupted
+ */
+int mb_lock(MessageBox *mb);
+
+/*! unlock the messagebox and allow it to be used by another thread
+ *
+ * \param mb    messagebox handle
+  */
+void mb_unlock(MessageBox *mb);
+
+/*! Receive an incoming (reply) message
+ *
+ * \param mb    messagebox handle
+ * \param len   pointer to instance to receive length of message
+ *
+ * \return      pointer to buffer containing received message
+ *
+ * Message data should be copied from the buffer before the call returns
+ */
+void *mb_receive(MessageBox *mb, uint32_t *len);
+
+/*! Get the device configured id
+ *
+ * \param mb    messagebox handle
+ *
+ * \return      value configured in the 'id' parameter in the lisa model
+ */
+uint32_t mb_id(MessageBox *mb);
+
+#endif /* MESSAGEBOX_H */
diff --git a/fs/vmfs/msg.c b/fs/vmfs/msg.c
new file mode 100644
index 0000000..0c811fe
--- /dev/null
+++ b/fs/vmfs/msg.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2008-2009 ARM Limited. All rights reserved.
+ */
+
+/*
+ *  Utility types/functions for constructing/deconstructing blocks
+ *  of data to be sent over a message box between the target and host
+ *  (and vice versa) this implementation is converted to be
+ *  used in the linux kernel.  These must match the behaviour in
+ *  the equivalent C++ classes used on the host side
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include "msg.h"
+
+typedef enum MsgDataType {
+       MSG_END,        /* (potential) marker for end of message data */
+       MSG_UINT32,     /* 32 bit data */
+       MSG_UINT64,     /* 64 bit data */
+       MSG_INT32,      /* 32 bit data */
+       MSG_CSTR,       /* zero terminated c string */
+       MSG_DATA,       /* raw data */
+       MSG_CHAR,       /* single character */
+       MSG_BOOL        /* packed int? */
+} MsgDataType;
+
+typedef enum MsgTraits {
+       TYPE_SHIFT = 0,
+       TYPE_BITS = 8,
+       TYPE_MASK = (1 << TYPE_BITS) - 1,
+
+       LEN_SHIFT = TYPE_BITS,
+       LEN_BITS = 20,
+       MAX_LEN = 1 << LEN_BITS,
+       LEN_MASK = MAX_LEN - 1
+} MsgTraits;
+
+struct MessageComposer {
+       uint8_t *b_data;        /* message data */
+       uint32_t b_size;        /* buffer size */
+       uint32_t b_index;       /* offset to next byte to fill */
+};
+
+void msgc_init(MessageComposer *mc, void *data, uint32_t len)
+{
+       mc->b_data = data;
+       mc->b_size = len;
+       mc->b_index = 0;
+}
+
+void msgc_cleanup(MessageComposer *mc)
+{
+}
+
+MessageComposer *msgc_new(void *data, uint32_t len)
+{
+       MessageComposer *mc =
+           (MessageComposer *) kmalloc(sizeof(struct MessageComposer),
+                                       GFP_KERNEL);
+
+       msgc_init(mc, data, len);
+
+       return mc;
+}
+
+void msgc_delete(MessageComposer *mc)
+{
+       msgc_cleanup(mc);
+       kfree(mc);
+}
+
+static int msgc_put(MessageComposer *mc, MsgDataType type, const void *data,
+                   uint32_t len)
+{
+       uint32_t tag;
+
+       if (len >= mc->b_size)
+               return 0;
+
+       if (!mc->b_data)
+               return 0;
+
+       tag = (len << LEN_SHIFT) | ((uint32_t) type);
+
+       *(uint32_t *) (mc->b_data + mc->b_index) = tag;
+       mc->b_index += 4;
+
+       memcpy(mc->b_data + mc->b_index, data, len);
+       mc->b_index += len;
+
+       mc->b_index = (mc->b_index + 3) & ~3;   /* word align */
+
+       return 1;
+}
+
+int msgc_put_int32(MessageComposer *mc, int32_t data)
+{
+       return msgc_put(mc, MSG_INT32, (void *)&data, sizeof(int32_t));
+}
+
+int msgc_put_uint32(MessageComposer *mc, uint32_t data)
+{
+       return msgc_put(mc, MSG_UINT32, (void *)&data, sizeof(uint32_t));
+}
+
+int msgc_put_uint64(MessageComposer *mc, uint64_t data)
+{
+       return msgc_put(mc, MSG_UINT64, (void *)&data, sizeof(uint64_t));
+}
+
+int msgc_put_cstr(MessageComposer *mc, const char *data)
+{
+       return msgc_put(mc, MSG_CSTR, (void *)data, strlen(data) + 1);
+}
+
+int msgc_put_data(MessageComposer *mc, const void *data, uint32_t len)
+{
+       return msgc_put(mc, MSG_DATA, data, len);
+}
+
+
+uint32_t msgc_get_size(MessageComposer *mc)
+{
+       return mc->b_index;
+}
+
+struct MessageDecomposer {
+       const uint8_t *b_data;  /* message data */
+       uint32_t b_size;        /* size of buffer */
+       uint32_t b_index;       /* current index into buffer */
+};
+
+static int msgd_get(MessageDecomposer *md, MsgDataType type, void *data,
+                   uint32_t *len)
+{
+       uint32_t tag;
+       uint32_t d_len;
+       MsgDataType d_type;
+
+       if (md->b_index + 4 > md->b_size)
+               return 0;
+
+       tag = *(uint32_t *) (md->b_data + md->b_index);
+
+       d_type = (MsgDataType) ((tag >> TYPE_SHIFT) & TYPE_MASK);
+       d_len = ((tag >> LEN_SHIFT) & LEN_MASK);
+
+       if (d_type != type)
+               return 0;
+
+       md->b_index += 4;
+
+       if (md->b_index + d_len > md->b_size)
+               return 0;
+
+       if (*len > d_len)
+               *len = d_len;
+
+       memcpy(data, md->b_data + md->b_index, *len);
+
+       md->b_index += d_len;
+
+       md->b_index = (md->b_index + 3) & ~3;   /* word align */
+
+       return 1;
+}
+
+void msgd_init(MessageDecomposer *md, const void *data, uint32_t len)
+{
+       md->b_data = (const unsigned char *)data;
+       md->b_size = len;
+       md->b_index = 0;
+}
+
+void msgd_cleanup(MessageDecomposer *md)
+{
+}
+
+MessageDecomposer *msgd_new(const void *data, uint32_t len)
+{
+       MessageDecomposer *md =
+           (MessageDecomposer *) kmalloc(sizeof(struct MessageDecomposer),
+                                         GFP_KERNEL);
+
+       msgd_init(md, data, len);
+
+       return md;
+}
+
+void msgd_delete(MessageDecomposer *md)
+{
+       msgd_cleanup(md);
+       kfree(md);
+}
+
+/* for decomposing */
+
+int msgd_get_int32(MessageDecomposer *md, int32_t *data)
+{
+       uint32_t len = sizeof(int32_t);
+
+       return msgd_get(md, MSG_INT32, (void *)data, &len);
+}
+
+int msgd_get_uint32(MessageDecomposer *md, uint32_t *data)
+{
+       uint32_t len = sizeof(uint32_t);
+
+       return msgd_get(md, MSG_UINT32, (void *)data, &len);
+}
+
+int msgd_get_uint64(MessageDecomposer *md, uint64_t *data)
+{
+       uint32_t len = sizeof(uint64_t);
+
+       return msgd_get(md, MSG_UINT64, (void *)data, &len);
+}
+
+int msgd_get_cstr(MessageDecomposer *md, char *data, unsigned int *len)
+{
+       return msgd_get(md, MSG_CSTR, (void *)data, len);
+}
+
+int msgd_get_data(MessageDecomposer *md, void *data, uint32_t *len)
+{
+       return msgd_get(md, MSG_DATA, data, len);
+}
diff --git a/fs/vmfs/msg.h b/fs/vmfs/msg.h
new file mode 100644
index 0000000..4bc0dfd
--- /dev/null
+++ b/fs/vmfs/msg.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2008-2009 ARM Limited. All rights reserved.
+ */
+
+/*!
+ * \file    msg.h
+ * \brief   objects and functions to covert function
+ * calls into messages and back
+ * \todo change return codes, and define some errors
+ */
+
+#ifndef MSG_H
+#define MSG_H
+
+/*! opaque type for an object that can compose messages */
+typedef struct MessageComposer MessageComposer;
+
+/*! instantiate a new message composer object that can
+ *  compose a message into the supplied buffer
+ *
+ * \param data  buffer into which messages can be composed (may be NULL)
+ * \param len   size of buffer (may be 0)
+ *
+ * \return message composer object
+ */
+MessageComposer *msgc_new(void *data, uint32_t len);
+
+/*! destroy a message composer object
+ *
+ * \param mc    message composer object
+ */
+void msgc_delete(MessageComposer *mc);
+
+/*! (re)initialise a message composer object to use a new buffer
+ *
+ * \param mc    message composer object
+ * \param data  buffer into which messages can be composed (may be NULL)
+ * \param len   size of buffer (may be 0)
+ */
+void msgc_init(MessageComposer *mc, void *data, uint32_t len);
+
+/*! disassociate a message composer object from a buffer
+ *
+ * \param mc    message composer object
+ */
+void msgc_cleanup(MessageComposer *mc);
+
+/*! add a signed integer to the message
+ *
+ * \param mc    message composer object
+ * \param data  data to add to message
+ *
+ * \return 1 for success, 0 for fail
+ */
+int msgc_put_int32(MessageComposer *mc, int32_t data);
+
+/*! add an unsigned signed integer to the message
+ *
+ * \param mc    message composer object
+ * \param data  data to add to message
+ *
+ * \return 1 for success, 0 for fail
+ */
+int msgc_put_uint32(MessageComposer *mc, uint32_t data);
+
+/*! add an unsigned 64bit integer to the message
+ *
+ * \param mc    message composer object
+ * \param data  data to add to message
+ *
+ * \return 1 for success, 0 for fail
+ */
+int msgc_put_uint64(MessageComposer *mc, uint64_t data);
+
+/*! add a zero terminated string to the message
+ *
+ * \param mc    message composer object
+ * \param data  data to add to message
+ *
+ * \return 1 for success, 0 for fail
+ */
+int msgc_put_cstr(MessageComposer *mc, const char *data);
+
+/*! add a data block the message
+ *
+ * \param mc    message composer object
+ * \param data  data to add to message
+ * \param len   length of data to add to the message
+ *
+ * \return 1 for success, 0 for fail
+ */
+int msgc_put_data(MessageComposer *mc, const void *data, uint32_t len);
+
+/*! return the current size of the message in bytes
+ *
+ * \param mc    message composer object
+ *
+ * \return size of the message in bytes
+ */
+uint32_t msgc_get_size(MessageComposer *mc);
+
+/*! opaque type for an object that can decompose messages */
+typedef struct MessageDecomposer MessageDecomposer;
+
+/*! instantiate a new message decomposer object that can decompose
+ *  a message from the supplied buffer
+ *
+ * \param data  buffer from which messages can be decomposed (may be NULL)
+ * \param len   size of buffer (may be 0)
+ *
+ * \return message decomposer object
+ */
+MessageDecomposer *msgd_new(const void *data, uint32_t len);
+
+/*! destroy a message decomposer object
+ *
+ * \param md    message decomposer object
+ */
+void msgd_delete(MessageDecomposer *md);
+
+/*! (re)initialise a message decomposer object to use a new buffer
+ *
+ * \param md    message decomposer object
+ * \param data  buffer from which messages can be decomposed (may be NULL)
+ * \param len   size of buffer (may be 0)
+ */
+void msgd_init(MessageDecomposer *md, const void *data, uint32_t len);
+
+/*! disassociate a message decomposer object from a buffer
+ *
+ * \param md    message decomposer object
+ */
+void msgd_cleanup(MessageDecomposer *md);
+
+/*! extract a signed integer from the message
+ *
+ * \param md    message decomposer object
+ * \param data  data to extract from message
+ *
+ * \return 1 for success, 0 for fail
+ */
+int msgd_get_int32(MessageDecomposer *md, int32_t *data);
+
+/*! extract an unsigned integer from the message
+ *
+ * \param md    message decomposer object
+ * \param data  data to extract from message
+ *
+ * \return 1 for success, 0 for fail
+ */
+int msgd_get_uint32(MessageDecomposer *md, uint32_t *data);
+
+/*! extract an unsigned 64 bit integer from the message
+ *
+ * \param md    message decomposer object
+ * \param data  data to extract from message
+ *
+ * \return 1 for success, 0 for fail
+ */
+int msgd_get_uint64(MessageDecomposer *md, uint64_t *data);
+
+/*! extract a zero terminated C string from the message
+ *
+ * \param md    message decomposer object
+ * \param data  data to extract from message
+ * \param len   in: max length to extract, out: length of string extracted
+ *
+ * \return 1 for success, 0 for fail
+ */
+int msgd_get_cstr(MessageDecomposer *md, char *data, unsigned int *len);
+
+/*! extract a data block from the message
+ *
+ * \param md    message decomposer object
+ * \param data  data to extract from message
+ * \param len   in: max length to extract, out: length of data extracted
+ *
+ * \return 1 for success, 0 for fail
+ */
+int msgd_get_data(MessageDecomposer *md, void *data, uint32_t *len);
+
+#endif /* MSG_H */
diff --git a/fs/vmfs/proc.c b/fs/vmfs/proc.c
new file mode 100644
index 0000000..7866391
--- /dev/null
+++ b/fs/vmfs/proc.c
@@ -0,0 +1,1086 @@
+/*
+ *  proc.c
+ *
+ *  Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *  Copyright (C) 1997 by Volker Lendecke
+ *
+ *  Please add a note about your changes to vmfs_ in the ChangeLog file.
+ *
+ *  Copyright (C) 2008-2009 by ARM Limited
+ */
+
+#include <linux/types.h>
+#include <linux/capability.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/dcache.h>
+#include <linux/dirent.h>
+#include <linux/vfs.h>
+#include <linux/version.h>
+#include "vmfs_fs.h"
+#include "vmfsno.h"
+#include "vmfs_mount.h"
+
+#include <asm/string.h>
+#include <asm/div64.h>
+
+#include "vmfs_debug.h"
+#include "proto.h"
+#include "vfs.h"
+
+/* Features. Undefine if they cause problems, this should perhaps be a
+   config option. */
+#define VMFSFS_POSIX_UNLINK 1
+
+#define VMFS_ST_BLKSIZE     (PAGE_SIZE)
+#define VMFS_ST_BLKSHIFT    (PAGE_SHIFT)
+
+/* dont seem to have ulldiv. This is good enough for  / 1000 */
+static uint64_t divmod64(uint64_t dividend, uint32_t divisor,
+                        uint32_t *remainder)
+{
+       uint64_t quotient = 0;
+       uint32_t pquot, prem = 0;
+       uint32_t i;
+
+       /* divide in 4 32x16->32 bit parts. As most ARMs don't have / anyway
+        * we should probably do this bit by bit */
+       for (i = 0; i < 4; ++i) {
+               uint32_t part = (prem << 16) | (dividend >> 48);
+
+               pquot = part / divisor;
+               prem = part % divisor;
+
+               dividend = dividend << 16;
+               quotient = (quotient << 16) | pquot;
+       }
+
+       if (remainder)
+               *remainder = prem;
+
+       return quotient;
+}
+
+
+#define VMFS_ATTR_MAX (PATH_MAX+256)
+
+struct vmfs_ws {
+       char path[PATH_MAX];
+       char path2[PATH_MAX];
+       uint8_t attr[VMFS_ATTR_MAX];
+};
+
+static struct vmfs_ws *vmfs_get_ws(struct vmfs_sb_info *server)
+{
+       return kmalloc(sizeof(struct vmfs_ws), GFP_NOFS);
+}
+
+static void vmfs_put_ws(struct vmfs_ws *ws)
+{
+       kfree(ws);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Encoding/Decoding section                                                */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+ * vmfs_build_path: build the path to entry and name storing it in buf.
+ * The path returned will have the trailing '\0'.
+ */
+static int vmfs_build_path(struct vmfs_sb_info *server, unsigned char *buf,
+                          int buflen, struct dentry *entry, struct qstr *name)
+{
+       unsigned char *path = buf;
+       int maxlen = buflen;
+       int len;
+
+       VERBOSE("for dir %s\n", entry->d_name.name);
+
+       if (maxlen > VMFS_MAXPATHLEN + 1)
+               maxlen = VMFS_MAXPATHLEN + 1;
+
+       if (maxlen < 1)
+               return -ENAMETOOLONG;
+
+       path = buf + buflen;
+       *--path = '\0';
+       --maxlen;
+
+       if (name) {
+               len = name->len + 1;
+
+               if (len > maxlen)
+                       return -ENAMETOOLONG;
+
+               path -= len;
+               maxlen -= len;
+               memcpy(path, name->name, len);
+
+               *--path = '/';
+               --maxlen;
+       }
+
+       if (entry != NULL) {
+               dget(entry);
+               do {
+                       struct dentry *parent;
+
+                       len = entry->d_name.len;
+
+                       /* +1 for separator */
+                       if (len + 1 > maxlen) {
+                               dput(entry);
+                               return -ENAMETOOLONG;
+                       }
+
+                       spin_lock(&entry->d_lock);
+                       path -= len;
+                       maxlen -= len;
+                       memcpy(path, entry->d_name.name, len);
+
+                       *--path = '/';
+                       --maxlen;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
+                       parent = entry->d_parent;
+                       dget(parent);
+                       spin_unlock(&entry->d_lock);
+#else
+                       spin_unlock(&entry->d_lock);
+                       parent = dget_parent(entry);
+#endif
+                       dput(entry);
+                       entry = parent;
+               } while (!IS_ROOT(entry));
+               dput(entry);
+       }
+       /* at the root we need to put on the root prefix, which is held
+        * in the server
+        * TODO - for now we assume it is called 'A' */
+
+       if (maxlen < 2)
+               return -ENAMETOOLONG;
+
+       maxlen -= 2;
+       *--path = ':';
+       *--path = 'A';
+
+       len = buflen - maxlen;
+       memmove(buf, path, len);
+
+       return len;
+}
+
+static int vmfs_encode_path(struct vmfs_sb_info *server, char *buf, int maxlen,
+                           struct dentry *dir, struct qstr *name)
+{
+       int result;
+
+       result = vmfs_build_path(server, buf, maxlen, dir, name);
+       if (result < 0)
+               goto out;
+
+out:
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Support section.                                                         */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+ * Convert VMFS error codes to -E... errno values.
+ */
+int vmfs_errno(int error)
+{
+       if (error < 0) {
+               VERBOSE("%d\n", error);
+
+               switch (error) {
+               case VFS_ERR_BADHANDLE:
+                       return -EBADF;
+               case VFS_ERR_NOENTRY:
+                       return -ENOENT;
+               case VFS_ERR_NOROOM:
+                       return -ENOMEM;
+               case VFS_ERR_MAXHANDLE:
+                       return -EMFILE;
+               case VFS_ERR_NOMOUNT:
+                       return -ENXIO;
+               case VFS_ERR_NOTFOUND:
+                       return -ENOENT;
+               case VFS_ERR_PERM:
+                       return -EACCES; /* EPERM? */
+               case VFS_ERR_NOTDIR:
+                       return -ENOTDIR;
+               case VFS_ERR_TOOLONG:
+                       return -ENAMETOOLONG;
+               case VFS_ERR_EXIST:
+                       return -EEXIST;
+               case VFS_ERR_NOTEMPTY:
+                       return -ENOTEMPTY;
+               case VFS_ERR_INVALID:
+                       return -EINVAL;
+               case VFS_ERR_ISDIR:
+                       return -EISDIR;
+               case VFS_ERR_TOOBIG:
+                       return -ERANGE;
+               case VFS_ERR_UNIMPL:
+                       return -ENOSYS;
+
+               default:
+                       /* something generic */
+                       return -EIO;
+               }
+       } else
+               return error;
+}
+
+static int
+vmfs_proc_open(struct vmfs_sb_info *server, struct dentry *dentry, int wish)
+{
+       struct inode *ino = dentry->d_inode;
+       struct vmfs_inode_info *ei = VMFS_I(ino);
+       VFSOpenFlags mode = wish;
+       int res;
+       struct vmfs_ws *ws = vmfs_get_ws(server);
+
+       FNENTER();
+
+       if (!(ino->i_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
+               mode &= ~VFS_OPEN_WRONLY;
+
+       res = vmfs_encode_path(server, ws->path, PATH_MAX, dentry, NULL);
+       if (res < 0)
+               goto out;
+
+       res = vfsop_openfile(server->vfs, ws->path, mode);
+       if ((res < 0) && ((mode & VFS_OPEN_RDWR) == VFS_OPEN_RDWR)) {
+               mode &= ~VFS_OPEN_WRONLY;
+               res = vfsop_openfile(server->vfs, ws->path, mode);
+       }
+
+       if (res < 0) {
+               res = vmfs_errno(res);
+               goto out;
+       }
+       /* we appear to need attribute information also. Not sure why.
+        * res = vfsop_getattr(server->vfs, p, VFS_ATTR_, attr, attrlen); */
+
+       ei->vhandle = res;
+       ei->vaccess = mode & VFS_OPEN_RDWR;
+       ei->vopen = 1;
+
+       /* may want to set up attr (as in dos attr) here, and access */
+
+       res = 0;
+out:
+       vmfs_put_ws(ws);
+
+       FNEXIT("%d\n", res);
+       return res;
+}
+
+/*
+ * Make sure the file is open, and check that the access
+ * is compatible with the desired access.
+ *
+ * wish is one of VMFS_O_RDONLY = 0/VMFS_O_WRONLY = 1/VMFS_O_RDWR = 2
+ */
+int vmfs_open(struct dentry *dentry, int flags, int wish)
+{
+       struct inode *inode = dentry->d_inode;
+       int result;
+       VFSOpenFlags vwish, vaccess;
+
+       FNENTER();
+
+       if (wish == VMFS_O_RDONLY)
+               vwish = VFS_OPEN_RDONLY;
+       else if (wish == VMFS_O_WRONLY)
+               vwish = VFS_OPEN_WRONLY;
+       else if (wish == VMFS_O_RDWR)
+               vwish = VFS_OPEN_RDWR;
+       else {
+               DEBUG1("unexpected open flags!\n");
+               vwish = VFS_OPEN_RDWR;
+       }
+
+       if (flags & O_CREAT)
+               vwish |= VFS_OPEN_CREATE;
+       if (flags & O_TRUNC)
+               vwish |= VFS_OPEN_TRUNCATE;
+       if (flags & O_EXCL)
+               vwish |= VFS_OPEN_NEW;
+
+       result = -ENOENT;
+       if (!inode) {
+               pr_err("vmfs_open: no inode for dentry!\n");
+               goto out;
+       }
+
+       if (!vmfs_is_open(inode)) {
+               struct vmfs_sb_info *server = server_from_inode(inode);
+               result = 0;
+               if (!vmfs_is_open(inode))
+                       result = vmfs_proc_open(server, dentry, vwish);
+               if (result)
+                       goto out;
+               /*
+                * A successful open means the path is still valid ...
+                */
+               vmfs_renew_times(dentry);
+       }
+
+       /*
+        * Check whether the access is compatible with the desired mode.
+        */
+
+       result = 0;
+       vaccess = VMFS_I(inode)->vaccess;
+
+       if (vaccess != (vwish & VFS_OPEN_RDWR) && vaccess != VFS_OPEN_RDWR) {
+               PARANOIA("%s/%s access denied, access=%x, wish=%x\n",
+                        DENTRY_PATH(dentry), vaccess, vwish);
+               result = -EACCES;
+       }
+out:
+       FNEXIT("%d", result);
+       return result;
+}
+
+static int vmfs_proc_close(struct vmfs_sb_info *server, int32_t handle)
+{
+       int result = -ENOMEM;
+
+       FNENTER();
+
+       result = vmfs_errno(vfsop_closefile(server->vfs, handle));
+
+       /* GPBTODO - may need to set mtime using utc2local(server, mtime) */
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+/*
+ * Win NT 4.0 has an apparent bug in that it fails to update the
+ * modify time when writing to a file. As a workaround, we update
+ * both modify and access time locally, and post the times to the
+ * server when closing the file.
+ */
+static int vmfs_proc_close_inode(struct vmfs_sb_info *server,
+                                struct inode *ino)
+{
+       struct vmfs_inode_info *ei = VMFS_I(ino);
+       int result = 0;
+       if (vmfs_is_open(ino)) {
+               /*
+                * We clear the open flag in advance, in case another
+                * process observes the value while we block below.
+                */
+               ei->vopen = 0;
+
+               result = vmfs_proc_close(server, ei->vhandle);
+
+               ei->closed = jiffies;
+       }
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+int vmfs_close(struct inode *ino)
+{
+       int result = 0;
+
+       if (vmfs_is_open(ino)) {
+               struct vmfs_sb_info *server = server_from_inode(ino);
+               result = vmfs_proc_close_inode(server, ino);
+       }
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+/*
+ * This is used to close a file following a failed instantiate.
+ * Since we don't have an inode, we can't use any of the above.
+ */
+int vmfs_close_fileid(struct dentry *dentry, int32_t fileid)
+{
+
+       struct vmfs_sb_info *server = server_from_dentry(dentry);
+       int result;
+
+       result = vmfs_proc_close(server, fileid);
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+static int
+vmfs_proc_read(struct inode *inode, loff_t offset, int count, char *data)
+{
+       struct vmfs_sb_info *server = server_from_inode(inode);
+       int result;
+
+       result =
+           vfsop_readfile(server->vfs, VMFS_I(inode)->vhandle, offset, data,
+                          count);
+       if (result < 0)
+               result = vmfs_errno(result);
+
+       VERBOSE("ino=%ld, handle=%d, count=%d, result=%d\n",
+               inode->i_ino, VMFS_I(inode)->vhandle, count, result);
+
+       return result;
+}
+
+static int
+vmfs_proc_write(struct inode *inode, loff_t offset,
+               int count, const char *data)
+{
+       struct vmfs_sb_info *server = server_from_inode(inode);
+       int result;
+
+       result =
+           vfsop_writefile(server->vfs, VMFS_I(inode)->vhandle, offset, data,
+                           count);
+       if (result < 0)
+               result = vmfs_errno(result);
+
+       VERBOSE("ino=%ld, handle=%d, count=%d, result=%d\n",
+               inode->i_ino, VMFS_I(inode)->vhandle, count, result);
+       return result;
+}
+
+/* GPB - this appears to be open(O_CREAT) which we do support */
+int vmfs_proc_create(struct dentry *dentry, uint32_t mode, int32_t *fileid)
+{
+       struct vmfs_sb_info *server = server_from_dentry(dentry);
+       struct vmfs_ws *ws = vmfs_get_ws(server);
+       int result;
+
+       result = vmfs_encode_path(server, ws->path, PATH_MAX, dentry, NULL);
+       if (result < 0)
+               goto out;
+
+       result =
+           vfsop_openfile(server->vfs, ws->path,
+                          VFS_OPEN_CREATE | VFS_OPEN_RDWR);
+       if (result < 0) {
+               result = vmfs_errno(result);
+               goto out;
+       }
+       /* GPBTODO - may need to set mtime when file is created */
+       /* GPBTODO - what should create do if the file already exists? */
+
+       *fileid = result;
+       result = 0;
+
+out:
+       vmfs_put_ws(ws);
+       FNEXIT("%d", result);
+       return result;
+}
+
+int vmfs_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry)
+{
+       struct vmfs_sb_info *server = server_from_dentry(old_dentry);
+       struct vmfs_ws *ws = vmfs_get_ws(server);
+
+       int result;
+
+       result =
+               vmfs_encode_path(server, ws->path, PATH_MAX, old_dentry, NULL);
+       if (result < 0)
+               goto out;
+       result =
+           vmfs_encode_path(server, ws->path2, PATH_MAX, new_dentry, NULL);
+       if (result < 0)
+               goto out;
+
+       result = vmfs_errno(vfsop_rename(server->vfs, ws->path, ws->path2));
+       if (result < 0)
+               goto out;
+
+       result = 0;
+
+out:
+       vmfs_put_ws(ws);
+       FNEXIT("%d", result);
+       return result;
+}
+
+int vmfs_proc_mkdir(struct dentry *dentry)
+{
+       struct vmfs_sb_info *server = server_from_dentry(dentry);
+       struct vmfs_ws *ws = vmfs_get_ws(server);
+       int result;
+
+       FNENTER();
+
+       result = vmfs_encode_path(server, ws->path, PATH_MAX, dentry, NULL);
+       if (result < 0)
+               goto out;
+
+       result = vmfs_errno(vfsop_mkdir(server->vfs, ws->path));
+       if (result < 0)
+               goto out;
+
+       result = 0;
+
+out:
+       vmfs_put_ws(ws);
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+int vmfs_proc_rmdir(struct dentry *dentry)
+{
+       struct vmfs_sb_info *server = server_from_dentry(dentry);
+       struct vmfs_ws *ws = vmfs_get_ws(server);
+       int result;
+
+       FNENTER();
+
+       result = vmfs_encode_path(server, ws->path, PATH_MAX, dentry, NULL);
+       if (result < 0)
+               goto out;
+
+       result = vmfs_errno(vfsop_rmdir(server->vfs, ws->path));
+       if (result < 0)
+               goto out;
+
+       result = 0;
+
+out:
+       vmfs_put_ws(ws);
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+#if VMFSFS_POSIX_UNLINK
+/*
+ * Removes readonly attribute from a file. Used by unlink to give posix
+ * semantics.
+ */
+#endif
+
+int vmfs_proc_unlink(struct dentry *dentry)
+{
+       struct vmfs_sb_info *server = server_from_dentry(dentry);
+       struct vmfs_ws *ws = vmfs_get_ws(server);
+       int result;
+
+       result = vmfs_encode_path(server, ws->path, PATH_MAX, dentry, NULL);
+       if (result < 0)
+               goto out;
+
+       /* GPBTODO - this needs to work even if the file is read only
+        *           should this be done on the host side? */
+
+       result = vmfs_errno(vfsop_remove(server->vfs, ws->path));
+out:
+       vmfs_put_ws(ws);
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+int vmfs_proc_flush(struct vmfs_sb_info *server, int32_t handle)
+{
+       int result;
+
+       result = vmfs_errno(vfsop_filesync(server->vfs, handle));
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+static int vmfs_proc_trunc32(struct inode *inode, loff_t length)
+{
+       struct vmfs_sb_info *server = server_from_inode(inode);
+       int result;
+
+       result =
+           vmfs_errno(vfsop_setfilesize
+                      (server->vfs, VMFS_I(inode)->vhandle, length));
+       if (result < 0)
+               goto out;
+
+       result = 0;
+
+       FNEXIT("%d", result);
+
+out:
+       return result;
+}
+
+static void
+vmfs_init_dirent(struct vmfs_sb_info *server, struct vmfs_fattr *fattr)
+{
+       memset(fattr, 0, sizeof(*fattr));
+
+       fattr->f_nlink = 1;
+       fattr->f_uid = server->mnt->uid;
+       fattr->f_gid = server->mnt->gid;
+       fattr->f_unix = 0;
+}
+
+static void
+vmfs_finish_dirent(struct vmfs_sb_info *server, struct vmfs_fattr *fattr)
+{
+       if (fattr->f_unix)
+               return;
+
+       fattr->f_mode = server->mnt->file_mode;
+       if (fattr->attr & aDIR) {
+               fattr->f_mode = server->mnt->dir_mode;
+               fattr->f_size = VMFS_ST_BLKSIZE;
+       }
+       /* Check the read-only flag */
+       if (fattr->attr & aRONLY)
+               fattr->f_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+
+       /* How many 512 byte blocks do we need for this file? */
+       fattr->f_blocks = 0;
+       if (fattr->f_size != 0)
+               fattr->f_blocks = 1 + ((fattr->f_size - 1) >> 9);
+       return;
+}
+
+void
+vmfs_init_root_dirent(struct vmfs_sb_info *server, struct vmfs_fattr *fattr,
+                     struct super_block *sb)
+{
+       vmfs_init_dirent(server, fattr);
+       fattr->attr = aDIR;
+       fattr->f_ino = 2;       /* traditional root inode number */
+       fattr->f_mtime = current_fs_time(sb);
+       vmfs_finish_dirent(server, fattr);
+}
+
+/*
+ * read in directory entries into the dentry cache
+ */
+static int
+vmfs_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
+                      struct vmfs_cache_control *ctl)
+{
+       struct dentry *dir = filp->f_path.dentry;
+       struct vmfs_sb_info *server = server_from_dentry(dir);
+       struct vmfs_ws *ws = vmfs_get_ws(server);
+       int vhandle;
+       struct vmfs_fattr fattr;
+       struct qstr qname;
+       int result;
+
+       result = vmfs_encode_path(server, ws->path, PATH_MAX, dir, NULL);
+       if (result < 0)
+               goto out;
+
+       result = vfsop_opendir(server->vfs, ws->path);
+       if (result < 0) {
+               result = vmfs_errno(result);
+               goto out;
+       }
+
+       vhandle = result;
+
+       while (result >= 0) {
+               uint32_t attrlen = VMFS_ATTR_MAX;
+               uint8_t *attrdata = ws->attr;
+               uint32_t attr =
+                   VFS_ATTR_MTIME | VFS_ATTR_TYPE | VFS_ATTR_SIZE |
+                   VFS_ATTR_CTIME | VFS_ATTR_ATIME | VFS_ATTR_NAME;
+               uint64_t mtime, ctime, atime;
+               VFSAttr ftype;
+               uint64_t fsize;
+               char *fname;
+
+               /* todo - get other attributes */
+               result =
+                   vmfs_errno(vfsop_readdir
+                              (server->vfs, vhandle, attr, (void *)attrdata,
+                               attrlen));
+               if (result < 0) {
+                       if (result == -ENOENT)
+                               result = 0;
+
+                       break;
+               }
+
+               mtime = *(uint64_t *) attrdata;
+               attrdata += sizeof(uint64_t);
+               ftype = *(uint32_t *) attrdata;
+               attrdata += sizeof(uint32_t);
+               fsize = *(uint64_t *) attrdata;
+               attrdata += sizeof(uint64_t);
+               ctime = *(uint64_t *) attrdata;
+               attrdata += sizeof(uint64_t);
+               atime = *(uint64_t *) attrdata;
+               attrdata += sizeof(uint64_t);
+               fname = (char *)attrdata;
+
+               if (fname[0] == '.'
+                   && ((fname[1] == 0) || (fname[1] == '.' && fname[2] == 0)))
+                       continue;
+
+               /* todo - decode attr */
+               vmfs_init_dirent(server, &fattr);
+               fattr.f_ino = 0;
+
+               /* mtime/ctime/atime are ms since linux epoch */
+               {
+                       uint32_t div, mod;
+
+                       div = (uint32_t) divmod64(mtime, 1000, &mod);
+
+                       fattr.f_mtime.tv_sec = div;
+                       fattr.f_mtime.tv_nsec = mod * 1000000;
+               }
+
+               {
+                       uint32_t div, mod;
+
+                       div = (uint32_t) divmod64(ctime, 1000, &mod);
+
+                       fattr.f_ctime.tv_sec = div;
+                       fattr.f_ctime.tv_nsec = mod * 1000000;
+               }
+
+               {
+                       uint32_t div, mod;
+
+                       div = (uint32_t) divmod64(atime, 1000, &mod);
+
+                       fattr.f_atime.tv_sec = div;
+                       fattr.f_atime.tv_nsec = mod * 1000000;
+               }
+
+               fattr.f_size = fsize;
+               fattr.attr = 0;
+               if (ftype == (VFSAttr)VFS_TYPE_DIR)
+                       fattr.attr |= aDIR;
+
+               qname.name = fname;
+               qname.len = strlen(fname);
+
+               vmfs_finish_dirent(server, &fattr);
+
+               if (!vmfs_fill_cache
+                   (filp, dirent, filldir, ctl, &qname, &fattr)) {
+                       /* smbfs carries on here... */
+               }
+       }
+
+       vfsop_closedir(server->vfs, vhandle);
+
+out:
+       vmfs_put_ws(ws);
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+static int
+vmfs_proc_getattr_unix(struct vmfs_sb_info *server, struct dentry *dir,
+                      struct vmfs_fattr *fattr)
+{
+       struct vmfs_ws *ws = vmfs_get_ws(server);
+       int attr;
+       uint8_t *attrdata = ws->attr;
+       int attrlen = VMFS_ATTR_MAX;
+       uint64_t mtime, ctime, atime;
+       enum VFSType ftype;
+       uint64_t fsize;
+       char *fname;
+       int result;
+
+       result = vmfs_encode_path(server, ws->path, PATH_MAX, dir, NULL);
+       if (result < 0)
+               goto out;
+
+       attr =
+           VFS_ATTR_MTIME | VFS_ATTR_TYPE | VFS_ATTR_SIZE | VFS_ATTR_CTIME |
+           VFS_ATTR_ATIME | VFS_ATTR_NAME;
+       result =
+           vmfs_errno(vfsop_getattr
+                      (server->vfs, ws->path, attr, (void *)attrdata,
+                       attrlen));
+       if (result < 0)
+               goto out;
+
+       mtime = *(uint64_t *) attrdata;
+       attrdata += sizeof(uint64_t);
+       ftype = *(uint32_t *) attrdata;
+       attrdata += sizeof(uint32_t);
+
+       /* GPBTODO - return this as an error code, why two codes? */
+       if (ftype == VFS_TYPE_NONE || ftype == VFS_TYPE_UNKNOWN) {
+               result = -ENOENT;
+               goto out;
+       }
+
+       fsize = *(uint64_t *) attrdata;
+       attrdata += sizeof(uint64_t);
+       ctime = *(uint64_t *) attrdata;
+       attrdata += sizeof(uint64_t);
+       atime = *(uint64_t *) attrdata;
+       attrdata += sizeof(uint64_t);
+       fname = (char *)(attrdata + 20);
+
+       vmfs_init_dirent(server, fattr);
+       {
+               uint32_t div, mod;
+
+               div = (uint32_t) divmod64(mtime, 1000, &mod);
+
+               fattr->f_mtime.tv_sec = div;
+               fattr->f_mtime.tv_nsec = mod * 1000000;
+       }
+
+       {
+               uint32_t div, mod;
+
+               div = (uint32_t) divmod64(ctime, 1000, &mod);
+
+               fattr->f_ctime.tv_sec = div;
+               fattr->f_ctime.tv_nsec = mod * 1000000;
+       }
+
+       {
+               uint32_t div, mod;
+
+               div = (uint32_t) divmod64(atime, 1000, &mod);
+
+               fattr->f_atime.tv_sec = div;
+               fattr->f_atime.tv_nsec = mod * 1000000;
+       }
+
+       fattr->f_size = fsize;
+
+       fattr->attr = 0;
+       if (ftype == VFS_TYPE_DIR)
+               fattr->attr |= aDIR;
+
+       vmfs_finish_dirent(server, fattr);
+
+out:
+       vmfs_put_ws(ws);
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+int vmfs_proc_getattr(struct dentry *dir, struct vmfs_fattr *fattr)
+{
+       struct vmfs_sb_info *server = server_from_dentry(dir);
+       int result;
+
+       vmfs_init_dirent(server, fattr);
+       result = server->ops->getattr(server, dir, fattr);
+       vmfs_finish_dirent(server, fattr);
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+/*
+ * Because of bugs in the trans2 setattr messages, we must set
+ * attributes and timestamps separately. The core VMFSsetatr
+ * message seems to be the only reliable way to set attributes.
+ */
+int vmfs_proc_setattr(struct dentry *dir, struct vmfs_fattr *fattr)
+{
+       struct vmfs_sb_info *server = server_from_dentry(dir);
+       struct vmfs_ws *ws = vmfs_get_ws(server);
+       int result;
+
+       result = vmfs_encode_path(server, ws->path, PATH_MAX, dir, NULL);
+       if (result < 0)
+               goto out;
+
+       VERBOSE("setting %s/%s, open=%d\n",
+               DENTRY_PATH(dir), vmfs_is_open(dir->d_inode));
+
+       result = 0;
+
+out:
+       vmfs_put_ws(ws);
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+/*
+ * Set the modify and access timestamps for a file.
+ */
+int vmfs_proc_settime(struct dentry *dentry, struct vmfs_fattr *fattr)
+{
+       struct vmfs_sb_info *server = server_from_dentry(dentry);
+       struct vmfs_ws *ws = vmfs_get_ws(server);
+       int result;
+
+       result = vmfs_encode_path(server, ws->path, PATH_MAX, dentry, NULL);
+       if (result < 0)
+               goto out;
+       result = 0;
+
+out:
+       vmfs_put_ws(ws);
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+int vmfs_proc_dskattr(struct dentry *dentry, struct kstatfs *kattr)
+{
+       int result;
+       struct vmfs_sb_info *server = VMFS_SB(dentry->d_sb);
+       struct vmfs_ws *ws = vmfs_get_ws(server);
+       uint32_t attr = VFS_ATTR_DISKSIZE | VFS_ATTR_DISKFREE;
+       uint8_t *attrdata = ws->attr;
+       uint32_t attrlen = 16;
+       uint64_t disksize, diskfree;
+
+       result = vmfs_encode_path(server, ws->path, PATH_MAX, dentry, NULL);
+       if (result < 0)
+               goto out;
+
+       result =
+           vmfs_errno(vfsop_getattr
+                      (server->vfs, ws->path, attr, (void *)attrdata,
+                       attrlen));
+       if (result < 0)
+               goto out;
+
+       disksize = *(uint64_t *) attrdata;
+       diskfree = *(uint64_t *) (attrdata + 8);
+
+       kattr->f_bsize = VMFS_ST_BLKSIZE;
+       kattr->f_blocks = disksize >> VMFS_ST_BLKSHIFT;
+       kattr->f_bavail = diskfree >> VMFS_ST_BLKSHIFT;
+
+       result = 0;
+out:
+       vmfs_put_ws(ws);
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+/* vfs may not support this operation
+ */
+
+int
+vmfs_proc_read_link(struct vmfs_sb_info *server, struct dentry *dentry,
+                   char *buffer, int len)
+{
+       int result;
+       struct vmfs_ws *ws = vmfs_get_ws(server);
+
+       result = vmfs_encode_path(server, ws->path, PATH_MAX, dentry, NULL);
+       if (result < 0)
+               goto out;
+
+       result =
+               vmfs_errno(vfsop_readlink(server->vfs, ws->path, buffer, len));
+       if (result < 0)
+               goto out;
+
+       buffer[len - 1] = 0;
+       result = strlen(buffer);
+
+out:
+       vmfs_put_ws(ws);
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+/*
+ * Create a symlink object called dentry which points to oldpath.
+ * vfs may not support this operation
+ */
+int
+vmfs_proc_symlink(struct vmfs_sb_info *server, struct dentry *dentry,
+                 const char *oldpath)
+{
+       int result;
+       struct vmfs_ws *ws = vmfs_get_ws(server);
+
+       result = vmfs_encode_path(server, ws->path, PATH_MAX, dentry, NULL);
+       if (result < 0)
+               goto out;
+
+       result = vmfs_errno(vfsop_symlink(server->vfs, ws->path, oldpath));
+
+out:
+       vmfs_put_ws(ws);
+
+       FNEXIT("%d", result);
+
+       return result;
+}
+
+/*
+ * Create a hard link object called new_dentry which points to dentry.
+ */
+int
+vmfs_proc_link(struct vmfs_sb_info *server, struct dentry *dentry,
+              struct dentry *new_dentry)
+{
+       /* we don't support hard links */
+       return -EPERM;
+}
+
+static void install_ops(struct vmfs_ops *dst, struct vmfs_ops *src)
+{
+       memcpy(dst, src, sizeof(void *) * VMFS_OPS_NUM_STATIC);
+}
+
+static struct vmfs_ops vmfs_server_ops = {
+       .read = vmfs_proc_read,
+       .write = vmfs_proc_write,
+       .readdir = vmfs_proc_readdir_long,
+       .getattr = vmfs_proc_getattr_unix,
+       /* .setattr = vmfs_proc_setattr_unix, */
+       .truncate = vmfs_proc_trunc32,
+};
+
+void vmfs_install_ops(struct vmfs_ops *ops)
+{
+       install_ops(ops, &vmfs_server_ops);
+}
diff --git a/fs/vmfs/proto.h b/fs/vmfs/proto.h
new file mode 100644
index 0000000..01ea8ed
--- /dev/null
+++ b/fs/vmfs/proto.h
@@ -0,0 +1,71 @@
+/*
+ *  Autogenerated with cproto on:  Thu May 8 12:58:52 BST 2008
+ */
+
+struct vmfs_request;
+struct sock;
+struct statfs;
+
+/* proc.c */
+extern int vmfs_errno(int error);
+extern int vmfs_open(struct dentry *dentry, int flags, int wish);
+extern int vmfs_close(struct inode *ino);
+extern int vmfs_close_fileid(struct dentry *dentry, int32_t fileid);
+extern int vmfs_proc_create(struct dentry *dentry, uint32_t mode,
+                           int32_t *fileid);
+extern int vmfs_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry);
+extern int vmfs_proc_mkdir(struct dentry *dentry);
+extern int vmfs_proc_rmdir(struct dentry *dentry);
+extern int vmfs_proc_unlink(struct dentry *dentry);
+extern int vmfs_proc_flush(struct vmfs_sb_info *server, int32_t handle);
+extern void vmfs_init_root_dirent(struct vmfs_sb_info *server,
+                                 struct vmfs_fattr *fattr,
+                                 struct super_block *sb);
+extern int vmfs_proc_getattr(struct dentry *dir, struct vmfs_fattr *fattr);
+extern int vmfs_proc_setattr(struct dentry *dir, struct vmfs_fattr *fattr);
+extern int vmfs_proc_settime(struct dentry *dentry, struct vmfs_fattr *fattr);
+extern int vmfs_proc_dskattr(struct dentry *dentry, struct kstatfs *kattr);
+extern int vmfs_proc_read_link(struct vmfs_sb_info *server,
+                              struct dentry *dentry, char *buffer, int len);
+extern int vmfs_proc_symlink(struct vmfs_sb_info *server,
+                            struct dentry *dentry,
+                            const char *oldpath);
+extern int vmfs_proc_link(struct vmfs_sb_info *server, struct dentry *dentry,
+                         struct dentry *new_dentry);
+extern void vmfs_install_ops(struct vmfs_ops *ops);
+/* dir.c */
+extern const struct file_operations vmfs_dir_operations;
+extern const struct inode_operations vmfs_dir_inode_operations;
+extern const struct inode_operations vmfs_dir_inode_operations_unix;
+extern void vmfs_new_dentry(struct dentry *dentry);
+extern void vmfs_renew_times(struct dentry *dentry);
+/* cache.c */
+extern int vmfs_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
+                          struct vmfs_cache_control *ctrl, struct qstr *qname,
+                          struct vmfs_fattr *entry);
+extern void vmfs_invalid_dir_cache(struct inode *dir);
+extern void vmfs_invalidate_dircache_entries(struct dentry *parent);
+extern struct dentry *vmfs_dget_fpos(struct dentry *dentry,
+                                    struct dentry *parent,
+                                    unsigned long fpos);
+/* inode.c */
+extern struct inode *vmfs_iget(struct super_block *sb,
+                              struct vmfs_fattr *fattr);
+extern void vmfs_get_inode_attr(struct inode *inode, struct vmfs_fattr *fattr);
+extern void vmfs_set_inode_attr(struct inode *inode, struct vmfs_fattr *fattr);
+extern void vmfs_invalidate_inodes(struct vmfs_sb_info *server);
+extern int vmfs_revalidate_inode(struct dentry *dentry);
+extern int vmfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
+                       struct kstat *stat);
+extern int vmfs_notify_change(struct dentry *dentry, struct iattr *attr);
+/* file.c */
+extern const struct address_space_operations vmfs_file_aops;
+extern const struct file_operations vmfs_file_operations;
+extern const struct inode_operations vmfs_file_inode_operations;
+/* ioctl.c */
+extern long vmfs_unlocked_ioctl(struct file *filp, unsigned int cmd,
+                               unsigned long arg);
+/* symlink.c */
+extern int vmfs_symlink(struct inode *inode, struct dentry *dentry,
+                       const char *oldname);
+extern const struct inode_operations vmfs_link_inode_operations;
diff --git a/fs/vmfs/symlink.c b/fs/vmfs/symlink.c
new file mode 100644
index 0000000..1d27545
--- /dev/null
+++ b/fs/vmfs/symlink.c
@@ -0,0 +1,68 @@
+/*
+ *  symlink.c
+ *
+ *  Copyright (C) 2002 by John Newbigin
+ *
+ *  Please add a note about your changes to vmfs_ in the ChangeLog file.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/net.h>
+#include <linux/namei.h>
+
+#include <linux/uaccess.h>
+#include <asm/system.h>
+
+#include "vmfsno.h"
+#include "vmfs_fs.h"
+
+#include "vmfs_debug.h"
+#include "proto.h"
+
+int vmfs_symlink(struct inode *inode, struct dentry *dentry,
+                const char *oldname)
+{
+       DEBUG1("create symlink %s -> %s/%s\n", oldname, DENTRY_PATH(dentry));
+
+       return vmfs_proc_symlink(server_from_dentry(dentry), dentry, oldname);
+}
+
+static void *vmfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+       char *link = __getname();
+       DEBUG1("followlink of %s/%s\n", DENTRY_PATH(dentry));
+
+       if (!link) {
+               link = ERR_PTR(-ENOMEM);
+       } else {
+               int len = vmfs_proc_read_link(server_from_dentry(dentry),
+                                             dentry, link, PATH_MAX - 1);
+               if (len < 0) {
+                       __putname(link);
+                       link = ERR_PTR(len);
+               } else {
+                       link[len] = 0;
+               }
+       }
+       nd_set_link(nd, link);
+       return NULL;
+}
+
+static void vmfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
+{
+       char *s = nd_get_link(nd);
+       if (!IS_ERR(s))
+               __putname(s);
+}
+
+const struct inode_operations vmfs_link_inode_operations = {
+       .readlink = generic_readlink,
+       .follow_link = vmfs_follow_link,
+       .put_link = vmfs_put_link,
+};
diff --git a/fs/vmfs/vfs.c b/fs/vmfs/vfs.c
new file mode 100644
index 0000000..14e03285
--- /dev/null
+++ b/fs/vmfs/vfs.c
@@ -0,0 +1,577 @@
+/*
+ * Copyright 2008-2009 ARM Limited. All rights reserved.
+ */
+
+/*!
+ * \file    vfs.cpp
+ * \brief   target side vfs implementation in C
+ *
+ * The vfs functions have been renamed to vfsop to avoid
+ * symbol clashes in linux. We should standardise on one or the other.
+ */
+
+/* linux kernel requires different includes */
+
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+
+#include "messagebox.h"
+#include "msg.h"
+#include "vfs.h"
+
+/********************************************************************
+ * vfs layer implementation
+ *
+ * VFS operations - these must match those defined in VFS.h
+ ********************************************************************/
+typedef enum VFSOp {
+       VFS_OPENMOUNTS,
+       VFS_READMOUNTS,
+       VFS_CLOSEMOUNTS,
+
+       VFS_OPENDIR,
+       VFS_READDIR,
+       VFS_CLOSEDIR,
+       VFS_MKDIR,
+       VFS_RMDIR,
+       VFS_REMOVE,
+       VFS_RENAME,
+       VFS_GETATTR,
+       VFS_SETATTR,
+
+       VFS_OPENFILE,
+       VFS_CLOSEFILE,
+       VFS_WRITEFILE,
+       VFS_READFILE,
+       VFS_GETFILESIZE,
+       VFS_SETFILESIZE,
+       VFS_FILESYNC,
+
+       VFS_SYMLINK,
+       VFS_READLINK
+} VFSOp;
+
+/********************************************************************
+ * maximum _data_ transfer in a message, this must allow for
+ * other message parameters
+ * \todo it should be derived from the maximum messsage size
+ ********************************************************************/
+#define VFS_MAX_DATA 4096
+#define VFS_MAX_MSG  8192
+
+struct VFS {
+       MessageBox *mb;
+       MessageComposer *mc;
+       MessageDecomposer *md;
+
+       int last_err;
+};
+
+void vfsop_init(VFS *vfs, MessageBox *mb)
+{
+       vfs->mb = mb;
+       vfs->mc = msgc_new(NULL, 0);
+       vfs->md = msgd_new(NULL, 0);
+
+       vfs->last_err = 0;
+}
+
+void vfsop_cleanup(VFS *vfs)
+{
+       vfs->mb = NULL;
+       msgc_delete(vfs->mc);
+       vfs->mc = NULL;
+       msgd_delete(vfs->md);
+       vfs->md = NULL;
+}
+
+VFS *vfsop_new(MessageBox *mb)
+{
+       VFS *vfs = (VFS *)kmalloc(sizeof(struct VFS), GFP_KERNEL);
+
+       /* vfs should check that MB is actually a VFS mb */
+
+       vfsop_init(vfs, mb);
+
+       return vfs;
+}
+
+void vfsop_delete(VFS *vfs)
+{
+       vfsop_cleanup(vfs);
+       kfree(vfs);
+}
+
+int vfsop_startcall(VFS *vfs, uint32_t op)
+{
+       void *buffer;
+
+       if (mb_lock(vfs->mb) < 0)
+               return -1;
+
+       buffer = mb_start(vfs->mb, VFS_MAX_MSG);
+
+       msgc_init(vfs->mc, buffer, VFS_MAX_MSG);
+
+       msgc_put_uint32(vfs->mc, 0);    /* message id */
+       msgc_put_uint32(vfs->mc, op);   /* vfs operation */
+
+       return 0;
+}
+
+void vfsop_call(VFS *vfs)
+{
+       void *buffer;
+       uint32_t blen;
+       uint32_t id;
+
+       /* int ret = */ mb_end(vfs->mb, msgc_get_size(vfs->mc));
+
+       msgc_cleanup(vfs->mc);
+
+       /* todo - this can currently return -1 if the thread was interrupted.
+        * we probably don't want to support interruption during the call */
+       mb_wait(vfs->mb);
+
+       buffer = mb_receive(vfs->mb, &blen);
+
+       msgd_init(vfs->md, buffer, blen);
+
+       msgd_get_uint32(vfs->md, &id);  /* message id inserted above */
+
+       /* todo - check the id's match */
+}
+
+void vfsop_endcall(VFS *vfs)
+{
+       msgd_cleanup(vfs->md);
+
+       mb_unlock(vfs->mb);
+}
+
+int32_t vfsop_openmounts(VFS *vfs)
+{
+       int32_t handle;
+
+       vfsop_startcall(vfs, VFS_OPENMOUNTS);
+
+       vfsop_call(vfs);
+       msgd_get_int32(vfs->md, &handle);
+
+       vfsop_endcall(vfs);
+
+       return handle;
+}
+
+int32_t vfsop_readmounts(VFS *vfs, int32_t handle, uint32_t attr,
+                        uint8_t *attrdata, uint32_t attrdatalen)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_READMOUNTS);
+
+       msgc_put_int32(vfs->mc, handle);
+       msgc_put_uint32(vfs->mc, attr);
+       msgc_put_uint32(vfs->mc, attrdatalen);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+       msgd_get_data(vfs->md, attrdata, &attrdatalen);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_closemounts(VFS *vfs, int32_t handle)
+{
+       int32_t ret;
+
+       ret = vfsop_startcall(vfs, VFS_READMOUNTS);
+       if (ret < 0)
+               return ret;
+
+       msgc_put_int32(vfs->mc, handle);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_opendir(VFS *vfs, const char *dirname)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_OPENDIR);
+
+       msgc_put_cstr(vfs->mc, dirname);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_readdir(VFS *vfs, int32_t handle, uint32_t attr,
+                     uint8_t *attrdata, uint32_t attrdatalen)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_READDIR);
+
+       msgc_put_int32(vfs->mc, handle);
+       msgc_put_uint32(vfs->mc, attr);
+       msgc_put_uint32(vfs->mc, attrdatalen);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+       msgd_get_data(vfs->md, attrdata, &attrdatalen);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_closedir(VFS *vfs, int32_t handle)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_CLOSEDIR);
+
+       msgc_put_int32(vfs->mc, handle);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_mkdir(VFS *vfs, const char *name)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_MKDIR);
+
+       msgc_put_cstr(vfs->mc, name);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_rmdir(VFS *vfs, const char *name)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_RMDIR);
+
+       msgc_put_cstr(vfs->mc, name);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_remove(VFS *vfs, const char *name)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_REMOVE);
+
+       msgc_put_cstr(vfs->mc, name);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_rename(VFS *vfs, const char *oldname, const char *newname)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_RENAME);
+
+       msgc_put_cstr(vfs->mc, oldname);
+       msgc_put_cstr(vfs->mc, newname);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_getattr(VFS *vfs, const char *name, uint32_t attr,
+                     uint8_t *attrdata, uint32_t attrdatalen)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_GETATTR);
+
+       msgc_put_cstr(vfs->mc, name);
+       msgc_put_uint32(vfs->mc, attr);
+       msgc_put_uint32(vfs->mc, attrdatalen);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+       msgd_get_data(vfs->md, attrdata, &attrdatalen);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_setattr(VFS *vfs, const char *name, uint32_t attr,
+                     const uint8_t *attrdata, uint32_t attrdatalen)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_SETATTR);
+
+       msgc_put_cstr(vfs->mc, name);
+       msgc_put_uint32(vfs->mc, attr);
+       msgc_put_data(vfs->mc, attrdata, attrdatalen);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_openfile(VFS *vfs, const char *name, uint32_t flags)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_OPENFILE);
+
+       msgc_put_cstr(vfs->mc, name);
+       msgc_put_uint32(vfs->mc, flags);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_closefile(VFS *vfs, int32_t handle)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_CLOSEFILE);
+
+       msgc_put_int32(vfs->mc, handle);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_writefile(VFS *vfs, int32_t handle, uint64_t offset,
+                       const void *data, int32_t len)
+{
+       int32_t ret;
+       int32_t residual = len;
+
+       /* Transfer has to be broken into manageable chunks */
+
+       while (residual > 0) {
+               int32_t t_len =
+                   (residual > VFS_MAX_DATA) ? VFS_MAX_DATA : residual;
+
+               vfsop_startcall(vfs, VFS_WRITEFILE);
+
+               msgc_put_int32(vfs->mc, handle);
+               msgc_put_uint64(vfs->mc, offset);
+               msgc_put_data(vfs->mc, data, t_len);
+               msgc_put_uint32(vfs->mc, t_len);
+
+               vfsop_call(vfs);
+
+               msgd_get_int32(vfs->md, &ret);
+
+               vfsop_endcall(vfs);
+
+               if (ret < 0)
+                       return ret;
+
+               offset += ret;
+               residual -= ret;
+               data = (uint8_t *) data + ret;
+
+               if (ret < t_len)
+                       break;
+       }
+
+       return len - residual;
+}
+
+int32_t vfsop_readfile(VFS *vfs, int32_t handle, uint64_t offset, void *data,
+                      int32_t len)
+{
+       int32_t ret;
+       int32_t residual = len;
+       uint32_t rlen = len;
+
+       /* data must be sent in manageable chunks */
+
+       while (residual > 0) {
+               int32_t t_len =
+                   (residual > VFS_MAX_DATA) ? VFS_MAX_DATA : residual;
+
+               vfsop_startcall(vfs, VFS_READFILE);
+
+               msgc_put_int32(vfs->mc, handle);
+               msgc_put_uint64(vfs->mc, offset);
+               msgc_put_uint32(vfs->mc, t_len);
+
+               vfsop_call(vfs);
+
+               msgd_get_int32(vfs->md, &ret);
+               msgd_get_data(vfs->md, data, &rlen);
+
+               vfsop_endcall(vfs);
+
+               if (ret < 0)
+                       return ret;
+
+               offset += ret;
+               residual -= ret;
+               data = (uint8_t *) data + ret;
+
+               if (ret < t_len)
+                       break;
+       }
+
+       return len - residual;
+}
+
+int32_t vfsop_getfilesize(VFS *vfs, int32_t handle, uint64_t *size)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_GETFILESIZE);
+
+       msgc_put_int32(vfs->mc, handle);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+       msgd_get_uint64(vfs->md, size);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_setfilesize(VFS *vfs, int32_t handle, uint64_t size)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_SETFILESIZE);
+
+       msgc_put_int32(vfs->mc, handle);
+       msgc_put_uint64(vfs->mc, size);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_filesync(VFS *vfs, int32_t handle)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_FILESYNC);
+
+       msgc_put_int32(vfs->mc, handle);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_symlink(VFS *vfs, const char *filename, const char *symlink)
+{
+       int32_t ret;
+
+       vfsop_startcall(vfs, VFS_SYMLINK);
+
+       msgc_put_cstr(vfs->mc, filename);
+       msgc_put_cstr(vfs->mc, symlink);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
+
+int32_t vfsop_readlink(VFS *vfs, const char *filename, char *buf,
+                      int32_t bufsiz)
+{
+       int32_t ret;
+       uint32_t rlen;
+
+       vfsop_startcall(vfs, VFS_READLINK);
+
+       msgc_put_cstr(vfs->mc, filename);
+       msgc_put_int32(vfs->mc, bufsiz);
+
+       vfsop_call(vfs);
+
+       msgd_get_int32(vfs->md, &ret);
+       msgd_get_data(vfs->md, buf, &rlen);
+
+       vfsop_endcall(vfs);
+
+       return ret;
+}
diff --git a/fs/vmfs/vfs.h b/fs/vmfs/vfs.h
new file mode 100644
index 0000000..5e04f69
--- /dev/null
+++ b/fs/vmfs/vfs.h
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2008-2009 ARM Limited. All rights reserved.
+ */
+
+/*!
+ * \file    vfs.h
+ * \brief   target side vfs interface in C
+ *
+ * This interface has been renamed to VFS (and the operations to vfsop) to
+ * avoid symbol clashes in linux. We should standardise on one or the other.
+ */
+
+#ifndef VFS_H
+#define VFS_H
+
+#include "messagebox.h"
+
+/* get these definitions from a shared header used on both host/target side */
+
+/* Objects types that can exist on a filesystem */
+enum VFSType {
+       VFS_TYPE_NONE,          /* file not found */
+       VFS_TYPE_FILE,          /* regular file */
+       VFS_TYPE_DIR,           /* directory */
+       VFS_TYPE_LINK,          /* symbolic link */
+       VFS_TYPE_UNKNOWN,       /* unknown object type */
+       VFS_TYPE_MOUNT          /* mount point */
+};
+
+/* \todo these should probably be +ve and return as -VFS_ERR_ etc. */
+enum VFSError {
+       VFS_ERR_OK = 0,         /* all ok (actually 0 or +ve means ok) */
+       VFS_ERR_BADHANDLE = -1, /* invalid or wrong type of handle */
+       VFS_ERR_NOENTRY = -2,   /* no more entries in a directory */
+       VFS_ERR_NOROOM = -3,    /* ran out of memory/buffer/disk space */
+       VFS_ERR_MAXHANDLE = -4, /* ran out of handles */
+       VFS_ERR_NOMOUNT = -5,   /* no such mount exists */
+       VFS_ERR_NOTFOUND = -6,  /* object not found */
+       VFS_ERR_PERM = -7,      /* permission error */
+       VFS_ERR_NOTDIR = -8,    /* path element wasn't a directory */
+       VFS_ERR_TOOLONG = -9,   /* path or path element too long */
+       VFS_ERR_EXIST = -10,    /* an object with the name already exists */
+       VFS_ERR_NOTEMPTY = -11, /* tried to remove dir that wasn't empty */
+       VFS_ERR_INVALID = -12,  /* invalid operation or operand */
+       VFS_ERR_ISDIR = -13,    /* object is a directory */
+       VFS_ERR_TOOBIG = -14,   /* return value too large to represent */
+       VFS_ERR_UNIMPL = -15,   /* unimplemented feature */
+       VFS_ERR_UNKNOWN = -100  /* unexpected host error */
+};
+
+typedef enum VFSAttr {
+       VFS_ATTR_MTIME = 0x0001,  /* uint64_t modification time */
+       VFS_ATTR_ACCESS = 0x0002, /* uint32_t access permissions (r/w/e) */
+       VFS_ATTR_TYPE = 0x0004,   /* uint32_t obj type (as above) */
+       VFS_ATTR_SIZE = 0x0008,   /* uint64_t obj size in bytes */
+       VFS_ATTR_CTIME = 0x0010,  /* uint64_t obj creation time */
+       VFS_ATTR_ATIME = 0x0020,  /* uint64_t obj access time */
+       VFS_ATTR_RTIME = 0x0040,  /* uint64_t current real time */
+       VFS_ATTR_DISKSIZE = 0x0100,  /* uint64_t size of disk in bytes */
+       VFS_ATTR_DISKFREE = 0x0200,  /* uint64_t free space on disk in bytes */
+       VFS_ATTR_NAME = 0x8000,   /* char* last to make variable length easy */
+} VFSAttr;
+
+/* flags passed to Mount::openFile */
+typedef enum VFSOpenFlags {
+       VFS_OPEN_RDONLY = 1,
+       VFS_OPEN_WRONLY = 2,
+       VFS_OPEN_RDWR = VFS_OPEN_RDONLY | VFS_OPEN_WRONLY,
+       VFS_OPEN_CREATE = 4,
+       VFS_OPEN_NEW = 8,
+       VFS_OPEN_TRUNCATE = 16
+} VFSOpenFlags;
+
+/*! Opaque instance handle for use in vfs calls */
+typedef struct VFS VFS;
+
+   /*! instantiate a new vfs object
+    *
+    * \param mb         message box instance to use as a transport layer
+    *
+    * \return           vfs instance handle to use in vfs calls
+    */
+VFS *vfsop_new(MessageBox *mb);
+
+   /*! delete a vfs instance
+    *
+    * \param vfs       instance to delete
+    */
+void vfsop_delete(VFS *vfs);
+
+    /*! Open an iterator on the list of mounts added with add Mount
+     *
+     * \param vfs      vfs instance
+     *
+     * \return a handle to be used with readmounts/closemounts or VFSError code
+     */
+int32_t vfsop_openmounts(VFS *vfs);
+
+    /* Read the next entry in a list of mounts
+     *
+     * \param vfs       vfs instance
+     * \param id        mount iterator handle
+     * \param attr      bit mask of attributes to return (one or more VFSAttr)
+     * \param attrdata  data block to receive attributes
+     * \param attrlen   size of attribute block
+     *
+     * \return VFSError code
+     *
+     * The attribute block is packed with data in VFSAttr order (lowest to
+     * highest). Be careful to unpack the attribute block using the correct
+     * data sizes. Not all attributes are relavent to mount data
+     *
+     */
+int32_t vfsop_readmounts(VFS *vfs, int32_t handle, uint32_t attr,
+                        uint8_t *attrdata, uint32_t attrdatalen);
+
+    /* Close a mount iterator handle
+     *
+     * \param vfs      vfs instance
+     * \param id        mount iterator handle
+     *
+     * \return VFSError code
+     */
+int32_t vfsop_closemounts(VFS *vfs, int32_t handle);
+
+    /* Open a directory iterator handle
+     *
+     * \param vfs      vfs instance
+     * \param name      full (vfs) path name to directory
+     *
+     * \return directory iterator handle for use with readdir/closedir
+     * or a VFSError code
+     */
+int32_t vfsop_opendir(VFS *vfs, const char *dirname);
+
+    /* Read an entry form a directory iterator
+     *
+     * \param vfs      vfs instance
+     * \param id        directory iterator handle
+     * \param attr      bit mask of attributes to return (one or more VFSAttr)
+     * \param attrdata  data block to receive attributes
+     * \param attrlen   size of attribute block
+     *
+     * \return VFSError code
+     *
+     * The attribute block is packed with data in VFSAttr order (lowest to
+     * highest). Be careful to unpack the attribute block using the correct
+     * data sizes
+     *
+     * \todo pass attrlen by reference so it can be updated with the size used
+     * \todo pass attr by reference so that the actual returned attributes can
+     * be indicated
+     */
+int32_t vfsop_readdir(VFS *vfs, int32_t handle, uint32_t attr,
+                     uint8_t *attrdata, uint32_t attrdatalen);
+
+    /* Close a directory iterator
+     *
+     * \param vfs      vfs instance
+     * \param id        directory iterator handle
+     *
+     * \return VFSError code
+     */
+int32_t vfsop_closedir(VFS *vfs, int32_t handle);
+
+    /* Create a directory
+     *
+     * \param vfs      vfs instance
+     * \param name      (vfs) directory name to create
+     *
+     * \return VFSError code
+     */
+int32_t vfsop_mkdir(VFS *vfs, const char *name);
+
+    /* Remove a directory
+     *
+     * \param vfs      vfs instance
+     * \param name      (vfs) directory name to create
+     *
+     * \return VFSError code
+     */
+int32_t vfsop_rmdir(VFS *vfs, const char *name);
+
+    /* Remove a file
+     *
+     * \param vfs      vfs instance
+     * \param name      (vfs) file to remove (may also work on other
+     * object types)
+     *
+     * \return VFSError code
+     */
+int32_t vfsop_remove(VFS *vfs, const char *name);
+
+    /* Rename an object
+     *
+     * \param vfs      vfs instance
+     * \param oldname   (vfs) object to rename
+     * \param newname   (vfs) new name of object
+     *
+     * \return VFSError code
+     */
+int32_t vfsop_rename(VFS *vfs, const char *oldname, const char *newname);
+
+    /* Retrieve attributes of an object on the filesystem
+     *
+     * \param vfs      vfs instance
+     * \param name      (vfs) object name
+     * \param attr      bit mask of attributes to return (one or more VFSAttr)
+     * \param attrdata  data block to receive attributes
+     * \param attrlen   size of attribute block
+     *
+     * \return VFSError code
+     *
+     * The attribute block is packed with data in VFSAttr order (lowest to
+     * highest). Be careful to unpack the attribute block using the
+     * correct data sizes
+     *
+     * \todo pass attrlen by reference so it can be updated with the size used
+     * \todo pass attr by reference so that the actual returned attributes
+     * can be indicated
+     */
+int32_t vfsop_getattr(VFS *vfs, const char *name, uint32_t attr,
+                     uint8_t *attrdata, uint32_t attrdatalen);
+
+    /* Retrieve attributes of an object on the filesystem
+     *
+     * \param vfs      vfs instance
+     * \param name      (vfs) object name
+     * \param attr      bit mask of attributes to modify (one or more VFSAttr)
+     * \param attrdata  data block containing packed attributes
+     * \param attrlen   size of attribute block
+     *
+     * \return VFSError code
+     *
+     * The attribute block should be packed with data in VFSAttr order
+     * (lowest to highest). Be careful to pack the attribute block using
+     * the correct data sizes
+     *
+     * Not all attributes can be modified using this (e.g. file size/disk
+     * free/file name)
+     *
+     * \todo pass attr by reference so that the actual modified
+     * attributes can be indicated
+     */
+int32_t vfsop_setattr(VFS *vfs, const char *name, uint32_t attr,
+                     const uint8_t *attrdata, uint32_t attrdatalen);
+
+    /* Open a file object on the filesystem for reading/writing
+     *
+     * \param vfs      vfs instance
+     * \param filename  (vfs) file name
+     * \param flags     VFSOpenFlags value indicating how to open the file
+     *
+     * \return file handle to use with readfile/writefile/closefile etc or
+     * a VFSError code
+     */
+int32_t vfsop_openfile(VFS *vfs, const char *name, uint32_t flags);
+
+    /* Close a file object by a handle returned from openfile
+     *
+     * \param vfs      vfs instance
+     * \param id        file handle
+     *
+     * \return VFSError code
+     */
+int32_t vfsop_closefile(VFS *vfs, int32_t handle);
+
+    /* Write data to a file
+     *
+     * \param vfs      vfs instance
+     * \param id        file handle returned from openfile
+     * \param offset    offset into file from where to start writing
+     * \param data      pointer to data block containing data to be written
+     * \param len       length of data to be written
+     *
+     * \return length of data actually written to the file or a VFSError code
+     */
+int32_t vfsop_writefile(VFS *vfs, int32_t handle, uint64_t offset,
+                       const void *data, int32_t len);
+
+    /* Read data from a file
+     *
+     * \param vfs      vfs instance
+     * \param id        file handle returned from openfile
+     * \param offset    offset into file from where to start reading
+     * \param data      pointer to data block to receive data read from file
+     * \param len       size of data block to receive data
+     *
+     * \return length of data actually read from the file or a VFSError code
+     */
+int32_t vfsop_readfile(VFS *vfs, int32_t handle, uint64_t offset, void *data,
+                      int32_t len);
+
+    /* Get the size of an open file
+     *
+     * \param vfs      vfs instance
+     * \param id        file handle returned from openfile
+     * \param size      pointer to instance data to receive file size
+     *
+     * \return VFSError code
+     */
+int32_t vfsop_getfilesize(VFS *vfs, int32_t handle, uint64_t *size);
+
+    /* Set the size of an open file
+     *
+     * \param vfs      vfs instance
+     * \param id        file handle returned from openfile
+     * \param size      new size of file
+     *
+     * \return VFSError code
+     *
+     * this will truncate or extend the file depending on whether the new
+     * size is smaller or larger than the current file size
+     */
+int32_t vfsop_setfilesize(VFS *vfs, int32_t handle, uint64_t size);
+
+    /* Force modified parts of a file back to persistent storage
+     *
+     * \param vfs      vfs instance
+     * \param id        file handle returned from openfile
+     *
+     * \return VFSError code
+     */
+int32_t vfsop_filesync(VFS *vfs, int32_t handle);
+
+/* Linux target support functions */
+
+   /* Create a symbolic link object
+    *
+    * \param vfs      vfs instance
+    * \param filename  (vfs) name of link object to be created
+    * \param data      content of link object (typically a path to
+    * another object)
+    *
+    * \return VFSError code
+    *
+    * \todo this is not yet implemented
+    */
+
+int32_t vfsop_symlink(VFS *vfs, const char *filename, const char *symlink);
+
+    /* Read the contents of a symbolic link object
+     *
+     * \param vfs      vfs instance
+     * \param filename  (vfs) name of link object to be read
+     * \param data      data block to receive link object contents
+     * \param bufsiz    size of data block to receive link object contents
+     *
+     * \return VFSError code
+     *
+     * \todo this is not yet implemented
+     */
+int32_t vfsop_readlink(VFS *vfs, const char *filename, char *buf,
+                      int32_t bufsiz);
+
+#endif /* VFS_H */
diff --git a/fs/vmfs/vmfs.h b/fs/vmfs/vmfs.h
new file mode 100644
index 0000000..ed01b51
--- /dev/null
+++ b/fs/vmfs/vmfs.h
@@ -0,0 +1,45 @@
+/*
+ *  vmfs.h
+ *
+ *  Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *  Copyright (C) 1997 by Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_VMFS_H
+#define _LINUX_VMFS_H
+
+#include <linux/types.h>
+#include <linux/magic.h>
+#include <linux/uidgid.h>
+
+#ifdef __KERNEL__
+
+#define VMFS_MAXNAMELEN 255
+#define VMFS_MAXPATHLEN 1024
+
+/*
+ * Contains all relevant data on a VMFS networked file.
+ */
+struct vmfs_fattr {
+       __u16 attr;
+
+       unsigned long f_ino;
+       umode_t f_mode;
+       nlink_t f_nlink;
+       kuid_t f_uid;
+       kgid_t f_gid;
+       dev_t f_rdev;
+       loff_t f_size;
+       struct timespec f_atime;
+       struct timespec f_mtime;
+       struct timespec f_ctime;
+       unsigned long f_blocks;
+       int f_unix;
+
+       /* vfs bits */
+       uint32_t f_type;
+};
+
+#endif
+#endif
diff --git a/fs/vmfs/vmfs_debug.h b/fs/vmfs/vmfs_debug.h
new file mode 100644
index 0000000..8e63f58
--- /dev/null
+++ b/fs/vmfs/vmfs_debug.h
@@ -0,0 +1,39 @@
+/*
+ * Defines some debug macros for vmfs_.
+ */
+
+/* This makes a dentry parent/child name pair. Useful for debugging printk's */
+#define DENTRY_PATH(dentry) \
+       ((dentry)->d_parent->d_name.name, (dentry)->d_name.name)
+
+/*
+ * safety checks that should never happen ???
+ * these are normally enabled.
+ */
+#ifdef VMFSFS_PARANOIA
+#define PARANOIA(f, a...) pr_notice()"%s: " f, __func__ , ## a)
+#else
+#define PARANOIA(f, a...) do { ; } while (0)
+#endif
+
+/* lots of debug messages */
+#ifdef VMFSFS_DEBUG_VERBOSE
+#define VERBOSE(f, a...) pr_debug("%s: " f, __func__ , ## a)
+#else
+#define VERBOSE(f, a...) do { ; } while (0)
+#endif
+
+/*
+ * "normal" debug messages, but not with a normal DEBUG define ... way
+ * too common name.
+ */
+#ifdef VMFSFS_DEBUG
+#define DEBUG1(f, a...) pr_debug("%s: " f, __func__ , ## a)
+#define FNENTER(f, a...) pr_debug("enter %s:\n" f, \
+                        __func__ , ## a)
+#define FNEXIT(f, a...) pr_debug("exit %s:\n" f, __func__ , ## a)
+#else
+#define DEBUG1(f, a...) do { ; } while (0)
+#define FNENTER(f, a...) do { ; } while (0)
+#define FNEXIT(f, a...) do { ; } while (0)
+#endif
diff --git a/fs/vmfs/vmfs_fs.h b/fs/vmfs/vmfs_fs.h
new file mode 100644
index 0000000..2e072a1
--- /dev/null
+++ b/fs/vmfs/vmfs_fs.h
@@ -0,0 +1,111 @@
+/*
+ *  vmfs_fs.h
+ *
+ *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *  Copyright (C) 1997 by Volker Lendecke
+ *
+ *  Copyright (C) 2008-2009 ARM Limited
+ */
+
+#ifndef _LINUX_VMFS_FS_H
+#define _LINUX_VMFS_FS_H
+
+#include "vmfs.h"
+
+/*
+ * ioctl commands
+ */
+#define VMFS_IOC_GETMOUNTUID        _IOR('u', 1, __kernel_old_uid_t)
+
+/* __kernel_uid_t can never change, so we have to use __kernel_uid32_t */
+#define VMFS_IOC_GETMOUNTUID32      _IOR('u', 3, __kernel_uid32_t)
+
+#ifdef __KERNEL__
+#include "vmfs_fs_i.h"
+#include "vmfs_fs_sb.h"
+
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include "vmfs_mount.h"
+#include <linux/jiffies.h>
+#include <asm/unaligned.h>
+
+static inline struct vmfs_sb_info *VMFS_SB(struct super_block *sb)
+{
+       return sb->s_fs_info;
+}
+
+static inline struct vmfs_inode_info *VMFS_I(struct inode *inode)
+{
+       return container_of(inode, struct vmfs_inode_info, vfs_inode);
+}
+
+/*
+ * This is the time we allow an inode, dentry or dir cache to live. It is bad
+ * for performance to have shorter ttl on an inode than on the cache. It can
+ * cause refresh on each inode for a dir listing ... one-by-one
+ */
+#define VMFS_MAX_AGE(server) (((server)->mnt->ttl * HZ) / 1000)
+
+static inline void
+vmfs_age_dentry(struct vmfs_sb_info *server, struct dentry *dentry)
+{
+       dentry->d_time = jiffies - VMFS_MAX_AGE(server);
+}
+
+struct vmfs_cache_head {
+       time_t mtime;           /* unused */
+       unsigned long time;     /* cache age */
+       unsigned long end;      /* last valid fpos in cache */
+       int eof;
+};
+
+#define VMFS_DIRCACHE_SIZE  ((int)(PAGE_CACHE_SIZE/sizeof(struct dentry *)))
+union vmfs_dir_cache {
+       struct vmfs_cache_head head;
+       struct dentry *dentry[VMFS_DIRCACHE_SIZE];
+};
+
+#define VMFS_FIRSTCACHE_SIZE    ((int)((VMFS_DIRCACHE_SIZE * \
+       sizeof(struct dentry *) - sizeof(struct vmfs_cache_head)) / \
+       sizeof(struct dentry *)))
+
+#define VMFS_DIRCACHE_START      (VMFS_DIRCACHE_SIZE - VMFS_FIRSTCACHE_SIZE)
+
+struct vmfs_cache_control {
+       struct vmfs_cache_head head;
+       struct page *page;
+       union vmfs_dir_cache *cache;
+       unsigned long fpos, ofs;
+       int filled, valid, idx;
+};
+
+#define VMFS_OPS_NUM_STATIC 5
+struct vmfs_ops {
+       int (*read)(struct inode *inode, loff_t offset, int count,
+                    char *data);
+       int (*write)(struct inode *inode, loff_t offset, int count, const
+                     char *data);
+       int (*readdir)(struct file *filp, void *dirent, filldir_t filldir,
+                       struct vmfs_cache_control *ctl);
+
+       int (*getattr)(struct vmfs_sb_info *server, struct dentry *dir,
+                       struct vmfs_fattr *fattr);
+       /* int (*setattr)(...); *//* setattr is really icky! */
+
+       int (*truncate)(struct inode *inode, loff_t length);
+};
+
+static inline int vmfs_is_open(struct inode *i)
+{
+       return VMFS_I(i)->vopen == 1;
+}
+
+extern void vmfs_install_ops(struct vmfs_ops *);
+
+extern struct mutex vmfs_mutex;
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_VMFS_FS_H */
diff --git a/fs/vmfs/vmfs_fs_i.h b/fs/vmfs/vmfs_fs_i.h
new file mode 100644
index 0000000..34f0028
--- /dev/null
+++ b/fs/vmfs/vmfs_fs_i.h
@@ -0,0 +1,39 @@
+/*
+ *  vmfs_fs_i.h
+ *
+ *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *  Copyright (C) 1997 by Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_VMFS_FS_I
+#define _LINUX_VMFS_FS_I
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/fs.h>
+
+/*
+ * vmfs fs inode data (in memory only)
+ */
+struct vmfs_inode_info {
+
+       unsigned int open;      /* open generation */
+
+       unsigned long oldmtime; /* last time refreshed */
+       unsigned long closed;   /* timestamp when closed */
+       unsigned openers;       /* number of fileid users */
+
+       /* GPB - vfs data - we also use access */
+
+       uint32_t vhandle;       /* host side handle */
+       uint32_t vaccess;       /* access (VMFS_OPEN_ ) */
+       uint32_t vopen;         /* set to 1 when the file is open
+                                  (why not use openers?) */
+
+       struct inode vfs_inode; /* must be at the end */
+
+};
+
+#endif
+#endif
diff --git a/fs/vmfs/vmfs_fs_sb.h b/fs/vmfs/vmfs_fs_sb.h
new file mode 100644
index 0000000..f98e35d
--- /dev/null
+++ b/fs/vmfs/vmfs_fs_sb.h
@@ -0,0 +1,64 @@
+/*
+ *  vmfs_fs_sb.h
+ *
+ *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *  Copyright (C) 1997 by Volker Lendecke
+ *
+ *  Copyright (C) 2008-2009 ARM Limited
+ *
+ */
+
+#ifndef _VMFS_FS_SB
+#define _VMFS_FS_SB
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include "vmfs.h"
+#include "vfs.h"
+
+/* structure access macros */
+#define server_from_inode(inode) VMFS_SB((inode)->i_sb)
+#define server_from_dentry(dentry) VMFS_SB((dentry)->d_sb)
+#define SB_of(server) ((server)->super_block)
+
+struct vmfs_sb_info {
+       /* List of all vmfsfs superblocks */
+       struct list_head entry;
+
+       /* GPBTODO - most of the 'server' code here should move to the
+        *           messagebox
+        */
+
+       struct vmfs_mount_data_kernel *mnt;
+
+       /* Connections are counted. Each time a new socket arrives,
+        * generation is incremented.
+        */
+       struct semaphore sem;
+
+       struct vmfs_ops *ops;
+
+       struct super_block *super_block;
+
+       VFS *vfs;
+};
+
+static inline int vmfs_lock_server_interruptible(struct vmfs_sb_info *server)
+{
+       return down_interruptible(&(server->sem));
+}
+
+static inline void vmfs_lock_server(struct vmfs_sb_info *server)
+{
+       down(&(server->sem));
+}
+
+static inline void vmfs_unlock_server(struct vmfs_sb_info *server)
+{
+       up(&(server->sem));
+}
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/fs/vmfs/vmfs_mount.h b/fs/vmfs/vmfs_mount.h
new file mode 100644
index 0000000..56123cc
--- /dev/null
+++ b/fs/vmfs/vmfs_mount.h
@@ -0,0 +1,62 @@
+/*
+ *  vmfs_mount.h
+ *
+ *  Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *  Copyright (C) 1997 by Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_VMFS_MOUNT_H
+#define _LINUX_VMFS_MOUNT_H
+
+#include <linux/types.h>
+
+#define VMFS_MOUNT_VERSION  6
+
+struct vmfs_mount_data {
+       int version;
+       __kernel_uid_t mounted_uid;     /* Who may umount() this filesystem? */
+       __kernel_uid_t uid;
+       __kernel_gid_t gid;
+       __kernel_mode_t file_mode;
+       __kernel_mode_t dir_mode;
+};
+
+#ifdef __KERNEL__
+
+/* "vers" in big-endian */
+#define VMFS_MOUNT_ASCII 0x76657273
+
+#define VMFS_MOUNT_OLDVERSION   6
+#undef VMFS_MOUNT_VERSION
+#define VMFS_MOUNT_VERSION  7
+
+/* flags */
+#define VMFS_MOUNT_WIN95        0x0001 /* Win 95 server */
+#define VMFS_MOUNT_OLDATTR  0x0002     /* Use core getattr (Win 95 speedup) */
+#define VMFS_MOUNT_DIRATTR  0x0004     /* Use find_first for getattr */
+#define VMFS_MOUNT_CASE     0x0008     /* Be case sensitive */
+#define VMFS_MOUNT_UNICODE  0x0010     /* Server talks unicode */
+#define VMFS_MOUNT_UID      0x0020     /* Use user specified uid */
+#define VMFS_MOUNT_GID      0x0040     /* Use user specified gid */
+#define VMFS_MOUNT_FMODE        0x0080 /* Use user specified file mode */
+#define VMFS_MOUNT_DMODE        0x0100 /* Use user specified dir mode */
+
+struct vmfs_mount_data_kernel {
+       int version;
+
+       kuid_t mounted_uid;     /* Who may umount() this filesystem? */
+       kuid_t uid;
+       kgid_t gid;
+       mode_t file_mode;
+       mode_t dir_mode;
+
+       u32 flags;
+
+       /* maximum age in jiffies (inode, dentry and dircache) */
+       int ttl;
+};
+
+#endif
+
+#endif
diff --git a/fs/vmfs/vmfsno.h b/fs/vmfs/vmfsno.h
new file mode 100644
index 0000000..b2734cc
--- /dev/null
+++ b/fs/vmfs/vmfsno.h
@@ -0,0 +1,138 @@
+#ifndef _VMFSNO_H_
+#define _VMFSNO_H_
+
+/* these define the attribute byte as seen by DOS */
+#define aRONLY  (1L<<0)
+#define aHIDDEN (1L<<1)
+#define aSYSTEM (1L<<2)
+#define aVOLID  (1L<<3)
+#define aDIR    (1L<<4)
+#define aARCH   (1L<<5)
+
+/* error classes */
+#define SUCCESS 0              /* The request was successful. */
+#define ERRDOS 0x01            /* Error is from the core DOS operating
+                                * system set. */
+#define ERRSRV 0x02            /* Error is generated by the server network
+                                * file manager. */
+#define ERRHRD 0x03            /* Error is an hardware error. */
+#define ERRCMD 0xFF            /* Command was not in the "VMFS" format. */
+
+/* VMFS X/Open error codes for the ERRdos error class */
+
+#define ERRbadfunc 1           /* Invalid function (or system call) */
+#define ERRbadfile 2           /* File not found (pathname error) */
+#define ERRbadpath 3           /* Directory not found */
+#define ERRnofids 4            /* Too many open files */
+#define ERRnoaccess 5          /* Access denied */
+#define ERRbadfid 6            /* Invalid fid */
+#define ERRbadmcb 7            /* Memory control blocks destroyed */
+#define ERRnomem 8             /* Out of memory */
+#define ERRbadmem 9            /* Invalid memory block address */
+#define ERRbadenv 10           /* Invalid environment */
+#define ERRbadformat 11                /* Invalid format */
+#define ERRbadaccess 12                /* Invalid open mode */
+#define ERRbaddata 13          /* Invalid data (only from ioctl call) */
+#define ERRres 14              /* reserved */
+#define ERRbaddrive 15         /* Invalid drive */
+#define ERRremcd 16            /* Attempt to delete current directory */
+#define ERRdiffdevice 17       /* rename/move across different filesystems */
+#define ERRnofiles 18          /* no more files found in file search */
+#define ERRbadshare 32         /* Share mode on file conflicts
+                                * with open mode */
+#define ERRlock 33             /* Lock request conflicts with existing lock */
+#define ERRfilexists 80                /* File in operation already exists */
+#define ERRbadpipe 230         /* Named pipe invalid */
+#define ERRpipebusy 231                /* All instances of pipe are busy */
+#define ERRpipeclosing 232     /* named pipe close in progress */
+#define ERRnotconnected 233    /* No process on other end of named pipe */
+#define ERRmoredata 234                /* More data to be returned */
+
+#define ERROR_INVALID_PARAMETER  87
+#define ERROR_DISK_FULL     112
+#define ERROR_INVALID_NAME  123
+#define ERROR_DIR_NOT_EMPTY 145
+#define ERROR_NOT_LOCKED    158
+#define ERROR_ALREADY_EXISTS    183    /* see also 80 ? */
+#define ERROR_EAS_DIDNT_FIT 275        /* Extended attributes didn't fit */
+#define ERROR_EAS_NOT_SUPPORTED 282    /* Extended attributes not supported */
+
+/* Error codes for the ERRSRV class */
+
+#define ERRerror 1             /* Non specific error code */
+#define ERRbadpw 2             /* Bad password */
+#define ERRbadtype 3           /* reserved */
+#define ERRaccess 4            /* No permissions to do requested operation */
+#define ERRinvnid 5            /* tid invalid */
+#define ERRinvnetname 6                /* Invalid servername */
+#define ERRinvdevice 7         /* Invalid device */
+#define ERRqfull 49            /* Print queue full */
+#define ERRqtoobig 50          /* Queued item too big */
+#define ERRinvpfid 52          /* Invalid print file in vmfs_fid */
+#define ERRvmfscmd 64          /* Unrecognised command */
+#define ERRsrverror 65         /* vmfs server internal error */
+#define ERRfilespecs 67                /* fid and pathname invalid combination 
*/
+#define ERRbadlink 68          /* reserved */
+#define ERRbadpermits 69       /* Access specified for a file is not valid */
+#define ERRbadpid 70           /* reserved */
+#define ERRsetattrmode 71      /* attribute mode invalid */
+#define ERRpaused 81           /* Message server paused */
+#define ERRmsgoff 82           /* Not receiving messages */
+#define ERRnoroom 83           /* No room for message */
+#define ERRrmuns 87            /* too many remote usernames */
+#define ERRtimeout 88          /* operation timed out */
+#define ERRnoresource  89      /* No resources currently avail for request.*/
+#define ERRtoomanyuids 90      /* too many userids */
+#define ERRbaduid 91           /* bad userid */
+#define ERRuseMPX 250          /* temp unable to use raw mode, use MPX mode */
+#define ERRuseSTD 251          /* temp unable to use raw mode, use std.mode */
+#define ERRcontMPX 252         /* resume MPX mode */
+#define ERRbadPW               /* reserved */
+#define ERRnosupport 0xFFFF
+
+/* Error codes for the ERRHRD class */
+
+#define ERRnowrite 19          /* read only media */
+#define ERRbadunit 20          /* Unknown device */
+#define ERRnotready 21         /* Drive not ready */
+#define ERRbadcmd 22           /* Unknown command */
+#define ERRdata 23             /* Data (CRC) error */
+#define ERRbadreq 24           /* Bad request structure length */
+#define ERRseek 25
+#define ERRbadmedia 26
+#define ERRbadsector 27
+#define ERRnopaper 28
+#define ERRwrite 29            /* write fault */
+#define ERRread 30             /* read fault */
+#define ERRgeneral 31          /* General hardware failure */
+#define ERRwrongdisk 34
+#define ERRFCBunavail 35
+#define ERRsharebufexc 36      /* share buffer exceeded */
+#define ERRdiskfull 39
+
+/*
+ * Access modes when opening a file
+ */
+#define VMFS_ACCMASK    0x0003
+#define VMFS_O_RDONLY   0x0000
+#define VMFS_O_WRONLY   0x0001
+#define VMFS_O_RDWR 0x0002
+
+/* values which means "don't change it" */
+#define VMFS_MODE_NO_CHANGE     0xFFFFFFFF
+#define VMFS_UID_NO_CHANGE      0xFFFFFFFF
+#define VMFS_GID_NO_CHANGE      0xFFFFFFFF
+#define VMFS_TIME_NO_CHANGE     0xFFFFFFFFFFFFFFFFULL
+#define VMFS_SIZE_NO_CHANGE     0xFFFFFFFFFFFFFFFFULL
+
+/* UNIX filetype mappings. */
+#define UNIX_TYPE_FILE      0
+#define UNIX_TYPE_DIR       1
+#define UNIX_TYPE_SYMLINK   2
+#define UNIX_TYPE_CHARDEV   3
+#define UNIX_TYPE_BLKDEV    4
+#define UNIX_TYPE_FIFO      5
+#define UNIX_TYPE_SOCKET    6
+#define UNIX_TYPE_UNKNOWN   0xFFFFFFFF
+
+#endif /* _VMFSNO_H_ */
-- 
1.7.9.5

-- 
_______________________________________________
linux-yocto mailing list
linux-yocto@yoctoproject.org
https://lists.yoctoproject.org/listinfo/linux-yocto

Reply via email to