[linux-yocto] [PATCH v2 17/39] fs/vmfs: Changes to add VMFS support for axxia.
Cristian Bercaru
cristian.bercaru at windriver.com
Thu May 21 02:40:42 PDT 2015
From: Charlie Paul <cpaul.windriver at gmail.com>
These files add VMFS support for the LSI
axxia 5500 board.
Signed-off-by: Charlie Paul <cpaul.windriver at gmail.com>
---
fs/Kconfig | 16 +
fs/Makefile | 1 +
fs/vmfs/Kconfig | 13 +
fs/vmfs/Makefile | 39 ++
fs/vmfs/cache.c | 235 +++++++++++
fs/vmfs/dir.c | 615 ++++++++++++++++++++++++++++
fs/vmfs/file.c | 491 +++++++++++++++++++++++
fs/vmfs/getopt.c | 67 ++++
fs/vmfs/getopt.h | 14 +
fs/vmfs/inode.c | 674 +++++++++++++++++++++++++++++++
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 | 1088 ++++++++++++++++++++++++++++++++++++++++++++++++++
fs/vmfs/proto.h | 71 ++++
fs/vmfs/symlink.c | 69 ++++
fs/vmfs/vfs.c | 577 ++++++++++++++++++++++++++
fs/vmfs/vfs.h | 356 +++++++++++++++++
fs/vmfs/vmfs.h | 45 +++
fs/vmfs/vmfs_debug.h | 39 ++
fs/vmfs/vmfs_fs.h | 111 +++++
fs/vmfs/vmfs_fs_i.h | 39 ++
fs/vmfs/vmfs_fs_sb.h | 64 +++
fs/vmfs/vmfs_mount.h | 62 +++
fs/vmfs/vmfsno.h | 138 +++++++
28 files changed, 5753 insertions(+)
create mode 100644 fs/vmfs/Kconfig
create mode 100644 fs/vmfs/Makefile
create mode 100644 fs/vmfs/cache.c
create mode 100644 fs/vmfs/dir.c
create mode 100644 fs/vmfs/file.c
create mode 100644 fs/vmfs/getopt.c
create mode 100644 fs/vmfs/getopt.h
create mode 100644 fs/vmfs/inode.c
create mode 100644 fs/vmfs/ioctl.c
create mode 100644 fs/vmfs/mboxtypes.h
create mode 100644 fs/vmfs/messagebox.c
create mode 100644 fs/vmfs/messagebox.h
create mode 100644 fs/vmfs/msg.c
create mode 100644 fs/vmfs/msg.h
create mode 100644 fs/vmfs/proc.c
create mode 100644 fs/vmfs/proto.h
create mode 100644 fs/vmfs/symlink.c
create mode 100644 fs/vmfs/vfs.c
create mode 100644 fs/vmfs/vfs.h
create mode 100644 fs/vmfs/vmfs.h
create mode 100644 fs/vmfs/vmfs_debug.h
create mode 100644 fs/vmfs/vmfs_fs.h
create mode 100644 fs/vmfs/vmfs_fs_i.h
create mode 100644 fs/vmfs/vmfs_fs_sb.h
create mode 100644 fs/vmfs/vmfs_mount.h
create mode 100644 fs/vmfs/vmfsno.h
diff --git a/fs/Kconfig b/fs/Kconfig
index b2427fd..ec3c620 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -272,4 +272,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 8d0513c..cdbe410 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -128,3 +128,4 @@ obj-$(CONFIG_PSTORE) += pstore/
obj-$(CONFIG_EFIVAR_FS) += efivarfs/
obj-$(CONFIG_YAFFS_FS) += yaffs2/
obj-$(CONFIG_AUFS_FS) += aufs/
+obj-$(CONFIG_VMFS_FS) += vmfs/
diff --git a/fs/vmfs/Kconfig b/fs/vmfs/Kconfig
new file mode 100644
index 0000000..a9d1439
--- /dev/null
+++ b/fs/vmfs/Kconfig
@@ -0,0 +1,13 @@
+config VMFS_FS
+ tristate "VMFS file system support (to mount host directories etc.)"
+ select NLS
+ default y if ARCH_AXXIA
+ 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
+ default 0x8021010000
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..9729404
--- /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_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_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, struct dir_context *dirent,
+ 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, 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 == dirent->pos)) {
+ if (!ino)
+ ino = 0; /* FIXME: find_inode_number(dentry, qname); */
+ if (!ino)
+ ino = iunique(inode->i_sb, 2);
+ ctl.filled = !dir_emit(dirent, qname->name, qname->len,
+ ino, DT_UNKNOWN);
+ if (!ctl.filled)
+ dirent->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..fd66ace
--- /dev/null
+++ b/fs/vmfs/dir.c
@@ -0,0 +1,615 @@
+/*
+ * 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 *, struct dir_context *);
+static int vmfs_dir_open(struct inode *, struct file *);
+
+static struct dentry *vmfs_lookup(struct inode *, struct dentry *,
+ unsigned int flags);
+static int vmfs_create(struct inode *, struct dentry *, umode_t,
+ bool excl);
+static int vmfs_mkdir(struct inode *, struct dentry *, umode_t);
+static int vmfs_rmdir(struct inode *, struct dentry *);
+static int vmfs_unlink(struct inode *, struct dentry *);
+static int vmfs_rename(struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
+static int vmfs_make_node(struct inode *, struct dentry *, umode_t, dev_t);
+static int vmfs_link(struct dentry *, struct inode *, struct dentry *);
+
+const struct file_operations vmfs_dir_operations = {
+ .read = generic_read_dir,
+ .iterate = vmfs_readdir,
+ .unlocked_ioctl = vmfs_unlocked_ioctl,
+ .open = vmfs_dir_open,
+};
+
+const struct inode_operations vmfs_dir_inode_operations = {
+ .create = vmfs_create,
+ .lookup = vmfs_lookup,
+ .unlink = vmfs_unlink,
+ .mkdir = vmfs_mkdir,
+ .rmdir = vmfs_rmdir,
+ .rename = vmfs_rename,
+ .getattr = vmfs_getattr,
+ .setattr = vmfs_notify_change,
+};
+
+const struct inode_operations vmfs_dir_inode_operations_unix = {
+ .create = vmfs_create,
+ .lookup = vmfs_lookup,
+ .unlink = vmfs_unlink,
+ .mkdir = vmfs_mkdir,
+ .rmdir = vmfs_rmdir,
+ .rename = vmfs_rename,
+ .getattr = vmfs_getattr,
+ .setattr = vmfs_notify_change,
+ .symlink = vmfs_symlink,
+ .mknod = vmfs_make_node,
+ .link = vmfs_link,
+};
+
+/*
+ * Read a directory, using filldir to fill the dirent memory.
+ * vmfs_proc_readdir does the actual reading from the vmfs server.
+ *
+ * The cache code is almost directly taken from ncpfs
+ */
+static int vmfs_readdir(struct file *filp, struct dir_context *ctx)
+{
+ 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);
+
+ if (!dir_emit_dots(filp, ctx))
+ return 0;
+
+ /*
+ * 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 (ctx->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 (ctx->pos > ctl.head.end)
+ goto finished;
+
+ ctl.fpos = ctx->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 = !dir_emit(ctx, dent->d_name.name,
+ dent->d_name.len,
+ dent->d_inode->i_ino, DT_UNKNOWN);
+ dput(dent);
+ if (res)
+ goto finished;
+ ctx->pos += 1;
+ ctl.idx += 1;
+ if (ctx->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, ctx, &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 *, unsigned int flags);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+static int vmfs_delete_dentry(struct dentry *);
+#else
+static int vmfs_delete_dentry(const struct dentry *);
+#endif
+
+
+static const struct dentry_operations vmfs__dentry_operations_case = {
+ .d_revalidate = vmfs_lookup_validate,
+ .d_delete = vmfs_delete_dentry,
+};
+
+/*
+ * This is the callback when the dcache has a lookup hit.
+ */
+static int vmfs_lookup_validate(struct dentry *dentry, unsigned int flags)
+{
+ struct vmfs_sb_info *server = server_from_dentry(dentry);
+ struct inode *inode = dentry->d_inode;
+ unsigned long age = jiffies - dentry->d_time;
+ int valid;
+
+ /*
+ * The default validation is based on dentry age:
+ * we believe in dentries for a few seconds. (But each
+ * successful server lookup renews the timestamp.)
+ */
+ valid = (age <= VMFS_MAX_AGE(server));
+#ifdef VMFSFS_DEBUG_VERBOSE
+ if (!valid)
+ VERBOSE("%s/%s not valid, age=%lu\n", DENTRY_PATH(dentry), age);
+#endif
+
+ if (inode) {
+ mutex_lock(&vmfs_mutex);
+ if (is_bad_inode(inode)) {
+ PARANOIA("%s/%s has dud inode\n", DENTRY_PATH(dentry));
+ valid = 0;
+ } else if (!valid)
+ valid = (vmfs_revalidate_inode(dentry) == 0);
+ mutex_unlock(&vmfs_mutex);
+ } else {
+ /*
+ * What should we do for negative dentries?
+ */
+ }
+ return valid;
+}
+
+
+/*
+ * This is the callback from dput() when d_count is going to 0.
+ * We use this to unhash dentries with bad inodes.
+ */
+static int
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+vmfs_delete_dentry(struct dentry *dentry)
+#else
+vmfs_delete_dentry(const struct dentry *dentry)
+#endif
+{
+ if (dentry->d_inode) {
+ if (is_bad_inode(dentry->d_inode)) {
+ PARANOIA("bad inode, unhashing %s/%s\n",
+ DENTRY_PATH(dentry));
+ return 1;
+ }
+ } else {
+ /* N.B. Unhash negative dentries? */
+ }
+ return 0;
+}
+
+/*
+ * Initialize a new dentry
+ */
+void vmfs_new_dentry(struct dentry *dentry)
+{
+ dentry->d_op = &vmfs__dentry_operations_case;
+}
+
+/*
+ * Whenever a lookup succeeds, we know the parent directories
+ * are all valid, so we want to update the dentry timestamps.
+ * N.B. Move this to dcache?
+ */
+void vmfs_renew_times(struct dentry *dentry)
+{
+ dget(dentry);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
+ spin_lock(&dentry->d_lock);
+ for (;;) {
+ struct dentry *parent;
+
+ dentry->d_time = jiffies;
+ if (IS_ROOT(dentry))
+ break;
+ parent = dentry->d_parent;
+ dget(parent);
+ spin_unlock(&dentry->d_lock);
+ dput(dentry);
+ dentry = parent;
+ spin_lock(&dentry->d_lock);
+ }
+ spin_unlock(&dentry->d_lock);
+#else
+ dentry->d_time = jiffies;
+
+ while (!IS_ROOT(dentry)) {
+ struct dentry *parent = dget_parent(dentry);
+
+ dput(dentry);
+ dentry = parent;
+
+ dentry->d_time = jiffies;
+ }
+#endif
+ dput(dentry);
+}
+
+static struct dentry *vmfs_lookup(struct inode *dir, struct dentry *dentry,
+ unsigned int flags)
+{
+ struct vmfs_fattr finfo;
+ struct inode *inode;
+ int error;
+ struct vmfs_sb_info *server;
+
+ VERBOSE("%s\n", dentry->d_name.name);
+
+ error = -ENAMETOOLONG;
+ if (dentry->d_name.len > VMFS_MAXNAMELEN)
+ goto out;
+
+ /* Do not allow lookup of names with backslashes in */
+ error = -EINVAL;
+
+ mutex_lock(&vmfs_mutex);
+ error = vmfs_proc_getattr(dentry, &finfo);
+#ifdef VMFSFS_PARANOIA
+ if (error && error != -ENOENT)
+ PARANOIA("find %s/%s failed, error=%d\n",
+ DENTRY_PATH(dentry), error);
+#endif
+
+ inode = NULL;
+ if (error == -ENOENT)
+ goto add_entry;
+ if (!error) {
+ error = -EACCES;
+ finfo.f_ino = iunique(dentry->d_sb, 2);
+ inode = vmfs_iget(dir->i_sb, &finfo);
+ if (inode) {
+add_entry:
+ server = server_from_dentry(dentry);
+ dentry->d_op = &vmfs__dentry_operations_case;
+ d_add(dentry, inode);
+ vmfs_renew_times(dentry);
+ error = 0;
+ }
+ }
+ mutex_unlock(&vmfs_mutex);
+out:
+ return ERR_PTR(error);
+}
+
+/*
+ * This code is common to all routines creating a new inode.
+ */
+static int vmfs_instantiate(struct dentry *dentry, int32_t vhandle,
+ int have_id)
+{
+ struct inode *inode;
+ int error;
+ struct vmfs_fattr fattr;
+
+ VERBOSE("file %s/%s, fileid=%u\n", DENTRY_PATH(dentry), vhandle);
+
+ error = vmfs_proc_getattr(dentry, &fattr);
+ if (error)
+ goto out_close;
+
+ vmfs_renew_times(dentry);
+ fattr.f_ino = iunique(dentry->d_sb, 2);
+ inode = vmfs_iget(dentry->d_sb, &fattr);
+ if (!inode)
+ goto out_no_inode;
+
+ if (have_id) {
+ /* this is really only for create, where there is a
+ * catch-22 between creating the file and inode */
+ struct vmfs_inode_info *ei = VMFS_I(inode);
+
+ ei->vhandle = vhandle;
+ ei->vopen = 1;
+ ei->vaccess = VFS_OPEN_RDWR;
+ }
+ d_instantiate(dentry, inode);
+out:
+ return error;
+
+out_no_inode:
+ error = -EACCES;
+out_close:
+ if (have_id) {
+ PARANOIA("%s/%s failed, error=%d, closing %u\n",
+ DENTRY_PATH(dentry), error, vhandle);
+ vmfs_close_fileid(dentry, vhandle);
+ }
+ goto out;
+}
+
+/* N.B. How should the mode argument be used? */
+static int
+vmfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+ bool excl)
+{
+ int32_t fileid;
+ int error;
+
+ VERBOSE("creating %s/%s, mode=%d\n", DENTRY_PATH(dentry), mode);
+
+ mutex_lock(&vmfs_mutex);
+
+ vmfs_invalid_dir_cache(dir);
+ error = vmfs_proc_create(dentry, mode, &fileid);
+ if (!error) {
+ error = vmfs_instantiate(dentry, fileid, 1);
+ } else {
+ PARANOIA("%s/%s failed, error=%d\n",
+ DENTRY_PATH(dentry), error);
+ }
+ mutex_unlock(&vmfs_mutex);
+ return error;
+}
+
+/* N.B. How should the mode argument be used? */
+static int vmfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+ int error;
+
+ VERBOSE("\n");
+
+ mutex_lock(&vmfs_mutex);
+ vmfs_invalid_dir_cache(dir);
+ error = vmfs_proc_mkdir(dentry);
+ if (!error)
+ error = vmfs_instantiate(dentry, 0, 0);
+ mutex_unlock(&vmfs_mutex);
+ return error;
+}
+
+static int vmfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+
+ VERBOSE("\n");
+ /*
+ * Close the directory if it's open.
+ */
+ mutex_lock(&vmfs_mutex);
+ vmfs_close(inode);
+
+ /*
+ * Check that nobody else is using the directory..
+ */
+ error = -EBUSY;
+ if (!d_unhashed(dentry))
+ goto out;
+
+ vmfs_invalid_dir_cache(dir);
+ error = vmfs_proc_rmdir(dentry);
+
+out:
+ mutex_unlock(&vmfs_mutex);
+ return error;
+}
+
+static int vmfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int error;
+
+ /*
+ * Close the file if it's open.
+ */
+ mutex_lock(&vmfs_mutex);
+ vmfs_close(dentry->d_inode);
+
+ vmfs_invalid_dir_cache(dir);
+ error = vmfs_proc_unlink(dentry);
+ if (!error)
+ vmfs_renew_times(dentry);
+ mutex_unlock(&vmfs_mutex);
+ return error;
+}
+
+static int
+vmfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int error;
+
+ VERBOSE("\n");
+
+ /*
+ * Close any open files, and check whether to delete the
+ * target before attempting the rename.
+ */
+ mutex_lock(&vmfs_mutex);
+ if (old_dentry->d_inode)
+ vmfs_close(old_dentry->d_inode);
+ if (new_dentry->d_inode) {
+ vmfs_close(new_dentry->d_inode);
+ error = vmfs_proc_unlink(new_dentry);
+ if (error) {
+ VERBOSE("unlink %s/%s, error=%d\n",
+ DENTRY_PATH(new_dentry), error);
+ goto out;
+ }
+ /* FIXME */
+ d_delete(new_dentry);
+ }
+
+ vmfs_invalid_dir_cache(old_dir);
+ vmfs_invalid_dir_cache(new_dir);
+ error = vmfs_proc_mv(old_dentry, new_dentry);
+ if (!error) {
+ vmfs_renew_times(old_dentry);
+ vmfs_renew_times(new_dentry);
+ }
+out:
+ mutex_unlock(&vmfs_mutex);
+ return error;
+}
+
+/*
+ * FIXME: samba servers won't let you create device nodes unless uid/gid
+ * matches the connection credentials (and we don't know which those are ...)
+ */
+static int
+vmfs_make_node(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
+{
+ return -EINVAL;
+}
+
+/*
+ * dentry = existing file
+ * new_dentry = new file
+ */
+static int
+vmfs_link(struct dentry *dentry, struct inode *dir, struct dentry *new_dentry)
+{
+ int error;
+
+ DEBUG1("vmfs_link old=%s/%s new=%s/%s\n",
+ DENTRY_PATH(dentry), DENTRY_PATH(new_dentry));
+ vmfs_invalid_dir_cache(dir);
+ error = vmfs_proc_link(server_from_dentry(dentry), dentry, new_dentry);
+ if (!error) {
+ vmfs_renew_times(dentry);
+ error = vmfs_instantiate(new_dentry, 0, 0);
+ }
+ return error;
+}
diff --git a/fs/vmfs/file.c b/fs/vmfs/file.c
new file mode 100644
index 0000000..098c863
--- /dev/null
+++ b/fs/vmfs/file.c
@@ -0,0 +1,491 @@
+/*
+ * file.c
+ *
+ * Copyright (C) 1995, 1996, 1997 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ * Please add a note about your changes to vmfs_ in the ChangeLog file.
+ */
+
+#include <linux/time.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/net.h>
+#include <linux/aio.h>
+
+#include <linux/uaccess.h>
+/*#include <asm/system.h>*/
+#include <linux/version.h>
+
+#include "vmfsno.h"
+#include "vmfs_fs.h"
+
+#include "vmfs_debug.h"
+#include "proto.h"
+
+static int
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+vmfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+#else
+vmfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+ struct dentry *dentry = file->f_path.dentry;
+#endif
+ struct vmfs_sb_info *server = server_from_dentry(dentry);
+ int result;
+
+ VERBOSE("sync file %s/%s\n", DENTRY_PATH(dentry));
+
+ /*
+ * The VFS will writepage() all dirty pages for us, but we
+ * should send a VMFSflush to the server, letting it know that
+ * we want things synchronized with actual storage.
+ *
+ * Note: this function requires all pages to have been written already
+ * (should be ok with writepage_sync)
+ */
+ mutex_lock(&vmfs_mutex);
+ result = vmfs_proc_flush(server, VMFS_I(dentry->d_inode)->vhandle);
+ mutex_unlock(&vmfs_mutex);
+
+ return result;
+}
+
+/*
+ * Read a page synchronously.
+ */
+static int vmfs_readpage_sync(struct dentry *dentry, struct page *page)
+{
+ char *buffer = kmap(page);
+ loff_t offset = (loff_t) page->index << PAGE_CACHE_SHIFT;
+ struct vmfs_sb_info *server = server_from_dentry(dentry);
+ int count = PAGE_SIZE;
+ unsigned int rsize = count;
+ int result;
+
+ VERBOSE("file %s/%s, count=%d@%lld, rsize=%d\n",
+ DENTRY_PATH(dentry), count, offset, rsize);
+
+ result = vmfs_open(dentry, 0, VMFS_O_RDONLY);
+ if (result < 0)
+ goto io_error;
+
+ do {
+ if (count < rsize)
+ rsize = count;
+
+ result =
+ server->ops->read(dentry->d_inode, offset, rsize, buffer);
+ if (result < 0)
+ goto io_error;
+
+ count -= result;
+ offset += result;
+ buffer += result;
+ dentry->d_inode->i_atime =
+ current_fs_time(dentry->d_inode->i_sb);
+ if (result < rsize)
+ break;
+ } while (count);
+
+ memset(buffer, 0, count);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ result = 0;
+
+io_error:
+ kunmap(page);
+ unlock_page(page);
+ return result;
+}
+
+/*
+ * We are called with the page locked and we unlock it when done.
+ */
+static int vmfs_readpage(struct file *file, struct page *page)
+{
+ int error;
+ struct dentry *dentry = file->f_path.dentry;
+
+ page_cache_get(page);
+ error = vmfs_readpage_sync(dentry, page);
+ page_cache_release(page);
+ return error;
+}
+
+/*
+ * Write a page synchronously.
+ * Offset is the data offset within the page.
+ */
+static int
+vmfs_writepage_sync(struct inode *inode, struct page *page,
+ unsigned long pageoffset, unsigned int count)
+{
+ loff_t offset;
+ char *buffer = kmap(page) + pageoffset;
+ struct vmfs_sb_info *server = server_from_inode(inode);
+ unsigned int wsize = count;
+ int ret = 0;
+
+ offset = ((loff_t) page->index << PAGE_CACHE_SHIFT) + pageoffset;
+ VERBOSE("file ino=%ld, handle=%d, count=%d@%lld, wsize=%d\n",
+ inode->i_ino, VMFS_I(inode)->vhandle, count, offset, wsize);
+
+ do {
+ int write_ret;
+
+ if (count < wsize)
+ wsize = count;
+
+ write_ret = server->ops->write(inode, offset, wsize, buffer);
+ if (write_ret < 0) {
+ PARANOIA("failed write, wsize=%d, write_ret=%d\n",
+ wsize, write_ret);
+ ret = write_ret;
+ break;
+ }
+
+ /* N.B. what if result < wsize?? */
+#ifdef VMFSFS_PARANOIA
+ if (write_ret < wsize)
+ PARANOIA("short write, wsize=%d, write_ret=%d\n",
+ wsize, write_ret);
+#endif
+ buffer += wsize;
+ offset += wsize;
+ count -= wsize;
+ /*
+ * Update the inode now rather than waiting for a refresh.
+ */
+
+ inode->i_mtime = inode->i_atime = current_fs_time(inode->i_sb);
+ if (offset > inode->i_size)
+ inode->i_size = offset;
+ } while (count);
+
+ kunmap(page);
+ return ret;
+}
+
+/*
+ * Write a page to the server. This will be used for NFS swapping only
+ * (for now), and we currently do this synchronously only.
+ *
+ * We are called with the page locked and we unlock it when done.
+ */
+static int vmfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+ struct address_space *mapping = page->mapping;
+ struct inode *inode;
+ unsigned long end_index;
+ unsigned offset = PAGE_CACHE_SIZE;
+ int err;
+
+ DEBUG1("\n");
+
+ BUG_ON(!mapping);
+ inode = mapping->host;
+ BUG_ON(!inode);
+
+ end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+
+ /* easy case */
+ if (page->index < end_index)
+ goto do_it;
+ /* things got complicated... */
+ offset = inode->i_size & (PAGE_CACHE_SIZE - 1);
+ /* OK, are we completely out? */
+ if (page->index >= end_index + 1 || !offset)
+ return 0; /* truncated - don't care */
+do_it:
+ page_cache_get(page);
+ err = vmfs_writepage_sync(inode, page, 0, offset);
+ SetPageUptodate(page);
+ unlock_page(page);
+ page_cache_release(page);
+ return err;
+}
+
+static int
+vmfs_updatepage(struct file *file, struct page *page, unsigned long offset,
+ unsigned int count)
+{
+ struct dentry *dentry = file->f_path.dentry;
+
+ DEBUG1("(%s/%s %d@%lld)\n", DENTRY_PATH(dentry), count,
+ ((unsigned long long)page->index << PAGE_CACHE_SHIFT) + offset);
+
+ return vmfs_writepage_sync(dentry->d_inode, page, offset, count);
+}
+
+static ssize_t
+vmfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iov)
+{
+ struct file *file = iocb->ki_filp;
+ struct dentry *dentry = file->f_path.dentry;
+ ssize_t status;
+
+ VERBOSE("file %s/%s, count=%llu@%zu\n", DENTRY_PATH(dentry),
+ iocb->ki_pos, iocb->ki_nbytes);
+
+ 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_read_iter(iocb, iov);
+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=%zu\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_write_iter(struct kiocb *iocb, struct iov_iter *iov)
+{
+ struct file *file = iocb->ki_filp;
+ struct dentry *dentry = file->f_path.dentry;
+ ssize_t result;
+
+ VERBOSE("file %s/%s, count=%llu@%zu\n", DENTRY_PATH(dentry),
+ iocb->ki_pos, iocb->ki_nbytes);
+
+ 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);
+
+ if (result)
+ goto out;
+
+ result = generic_file_write_iter(iocb, iov);
+ 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);
+
+out:
+ DEBUG1("return\n");
+ return result;
+}
+
+static int vmfs_file_open(struct inode *inode, struct file *file)
+{
+ int result;
+ struct dentry *dentry = file->f_path.dentry;
+ int vmfs_mode = (file->f_mode & O_ACCMODE) - 1;
+ int vmfs_flags = file->f_flags;
+
+ VERBOSE("\n");
+
+ mutex_lock(&vmfs_mutex);
+ result = vmfs_open(dentry, vmfs_flags, vmfs_mode);
+ if (result)
+ goto out;
+
+ DEBUG1("inode=%p, ei=%p\n", inode, VMFS_I(inode));
+
+ VMFS_I(inode)->openers++;
+out:
+ mutex_unlock(&vmfs_mutex);
+ DEBUG1("return\n");
+
+ return result;
+}
+
+static int vmfs_file_release(struct inode *inode, struct file *file)
+{
+ mutex_lock(&vmfs_mutex);
+ if (!--VMFS_I(inode)->openers) {
+ /* We must flush any dirty pages now as we won't be able to
+ write anything after close. mmap can trigger this.
+ "openers" should perhaps include mmap'ers ... */
+ filemap_write_and_wait(inode->i_mapping);
+ vmfs_close(inode);
+ }
+ mutex_unlock(&vmfs_mutex);
+ return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
+static loff_t vmfs_remote_llseek(struct file *file, loff_t offset, int origin)
+{
+ loff_t ret;
+
+ mutex_lock(&vmfs_mutex);
+ ret = generic_file_llseek(file, offset, origin);
+ mutex_unlock(&vmfs_mutex);
+ return ret;
+}
+#endif
+/*
+ * Check whether the required access is compatible with
+ * an inode's permission. VMFS doesn't recognize superuser
+ * privileges, so we need our own check for this.
+ */
+static int
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
+vmfs_file_permission(struct inode *inode, int mask, struct nameidata *nd)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+vmfs_file_permission(struct inode *inode, int mask)
+#else
+vmfs_file_permission(struct inode *inode, int mask)
+#endif
+{
+ int mode = inode->i_mode;
+ int error = 0;
+
+ VERBOSE("mode=%x, mask=%x\n", mode, mask);
+
+ /* Look at user permissions */
+ mode >>= 6;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
+ if ((mask & ~mode) & (MAY_READ | MAY_WRITE | MAY_EXEC))
+#else
+ if ((mode & 7 & mask) != mask)
+#endif
+ error = -EACCES;
+ return error;
+}
+
+const struct file_operations vmfs_file_operations = {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
+ .llseek = vmfs_remote_llseek,
+#else
+ .llseek = remote_llseek,
+#endif
+ .read = new_sync_read,
+ .write = new_sync_write,
+ .read_iter = vmfs_file_read_iter,
+ .write_iter = vmfs_file_write_iter,
+ .unlocked_ioctl = vmfs_unlocked_ioctl,
+ .mmap = vmfs_file_mmap,
+ .open = vmfs_file_open,
+ .release = vmfs_file_release,
+ .fsync = vmfs_fsync,
+ .splice_read = vmfs_file_splice_read,
+};
+
+const struct inode_operations vmfs_file_inode_operations = {
+ .permission = vmfs_file_permission,
+ .getattr = vmfs_getattr,
+ .setattr = vmfs_notify_change,
+};
diff --git a/fs/vmfs/getopt.c b/fs/vmfs/getopt.c
new file mode 100644
index 0000000..2ce5adc
--- /dev/null
+++ b/fs/vmfs/getopt.c
@@ -0,0 +1,67 @@
+/*
+ * getopt.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/net.h>
+
+#include "getopt.h"
+
+/**
+ * vmfs_getopt - option parser
+ * @caller: name of the caller, for error messages
+ * @options: the options string
+ * @opts: an array of &struct option entries controlling parser operations
+ * @optopt: output; will contain the current option
+ * @optarg: output; will contain the value (if one exists)
+ * @flag: output; may be NULL; should point to a long for or'ing flags
+ * @value: output; may be NULL; will be overwritten with the integer value
+ * of the current argument.
+ *
+ * Helper to parse options on the format used by mount ("a=b,c=d,e,f").
+ * Returns opts->val if a matching entry in the 'opts' array is found,
+ * 0 when no more tokens are found, -1 if an error is encountered.
+ */
+int vmfs_getopt(char *caller, char **options, struct option *opts,
+ char **optopt, char **optarg, unsigned long *flag,
+ unsigned long *value)
+{
+ char *token;
+ char *val;
+ int i;
+
+ do {
+ token = strsep(options, ",");
+ if (token == NULL)
+ return 0;
+ } while (*token == '\0');
+ *optopt = token;
+
+ *optarg = NULL;
+ val = strchr(token, '=');
+ if (val != NULL) {
+ *val++ = 0;
+ if (value)
+ *value = kstrtoul(val, 0, NULL);
+ *optarg = val;
+ }
+
+ for (i = 0; opts[i].name != NULL; i++) {
+ if (!strcmp(opts[i].name, token)) {
+ if (!opts[i].flag && (!val || !*val)) {
+ printk
+ ("%s: the %s option requires an argument\n",
+ caller, token);
+ return -1;
+ }
+
+ if (flag && opts[i].flag)
+ *flag |= opts[i].flag;
+
+ return opts[i].val;
+ }
+ }
+ pr_info("%s: Unrecognized mount option %s\n", caller, token);
+ return -1;
+}
diff --git a/fs/vmfs/getopt.h b/fs/vmfs/getopt.h
new file mode 100644
index 0000000..98dddfc
--- /dev/null
+++ b/fs/vmfs/getopt.h
@@ -0,0 +1,14 @@
+#ifndef _LINUX_GETOPT_H
+#define _LINUX_GETOPT_H
+
+struct option {
+ const char *name;
+ unsigned long flag;
+ int val;
+};
+
+extern int vmfs_getopt(char *caller, char **options, struct option *opts,
+ char **optopt, char **optarg, unsigned long *flag,
+ unsigned long *value);
+
+#endif /* _LINUX_GETOPT_H */
diff --git a/fs/vmfs/inode.c b/fs/vmfs/inode.c
new file mode 100644
index 0000000..fec0a31
--- /dev/null
+++ b/fs/vmfs/inode.c
@@ -0,0 +1,674 @@
+/*
+ * 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 <linux/of_address.h>
+#include <linux/of_irq.h>
+#include "vmfs_fs.h"
+#include "vmfsno.h"
+#include "vmfs_mount.h"
+
+#include <linux/uaccess.h>
+#include <linux/version.h>
+
+#include "vmfs_debug.h"
+#include "getopt.h"
+#include "proto.h"
+
+#include "messagebox.h"
+#include "vmfs.h"
+
+/* gpb: no reason this has to be in linux/magic.h */
+
+#define VMFS_SUPER_MAGIC 0x564D4653 /* VMFS */
+
+/* gpb: this needs to be made configurable */
+
+#define VMFS_TTL_DEFAULT 1000
+
+DEFINE_MUTEX(vmfs_mutex);
+
+static void vmfs_delete_inode(struct inode *);
+static void vmfs_put_super(struct super_block *);
+static int vmfs_statfs(struct dentry *, struct kstatfs *);
+static int vmfs_show_options(struct seq_file *, struct dentry *);
+
+static struct kmem_cache *vmfs_inode_cachep;
+
+static MessageBox *mbox;
+static VFS *vfs;
+
+static struct inode *vmfs_alloc_inode(struct super_block *sb)
+{
+ struct vmfs_inode_info *ei;
+
+ ei = (struct vmfs_inode_info *)kmem_cache_alloc(vmfs_inode_cachep,
+ GFP_KERNEL);
+ if (!ei)
+ return NULL;
+ return &ei->vfs_inode;
+}
+
+static void vmfs_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(vmfs_inode_cachep, VMFS_I(inode));
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
+static void init_once(void *foo)
+#else
+static void init_once(struct kmem_cache *cachep, void *foo)
+#endif
+{
+ struct vmfs_inode_info *ei = (struct vmfs_inode_info *)foo;
+
+ inode_init_once(&ei->vfs_inode);
+}
+
+static int init_inodecache(void)
+{
+ vmfs_inode_cachep = kmem_cache_create("vmfs_inode_cache",
+ sizeof(struct vmfs_inode_info),
+ 0, (SLAB_RECLAIM_ACCOUNT |
+ SLAB_MEM_SPREAD), init_once);
+ if (vmfs_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void destroy_inodecache(void)
+{
+ kmem_cache_destroy(vmfs_inode_cachep);
+}
+
+static int vmfs_remount(struct super_block *sb, int *flags, char *data)
+{
+ *flags |= MS_NODIRATIME;
+ return 0;
+}
+
+static const struct super_operations vmfs_sops = {
+ .alloc_inode = vmfs_alloc_inode,
+ .destroy_inode = vmfs_destroy_inode,
+ .drop_inode = generic_delete_inode,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+ .delete_inode = vmfs_delete_inode,
+#else
+ .evict_inode = vmfs_delete_inode,
+#endif
+ .put_super = vmfs_put_super,
+ .statfs = vmfs_statfs,
+ .show_options = vmfs_show_options,
+ .remount_fs = vmfs_remount,
+};
+
+/* We are always generating a new inode here */
+struct inode *vmfs_iget(struct super_block *sb, struct vmfs_fattr *fattr)
+{
+ struct inode *result;
+
+ DEBUG1("vmfs_iget: %p\n", fattr);
+
+ result = new_inode(sb);
+ if (!result)
+ return result;
+ result->i_ino = fattr->f_ino;
+ VMFS_I(result)->open = 0;
+ VMFS_I(result)->closed = 0;
+ VMFS_I(result)->openers = 0;
+ VMFS_I(result)->vhandle = -1;
+ VMFS_I(result)->vaccess = 0;
+ VMFS_I(result)->vopen = 0;
+
+ vmfs_set_inode_attr(result, fattr);
+ if (S_ISREG(result->i_mode)) {
+ result->i_op = &vmfs_file_inode_operations;
+ result->i_fop = &vmfs_file_operations;
+ result->i_data.a_ops = &vmfs_file_aops;
+ } else if (S_ISDIR(result->i_mode)) {
+ result->i_op = &vmfs_dir_inode_operations_unix;
+ result->i_fop = &vmfs_dir_operations;
+ } else if (S_ISLNK(result->i_mode)) {
+ result->i_op = &vmfs_link_inode_operations;
+ } else {
+ init_special_inode(result, result->i_mode, fattr->f_rdev);
+ }
+ insert_inode_hash(result);
+ return result;
+}
+
+/*
+ * Copy the inode data to a vmfs_fattr structure.
+ */
+void vmfs_get_inode_attr(struct inode *inode, struct vmfs_fattr *fattr)
+{
+ memset(fattr, 0, sizeof(struct vmfs_fattr));
+ fattr->f_mode = inode->i_mode;
+ fattr->f_nlink = inode->i_nlink;
+ fattr->f_ino = inode->i_ino;
+ fattr->f_uid = inode->i_uid;
+ fattr->f_gid = inode->i_gid;
+ fattr->f_size = inode->i_size;
+ fattr->f_mtime = inode->i_mtime;
+ fattr->f_ctime = inode->i_ctime;
+ fattr->f_atime = inode->i_atime;
+ fattr->f_blocks = inode->i_blocks;
+
+}
+
+/*
+ * Update the inode, possibly causing it to invalidate its pages if mtime/size
+ * is different from last time.
+ */
+void vmfs_set_inode_attr(struct inode *inode, struct vmfs_fattr *fattr)
+{
+ struct vmfs_inode_info *ei = VMFS_I(inode);
+
+ /*
+ * A size change should have a different mtime, or same mtime
+ * but different size.
+ */
+ time_t last_time = inode->i_mtime.tv_sec;
+ loff_t last_sz = inode->i_size;
+
+ inode->i_mode = fattr->f_mode;
+ set_nlink(inode, fattr->f_nlink);
+ inode->i_uid = fattr->f_uid;
+ inode->i_gid = fattr->f_gid;
+ inode->i_ctime = fattr->f_ctime;
+ inode->i_blocks = fattr->f_blocks;
+ inode->i_size = fattr->f_size;
+ inode->i_mtime = fattr->f_mtime;
+ inode->i_atime = fattr->f_atime;
+
+
+ /*
+ * Update the "last time refreshed" field for revalidation.
+ */
+ ei->oldmtime = jiffies;
+
+ if (inode->i_mtime.tv_sec != last_time || inode->i_size != last_sz) {
+ VERBOSE("%ld changed, old=%ld, new=%ld, oz=%ld, nz=%ld\n",
+ inode->i_ino,
+ (long)last_time, (long)inode->i_mtime.tv_sec,
+ (long)last_sz, (long)inode->i_size);
+
+ if (!S_ISDIR(inode->i_mode))
+ invalidate_remote_inode(inode);
+ }
+}
+
+/*
+ * This is called if the connection has gone bad ...
+ * try to kill off all the current inodes.
+ */
+void vmfs_invalidate_inodes(struct vmfs_sb_info *server)
+{
+ VERBOSE("\n");
+ shrink_dcache_sb(SB_of(server));
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
+ invalidate_inodes(SB_of(server));
+#endif
+}
+
+/*
+ * This is called to update the inode attributes after
+ * we've made changes to a file or directory.
+ */
+static int vmfs_refresh_inode(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+ struct vmfs_fattr fattr;
+
+ DEBUG1("");
+
+ error = vmfs_proc_getattr(dentry, &fattr);
+ if (!error) {
+ vmfs_renew_times(dentry);
+ /*
+ * Check whether the type part of the mode changed,
+ * and don't update the attributes if it did.
+ *
+ * And don't dick with the root inode
+ */
+ if (inode->i_ino == 2)
+ return error;
+ if (S_ISLNK(inode->i_mode))
+ return error; /* VFS will deal with it */
+
+ if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) {
+ vmfs_set_inode_attr(inode, &fattr);
+ } else {
+ /*
+ * Big trouble! The inode has become a new object,
+ * so any operations attempted on it are invalid.
+ *
+ * To limit damage, mark the inode as bad so that
+ * subsequent lookup validations will fail.
+ */
+ PARANOIA("%s/%s changed mode, %07o to %07o\n",
+ DENTRY_PATH(dentry),
+ inode->i_mode, fattr.f_mode);
+
+ fattr.f_mode = inode->i_mode; /* save mode */
+ make_bad_inode(inode);
+ inode->i_mode = fattr.f_mode; /* restore mode */
+ /*
+ * No need to worry about unhashing the dentry: the
+ * lookup validation will see that the inode is bad.
+ * But we do want to invalidate the caches ...
+ */
+ if (!S_ISDIR(inode->i_mode))
+ invalidate_remote_inode(inode);
+ else
+ vmfs_invalid_dir_cache(inode);
+ error = -EIO;
+ }
+ }
+ return error;
+}
+
+/*
+ * This is called when we want to check whether the inode
+ * has changed on the server. If it has changed, we must
+ * invalidate our local caches.
+ */
+int vmfs_revalidate_inode(struct dentry *dentry)
+{
+ struct vmfs_sb_info *s = server_from_dentry(dentry);
+ struct inode *inode = dentry->d_inode;
+ int error = 0;
+
+ DEBUG1("vmfs_revalidate_inode\n");
+
+ /*
+ * Check whether we've recently refreshed the inode.
+ */
+ if (time_before(jiffies, VMFS_I(inode)->oldmtime + VMFS_MAX_AGE(s))) {
+ VERBOSE("up-to-date, ino=%ld, jiffies=%lu, oldtime=%lu\n",
+ inode->i_ino, jiffies, VMFS_I(inode)->oldmtime);
+ goto out;
+ }
+
+ error = vmfs_refresh_inode(dentry);
+out:
+ return error;
+}
+
+/*
+ * This routine is called when i_nlink == 0 and i_count goes to 0.
+ * All blocking cleanup operations need to go here to avoid races.
+ */
+static void vmfs_delete_inode(struct inode *ino)
+{
+ DEBUG1("ino=%ld\n", ino->i_ino);
+ truncate_inode_pages(&ino->i_data, 0);
+ clear_inode(ino);
+ mutex_lock(&vmfs_mutex);
+ if (vmfs_close(ino))
+ PARANOIA("could not close inode %ld\n", ino->i_ino);
+ mutex_unlock(&vmfs_mutex);
+}
+
+/*
+ * vmfs_show_options() is for displaying mount options in /proc/mounts.
+ * It tries to avoid showing settings that were not changed from their
+ * defaults.
+ */
+static int vmfs_show_options(struct seq_file *s, struct dentry *m)
+{
+
+ return 0;
+}
+
+static void vmfs_put_super(struct super_block *sb)
+{
+ struct vmfs_sb_info *server = VMFS_SB(sb);
+
+ mutex_lock(&vmfs_mutex);
+
+ kfree(server->ops);
+
+ sb->s_fs_info = NULL;
+
+ mutex_unlock(&vmfs_mutex);
+ kfree(server);
+}
+
+static int vmfs_fill_super(struct super_block *sb, void *raw_data_unused,
+ int silent)
+{
+ struct vmfs_sb_info *server;
+ struct vmfs_mount_data_kernel *mnt;
+ struct inode *root_inode;
+ struct vmfs_fattr root;
+ void *mem;
+
+ sb->s_flags |= MS_NODIRATIME;
+ sb->s_blocksize = 1024;
+ sb->s_blocksize_bits = 10;
+ sb->s_magic = VMFS_SUPER_MAGIC;
+ sb->s_op = &vmfs_sops;
+ sb->s_time_gran = 100;
+
+ server = kzalloc(sizeof(struct vmfs_sb_info), GFP_KERNEL);
+ if (!server)
+ goto out_no_server;
+ sb->s_fs_info = server;
+
+ server->super_block = sb;
+ server->mnt = NULL;
+ server->vfs = vfs;
+
+ sema_init(&server->sem, 1);
+ INIT_LIST_HEAD(&server->entry);
+
+ /* Allocate global temp buffer and some superblock helper structs */
+ /* FIXME: move these to the vmfs_sb_info struct */
+ VERBOSE("alloc chunk = %zu\n", sizeof(struct vmfs_ops) +
+ sizeof(struct vmfs_mount_data_kernel));
+ mem = kmalloc(sizeof(struct vmfs_ops) +
+ sizeof(struct vmfs_mount_data_kernel), GFP_KERNEL);
+ if (!mem)
+ goto out_no_mem;
+
+ server->ops = mem;
+ vmfs_install_ops(server->ops);
+ server->mnt = mem + sizeof(struct vmfs_ops);
+
+ mnt = server->mnt;
+
+ memset(mnt, 0, sizeof(struct vmfs_mount_data_kernel));
+
+ mnt->ttl = VMFS_TTL_DEFAULT;
+ mnt->file_mode = S_IRWXU | S_IRGRP | S_IXGRP |
+ S_IROTH | S_IXOTH | S_IFREG;
+ mnt->dir_mode = S_IRWXU | S_IRGRP | S_IXGRP |
+ S_IROTH | S_IXOTH | S_IFDIR;
+
+ mnt->mounted_uid = current->real_cred->uid;
+
+ /*
+ * Keep the super block locked while we get the root inode.
+ */
+ vmfs_init_root_dirent(server, &root, sb);
+ root_inode = vmfs_iget(sb, &root);
+ if (!root_inode)
+ goto out_no_root;
+
+ sb->s_root = d_make_root(root_inode);
+ if (!sb->s_root)
+ goto out_no_root;
+
+ vmfs_new_dentry(sb->s_root);
+
+ return 0;
+
+out_no_root:
+ iput(root_inode);
+ kfree(mem);
+out_no_mem:
+ if (!server->mnt)
+ pr_err("vmfs_fill_super: allocation failure\n");
+ sb->s_fs_info = NULL;
+ kfree(server);
+ return -EINVAL;
+out_no_server:
+ pr_err("vmfs_fill_super: cannot allocate struct vmfs_sb_info\n");
+ return -ENOMEM;
+}
+
+static int vmfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ int result;
+
+ mutex_lock(&vmfs_mutex);
+
+ result = vmfs_proc_dskattr(dentry, buf);
+
+ mutex_unlock(&vmfs_mutex);
+
+ buf->f_type = VMFS_SUPER_MAGIC;
+ buf->f_namelen = VMFS_MAXPATHLEN;
+ return result;
+}
+
+int vmfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
+ struct kstat *stat)
+{
+ int err = vmfs_revalidate_inode(dentry);
+
+ if (!err)
+ generic_fillattr(dentry->d_inode, stat);
+ return err;
+}
+
+int vmfs_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+ struct inode *inode = dentry->d_inode;
+ struct vmfs_sb_info *server = server_from_dentry(dentry);
+ unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXUGO);
+ int error, changed, refresh = 0;
+ struct vmfs_fattr fattr;
+
+ DEBUG1("\n");
+
+ mutex_lock(&vmfs_mutex);
+
+ error = vmfs_revalidate_inode(dentry);
+ if (error)
+ goto out;
+
+ error = inode_change_ok(inode, attr);
+ if (error < 0)
+ goto out;
+
+ error = -EPERM;
+
+ if ((attr->ia_valid & ATTR_UID) &&
+ (attr->ia_uid.val != (server->mnt->uid).val))
+ goto out;
+
+ if ((attr->ia_valid & ATTR_GID) &&
+ ((attr->ia_gid).val != (server->mnt->gid).val))
+ 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)
+{
+ phys_addr_t dev_base = CONFIG_VMFS_DEV_BASE;
+ uint32_t dev_irq = -1;
+ int err;
+
+ DEBUG1("registering ...\n");
+
+ err = init_inodecache();
+ if (err)
+ goto out_inode;
+
+ if (IS_ENABLED(CONFIG_OF)) {
+ struct device_node *np;
+
+ np = of_find_compatible_node(NULL, NULL, "arm,messagebox");
+ if (np) {
+ struct resource res;
+
+ if (of_address_to_resource(np, 0, &res) == 0)
+ dev_base = res.start;
+ }
+ }
+
+ /* map the message box device into memory */
+
+ mbox = mb_new(dev_base, dev_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..ece7ad8
--- /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 <linux/uaccess.h>
+
+#include "proto.h"
+
+long vmfs_unlocked_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ struct vmfs_sb_info *server = server_from_inode(inode);
+ int result = -EINVAL;
+
+ switch (cmd) {
+ uid16_t uid16;
+ uid_t uid32;
+ case VMFS_IOC_GETMOUNTUID:
+ SET_UID(uid16, (server->mnt->mounted_uid).val);
+ result = put_user(uid16, (uid16_t __user *) arg);
+ break;
+ case VMFS_IOC_GETMOUNTUID32:
+ SET_UID(uid32, (server->mnt->mounted_uid).val);
+ 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..ee3deb1
--- /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%llx ...\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%llx 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..f4fb0c4
--- /dev/null
+++ b/fs/vmfs/proc.c
@@ -0,0 +1,1088 @@
+/*
+ * 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 <linux/string.h>
+#include <asm/div64.h>
+
+#include "vmfs_debug.h"
+#include "proto.h"
+#include "vfs.h"
+
+/* Features. Undefine if they cause problems, this should perhaps be a
+ config option. */
+#define VMFSFS_POSIX_UNLINK 1
+
+#define VMFS_ST_BLKSIZE (PAGE_SIZE)
+#define VMFS_ST_BLKSHIFT (PAGE_SHIFT)
+
+/* dont seem to have ulldiv. This is good enough for / 1000 */
+static uint64_t divmod64(uint64_t dividend, uint32_t divisor,
+ uint32_t *remainder)
+{
+ uint64_t quotient = 0;
+ uint32_t pquot, prem = 0;
+ uint32_t i;
+
+ /* divide in 4 32x16->32 bit parts. As most ARMs don't have / anyway
+ * we should probably do this bit by bit */
+ for (i = 0; i < 4; ++i) {
+ uint32_t part = (prem << 16) | (dividend >> 48);
+
+ pquot = part / divisor;
+ prem = part % divisor;
+
+ dividend = dividend << 16;
+ quotient = (quotient << 16) | pquot;
+ }
+
+ if (remainder)
+ *remainder = prem;
+
+ return quotient;
+}
+
+
+#define VMFS_ATTR_MAX (PATH_MAX+256)
+
+struct vmfs_ws {
+ char path[PATH_MAX];
+ char path2[PATH_MAX];
+ uint8_t attr[VMFS_ATTR_MAX];
+};
+
+static struct vmfs_ws *vmfs_get_ws(struct vmfs_sb_info *server)
+{
+ return kmalloc(sizeof(struct vmfs_ws), GFP_NOFS);
+}
+
+static void vmfs_put_ws(struct vmfs_ws *ws)
+{
+ kfree(ws);
+}
+
+/*****************************************************************************/
+/* */
+/* Encoding/Decoding section */
+/* */
+/*****************************************************************************/
+
+/*
+ * vmfs_build_path: build the path to entry and name storing it in buf.
+ * The path returned will have the trailing '\0'.
+ */
+static int vmfs_build_path(struct vmfs_sb_info *server, unsigned char *buf,
+ int buflen, struct dentry *entry, struct qstr *name)
+{
+ unsigned char *path = buf;
+ int maxlen = buflen;
+ int len;
+
+ VERBOSE("for dir %s\n", entry->d_name.name);
+
+ if (maxlen > VMFS_MAXPATHLEN + 1)
+ maxlen = VMFS_MAXPATHLEN + 1;
+
+ if (maxlen < 1)
+ return -ENAMETOOLONG;
+
+ path = buf + buflen;
+ *--path = '\0';
+ --maxlen;
+
+ if (name) {
+ len = name->len + 1;
+
+ if (len > maxlen)
+ return -ENAMETOOLONG;
+
+ path -= len;
+ maxlen -= len;
+ memcpy(path, name->name, len);
+
+ *--path = '/';
+ --maxlen;
+ }
+
+ if (entry != NULL) {
+ dget(entry);
+ do {
+ struct dentry *parent;
+
+ len = entry->d_name.len;
+
+ /* +1 for separator */
+ if (len + 1 > maxlen) {
+ dput(entry);
+ return -ENAMETOOLONG;
+ }
+
+ spin_lock(&entry->d_lock);
+ path -= len;
+ maxlen -= len;
+ memcpy(path, entry->d_name.name, len);
+
+ *--path = '/';
+ --maxlen;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
+ parent = entry->d_parent;
+ dget(parent);
+ spin_unlock(&entry->d_lock);
+#else
+ spin_unlock(&entry->d_lock);
+ parent = dget_parent(entry);
+#endif
+ dput(entry);
+ entry = parent;
+ } while (!IS_ROOT(entry));
+ dput(entry);
+ }
+ /* at the root we need to put on the root prefix, which is held
+ * in the server
+ * TODO - for now we assume it is called 'A' */
+
+ if (maxlen < 2)
+ return -ENAMETOOLONG;
+
+ maxlen -= 2;
+ *--path = ':';
+ *--path = 'A';
+
+ len = buflen - maxlen;
+ memmove(buf, path, len);
+
+ return len;
+}
+
+static int vmfs_encode_path(struct vmfs_sb_info *server, char *buf, int maxlen,
+ struct dentry *dir, struct qstr *name)
+{
+ int result;
+
+ result = vmfs_build_path(server, buf, maxlen, dir, name);
+ if (result < 0)
+ goto out;
+
+out:
+ FNEXIT("%d", result);
+
+ return result;
+}
+
+
+/*****************************************************************************/
+/* */
+/* Support section. */
+/* */
+/*****************************************************************************/
+
+/*
+ * Convert VMFS error codes to -E... errno values.
+ */
+int vmfs_errno(int error)
+{
+ if (error < 0) {
+ VERBOSE("%d\n", error);
+
+ switch (error) {
+ case VFS_ERR_BADHANDLE:
+ return -EBADF;
+ case VFS_ERR_NOENTRY:
+ return -ENOENT;
+ case VFS_ERR_NOROOM:
+ return -ENOMEM;
+ case VFS_ERR_MAXHANDLE:
+ return -EMFILE;
+ case VFS_ERR_NOMOUNT:
+ return -ENXIO;
+ case VFS_ERR_NOTFOUND:
+ return -ENOENT;
+ case VFS_ERR_PERM:
+ return -EACCES; /* EPERM? */
+ case VFS_ERR_NOTDIR:
+ return -ENOTDIR;
+ case VFS_ERR_TOOLONG:
+ return -ENAMETOOLONG;
+ case VFS_ERR_EXIST:
+ return -EEXIST;
+ case VFS_ERR_NOTEMPTY:
+ return -ENOTEMPTY;
+ case VFS_ERR_INVALID:
+ return -EINVAL;
+ case VFS_ERR_ISDIR:
+ return -EISDIR;
+ case VFS_ERR_TOOBIG:
+ return -ERANGE;
+ case VFS_ERR_UNIMPL:
+ return -ENOSYS;
+
+ default:
+ /* something generic */
+ return -EIO;
+ }
+ } else
+ return error;
+}
+
+static int
+vmfs_proc_open(struct vmfs_sb_info *server, struct dentry *dentry, int wish)
+{
+ struct inode *ino = dentry->d_inode;
+ struct vmfs_inode_info *ei = VMFS_I(ino);
+ VFSOpenFlags mode = wish;
+ int res;
+ struct vmfs_ws *ws = vmfs_get_ws(server);
+
+ FNENTER();
+
+ if (!(ino->i_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
+ mode &= ~VFS_OPEN_WRONLY;
+
+ res = vmfs_encode_path(server, ws->path, PATH_MAX, dentry, NULL);
+ if (res < 0)
+ goto out;
+
+ res = vfsop_openfile(server->vfs, ws->path, mode);
+ if ((res < 0) && ((mode & VFS_OPEN_RDWR) == VFS_OPEN_RDWR)) {
+ mode &= ~VFS_OPEN_WRONLY;
+ res = vfsop_openfile(server->vfs, ws->path, mode);
+ }
+
+ if (res < 0) {
+ res = vmfs_errno(res);
+ goto out;
+ }
+ /* we appear to need attribute information also. Not sure why.
+ * res = vfsop_getattr(server->vfs, p, VFS_ATTR_, attr, attrlen); */
+
+ ei->vhandle = res;
+ ei->vaccess = mode & VFS_OPEN_RDWR;
+ ei->vopen = 1;
+
+ /* may want to set up attr (as in dos attr) here, and access */
+
+ res = 0;
+out:
+ vmfs_put_ws(ws);
+
+ FNEXIT("%d\n", res);
+ return res;
+}
+
+/*
+ * Make sure the file is open, and check that the access
+ * is compatible with the desired access.
+ *
+ * wish is one of VMFS_O_RDONLY = 0/VMFS_O_WRONLY = 1/VMFS_O_RDWR = 2
+ */
+int vmfs_open(struct dentry *dentry, int flags, int wish)
+{
+ struct inode *inode = dentry->d_inode;
+ int result;
+ VFSOpenFlags vwish, vaccess;
+
+ FNENTER();
+
+ if (wish == VMFS_O_RDONLY)
+ vwish = VFS_OPEN_RDONLY;
+ else if (wish == VMFS_O_WRONLY)
+ vwish = VFS_OPEN_WRONLY;
+ else if (wish == VMFS_O_RDWR)
+ vwish = VFS_OPEN_RDWR;
+ else {
+ DEBUG1("unexpected open flags!\n");
+ vwish = VFS_OPEN_RDWR;
+ }
+
+ if (flags & O_CREAT)
+ vwish |= VFS_OPEN_CREATE;
+ if (flags & O_TRUNC)
+ vwish |= VFS_OPEN_TRUNCATE;
+ if (flags & O_EXCL)
+ vwish |= VFS_OPEN_NEW;
+
+ result = -ENOENT;
+ if (!inode) {
+ pr_err("vmfs_open: no inode for dentry!\n");
+ goto out;
+ }
+
+ if (!vmfs_is_open(inode)) {
+ struct vmfs_sb_info *server = server_from_inode(inode);
+
+ result = 0;
+ if (!vmfs_is_open(inode))
+ result = vmfs_proc_open(server, dentry, vwish);
+ if (result)
+ goto out;
+ /*
+ * A successful open means the path is still valid ...
+ */
+ vmfs_renew_times(dentry);
+ }
+
+ /*
+ * Check whether the access is compatible with the desired mode.
+ */
+
+ result = 0;
+ vaccess = VMFS_I(inode)->vaccess;
+
+ if (vaccess != (vwish & VFS_OPEN_RDWR) && vaccess != VFS_OPEN_RDWR) {
+ PARANOIA("%s/%s access denied, access=%x, wish=%x\n",
+ DENTRY_PATH(dentry), vaccess, vwish);
+ result = -EACCES;
+ }
+out:
+ FNEXIT("%d", result);
+ return result;
+}
+
+static int vmfs_proc_close(struct vmfs_sb_info *server, int32_t handle)
+{
+ int result = -ENOMEM;
+
+ FNENTER();
+
+ result = vmfs_errno(vfsop_closefile(server->vfs, handle));
+
+ /* GPBTODO - may need to set mtime using utc2local(server, mtime) */
+
+ FNEXIT("%d", result);
+
+ return result;
+}
+
+/*
+ * Win NT 4.0 has an apparent bug in that it fails to update the
+ * modify time when writing to a file. As a workaround, we update
+ * both modify and access time locally, and post the times to the
+ * server when closing the file.
+ */
+static int vmfs_proc_close_inode(struct vmfs_sb_info *server,
+ struct inode *ino)
+{
+ struct vmfs_inode_info *ei = VMFS_I(ino);
+ int result = 0;
+
+ if (vmfs_is_open(ino)) {
+ /*
+ * We clear the open flag in advance, in case another
+ * process observes the value while we block below.
+ */
+ ei->vopen = 0;
+
+ result = vmfs_proc_close(server, ei->vhandle);
+
+ ei->closed = jiffies;
+ }
+
+ FNEXIT("%d", result);
+
+ return result;
+}
+
+int vmfs_close(struct inode *ino)
+{
+ int result = 0;
+
+ if (vmfs_is_open(ino)) {
+ struct vmfs_sb_info *server = server_from_inode(ino);
+
+ result = vmfs_proc_close_inode(server, ino);
+ }
+
+ FNEXIT("%d", result);
+
+ return result;
+}
+
+/*
+ * This is used to close a file following a failed instantiate.
+ * Since we don't have an inode, we can't use any of the above.
+ */
+int vmfs_close_fileid(struct dentry *dentry, int32_t fileid)
+{
+
+ struct vmfs_sb_info *server = server_from_dentry(dentry);
+ int result;
+
+ result = vmfs_proc_close(server, fileid);
+
+ FNEXIT("%d", result);
+
+ return result;
+}
+
+static int
+vmfs_proc_read(struct inode *inode, loff_t offset, int count, char *data)
+{
+ struct vmfs_sb_info *server = server_from_inode(inode);
+ int result;
+
+ result =
+ vfsop_readfile(server->vfs, VMFS_I(inode)->vhandle, offset, data,
+ count);
+ if (result < 0)
+ result = vmfs_errno(result);
+
+ VERBOSE("ino=%ld, handle=%d, count=%d, result=%d\n",
+ inode->i_ino, VMFS_I(inode)->vhandle, count, result);
+
+ return result;
+}
+
+static int
+vmfs_proc_write(struct inode *inode, loff_t offset,
+ int count, const char *data)
+{
+ struct vmfs_sb_info *server = server_from_inode(inode);
+ int result;
+
+ result =
+ vfsop_writefile(server->vfs, VMFS_I(inode)->vhandle, offset, data,
+ count);
+ if (result < 0)
+ result = vmfs_errno(result);
+
+ VERBOSE("ino=%ld, handle=%d, count=%d, result=%d\n",
+ inode->i_ino, VMFS_I(inode)->vhandle, count, result);
+ return result;
+}
+
+/* GPB - this appears to be open(O_CREAT) which we do support */
+int vmfs_proc_create(struct dentry *dentry, uint32_t mode, int32_t *fileid)
+{
+ struct vmfs_sb_info *server = server_from_dentry(dentry);
+ struct vmfs_ws *ws = vmfs_get_ws(server);
+ int result;
+
+ result = vmfs_encode_path(server, ws->path, PATH_MAX, dentry, NULL);
+ if (result < 0)
+ goto out;
+
+ result =
+ vfsop_openfile(server->vfs, ws->path,
+ VFS_OPEN_CREATE | VFS_OPEN_RDWR);
+ if (result < 0) {
+ result = vmfs_errno(result);
+ goto out;
+ }
+ /* GPBTODO - may need to set mtime when file is created */
+ /* GPBTODO - what should create do if the file already exists? */
+
+ *fileid = result;
+ result = 0;
+
+out:
+ vmfs_put_ws(ws);
+ FNEXIT("%d", result);
+ return result;
+}
+
+int vmfs_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry)
+{
+ struct vmfs_sb_info *server = server_from_dentry(old_dentry);
+ struct vmfs_ws *ws = vmfs_get_ws(server);
+
+ int result;
+
+ result =
+ vmfs_encode_path(server, ws->path, PATH_MAX, old_dentry, NULL);
+ if (result < 0)
+ goto out;
+ result =
+ vmfs_encode_path(server, ws->path2, PATH_MAX, new_dentry, NULL);
+ if (result < 0)
+ goto out;
+
+ result = vmfs_errno(vfsop_rename(server->vfs, ws->path, ws->path2));
+ if (result < 0)
+ goto out;
+
+ result = 0;
+
+out:
+ vmfs_put_ws(ws);
+ FNEXIT("%d", result);
+ return result;
+}
+
+int vmfs_proc_mkdir(struct dentry *dentry)
+{
+ struct vmfs_sb_info *server = server_from_dentry(dentry);
+ struct vmfs_ws *ws = vmfs_get_ws(server);
+ int result;
+
+ FNENTER();
+
+ result = vmfs_encode_path(server, ws->path, PATH_MAX, dentry, NULL);
+ if (result < 0)
+ goto out;
+
+ result = vmfs_errno(vfsop_mkdir(server->vfs, ws->path));
+ if (result < 0)
+ goto out;
+
+ result = 0;
+
+out:
+ vmfs_put_ws(ws);
+
+ FNEXIT("%d", result);
+
+ return result;
+}
+
+int vmfs_proc_rmdir(struct dentry *dentry)
+{
+ struct vmfs_sb_info *server = server_from_dentry(dentry);
+ struct vmfs_ws *ws = vmfs_get_ws(server);
+ int result;
+
+ FNENTER();
+
+ result = vmfs_encode_path(server, ws->path, PATH_MAX, dentry, NULL);
+ if (result < 0)
+ goto out;
+
+ result = vmfs_errno(vfsop_rmdir(server->vfs, ws->path));
+ if (result < 0)
+ goto out;
+
+ result = 0;
+
+out:
+ vmfs_put_ws(ws);
+
+ FNEXIT("%d", result);
+
+ return result;
+}
+
+#if VMFSFS_POSIX_UNLINK
+/*
+ * Removes readonly attribute from a file. Used by unlink to give posix
+ * semantics.
+ */
+#endif
+
+int vmfs_proc_unlink(struct dentry *dentry)
+{
+ struct vmfs_sb_info *server = server_from_dentry(dentry);
+ struct vmfs_ws *ws = vmfs_get_ws(server);
+ int result;
+
+ result = vmfs_encode_path(server, ws->path, PATH_MAX, dentry, NULL);
+ if (result < 0)
+ goto out;
+
+ /* GPBTODO - this needs to work even if the file is read only
+ * should this be done on the host side? */
+
+ result = vmfs_errno(vfsop_remove(server->vfs, ws->path));
+out:
+ vmfs_put_ws(ws);
+
+ FNEXIT("%d", result);
+
+ return result;
+}
+
+int vmfs_proc_flush(struct vmfs_sb_info *server, int32_t handle)
+{
+ int result;
+
+ result = vmfs_errno(vfsop_filesync(server->vfs, handle));
+
+ FNEXIT("%d", result);
+
+ return result;
+}
+
+static int vmfs_proc_trunc32(struct inode *inode, loff_t length)
+{
+ struct vmfs_sb_info *server = server_from_inode(inode);
+ int result;
+
+ result =
+ vmfs_errno(vfsop_setfilesize
+ (server->vfs, VMFS_I(inode)->vhandle, length));
+ if (result < 0)
+ goto out;
+
+ result = 0;
+
+ FNEXIT("%d", result);
+
+out:
+ return result;
+}
+
+static void
+vmfs_init_dirent(struct vmfs_sb_info *server, struct vmfs_fattr *fattr)
+{
+ memset(fattr, 0, sizeof(*fattr));
+
+ fattr->f_nlink = 1;
+ fattr->f_uid = server->mnt->uid;
+ fattr->f_gid = server->mnt->gid;
+ fattr->f_unix = 0;
+}
+
+static void
+vmfs_finish_dirent(struct vmfs_sb_info *server, struct vmfs_fattr *fattr)
+{
+ if (fattr->f_unix)
+ return;
+
+ fattr->f_mode = server->mnt->file_mode;
+ if (fattr->attr & aDIR) {
+ fattr->f_mode = server->mnt->dir_mode;
+ fattr->f_size = VMFS_ST_BLKSIZE;
+ }
+ /* Check the read-only flag */
+ if (fattr->attr & aRONLY)
+ fattr->f_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+
+ /* How many 512 byte blocks do we need for this file? */
+ fattr->f_blocks = 0;
+ if (fattr->f_size != 0)
+ fattr->f_blocks = 1 + ((fattr->f_size - 1) >> 9);
+}
+
+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,
+ 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, 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..e02803d
--- /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, struct dir_context *dirent,
+ 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..441a240
--- /dev/null
+++ b/fs/vmfs/symlink.c
@@ -0,0 +1,69 @@
+/*
+ * symlink.c
+ *
+ * Copyright (C) 2002 by John Newbigin
+ *
+ * Please add a note about your changes to vmfs_ in the ChangeLog file.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/net.h>
+#include <linux/namei.h>
+
+#include <linux/uaccess.h>
+
+#include "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..ed01b51
--- /dev/null
+++ b/fs/vmfs/vmfs.h
@@ -0,0 +1,45 @@
+/*
+ * vmfs.h
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_VMFS_H
+#define _LINUX_VMFS_H
+
+#include <linux/types.h>
+#include <linux/magic.h>
+#include <linux/uidgid.h>
+
+#ifdef __KERNEL__
+
+#define VMFS_MAXNAMELEN 255
+#define VMFS_MAXPATHLEN 1024
+
+/*
+ * Contains all relevant data on a VMFS networked file.
+ */
+struct vmfs_fattr {
+ __u16 attr;
+
+ unsigned long f_ino;
+ umode_t f_mode;
+ nlink_t f_nlink;
+ kuid_t f_uid;
+ kgid_t f_gid;
+ dev_t f_rdev;
+ loff_t f_size;
+ struct timespec f_atime;
+ struct timespec f_mtime;
+ struct timespec f_ctime;
+ unsigned long f_blocks;
+ int f_unix;
+
+ /* vfs bits */
+ uint32_t f_type;
+};
+
+#endif
+#endif
diff --git a/fs/vmfs/vmfs_debug.h b/fs/vmfs/vmfs_debug.h
new file mode 100644
index 0000000..27d7316
--- /dev/null
+++ b/fs/vmfs/vmfs_debug.h
@@ -0,0 +1,39 @@
+/*
+ * Defines some debug macros for vmfs_.
+ */
+
+/* This makes a dentry parent/child name pair. Useful for debugging printk's */
+#define DENTRY_PATH(dentry) \
+ ((dentry)->d_parent->d_name.name, (dentry)->d_name.name)
+
+/*
+ * safety checks that should never happen ???
+ * these are normally enabled.
+ */
+#ifdef VMFSFS_PARANOIA
+#define PARANOIA(f, a...) pr_notice("%s: " f, __func__, ## a)
+#else
+#define PARANOIA(f, a...) do { ; } while (0)
+#endif
+
+/* lots of debug messages */
+#ifdef VMFSFS_DEBUG_VERBOSE
+#define VERBOSE(f, a...) pr_debug("%s: " f, __func__, ## a)
+#else
+#define VERBOSE(f, a...) do { ; } while (0)
+#endif
+
+/*
+ * "normal" debug messages, but not with a normal DEBUG define ... way
+ * too common name.
+ */
+#ifdef VMFSFS_DEBUG
+#define DEBUG1(f, a...) pr_debug("%s: " f, __func__, ## a)
+#define FNENTER(f, a...) pr_debug("enter %s:\n" f, \
+ __func__, ## a)
+#define FNEXIT(f, a...) pr_debug("exit %s:\n" f, __func__, ## a)
+#else
+#define DEBUG1(f, a...) do { ; } while (0)
+#define FNENTER(f, a...) do { ; } while (0)
+#define FNEXIT(f, a...) do { ; } while (0)
+#endif
diff --git a/fs/vmfs/vmfs_fs.h b/fs/vmfs/vmfs_fs.h
new file mode 100644
index 0000000..95a03bf
--- /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,
+ struct vmfs_cache_control *ctl);
+
+ int (*getattr)(struct vmfs_sb_info *server, struct dentry *dir,
+ struct vmfs_fattr *fattr);
+ /* int (*setattr)(...); *//* setattr is really icky! */
+
+ int (*truncate)(struct inode *inode, loff_t length);
+};
+
+static inline int vmfs_is_open(struct inode *i)
+{
+ return (VMFS_I(i)->vopen == 1);
+}
+
+extern void vmfs_install_ops(struct vmfs_ops *);
+
+extern struct mutex vmfs_mutex;
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_VMFS_FS_H */
diff --git a/fs/vmfs/vmfs_fs_i.h b/fs/vmfs/vmfs_fs_i.h
new file mode 100644
index 0000000..34f0028
--- /dev/null
+++ b/fs/vmfs/vmfs_fs_i.h
@@ -0,0 +1,39 @@
+/*
+ * vmfs_fs_i.h
+ *
+ * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_VMFS_FS_I
+#define _LINUX_VMFS_FS_I
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/fs.h>
+
+/*
+ * vmfs fs inode data (in memory only)
+ */
+struct vmfs_inode_info {
+
+ unsigned int open; /* open generation */
+
+ unsigned long oldmtime; /* last time refreshed */
+ unsigned long closed; /* timestamp when closed */
+ unsigned openers; /* number of fileid users */
+
+ /* GPB - vfs data - we also use access */
+
+ uint32_t vhandle; /* host side handle */
+ uint32_t vaccess; /* access (VMFS_OPEN_ ) */
+ uint32_t vopen; /* set to 1 when the file is open
+ (why not use openers?) */
+
+ struct inode vfs_inode; /* must be at the end */
+
+};
+
+#endif
+#endif
diff --git a/fs/vmfs/vmfs_fs_sb.h b/fs/vmfs/vmfs_fs_sb.h
new file mode 100644
index 0000000..f98e35d
--- /dev/null
+++ b/fs/vmfs/vmfs_fs_sb.h
@@ -0,0 +1,64 @@
+/*
+ * vmfs_fs_sb.h
+ *
+ * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ * Copyright (C) 2008-2009 ARM Limited
+ *
+ */
+
+#ifndef _VMFS_FS_SB
+#define _VMFS_FS_SB
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include "vmfs.h"
+#include "vfs.h"
+
+/* structure access macros */
+#define server_from_inode(inode) VMFS_SB((inode)->i_sb)
+#define server_from_dentry(dentry) VMFS_SB((dentry)->d_sb)
+#define SB_of(server) ((server)->super_block)
+
+struct vmfs_sb_info {
+ /* List of all vmfsfs superblocks */
+ struct list_head entry;
+
+ /* GPBTODO - most of the 'server' code here should move to the
+ * messagebox
+ */
+
+ struct vmfs_mount_data_kernel *mnt;
+
+ /* Connections are counted. Each time a new socket arrives,
+ * generation is incremented.
+ */
+ struct semaphore sem;
+
+ struct vmfs_ops *ops;
+
+ struct super_block *super_block;
+
+ VFS *vfs;
+};
+
+static inline int vmfs_lock_server_interruptible(struct vmfs_sb_info *server)
+{
+ return down_interruptible(&(server->sem));
+}
+
+static inline void vmfs_lock_server(struct vmfs_sb_info *server)
+{
+ down(&(server->sem));
+}
+
+static inline void vmfs_unlock_server(struct vmfs_sb_info *server)
+{
+ up(&(server->sem));
+}
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/fs/vmfs/vmfs_mount.h b/fs/vmfs/vmfs_mount.h
new file mode 100644
index 0000000..56123cc
--- /dev/null
+++ b/fs/vmfs/vmfs_mount.h
@@ -0,0 +1,62 @@
+/*
+ * vmfs_mount.h
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_VMFS_MOUNT_H
+#define _LINUX_VMFS_MOUNT_H
+
+#include <linux/types.h>
+
+#define VMFS_MOUNT_VERSION 6
+
+struct vmfs_mount_data {
+ int version;
+ __kernel_uid_t mounted_uid; /* Who may umount() this filesystem? */
+ __kernel_uid_t uid;
+ __kernel_gid_t gid;
+ __kernel_mode_t file_mode;
+ __kernel_mode_t dir_mode;
+};
+
+#ifdef __KERNEL__
+
+/* "vers" in big-endian */
+#define VMFS_MOUNT_ASCII 0x76657273
+
+#define VMFS_MOUNT_OLDVERSION 6
+#undef VMFS_MOUNT_VERSION
+#define VMFS_MOUNT_VERSION 7
+
+/* flags */
+#define VMFS_MOUNT_WIN95 0x0001 /* Win 95 server */
+#define VMFS_MOUNT_OLDATTR 0x0002 /* Use core getattr (Win 95 speedup) */
+#define VMFS_MOUNT_DIRATTR 0x0004 /* Use find_first for getattr */
+#define VMFS_MOUNT_CASE 0x0008 /* Be case sensitive */
+#define VMFS_MOUNT_UNICODE 0x0010 /* Server talks unicode */
+#define VMFS_MOUNT_UID 0x0020 /* Use user specified uid */
+#define VMFS_MOUNT_GID 0x0040 /* Use user specified gid */
+#define VMFS_MOUNT_FMODE 0x0080 /* Use user specified file mode */
+#define VMFS_MOUNT_DMODE 0x0100 /* Use user specified dir mode */
+
+struct vmfs_mount_data_kernel {
+ int version;
+
+ kuid_t mounted_uid; /* Who may umount() this filesystem? */
+ kuid_t uid;
+ kgid_t gid;
+ mode_t file_mode;
+ mode_t dir_mode;
+
+ u32 flags;
+
+ /* maximum age in jiffies (inode, dentry and dircache) */
+ int ttl;
+};
+
+#endif
+
+#endif
diff --git a/fs/vmfs/vmfsno.h b/fs/vmfs/vmfsno.h
new file mode 100644
index 0000000..b2734cc
--- /dev/null
+++ b/fs/vmfs/vmfsno.h
@@ -0,0 +1,138 @@
+#ifndef _VMFSNO_H_
+#define _VMFSNO_H_
+
+/* these define the attribute byte as seen by DOS */
+#define aRONLY (1L<<0)
+#define aHIDDEN (1L<<1)
+#define aSYSTEM (1L<<2)
+#define aVOLID (1L<<3)
+#define aDIR (1L<<4)
+#define aARCH (1L<<5)
+
+/* error classes */
+#define SUCCESS 0 /* The request was successful. */
+#define ERRDOS 0x01 /* Error is from the core DOS operating
+ * system set. */
+#define ERRSRV 0x02 /* Error is generated by the server network
+ * file manager. */
+#define ERRHRD 0x03 /* Error is an hardware error. */
+#define ERRCMD 0xFF /* Command was not in the "VMFS" format. */
+
+/* VMFS X/Open error codes for the ERRdos error class */
+
+#define ERRbadfunc 1 /* Invalid function (or system call) */
+#define ERRbadfile 2 /* File not found (pathname error) */
+#define ERRbadpath 3 /* Directory not found */
+#define ERRnofids 4 /* Too many open files */
+#define ERRnoaccess 5 /* Access denied */
+#define ERRbadfid 6 /* Invalid fid */
+#define ERRbadmcb 7 /* Memory control blocks destroyed */
+#define ERRnomem 8 /* Out of memory */
+#define ERRbadmem 9 /* Invalid memory block address */
+#define ERRbadenv 10 /* Invalid environment */
+#define ERRbadformat 11 /* Invalid format */
+#define ERRbadaccess 12 /* Invalid open mode */
+#define ERRbaddata 13 /* Invalid data (only from ioctl call) */
+#define ERRres 14 /* reserved */
+#define ERRbaddrive 15 /* Invalid drive */
+#define ERRremcd 16 /* Attempt to delete current directory */
+#define ERRdiffdevice 17 /* rename/move across different filesystems */
+#define ERRnofiles 18 /* no more files found in file search */
+#define ERRbadshare 32 /* Share mode on file conflicts
+ * with open mode */
+#define ERRlock 33 /* Lock request conflicts with existing lock */
+#define ERRfilexists 80 /* File in operation already exists */
+#define ERRbadpipe 230 /* Named pipe invalid */
+#define ERRpipebusy 231 /* All instances of pipe are busy */
+#define ERRpipeclosing 232 /* named pipe close in progress */
+#define ERRnotconnected 233 /* No process on other end of named pipe */
+#define ERRmoredata 234 /* More data to be returned */
+
+#define ERROR_INVALID_PARAMETER 87
+#define ERROR_DISK_FULL 112
+#define ERROR_INVALID_NAME 123
+#define ERROR_DIR_NOT_EMPTY 145
+#define ERROR_NOT_LOCKED 158
+#define ERROR_ALREADY_EXISTS 183 /* see also 80 ? */
+#define ERROR_EAS_DIDNT_FIT 275 /* Extended attributes didn't fit */
+#define ERROR_EAS_NOT_SUPPORTED 282 /* Extended attributes not supported */
+
+/* Error codes for the ERRSRV class */
+
+#define ERRerror 1 /* Non specific error code */
+#define ERRbadpw 2 /* Bad password */
+#define ERRbadtype 3 /* reserved */
+#define ERRaccess 4 /* No permissions to do requested operation */
+#define ERRinvnid 5 /* tid invalid */
+#define ERRinvnetname 6 /* Invalid servername */
+#define ERRinvdevice 7 /* Invalid device */
+#define ERRqfull 49 /* Print queue full */
+#define ERRqtoobig 50 /* Queued item too big */
+#define ERRinvpfid 52 /* Invalid print file in vmfs_fid */
+#define ERRvmfscmd 64 /* Unrecognised command */
+#define ERRsrverror 65 /* vmfs server internal error */
+#define ERRfilespecs 67 /* fid and pathname invalid combination */
+#define ERRbadlink 68 /* reserved */
+#define ERRbadpermits 69 /* Access specified for a file is not valid */
+#define ERRbadpid 70 /* reserved */
+#define ERRsetattrmode 71 /* attribute mode invalid */
+#define ERRpaused 81 /* Message server paused */
+#define ERRmsgoff 82 /* Not receiving messages */
+#define ERRnoroom 83 /* No room for message */
+#define ERRrmuns 87 /* too many remote usernames */
+#define ERRtimeout 88 /* operation timed out */
+#define ERRnoresource 89 /* No resources currently avail for request.*/
+#define ERRtoomanyuids 90 /* too many userids */
+#define ERRbaduid 91 /* bad userid */
+#define ERRuseMPX 250 /* temp unable to use raw mode, use MPX mode */
+#define ERRuseSTD 251 /* temp unable to use raw mode, use std.mode */
+#define ERRcontMPX 252 /* resume MPX mode */
+#define ERRbadPW /* reserved */
+#define ERRnosupport 0xFFFF
+
+/* Error codes for the ERRHRD class */
+
+#define ERRnowrite 19 /* read only media */
+#define ERRbadunit 20 /* Unknown device */
+#define ERRnotready 21 /* Drive not ready */
+#define ERRbadcmd 22 /* Unknown command */
+#define ERRdata 23 /* Data (CRC) error */
+#define ERRbadreq 24 /* Bad request structure length */
+#define ERRseek 25
+#define ERRbadmedia 26
+#define ERRbadsector 27
+#define ERRnopaper 28
+#define ERRwrite 29 /* write fault */
+#define ERRread 30 /* read fault */
+#define ERRgeneral 31 /* General hardware failure */
+#define ERRwrongdisk 34
+#define ERRFCBunavail 35
+#define ERRsharebufexc 36 /* share buffer exceeded */
+#define ERRdiskfull 39
+
+/*
+ * Access modes when opening a file
+ */
+#define VMFS_ACCMASK 0x0003
+#define VMFS_O_RDONLY 0x0000
+#define VMFS_O_WRONLY 0x0001
+#define VMFS_O_RDWR 0x0002
+
+/* values which means "don't change it" */
+#define VMFS_MODE_NO_CHANGE 0xFFFFFFFF
+#define VMFS_UID_NO_CHANGE 0xFFFFFFFF
+#define VMFS_GID_NO_CHANGE 0xFFFFFFFF
+#define VMFS_TIME_NO_CHANGE 0xFFFFFFFFFFFFFFFFULL
+#define VMFS_SIZE_NO_CHANGE 0xFFFFFFFFFFFFFFFFULL
+
+/* UNIX filetype mappings. */
+#define UNIX_TYPE_FILE 0
+#define UNIX_TYPE_DIR 1
+#define UNIX_TYPE_SYMLINK 2
+#define UNIX_TYPE_CHARDEV 3
+#define UNIX_TYPE_BLKDEV 4
+#define UNIX_TYPE_FIFO 5
+#define UNIX_TYPE_SOCKET 6
+#define UNIX_TYPE_UNKNOWN 0xFFFFFFFF
+
+#endif /* _VMFSNO_H_ */
--
1.7.9.5
More information about the linux-yocto
mailing list