[linux-yocto] [PATCH 10/87] fs/vmfs: adding arm vmfs patch

Bruce Ashfield bruce.ashfield at windriver.com
Mon May 27 11:04:00 PDT 2013


On 13-05-27 12:55 PM, Paul Butler wrote:

The commit log should tell us something about what a "vmfs" is. I have
no idea without reading the patch, and that's not a good thing.

Bruce

> 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_ */
>




More information about the linux-yocto mailing list