[linux-yocto] [PATCH 05/70] fs/vmfs: adding arm vmfs file system

Paul Butler butler.paul at gmail.com
Mon Jun 10 18:45:28 PDT 2013


Virtual file system originally available from ARM
Deprecated and not supported, but still needed for amx5500 simulator.

Signed-off-by: Paul Butler <paul.butler at windriver.com>
---
 arch/arm/include/asm/lsi/acp_ncr.h |   42 ++
 fs/Kconfig                         |   16 +
 fs/Makefile                        |    5 +-
 fs/vmfs/Makefile                   |   39 ++
 fs/vmfs/cache.c                    |  235 ++++++++
 fs/vmfs/dir.c                      |  626 +++++++++++++++++++++
 fs/vmfs/file.c                     |  500 +++++++++++++++++
 fs/vmfs/getopt.c                   |   67 +++
 fs/vmfs/getopt.h                   |   14 +
 fs/vmfs/inode.c                    |  653 ++++++++++++++++++++++
 fs/vmfs/ioctl.c                    |   49 ++
 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                     |   44 ++
 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, 5779 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/include/asm/lsi/acp_ncr.h
 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/arch/arm/include/asm/lsi/acp_ncr.h b/arch/arm/include/asm/lsi/acp_ncr.h
new file mode 100644
index 0000000..a7399e7
--- /dev/null
+++ b/arch/arm/include/asm/lsi/acp_ncr.h
@@ -0,0 +1,42 @@
+/*
+ * asm/lsi/acp_ncr.h
+ *
+ * Copyright (C) 2010 LSI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307	 USA
+ */
+
+#ifndef __DRIVERS_LSI_ACP_NCR_H
+#define __DRIVERS_LSI_ACP_NCR_H
+
+#ifndef NCP_REGION_ID
+#define NCP_REGION_ID(node, target) \
+(unsigned long)((((node) & 0xffff) << 16) | ((target) & 0xffff))
+#endif
+
+#ifndef NCP_NODE_ID
+#define NCP_NODE_ID(region) (((region) >> 16) & 0xffff)
+#endif
+
+#ifndef NCP_TARGET_ID
+#define NCP_TARGET_ID(region) ((region) & 0xffff)
+#endif
+
+int ncr_read(unsigned long, unsigned long, int, void *);
+int ncr_write(unsigned long, unsigned long, int, void *);
+
+int is_asic(void);
+
+#endif /*  __DRIVERS_LSI_ACP_NCR_H */
diff --git a/fs/Kconfig b/fs/Kconfig
index df7f99e..df7dd95c 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -281,4 +281,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 b182709..0158e43 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -3,7 +3,7 @@
 #
 # 14 Sep 2000, Christoph Hellwig <hch at infradead.org>
 # Rewritten to use lists instead of if-statements.
-# 
+#
 
 obj-y :=	open.o read_write.o file_table.o super.o \
 		char_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
@@ -60,7 +60,7 @@ obj-y				+= devpts/
 
 obj-$(CONFIG_PROFILING)		+= dcookies.o
 obj-$(CONFIG_DLM)		+= dlm/
- 
+
 # Do not add any filesystems before this line
 obj-$(CONFIG_FSCACHE)		+= fscache/
 obj-$(CONFIG_REISERFS_FS)	+= reiserfs/
@@ -129,3 +129,4 @@ obj-$(CONFIG_CEPH_FS)		+= ceph/
 obj-$(CONFIG_PSTORE)		+= pstore/
 obj-$(CONFIG_YAFFS_FS)		+= yaffs2/
 obj-$(CONFIG_PRAMFS)		+= pramfs/
+obj-$(CONFIG_VMFS_FS)           += vmfs/
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..b667698
--- /dev/null
+++ b/fs/vmfs/cache.c
@@ -0,0 +1,235 @@
+/*
+ *  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)
+#else
+		if (dentry->d_op->d_hash(dentry, inode, 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 = find_inode_number(dentry, qname);
+		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..cde4a5e
--- /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"
+
+static int vmfs_readdir(struct file *, void *, filldir_t);
+static int vmfs_dir_open(struct inode *, struct file *);
+
+static struct dentry *vmfs_lookup(struct inode *, struct dentry *,
+				  struct nameidata *);
+static int vmfs_create(struct inode *, struct dentry *, int,
+		       struct nameidata *);
+static int vmfs_mkdir(struct inode *, struct dentry *, int);
+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 *, int, dev_t);
+static int vmfs_link(struct dentry *, struct inode *, struct dentry *);
+
+const struct file_operations vmfs_dir_operations = {
+	.read = generic_read_dir,
+	.readdir = 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
+ */
+static int vmfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	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;
+	int result;
+
+	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);
+	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 *, struct nameidata *);
+#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 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, struct nameidata *nd)
+{
+	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,
+				  struct nameidata *nd)
+{
+	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;
+#if 0
+	if (memchr(dentry->d_name.name, '\\', dentry->d_name.len))
+		goto out;
+#endif
+
+	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, int mode,
+	    struct nameidata *nd)
+{
+	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, int 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, int 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..833e7a7
--- /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 <asm/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, 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, unsigned int flags)
+#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..d5a76c8
--- /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, NULL, 0);
+		*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;
+		}
+	}
+	printk("%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..2d92cf2
--- /dev/null
+++ b/fs/vmfs/inode.c
@@ -0,0 +1,653 @@
+/*
+ *  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 <asm/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 vfsmount *);
+
+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 vfsmount *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)
+		printk(KERN_ERR "vmfs_fill_super: allocation failure\n");
+	sb->s_fs_info = NULL;
+	kfree(server);
+	return -EINVAL;
+out_no_server:
+	printk(KERN_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;
+	if ((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->mnt->uid))
+		goto out;
+
+	if ((attr->ia_valid & ATTR_GID) && (attr->ia_uid != server->mnt->gid))
+		goto out;
+
+	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..b08531e
--- /dev/null
+++ b/fs/vmfs/ioctl.c
@@ -0,0 +1,49 @@
+/*
+ *  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 <asm/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:
+		SET_UID(uid16, server->mnt->mounted_uid);
+		result = put_user(uid16, (uid16_t __user *) arg);
+		break;
+	case VMFS_IOC_GETMOUNTUID32:
+		SET_UID(uid32, server->mnt->mounted_uid);
+		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..be6f77b
--- /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) {
+		printk(KERN_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..b5dcf3d
--- /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 <asm/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..a5891dc
--- /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..82a5c64
--- /dev/null
+++ b/fs/vmfs/vmfs.h
@@ -0,0 +1,44 @@
+/*
+ *  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>
+
+#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;
+	uid_t f_uid;
+	gid_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..b9ba4d3
--- /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...) printk(KERN_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...) printk(KERN_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...) printk(KERN_DEBUG "%s: " f, __func__ , ## a)
+#define FNENTER(f, a...) printk(KERN_DEBUG "enter %s:\n" f, \
+			 __func__ , ## a)
+#define FNEXIT(f, a...) printk(KERN_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..73c88aa
--- /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..2dc784d
--- /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;
+
+	uid_t mounted_uid;	/* Who may umount() this filesystem? */
+	uid_t uid;
+	gid_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.8.3




More information about the linux-yocto mailing list