From: Charlie Paul <cpaul.windri...@gmail.com> These files add VMFS support for the LSI axxia 5500 board.
Signed-off-by: Charlie Paul <cpaul.windri...@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 -- _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto