summaryrefslogtreecommitdiff
path: root/fs/coda
diff options
context:
space:
mode:
Diffstat (limited to 'fs/coda')
-rw-r--r--fs/coda/Makefile12
-rw-r--r--fs/coda/cache.c120
-rw-r--r--fs/coda/cnode.c172
-rw-r--r--fs/coda/coda_linux.c197
-rw-r--r--fs/coda/dir.c704
-rw-r--r--fs/coda/file.c300
-rw-r--r--fs/coda/inode.c321
-rw-r--r--fs/coda/pioctl.c95
-rw-r--r--fs/coda/psdev.c462
-rw-r--r--fs/coda/symlink.c55
-rw-r--r--fs/coda/sysctl.c254
-rw-r--r--fs/coda/upcall.c914
12 files changed, 3606 insertions, 0 deletions
diff --git a/fs/coda/Makefile b/fs/coda/Makefile
new file mode 100644
index 000000000000..6c22e61da397
--- /dev/null
+++ b/fs/coda/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the Linux Coda filesystem routines.
+#
+
+obj-$(CONFIG_CODA_FS) += coda.o
+
+coda-objs := psdev.o cache.o cnode.o inode.o dir.o file.o upcall.o \
+ coda_linux.o symlink.o pioctl.o sysctl.o
+
+# If you want debugging output, please uncomment the following line.
+
+# EXTRA_CFLAGS += -DDEBUG -DDEBUG_SMB_MALLOC=1
diff --git a/fs/coda/cache.c b/fs/coda/cache.c
new file mode 100644
index 000000000000..80072fd9b7fa
--- /dev/null
+++ b/fs/coda/cache.c
@@ -0,0 +1,120 @@
+/*
+ * Cache operations for Coda.
+ * For Linux 2.1: (C) 1997 Carnegie Mellon University
+ * For Linux 2.3: (C) 2000 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users of this code to contribute improvements
+ * to the Coda project http://www.coda.cs.cmu.edu/ <coda@cs.cmu.edu>.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/list.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
+
+static atomic_t permission_epoch = ATOMIC_INIT(0);
+
+/* replace or extend an acl cache hit */
+void coda_cache_enter(struct inode *inode, int mask)
+{
+ struct coda_inode_info *cii = ITOC(inode);
+
+ cii->c_cached_epoch = atomic_read(&permission_epoch);
+ if (cii->c_uid != current->fsuid) {
+ cii->c_uid = current->fsuid;
+ cii->c_cached_perm = mask;
+ } else
+ cii->c_cached_perm |= mask;
+}
+
+/* remove cached acl from an inode */
+void coda_cache_clear_inode(struct inode *inode)
+{
+ struct coda_inode_info *cii = ITOC(inode);
+ cii->c_cached_perm = 0;
+}
+
+/* remove all acl caches */
+void coda_cache_clear_all(struct super_block *sb)
+{
+ struct coda_sb_info *sbi;
+
+ sbi = coda_sbp(sb);
+ if (!sbi) BUG();
+
+ atomic_inc(&permission_epoch);
+}
+
+
+/* check if the mask has been matched against the acl already */
+int coda_cache_check(struct inode *inode, int mask)
+{
+ struct coda_inode_info *cii = ITOC(inode);
+ int hit;
+
+ hit = (mask & cii->c_cached_perm) == mask &&
+ cii->c_uid == current->fsuid &&
+ cii->c_cached_epoch == atomic_read(&permission_epoch);
+
+ return hit;
+}
+
+
+/* Purging dentries and children */
+/* The following routines drop dentries which are not
+ in use and flag dentries which are in use to be
+ zapped later.
+
+ The flags are detected by:
+ - coda_dentry_revalidate (for lookups) if the flag is C_PURGE
+ - coda_dentry_delete: to remove dentry from the cache when d_count
+ falls to zero
+ - an inode method coda_revalidate (for attributes) if the
+ flag is C_VATTR
+*/
+
+/* this won't do any harm: just flag all children */
+static void coda_flag_children(struct dentry *parent, int flag)
+{
+ struct list_head *child;
+ struct dentry *de;
+
+ spin_lock(&dcache_lock);
+ list_for_each(child, &parent->d_subdirs)
+ {
+ de = list_entry(child, struct dentry, d_child);
+ /* don't know what to do with negative dentries */
+ if ( ! de->d_inode )
+ continue;
+ coda_flag_inode(de->d_inode, flag);
+ }
+ spin_unlock(&dcache_lock);
+ return;
+}
+
+void coda_flag_inode_children(struct inode *inode, int flag)
+{
+ struct dentry *alias_de;
+
+ if ( !inode || !S_ISDIR(inode->i_mode))
+ return;
+
+ alias_de = d_find_alias(inode);
+ if (!alias_de)
+ return;
+ coda_flag_children(alias_de, flag);
+ shrink_dcache_parent(alias_de);
+ dput(alias_de);
+}
+
diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c
new file mode 100644
index 000000000000..23aeef5aa814
--- /dev/null
+++ b/fs/coda/cnode.c
@@ -0,0 +1,172 @@
+/* cnode related routines for the coda kernel code
+ (C) 1996 Peter Braam
+ */
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/time.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_psdev.h>
+
+static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2)
+{
+ return memcmp(fid1, fid2, sizeof(*fid1)) == 0;
+}
+
+static struct inode_operations coda_symlink_inode_operations = {
+ .readlink = generic_readlink,
+ .follow_link = page_follow_link_light,
+ .put_link = page_put_link,
+ .setattr = coda_setattr,
+};
+
+/* cnode.c */
+static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr)
+{
+ coda_vattr_to_iattr(inode, attr);
+
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &coda_file_inode_operations;
+ inode->i_fop = &coda_file_operations;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &coda_dir_inode_operations;
+ inode->i_fop = &coda_dir_operations;
+ } else if (S_ISLNK(inode->i_mode)) {
+ inode->i_op = &coda_symlink_inode_operations;
+ inode->i_data.a_ops = &coda_symlink_aops;
+ inode->i_mapping = &inode->i_data;
+ } else
+ init_special_inode(inode, inode->i_mode, huge_decode_dev(attr->va_rdev));
+}
+
+static int coda_test_inode(struct inode *inode, void *data)
+{
+ struct CodaFid *fid = (struct CodaFid *)data;
+ return coda_fideq(&(ITOC(inode)->c_fid), fid);
+}
+
+static int coda_set_inode(struct inode *inode, void *data)
+{
+ struct CodaFid *fid = (struct CodaFid *)data;
+ ITOC(inode)->c_fid = *fid;
+ return 0;
+}
+
+static int coda_fail_inode(struct inode *inode, void *data)
+{
+ return -1;
+}
+
+struct inode * coda_iget(struct super_block * sb, struct CodaFid * fid,
+ struct coda_vattr * attr)
+{
+ struct inode *inode;
+ struct coda_inode_info *cii;
+ unsigned long hash = coda_f2i(fid);
+
+ inode = iget5_locked(sb, hash, coda_test_inode, coda_set_inode, fid);
+
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+
+ if (inode->i_state & I_NEW) {
+ cii = ITOC(inode);
+ /* we still need to set i_ino for things like stat(2) */
+ inode->i_ino = hash;
+ cii->c_mapcount = 0;
+ unlock_new_inode(inode);
+ }
+
+ /* always replace the attributes, type might have changed */
+ coda_fill_inode(inode, attr);
+ return inode;
+}
+
+/* this is effectively coda_iget:
+ - get attributes (might be cached)
+ - get the inode for the fid using vfs iget
+ - link the two up if this is needed
+ - fill in the attributes
+*/
+int coda_cnode_make(struct inode **inode, struct CodaFid *fid, struct super_block *sb)
+{
+ struct coda_vattr attr;
+ int error;
+
+ /* We get inode numbers from Venus -- see venus source */
+ error = venus_getattr(sb, fid, &attr);
+ if ( error ) {
+ *inode = NULL;
+ return error;
+ }
+
+ *inode = coda_iget(sb, fid, &attr);
+ if ( IS_ERR(*inode) ) {
+ printk("coda_cnode_make: coda_iget failed\n");
+ return PTR_ERR(*inode);
+ }
+ return 0;
+}
+
+
+void coda_replace_fid(struct inode *inode, struct CodaFid *oldfid,
+ struct CodaFid *newfid)
+{
+ struct coda_inode_info *cii;
+ unsigned long hash = coda_f2i(newfid);
+
+ cii = ITOC(inode);
+
+ if (!coda_fideq(&cii->c_fid, oldfid))
+ BUG();
+
+ /* replace fid and rehash inode */
+ /* XXX we probably need to hold some lock here! */
+ remove_inode_hash(inode);
+ cii->c_fid = *newfid;
+ inode->i_ino = hash;
+ __insert_inode_hash(inode, hash);
+}
+
+/* convert a fid to an inode. */
+struct inode *coda_fid_to_inode(struct CodaFid *fid, struct super_block *sb)
+{
+ struct inode *inode;
+ unsigned long hash = coda_f2i(fid);
+
+ if ( !sb ) {
+ printk("coda_fid_to_inode: no sb!\n");
+ return NULL;
+ }
+
+ inode = iget5_locked(sb, hash, coda_test_inode, coda_fail_inode, fid);
+ if ( !inode )
+ return NULL;
+
+ /* we should never see newly created inodes because we intentionally
+ * fail in the initialization callback */
+ BUG_ON(inode->i_state & I_NEW);
+
+ return inode;
+}
+
+/* the CONTROL inode is made without asking attributes from Venus */
+int coda_cnode_makectl(struct inode **inode, struct super_block *sb)
+{
+ int error = -ENOMEM;
+
+ *inode = new_inode(sb);
+ if (*inode) {
+ (*inode)->i_ino = CTL_INO;
+ (*inode)->i_op = &coda_ioctl_inode_operations;
+ (*inode)->i_fop = &coda_ioctl_operations;
+ (*inode)->i_mode = 0444;
+ error = 0;
+ }
+
+ return error;
+}
+
diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c
new file mode 100644
index 000000000000..5597080cb811
--- /dev/null
+++ b/fs/coda/coda_linux.c
@@ -0,0 +1,197 @@
+/*
+ * Inode operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_fs_i.h>
+
+/* initialize the debugging variables */
+int coda_fake_statfs;
+
+/* print a fid */
+char * coda_f2s(struct CodaFid *f)
+{
+ static char s[60];
+#ifdef CONFIG_CODA_FS_OLD_API
+ sprintf(s, "(%08x.%08x.%08x)", f->opaque[0], f->opaque[1], f->opaque[2]);
+#else
+ sprintf(s, "(%08x.%08x.%08x.%08x)", f->opaque[0], f->opaque[1], f->opaque[2], f->opaque[3]);
+#endif
+ return s;
+}
+
+/* recognize special .CONTROL name */
+int coda_iscontrol(const char *name, size_t length)
+{
+ return ((CODA_CONTROLLEN == length) &&
+ (strncmp(name, CODA_CONTROL, CODA_CONTROLLEN) == 0));
+}
+
+/* recognize /coda inode */
+int coda_isroot(struct inode *i)
+{
+ return ( i->i_sb->s_root->d_inode == i );
+}
+
+unsigned short coda_flags_to_cflags(unsigned short flags)
+{
+ unsigned short coda_flags = 0;
+
+ if ((flags & O_ACCMODE) == O_RDONLY)
+ coda_flags |= C_O_READ;
+
+ if ((flags & O_ACCMODE) == O_RDWR)
+ coda_flags |= C_O_READ | C_O_WRITE;
+
+ if ((flags & O_ACCMODE) == O_WRONLY)
+ coda_flags |= C_O_WRITE;
+
+ if (flags & O_TRUNC)
+ coda_flags |= C_O_TRUNC;
+
+ if (flags & O_CREAT)
+ coda_flags |= C_O_CREAT;
+
+ if (flags & O_EXCL)
+ coda_flags |= C_O_EXCL;
+
+ return coda_flags;
+}
+
+
+/* utility functions below */
+void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr)
+{
+ int inode_type;
+ /* inode's i_flags, i_ino are set by iget
+ XXX: is this all we need ??
+ */
+ switch (attr->va_type) {
+ case C_VNON:
+ inode_type = 0;
+ break;
+ case C_VREG:
+ inode_type = S_IFREG;
+ break;
+ case C_VDIR:
+ inode_type = S_IFDIR;
+ break;
+ case C_VLNK:
+ inode_type = S_IFLNK;
+ break;
+ default:
+ inode_type = 0;
+ }
+ inode->i_mode |= inode_type;
+
+ if (attr->va_mode != (u_short) -1)
+ inode->i_mode = attr->va_mode | inode_type;
+ if (attr->va_uid != -1)
+ inode->i_uid = (uid_t) attr->va_uid;
+ if (attr->va_gid != -1)
+ inode->i_gid = (gid_t) attr->va_gid;
+ if (attr->va_nlink != -1)
+ inode->i_nlink = attr->va_nlink;
+ if (attr->va_size != -1)
+ inode->i_size = attr->va_size;
+ if (attr->va_blocksize != -1)
+ inode->i_blksize = attr->va_blocksize;
+ if (attr->va_size != -1)
+ inode->i_blocks = (attr->va_size + 511) >> 9;
+ if (attr->va_atime.tv_sec != -1)
+ inode->i_atime = attr->va_atime;
+ if (attr->va_mtime.tv_sec != -1)
+ inode->i_mtime = attr->va_mtime;
+ if (attr->va_ctime.tv_sec != -1)
+ inode->i_ctime = attr->va_ctime;
+}
+
+
+/*
+ * BSD sets attributes that need not be modified to -1.
+ * Linux uses the valid field to indicate what should be
+ * looked at. The BSD type field needs to be deduced from linux
+ * mode.
+ * So we have to do some translations here.
+ */
+
+void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr)
+{
+ unsigned int valid;
+
+ /* clean out */
+ vattr->va_mode = (umode_t) -1;
+ vattr->va_uid = (vuid_t) -1;
+ vattr->va_gid = (vgid_t) -1;
+ vattr->va_size = (off_t) -1;
+ vattr->va_atime.tv_sec = (time_t) -1;
+ vattr->va_atime.tv_nsec = (time_t) -1;
+ vattr->va_mtime.tv_sec = (time_t) -1;
+ vattr->va_mtime.tv_nsec = (time_t) -1;
+ vattr->va_ctime.tv_sec = (time_t) -1;
+ vattr->va_ctime.tv_nsec = (time_t) -1;
+ vattr->va_type = C_VNON;
+ vattr->va_fileid = -1;
+ vattr->va_gen = -1;
+ vattr->va_bytes = -1;
+ vattr->va_nlink = -1;
+ vattr->va_blocksize = -1;
+ vattr->va_rdev = -1;
+ vattr->va_flags = 0;
+
+ /* determine the type */
+#if 0
+ mode = iattr->ia_mode;
+ if ( S_ISDIR(mode) ) {
+ vattr->va_type = C_VDIR;
+ } else if ( S_ISREG(mode) ) {
+ vattr->va_type = C_VREG;
+ } else if ( S_ISLNK(mode) ) {
+ vattr->va_type = C_VLNK;
+ } else {
+ /* don't do others */
+ vattr->va_type = C_VNON;
+ }
+#endif
+
+ /* set those vattrs that need change */
+ valid = iattr->ia_valid;
+ if ( valid & ATTR_MODE ) {
+ vattr->va_mode = iattr->ia_mode;
+ }
+ if ( valid & ATTR_UID ) {
+ vattr->va_uid = (vuid_t) iattr->ia_uid;
+ }
+ if ( valid & ATTR_GID ) {
+ vattr->va_gid = (vgid_t) iattr->ia_gid;
+ }
+ if ( valid & ATTR_SIZE ) {
+ vattr->va_size = iattr->ia_size;
+ }
+ if ( valid & ATTR_ATIME ) {
+ vattr->va_atime = iattr->ia_atime;
+ }
+ if ( valid & ATTR_MTIME ) {
+ vattr->va_mtime = iattr->ia_mtime;
+ }
+ if ( valid & ATTR_CTIME ) {
+ vattr->va_ctime = iattr->ia_ctime;
+ }
+}
+
diff --git a/fs/coda/dir.c b/fs/coda/dir.c
new file mode 100644
index 000000000000..2391766e9c7c
--- /dev/null
+++ b/fs/coda/dir.c
@@ -0,0 +1,704 @@
+
+/*
+ * Directory operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
+#include <linux/coda_proc.h>
+
+/* dir inode-ops */
+static int coda_create(struct inode *dir, struct dentry *new, int mode, struct nameidata *nd);
+static struct dentry *coda_lookup(struct inode *dir, struct dentry *target, struct nameidata *nd);
+static int coda_link(struct dentry *old_dentry, struct inode *dir_inode,
+ struct dentry *entry);
+static int coda_unlink(struct inode *dir_inode, struct dentry *entry);
+static int coda_symlink(struct inode *dir_inode, struct dentry *entry,
+ const char *symname);
+static int coda_mkdir(struct inode *dir_inode, struct dentry *entry, int mode);
+static int coda_rmdir(struct inode *dir_inode, struct dentry *entry);
+static int coda_rename(struct inode *old_inode, struct dentry *old_dentry,
+ struct inode *new_inode, struct dentry *new_dentry);
+
+/* dir file-ops */
+static int coda_readdir(struct file *file, void *dirent, filldir_t filldir);
+
+/* dentry ops */
+static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd);
+static int coda_dentry_delete(struct dentry *);
+
+/* support routines */
+static int coda_venus_readdir(struct file *filp, filldir_t filldir,
+ void *dirent, struct dentry *dir);
+int coda_fsync(struct file *, struct dentry *dentry, int datasync);
+
+/* same as fs/bad_inode.c */
+static int coda_return_EIO(void)
+{
+ return -EIO;
+}
+#define CODA_EIO_ERROR ((void *) (coda_return_EIO))
+
+static struct dentry_operations coda_dentry_operations =
+{
+ .d_revalidate = coda_dentry_revalidate,
+ .d_delete = coda_dentry_delete,
+};
+
+struct inode_operations coda_dir_inode_operations =
+{
+ .create = coda_create,
+ .lookup = coda_lookup,
+ .link = coda_link,
+ .unlink = coda_unlink,
+ .symlink = coda_symlink,
+ .mkdir = coda_mkdir,
+ .rmdir = coda_rmdir,
+ .mknod = CODA_EIO_ERROR,
+ .rename = coda_rename,
+ .permission = coda_permission,
+ .getattr = coda_getattr,
+ .setattr = coda_setattr,
+};
+
+struct file_operations coda_dir_operations = {
+ .llseek = generic_file_llseek,
+ .read = generic_read_dir,
+ .readdir = coda_readdir,
+ .open = coda_open,
+ .flush = coda_flush,
+ .release = coda_release,
+ .fsync = coda_fsync,
+};
+
+
+/* inode operations for directories */
+/* access routines: lookup, readlink, permission */
+static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, struct nameidata *nd)
+{
+ struct inode *res_inode = NULL;
+ struct CodaFid resfid = { { 0, } };
+ int dropme = 0; /* to indicate entry should not be cached */
+ int type = 0;
+ int error = 0;
+ const char *name = entry->d_name.name;
+ size_t length = entry->d_name.len;
+
+ if ( length > CODA_MAXNAMLEN ) {
+ printk("name too long: lookup, %s (%*s)\n",
+ coda_i2s(dir), (int)length, name);
+ return ERR_PTR(-ENAMETOOLONG);
+ }
+
+ lock_kernel();
+ /* control object, create inode on the fly */
+ if (coda_isroot(dir) && coda_iscontrol(name, length)) {
+ error = coda_cnode_makectl(&res_inode, dir->i_sb);
+ dropme = 1;
+ goto exit;
+ }
+
+ error = venus_lookup(dir->i_sb, coda_i2f(dir),
+ (const char *)name, length, &type, &resfid);
+
+ res_inode = NULL;
+ if (!error) {
+ if (type & CODA_NOCACHE) {
+ type &= (~CODA_NOCACHE);
+ dropme = 1;
+ }
+
+ error = coda_cnode_make(&res_inode, &resfid, dir->i_sb);
+ if (error) {
+ unlock_kernel();
+ return ERR_PTR(error);
+ }
+ } else if (error != -ENOENT) {
+ unlock_kernel();
+ return ERR_PTR(error);
+ }
+
+exit:
+ entry->d_time = 0;
+ entry->d_op = &coda_dentry_operations;
+ d_add(entry, res_inode);
+ if ( dropme ) {
+ d_drop(entry);
+ coda_flag_inode(res_inode, C_VATTR);
+ }
+ unlock_kernel();
+ return NULL;
+}
+
+
+int coda_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+ int error = 0;
+
+ if (!mask)
+ return 0;
+
+ lock_kernel();
+
+ coda_vfs_stat.permission++;
+
+ if (coda_cache_check(inode, mask))
+ goto out;
+
+ error = venus_access(inode->i_sb, coda_i2f(inode), mask);
+
+ if (!error)
+ coda_cache_enter(inode, mask);
+
+ out:
+ unlock_kernel();
+
+ return error;
+}
+
+
+static inline void coda_dir_changed(struct inode *dir, int link)
+{
+#ifdef REQUERY_VENUS_FOR_MTIME
+ /* invalidate the directory cnode's attributes so we refetch the
+ * attributes from venus next time the inode is referenced */
+ coda_flag_inode(dir, C_VATTR);
+#else
+ /* optimistically we can also act as if our nose bleeds. The
+ * granularity of the mtime is coarse anyways so we might actually be
+ * right most of the time. Note: we only do this for directories. */
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
+#endif
+ if (link)
+ dir->i_nlink += link;
+}
+
+/* creation routines: create, mknod, mkdir, link, symlink */
+static int coda_create(struct inode *dir, struct dentry *de, int mode, struct nameidata *nd)
+{
+ int error=0;
+ const char *name=de->d_name.name;
+ int length=de->d_name.len;
+ struct inode *inode;
+ struct CodaFid newfid;
+ struct coda_vattr attrs;
+
+ lock_kernel();
+ coda_vfs_stat.create++;
+
+ if (coda_isroot(dir) && coda_iscontrol(name, length)) {
+ unlock_kernel();
+ return -EPERM;
+ }
+
+ error = venus_create(dir->i_sb, coda_i2f(dir), name, length,
+ 0, mode, &newfid, &attrs);
+
+ if ( error ) {
+ unlock_kernel();
+ d_drop(de);
+ return error;
+ }
+
+ inode = coda_iget(dir->i_sb, &newfid, &attrs);
+ if ( IS_ERR(inode) ) {
+ unlock_kernel();
+ d_drop(de);
+ return PTR_ERR(inode);
+ }
+
+ /* invalidate the directory cnode's attributes */
+ coda_dir_changed(dir, 0);
+ unlock_kernel();
+ d_instantiate(de, inode);
+ return 0;
+}
+
+static int coda_mkdir(struct inode *dir, struct dentry *de, int mode)
+{
+ struct inode *inode;
+ struct coda_vattr attrs;
+ const char *name = de->d_name.name;
+ int len = de->d_name.len;
+ int error;
+ struct CodaFid newfid;
+
+ lock_kernel();
+ coda_vfs_stat.mkdir++;
+
+ if (coda_isroot(dir) && coda_iscontrol(name, len)) {
+ unlock_kernel();
+ return -EPERM;
+ }
+
+ attrs.va_mode = mode;
+ error = venus_mkdir(dir->i_sb, coda_i2f(dir),
+ name, len, &newfid, &attrs);
+
+ if ( error ) {
+ unlock_kernel();
+ d_drop(de);
+ return error;
+ }
+
+ inode = coda_iget(dir->i_sb, &newfid, &attrs);
+ if ( IS_ERR(inode) ) {
+ unlock_kernel();
+ d_drop(de);
+ return PTR_ERR(inode);
+ }
+
+ /* invalidate the directory cnode's attributes */
+ coda_dir_changed(dir, 1);
+ unlock_kernel();
+ d_instantiate(de, inode);
+ return 0;
+}
+
+/* try to make de an entry in dir_inodde linked to source_de */
+static int coda_link(struct dentry *source_de, struct inode *dir_inode,
+ struct dentry *de)
+{
+ struct inode *inode = source_de->d_inode;
+ const char * name = de->d_name.name;
+ int len = de->d_name.len;
+ int error;
+
+ lock_kernel();
+ coda_vfs_stat.link++;
+
+ if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) {
+ unlock_kernel();
+ return -EPERM;
+ }
+
+ error = venus_link(dir_inode->i_sb, coda_i2f(inode),
+ coda_i2f(dir_inode), (const char *)name, len);
+
+ if (error) {
+ d_drop(de);
+ goto out;
+ }
+
+ coda_dir_changed(dir_inode, 0);
+ atomic_inc(&inode->i_count);
+ d_instantiate(de, inode);
+ inode->i_nlink++;
+
+out:
+ unlock_kernel();
+ return(error);
+}
+
+
+static int coda_symlink(struct inode *dir_inode, struct dentry *de,
+ const char *symname)
+{
+ const char *name = de->d_name.name;
+ int len = de->d_name.len;
+ int symlen;
+ int error=0;
+
+ lock_kernel();
+ coda_vfs_stat.symlink++;
+
+ if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) {
+ unlock_kernel();
+ return -EPERM;
+ }
+
+ symlen = strlen(symname);
+ if ( symlen > CODA_MAXPATHLEN ) {
+ unlock_kernel();
+ return -ENAMETOOLONG;
+ }
+
+ /*
+ * This entry is now negative. Since we do not create
+ * an inode for the entry we have to drop it.
+ */
+ d_drop(de);
+ error = venus_symlink(dir_inode->i_sb, coda_i2f(dir_inode), name, len,
+ symname, symlen);
+
+ /* mtime is no good anymore */
+ if ( !error )
+ coda_dir_changed(dir_inode, 0);
+
+ unlock_kernel();
+ return error;
+}
+
+/* destruction routines: unlink, rmdir */
+int coda_unlink(struct inode *dir, struct dentry *de)
+{
+ int error;
+ const char *name = de->d_name.name;
+ int len = de->d_name.len;
+
+ lock_kernel();
+ coda_vfs_stat.unlink++;
+
+ error = venus_remove(dir->i_sb, coda_i2f(dir), name, len);
+ if ( error ) {
+ unlock_kernel();
+ return error;
+ }
+
+ coda_dir_changed(dir, 0);
+ de->d_inode->i_nlink--;
+ unlock_kernel();
+
+ return 0;
+}
+
+int coda_rmdir(struct inode *dir, struct dentry *de)
+{
+ const char *name = de->d_name.name;
+ int len = de->d_name.len;
+ int error;
+
+ lock_kernel();
+ coda_vfs_stat.rmdir++;
+
+ if (!d_unhashed(de)) {
+ unlock_kernel();
+ return -EBUSY;
+ }
+ error = venus_rmdir(dir->i_sb, coda_i2f(dir), name, len);
+
+ if ( error ) {
+ unlock_kernel();
+ return error;
+ }
+
+ coda_dir_changed(dir, -1);
+ de->d_inode->i_nlink--;
+ d_delete(de);
+ unlock_kernel();
+
+ return 0;
+}
+
+/* rename */
+static int coda_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ const char *old_name = old_dentry->d_name.name;
+ const char *new_name = new_dentry->d_name.name;
+ int old_length = old_dentry->d_name.len;
+ int new_length = new_dentry->d_name.len;
+ int link_adjust = 0;
+ int error;
+
+ lock_kernel();
+ coda_vfs_stat.rename++;
+
+ error = venus_rename(old_dir->i_sb, coda_i2f(old_dir),
+ coda_i2f(new_dir), old_length, new_length,
+ (const char *) old_name, (const char *)new_name);
+
+ if ( !error ) {
+ if ( new_dentry->d_inode ) {
+ if ( S_ISDIR(new_dentry->d_inode->i_mode) )
+ link_adjust = 1;
+
+ coda_dir_changed(old_dir, -link_adjust);
+ coda_dir_changed(new_dir, link_adjust);
+ coda_flag_inode(new_dentry->d_inode, C_VATTR);
+ } else {
+ coda_flag_inode(old_dir, C_VATTR);
+ coda_flag_inode(new_dir, C_VATTR);
+ }
+ }
+ unlock_kernel();
+
+ return error;
+}
+
+
+/* file operations for directories */
+int coda_readdir(struct file *coda_file, void *dirent, filldir_t filldir)
+{
+ struct dentry *coda_dentry = coda_file->f_dentry;
+ struct coda_file_info *cfi;
+ struct file *host_file;
+ struct inode *host_inode;
+ int ret;
+
+ cfi = CODA_FTOC(coda_file);
+ BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+ host_file = cfi->cfi_container;
+
+ coda_vfs_stat.readdir++;
+
+ host_inode = host_file->f_dentry->d_inode;
+ down(&host_inode->i_sem);
+ host_file->f_pos = coda_file->f_pos;
+
+ if (!host_file->f_op->readdir) {
+ /* Venus: we must read Venus dirents from the file */
+ ret = coda_venus_readdir(host_file, filldir, dirent, coda_dentry);
+ } else {
+ /* potemkin case: we were handed a directory inode. */
+ /* Yuk, we can't call vfs_readdir because we are already
+ * holding the inode semaphore. */
+ ret = -ENOTDIR;
+ if (!host_file->f_op || !host_file->f_op->readdir)
+ goto out;
+
+ ret = -ENOENT;
+ if (!IS_DEADDIR(host_inode)) {
+ ret = host_file->f_op->readdir(host_file, filldir, dirent);
+ file_accessed(host_file);
+ }
+ }
+out:
+ coda_file->f_pos = host_file->f_pos;
+ up(&host_inode->i_sem);
+
+ return ret;
+}
+
+static inline unsigned int CDT2DT(unsigned char cdt)
+{
+ unsigned int dt;
+
+ switch(cdt) {
+ case CDT_UNKNOWN: dt = DT_UNKNOWN; break;
+ case CDT_FIFO: dt = DT_FIFO; break;
+ case CDT_CHR: dt = DT_CHR; break;
+ case CDT_DIR: dt = DT_DIR; break;
+ case CDT_BLK: dt = DT_BLK; break;
+ case CDT_REG: dt = DT_REG; break;
+ case CDT_LNK: dt = DT_LNK; break;
+ case CDT_SOCK: dt = DT_SOCK; break;
+ case CDT_WHT: dt = DT_WHT; break;
+ default: dt = DT_UNKNOWN; break;
+ }
+ return dt;
+}
+
+/* support routines */
+static int coda_venus_readdir(struct file *filp, filldir_t filldir,
+ void *dirent, struct dentry *dir)
+{
+ int result = 0; /* # of entries returned */
+ struct venus_dirent *vdir;
+ unsigned long vdir_size =
+ (unsigned long)(&((struct venus_dirent *)0)->d_name);
+ unsigned int type;
+ struct qstr name;
+ ino_t ino;
+ int ret, i;
+
+ vdir = (struct venus_dirent *)kmalloc(sizeof(*vdir), GFP_KERNEL);
+ if (!vdir) return -ENOMEM;
+
+ i = filp->f_pos;
+ switch(i) {
+ case 0:
+ ret = filldir(dirent, ".", 1, 0, dir->d_inode->i_ino, DT_DIR);
+ if (ret < 0) break;
+ result++;
+ filp->f_pos++;
+ /* fallthrough */
+ case 1:
+ ret = filldir(dirent, "..", 2, 1, dir->d_parent->d_inode->i_ino, DT_DIR);
+ if (ret < 0) break;
+ result++;
+ filp->f_pos++;
+ /* fallthrough */
+ default:
+ while (1) {
+ /* read entries from the directory file */
+ ret = kernel_read(filp, filp->f_pos - 2, (char *)vdir,
+ sizeof(*vdir));
+ if (ret < 0) {
+ printk("coda_venus_readdir: read dir failed %d\n", ret);
+ break;
+ }
+ if (ret == 0) break; /* end of directory file reached */
+
+ /* catch truncated reads */
+ if (ret < vdir_size || ret < vdir_size + vdir->d_namlen) {
+ printk("coda_venus_readdir: short read: %ld\n",
+ filp->f_dentry->d_inode->i_ino);
+ ret = -EBADF;
+ break;
+ }
+ /* validate whether the directory file actually makes sense */
+ if (vdir->d_reclen < vdir_size + vdir->d_namlen) {
+ printk("coda_venus_readdir: Invalid dir: %ld\n",
+ filp->f_dentry->d_inode->i_ino);
+ ret = -EBADF;
+ break;
+ }
+
+ name.len = vdir->d_namlen;
+ name.name = vdir->d_name;
+
+ /* Make sure we skip '.' and '..', we already got those */
+ if (name.name[0] == '.' && (name.len == 1 ||
+ (vdir->d_name[1] == '.' && name.len == 2)))
+ vdir->d_fileno = name.len = 0;
+
+ /* skip null entries */
+ if (vdir->d_fileno && name.len) {
+ /* try to look up this entry in the dcache, that way
+ * userspace doesn't have to worry about breaking
+ * getcwd by having mismatched inode numbers for
+ * internal volume mountpoints. */
+ ino = find_inode_number(dir, &name);
+ if (!ino) ino = vdir->d_fileno;
+
+ type = CDT2DT(vdir->d_type);
+ ret = filldir(dirent, name.name, name.len, filp->f_pos,
+ ino, type);
+ /* failure means no space for filling in this round */
+ if (ret < 0) break;
+ result++;
+ }
+ /* we'll always have progress because d_reclen is unsigned and
+ * we've already established it is non-zero. */
+ filp->f_pos += vdir->d_reclen;
+ }
+ }
+ kfree(vdir);
+ return result ? result : ret;
+}
+
+/* called when a cache lookup succeeds */
+static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd)
+{
+ struct inode *inode = de->d_inode;
+ struct coda_inode_info *cii;
+
+ if (!inode)
+ return 1;
+ lock_kernel();
+ if (coda_isroot(inode))
+ goto out;
+ if (is_bad_inode(inode))
+ goto bad;
+
+ cii = ITOC(de->d_inode);
+ if (!(cii->c_flags & (C_PURGE | C_FLUSH)))
+ goto out;
+
+ shrink_dcache_parent(de);
+
+ /* propagate for a flush */
+ if (cii->c_flags & C_FLUSH)
+ coda_flag_inode_children(inode, C_FLUSH);
+
+ if (atomic_read(&de->d_count) > 1)
+ /* pretend it's valid, but don't change the flags */
+ goto out;
+
+ /* clear the flags. */
+ cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH);
+
+bad:
+ unlock_kernel();
+ return 0;
+out:
+ unlock_kernel();
+ return 1;
+}
+
+/*
+ * This is the callback from dput() when d_count is going to 0.
+ * We use this to unhash dentries with bad inodes.
+ */
+static int coda_dentry_delete(struct dentry * dentry)
+{
+ int flags;
+
+ if (!dentry->d_inode)
+ return 0;
+
+ flags = (ITOC(dentry->d_inode)->c_flags) & C_PURGE;
+ if (is_bad_inode(dentry->d_inode) || flags) {
+ return 1;
+ }
+ return 0;
+}
+
+
+
+/*
+ * This is called when we want to check if the inode has
+ * changed on the server. Coda makes this easy since the
+ * cache manager Venus issues a downcall to the kernel when this
+ * happens
+ */
+int coda_revalidate_inode(struct dentry *dentry)
+{
+ struct coda_vattr attr;
+ int error = 0;
+ int old_mode;
+ ino_t old_ino;
+ struct inode *inode = dentry->d_inode;
+ struct coda_inode_info *cii = ITOC(inode);
+
+ lock_kernel();
+ if ( !cii->c_flags )
+ goto ok;
+
+ if (cii->c_flags & (C_VATTR | C_PURGE | C_FLUSH)) {
+ error = venus_getattr(inode->i_sb, &(cii->c_fid), &attr);
+ if ( error )
+ goto return_bad;
+
+ /* this inode may be lost if:
+ - it's ino changed
+ - type changes must be permitted for repair and
+ missing mount points.
+ */
+ old_mode = inode->i_mode;
+ old_ino = inode->i_ino;
+ coda_vattr_to_iattr(inode, &attr);
+
+ if ((old_mode & S_IFMT) != (inode->i_mode & S_IFMT)) {
+ printk("Coda: inode %ld, fid %s changed type!\n",
+ inode->i_ino, coda_f2s(&(cii->c_fid)));
+ }
+
+ /* the following can happen when a local fid is replaced
+ with a global one, here we lose and declare the inode bad */
+ if (inode->i_ino != old_ino)
+ goto return_bad;
+
+ coda_flag_inode_children(inode, C_FLUSH);
+ cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH);
+ }
+
+ok:
+ unlock_kernel();
+ return 0;
+
+return_bad:
+ unlock_kernel();
+ return -EIO;
+}
diff --git a/fs/coda/file.c b/fs/coda/file.c
new file mode 100644
index 000000000000..e6bc022568f3
--- /dev/null
+++ b/fs/coda/file.c
@@ -0,0 +1,300 @@
+/*
+ * File operations for Coda.
+ * Original version: (C) 1996 Peter Braam
+ * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users of this code to contribute improvements
+ * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/smp_lock.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_proc.h>
+
+/* if CODA_STORE fails with EOPNOTSUPP, venus clearly doesn't support
+ * CODA_STORE/CODA_RELEASE and we fall back on using the CODA_CLOSE upcall */
+static int use_coda_close;
+
+static ssize_t
+coda_file_read(struct file *coda_file, char __user *buf, size_t count, loff_t *ppos)
+{
+ struct coda_file_info *cfi;
+ struct file *host_file;
+
+ cfi = CODA_FTOC(coda_file);
+ BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+ host_file = cfi->cfi_container;
+
+ if (!host_file->f_op || !host_file->f_op->read)
+ return -EINVAL;
+
+ return host_file->f_op->read(host_file, buf, count, ppos);
+}
+
+static ssize_t
+coda_file_sendfile(struct file *coda_file, loff_t *ppos, size_t count,
+ read_actor_t actor, void *target)
+{
+ struct coda_file_info *cfi;
+ struct file *host_file;
+
+ cfi = CODA_FTOC(coda_file);
+ BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+ host_file = cfi->cfi_container;
+
+ if (!host_file->f_op || !host_file->f_op->sendfile)
+ return -EINVAL;
+
+ return host_file->f_op->sendfile(host_file, ppos, count, actor, target);
+}
+
+static ssize_t
+coda_file_write(struct file *coda_file, const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct inode *host_inode, *coda_inode = coda_file->f_dentry->d_inode;
+ struct coda_file_info *cfi;
+ struct file *host_file;
+ ssize_t ret;
+
+ cfi = CODA_FTOC(coda_file);
+ BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+ host_file = cfi->cfi_container;
+
+ if (!host_file->f_op || !host_file->f_op->write)
+ return -EINVAL;
+
+ host_inode = host_file->f_dentry->d_inode;
+ down(&coda_inode->i_sem);
+
+ ret = host_file->f_op->write(host_file, buf, count, ppos);
+
+ coda_inode->i_size = host_inode->i_size;
+ coda_inode->i_blocks = (coda_inode->i_size + 511) >> 9;
+ coda_inode->i_mtime = coda_inode->i_ctime = CURRENT_TIME_SEC;
+ up(&coda_inode->i_sem);
+
+ return ret;
+}
+
+static int
+coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma)
+{
+ struct coda_file_info *cfi;
+ struct coda_inode_info *cii;
+ struct file *host_file;
+ struct inode *coda_inode, *host_inode;
+
+ cfi = CODA_FTOC(coda_file);
+ BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+ host_file = cfi->cfi_container;
+
+ if (!host_file->f_op || !host_file->f_op->mmap)
+ return -ENODEV;
+
+ coda_inode = coda_file->f_dentry->d_inode;
+ host_inode = host_file->f_dentry->d_inode;
+ coda_file->f_mapping = host_file->f_mapping;
+ if (coda_inode->i_mapping == &coda_inode->i_data)
+ coda_inode->i_mapping = host_inode->i_mapping;
+
+ /* only allow additional mmaps as long as userspace isn't changing
+ * the container file on us! */
+ else if (coda_inode->i_mapping != host_inode->i_mapping)
+ return -EBUSY;
+
+ /* keep track of how often the coda_inode/host_file has been mmapped */
+ cii = ITOC(coda_inode);
+ cii->c_mapcount++;
+ cfi->cfi_mapcount++;
+
+ return host_file->f_op->mmap(host_file, vma);
+}
+
+int coda_open(struct inode *coda_inode, struct file *coda_file)
+{
+ struct file *host_file = NULL;
+ int error;
+ unsigned short flags = coda_file->f_flags & (~O_EXCL);
+ unsigned short coda_flags = coda_flags_to_cflags(flags);
+ struct coda_file_info *cfi;
+
+ coda_vfs_stat.open++;
+
+ cfi = kmalloc(sizeof(struct coda_file_info), GFP_KERNEL);
+ if (!cfi) {
+ unlock_kernel();
+ return -ENOMEM;
+ }
+
+ lock_kernel();
+
+ error = venus_open(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags,
+ &host_file);
+ if (error || !host_file) {
+ kfree(cfi);
+ unlock_kernel();
+ return error;
+ }
+
+ host_file->f_flags |= coda_file->f_flags & (O_APPEND | O_SYNC);
+
+ cfi->cfi_magic = CODA_MAGIC;
+ cfi->cfi_mapcount = 0;
+ cfi->cfi_container = host_file;
+
+ BUG_ON(coda_file->private_data != NULL);
+ coda_file->private_data = cfi;
+
+ unlock_kernel();
+ return 0;
+}
+
+int coda_flush(struct file *coda_file)
+{
+ unsigned short flags = coda_file->f_flags & ~O_EXCL;
+ unsigned short coda_flags = coda_flags_to_cflags(flags);
+ struct coda_file_info *cfi;
+ struct inode *coda_inode;
+ int err = 0, fcnt;
+
+ lock_kernel();
+
+ coda_vfs_stat.flush++;
+
+ /* last close semantics */
+ fcnt = file_count(coda_file);
+ if (fcnt > 1)
+ goto out;
+
+ /* No need to make an upcall when we have not made any modifications
+ * to the file */
+ if ((coda_file->f_flags & O_ACCMODE) == O_RDONLY)
+ goto out;
+
+ if (use_coda_close)
+ goto out;
+
+ cfi = CODA_FTOC(coda_file);
+ BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+
+ coda_inode = coda_file->f_dentry->d_inode;
+
+ err = venus_store(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags,
+ coda_file->f_uid);
+
+ if (err == -EOPNOTSUPP) {
+ use_coda_close = 1;
+ err = 0;
+ }
+
+out:
+ unlock_kernel();
+ return err;
+}
+
+int coda_release(struct inode *coda_inode, struct file *coda_file)
+{
+ unsigned short flags = (coda_file->f_flags) & (~O_EXCL);
+ unsigned short coda_flags = coda_flags_to_cflags(flags);
+ struct coda_file_info *cfi;
+ struct coda_inode_info *cii;
+ struct inode *host_inode;
+ int err = 0;
+
+ lock_kernel();
+ coda_vfs_stat.release++;
+
+ if (!use_coda_close) {
+ err = venus_release(coda_inode->i_sb, coda_i2f(coda_inode),
+ coda_flags);
+ if (err == -EOPNOTSUPP) {
+ use_coda_close = 1;
+ err = 0;
+ }
+ }
+
+ cfi = CODA_FTOC(coda_file);
+ BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+
+ if (use_coda_close)
+ err = venus_close(coda_inode->i_sb, coda_i2f(coda_inode),
+ coda_flags, coda_file->f_uid);
+
+ host_inode = cfi->cfi_container->f_dentry->d_inode;
+ cii = ITOC(coda_inode);
+
+ /* did we mmap this file? */
+ if (coda_inode->i_mapping == &host_inode->i_data) {
+ cii->c_mapcount -= cfi->cfi_mapcount;
+ if (!cii->c_mapcount)
+ coda_inode->i_mapping = &coda_inode->i_data;
+ }
+
+ fput(cfi->cfi_container);
+ kfree(coda_file->private_data);
+ coda_file->private_data = NULL;
+
+ unlock_kernel();
+ return err;
+}
+
+int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync)
+{
+ struct file *host_file;
+ struct dentry *host_dentry;
+ struct inode *host_inode, *coda_inode = coda_dentry->d_inode;
+ struct coda_file_info *cfi;
+ int err = 0;
+
+ if (!(S_ISREG(coda_inode->i_mode) || S_ISDIR(coda_inode->i_mode) ||
+ S_ISLNK(coda_inode->i_mode)))
+ return -EINVAL;
+
+ cfi = CODA_FTOC(coda_file);
+ BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+ host_file = cfi->cfi_container;
+
+ coda_vfs_stat.fsync++;
+
+ if (host_file->f_op && host_file->f_op->fsync) {
+ host_dentry = host_file->f_dentry;
+ host_inode = host_dentry->d_inode;
+ down(&host_inode->i_sem);
+ err = host_file->f_op->fsync(host_file, host_dentry, datasync);
+ up(&host_inode->i_sem);
+ }
+
+ if ( !err && !datasync ) {
+ lock_kernel();
+ err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode));
+ unlock_kernel();
+ }
+
+ return err;
+}
+
+struct file_operations coda_file_operations = {
+ .llseek = generic_file_llseek,
+ .read = coda_file_read,
+ .write = coda_file_write,
+ .mmap = coda_file_mmap,
+ .open = coda_open,
+ .flush = coda_flush,
+ .release = coda_release,
+ .fsync = coda_fsync,
+ .sendfile = coda_file_sendfile,
+};
+
diff --git a/fs/coda/inode.c b/fs/coda/inode.c
new file mode 100644
index 000000000000..04a73fb4848f
--- /dev/null
+++ b/fs/coda/inode.c
@@ -0,0 +1,321 @@
+/*
+ * Super block/filesystem wide operations
+ *
+ * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk> and
+ * Michael Callahan <callahan@maths.ox.ac.uk>
+ *
+ * Rewritten for Linux 2.1. Peter Braam <braam@cs.cmu.edu>
+ * Copyright (C) Carnegie Mellon University
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/smp_lock.h>
+#include <linux/file.h>
+#include <linux/vfs.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
+
+/* VFS super_block ops */
+static void coda_clear_inode(struct inode *);
+static void coda_put_super(struct super_block *);
+static int coda_statfs(struct super_block *sb, struct kstatfs *buf);
+
+static kmem_cache_t * coda_inode_cachep;
+
+static struct inode *coda_alloc_inode(struct super_block *sb)
+{
+ struct coda_inode_info *ei;
+ ei = (struct coda_inode_info *)kmem_cache_alloc(coda_inode_cachep, SLAB_KERNEL);
+ if (!ei)
+ return NULL;
+ memset(&ei->c_fid, 0, sizeof(struct CodaFid));
+ ei->c_flags = 0;
+ ei->c_uid = 0;
+ ei->c_cached_perm = 0;
+ return &ei->vfs_inode;
+}
+
+static void coda_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(coda_inode_cachep, ITOC(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct coda_inode_info *ei = (struct coda_inode_info *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR)
+ inode_init_once(&ei->vfs_inode);
+}
+
+int coda_init_inodecache(void)
+{
+ coda_inode_cachep = kmem_cache_create("coda_inode_cache",
+ sizeof(struct coda_inode_info),
+ 0, SLAB_RECLAIM_ACCOUNT,
+ init_once, NULL);
+ if (coda_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+void coda_destroy_inodecache(void)
+{
+ if (kmem_cache_destroy(coda_inode_cachep))
+ printk(KERN_INFO "coda_inode_cache: not all structures were freed\n");
+}
+
+static int coda_remount(struct super_block *sb, int *flags, char *data)
+{
+ *flags |= MS_NODIRATIME;
+ return 0;
+}
+
+/* exported operations */
+static struct super_operations coda_super_operations =
+{
+ .alloc_inode = coda_alloc_inode,
+ .destroy_inode = coda_destroy_inode,
+ .clear_inode = coda_clear_inode,
+ .put_super = coda_put_super,
+ .statfs = coda_statfs,
+ .remount_fs = coda_remount,
+};
+
+static int get_device_index(struct coda_mount_data *data)
+{
+ struct file *file;
+ struct inode *inode;
+ int idx;
+
+ if(data == NULL) {
+ printk("coda_read_super: Bad mount data\n");
+ return -1;
+ }
+
+ if(data->version != CODA_MOUNT_VERSION) {
+ printk("coda_read_super: Bad mount version\n");
+ return -1;
+ }
+
+ file = fget(data->fd);
+ inode = NULL;
+ if(file)
+ inode = file->f_dentry->d_inode;
+
+ if(!inode || !S_ISCHR(inode->i_mode) ||
+ imajor(inode) != CODA_PSDEV_MAJOR) {
+ if(file)
+ fput(file);
+
+ printk("coda_read_super: Bad file\n");
+ return -1;
+ }
+
+ idx = iminor(inode);
+ fput(file);
+
+ if(idx < 0 || idx >= MAX_CODADEVS) {
+ printk("coda_read_super: Bad minor number\n");
+ return -1;
+ }
+
+ return idx;
+}
+
+static int coda_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct inode *root = NULL;
+ struct coda_sb_info *sbi = NULL;
+ struct venus_comm *vc = NULL;
+ struct CodaFid fid;
+ int error;
+ int idx;
+
+ idx = get_device_index((struct coda_mount_data *) data);
+
+ /* Ignore errors in data, for backward compatibility */
+ if(idx == -1)
+ idx = 0;
+
+ printk(KERN_INFO "coda_read_super: device index: %i\n", idx);
+
+ vc = &coda_comms[idx];
+ if (!vc->vc_inuse) {
+ printk("coda_read_super: No pseudo device\n");
+ return -EINVAL;
+ }
+
+ if ( vc->vc_sb ) {
+ printk("coda_read_super: Device already mounted\n");
+ return -EBUSY;
+ }
+
+ sbi = kmalloc(sizeof(struct coda_sb_info), GFP_KERNEL);
+ if(!sbi) {
+ return -ENOMEM;
+ }
+
+ vc->vc_sb = sb;
+
+ sbi->sbi_vcomm = vc;
+
+ sb->s_fs_info = sbi;
+ sb->s_flags |= MS_NODIRATIME; /* probably even noatime */
+ sb->s_blocksize = 1024; /* XXXXX what do we put here?? */
+ sb->s_blocksize_bits = 10;
+ sb->s_magic = CODA_SUPER_MAGIC;
+ sb->s_op = &coda_super_operations;
+
+ /* get root fid from Venus: this needs the root inode */
+ error = venus_rootfid(sb, &fid);
+ if ( error ) {
+ printk("coda_read_super: coda_get_rootfid failed with %d\n",
+ error);
+ goto error;
+ }
+ printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid));
+
+ /* make root inode */
+ error = coda_cnode_make(&root, &fid, sb);
+ if ( error || !root ) {
+ printk("Failure of coda_cnode_make for root: error %d\n", error);
+ goto error;
+ }
+
+ printk("coda_read_super: rootinode is %ld dev %s\n",
+ root->i_ino, root->i_sb->s_id);
+ sb->s_root = d_alloc_root(root);
+ if (!sb->s_root)
+ goto error;
+ return 0;
+
+ error:
+ if (sbi) {
+ kfree(sbi);
+ if(vc)
+ vc->vc_sb = NULL;
+ }
+ if (root)
+ iput(root);
+
+ return -EINVAL;
+}
+
+static void coda_put_super(struct super_block *sb)
+{
+ struct coda_sb_info *sbi;
+
+ sbi = coda_sbp(sb);
+ sbi->sbi_vcomm->vc_sb = NULL;
+
+ printk("Coda: Bye bye.\n");
+ kfree(sbi);
+}
+
+static void coda_clear_inode(struct inode *inode)
+{
+ coda_cache_clear_inode(inode);
+}
+
+int coda_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+{
+ int err = coda_revalidate_inode(dentry);
+ if (!err)
+ generic_fillattr(dentry->d_inode, stat);
+ return err;
+}
+
+int coda_setattr(struct dentry *de, struct iattr *iattr)
+{
+ struct inode *inode = de->d_inode;
+ struct coda_vattr vattr;
+ int error;
+
+ lock_kernel();
+
+ memset(&vattr, 0, sizeof(vattr));
+
+ inode->i_ctime = CURRENT_TIME_SEC;
+ coda_iattr_to_vattr(iattr, &vattr);
+ vattr.va_type = C_VNON; /* cannot set type */
+
+ /* Venus is responsible for truncating the container-file!!! */
+ error = venus_setattr(inode->i_sb, coda_i2f(inode), &vattr);
+
+ if ( !error ) {
+ coda_vattr_to_iattr(inode, &vattr);
+ coda_cache_clear_inode(inode);
+ }
+
+ unlock_kernel();
+
+ return error;
+}
+
+struct inode_operations coda_file_inode_operations = {
+ .permission = coda_permission,
+ .getattr = coda_getattr,
+ .setattr = coda_setattr,
+};
+
+static int coda_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+ int error;
+
+ lock_kernel();
+
+ error = venus_statfs(sb, buf);
+
+ unlock_kernel();
+
+ if (error) {
+ /* fake something like AFS does */
+ buf->f_blocks = 9000000;
+ buf->f_bfree = 9000000;
+ buf->f_bavail = 9000000;
+ buf->f_files = 9000000;
+ buf->f_ffree = 9000000;
+ }
+
+ /* and fill in the rest */
+ buf->f_type = CODA_SUPER_MAGIC;
+ buf->f_bsize = 1024;
+ buf->f_namelen = CODA_MAXNAMLEN;
+
+ return 0;
+}
+
+/* init_coda: used by filesystems.c to register coda */
+
+static struct super_block *coda_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data)
+{
+ return get_sb_nodev(fs_type, flags, data, coda_fill_super);
+}
+
+struct file_system_type coda_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "coda",
+ .get_sb = coda_get_sb,
+ .kill_sb = kill_anon_super,
+ .fs_flags = FS_BINARY_MOUNTDATA,
+};
+
diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c
new file mode 100644
index 000000000000..127714936c66
--- /dev/null
+++ b/fs/coda/pioctl.c
@@ -0,0 +1,95 @@
+/*
+ * Pioctl operations for Coda.
+ * Original version: (C) 1996 Peter Braam
+ * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users of this code to contribute improvements
+ * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/namei.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_psdev.h>
+
+/* pioctl ops */
+static int coda_ioctl_permission(struct inode *inode, int mask,
+ struct nameidata *nd);
+static int coda_pioctl(struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long user_data);
+
+/* exported from this file */
+struct inode_operations coda_ioctl_inode_operations =
+{
+ .permission = coda_ioctl_permission,
+ .setattr = coda_setattr,
+};
+
+struct file_operations coda_ioctl_operations = {
+ .owner = THIS_MODULE,
+ .ioctl = coda_pioctl,
+};
+
+/* the coda pioctl inode ops */
+static int coda_ioctl_permission(struct inode *inode, int mask,
+ struct nameidata *nd)
+{
+ return 0;
+}
+
+static int coda_pioctl(struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long user_data)
+{
+ struct nameidata nd;
+ int error;
+ struct PioctlData data;
+ struct inode *target_inode = NULL;
+ struct coda_inode_info *cnp;
+
+ /* get the Pioctl data arguments from user space */
+ if (copy_from_user(&data, (void __user *)user_data, sizeof(data))) {
+ return -EINVAL;
+ }
+
+ /*
+ * Look up the pathname. Note that the pathname is in
+ * user memory, and namei takes care of this
+ */
+ if ( data.follow ) {
+ error = user_path_walk(data.path, &nd);
+ } else {
+ error = user_path_walk_link(data.path, &nd);
+ }
+
+ if ( error ) {
+ return error;
+ } else {
+ target_inode = nd.dentry->d_inode;
+ }
+
+ /* return if it is not a Coda inode */
+ if ( target_inode->i_sb != inode->i_sb ) {
+ path_release(&nd);
+ return -EINVAL;
+ }
+
+ /* now proceed to make the upcall */
+ cnp = ITOC(target_inode);
+
+ error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data);
+
+ path_release(&nd);
+ return error;
+}
+
diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c
new file mode 100644
index 000000000000..ef001a9313e6
--- /dev/null
+++ b/fs/coda/psdev.c
@@ -0,0 +1,462 @@
+/*
+ * An implementation of a loadable kernel mode driver providing
+ * multiple kernel/user space bidirectional communications links.
+ *
+ * Author: Alan Cox <alan@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Adapted to become the Linux 2.0 Coda pseudo device
+ * Peter Braam <braam@maths.ox.ac.uk>
+ * Michael Callahan <mjc@emmy.smith.edu>
+ *
+ * Changes for Linux 2.1
+ * Copyright (c) 1997 Carnegie-Mellon University
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/time.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <linux/device.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/poll.h>
+#include <asm/uaccess.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_proc.h>
+
+#define upc_free(r) kfree(r)
+
+/*
+ * Coda stuff
+ */
+extern struct file_system_type coda_fs_type;
+
+/* statistics */
+int coda_hard; /* allows signals during upcalls */
+unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */
+
+
+struct venus_comm coda_comms[MAX_CODADEVS];
+static struct class_simple *coda_psdev_class;
+
+/*
+ * Device operations
+ */
+
+static unsigned int coda_psdev_poll(struct file *file, poll_table * wait)
+{
+ struct venus_comm *vcp = (struct venus_comm *) file->private_data;
+ unsigned int mask = POLLOUT | POLLWRNORM;
+
+ poll_wait(file, &vcp->vc_waitq, wait);
+ if (!list_empty(&vcp->vc_pending))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+static int coda_psdev_ioctl(struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned int data;
+
+ switch(cmd) {
+ case CIOC_KERNEL_VERSION:
+ data = CODA_KERNEL_VERSION;
+ return put_user(data, (int __user *) arg);
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+/*
+ * Receive a message written by Venus to the psdev
+ */
+
+static ssize_t coda_psdev_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *off)
+{
+ struct venus_comm *vcp = (struct venus_comm *) file->private_data;
+ struct upc_req *req = NULL;
+ struct upc_req *tmp;
+ struct list_head *lh;
+ struct coda_in_hdr hdr;
+ ssize_t retval = 0, count = 0;
+ int error;
+
+ /* Peek at the opcode, uniquefier */
+ if (copy_from_user(&hdr, buf, 2 * sizeof(u_long)))
+ return -EFAULT;
+
+ if (DOWNCALL(hdr.opcode)) {
+ struct super_block *sb = NULL;
+ union outputArgs *dcbuf;
+ int size = sizeof(*dcbuf);
+
+ sb = vcp->vc_sb;
+ if ( !sb ) {
+ count = nbytes;
+ goto out;
+ }
+
+ if ( nbytes < sizeof(struct coda_out_hdr) ) {
+ printk("coda_downcall opc %d uniq %d, not enough!\n",
+ hdr.opcode, hdr.unique);
+ count = nbytes;
+ goto out;
+ }
+ if ( nbytes > size ) {
+ printk("Coda: downcall opc %d, uniq %d, too much!",
+ hdr.opcode, hdr.unique);
+ nbytes = size;
+ }
+ CODA_ALLOC(dcbuf, union outputArgs *, nbytes);
+ if (copy_from_user(dcbuf, buf, nbytes)) {
+ CODA_FREE(dcbuf, nbytes);
+ retval = -EFAULT;
+ goto out;
+ }
+
+ /* what downcall errors does Venus handle ? */
+ lock_kernel();
+ error = coda_downcall(hdr.opcode, dcbuf, sb);
+ unlock_kernel();
+
+ CODA_FREE(dcbuf, nbytes);
+ if (error) {
+ printk("psdev_write: coda_downcall error: %d\n", error);
+ retval = error;
+ goto out;
+ }
+ count = nbytes;
+ goto out;
+ }
+
+ /* Look for the message on the processing queue. */
+ lock_kernel();
+ list_for_each(lh, &vcp->vc_processing) {
+ tmp = list_entry(lh, struct upc_req , uc_chain);
+ if (tmp->uc_unique == hdr.unique) {
+ req = tmp;
+ list_del(&req->uc_chain);
+ break;
+ }
+ }
+ unlock_kernel();
+
+ if (!req) {
+ printk("psdev_write: msg (%d, %d) not found\n",
+ hdr.opcode, hdr.unique);
+ retval = -ESRCH;
+ goto out;
+ }
+
+ /* move data into response buffer. */
+ if (req->uc_outSize < nbytes) {
+ printk("psdev_write: too much cnt: %d, cnt: %ld, opc: %d, uniq: %d.\n",
+ req->uc_outSize, (long)nbytes, hdr.opcode, hdr.unique);
+ nbytes = req->uc_outSize; /* don't have more space! */
+ }
+ if (copy_from_user(req->uc_data, buf, nbytes)) {
+ req->uc_flags |= REQ_ABORT;
+ wake_up(&req->uc_sleep);
+ retval = -EFAULT;
+ goto out;
+ }
+
+ /* adjust outsize. is this useful ?? */
+ req->uc_outSize = nbytes;
+ req->uc_flags |= REQ_WRITE;
+ count = nbytes;
+
+ /* Convert filedescriptor into a file handle */
+ if (req->uc_opcode == CODA_OPEN_BY_FD) {
+ struct coda_open_by_fd_out *outp =
+ (struct coda_open_by_fd_out *)req->uc_data;
+ outp->fh = fget(outp->fd);
+ }
+
+ wake_up(&req->uc_sleep);
+out:
+ return(count ? count : retval);
+}
+
+/*
+ * Read a message from the kernel to Venus
+ */
+
+static ssize_t coda_psdev_read(struct file * file, char __user * buf,
+ size_t nbytes, loff_t *off)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct venus_comm *vcp = (struct venus_comm *) file->private_data;
+ struct upc_req *req;
+ ssize_t retval = 0, count = 0;
+
+ if (nbytes == 0)
+ return 0;
+
+ lock_kernel();
+
+ add_wait_queue(&vcp->vc_waitq, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ while (list_empty(&vcp->vc_pending)) {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&vcp->vc_waitq, &wait);
+
+ if (retval)
+ goto out;
+
+ req = list_entry(vcp->vc_pending.next, struct upc_req,uc_chain);
+ list_del(&req->uc_chain);
+
+ /* Move the input args into userspace */
+ count = req->uc_inSize;
+ if (nbytes < req->uc_inSize) {
+ printk ("psdev_read: Venus read %ld bytes of %d in message\n",
+ (long)nbytes, req->uc_inSize);
+ count = nbytes;
+ }
+
+ if (copy_to_user(buf, req->uc_data, count))
+ retval = -EFAULT;
+
+ /* If request was not a signal, enqueue and don't free */
+ if (!(req->uc_flags & REQ_ASYNC)) {
+ req->uc_flags |= REQ_READ;
+ list_add(&(req->uc_chain), vcp->vc_processing.prev);
+ goto out;
+ }
+
+ CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
+ upc_free(req);
+out:
+ unlock_kernel();
+ return (count ? count : retval);
+}
+
+static int coda_psdev_open(struct inode * inode, struct file * file)
+{
+ struct venus_comm *vcp;
+ int idx;
+
+ lock_kernel();
+ idx = iminor(inode);
+ if(idx >= MAX_CODADEVS) {
+ unlock_kernel();
+ return -ENODEV;
+ }
+
+ vcp = &coda_comms[idx];
+ if(vcp->vc_inuse) {
+ unlock_kernel();
+ return -EBUSY;
+ }
+
+ if (!vcp->vc_inuse++) {
+ INIT_LIST_HEAD(&vcp->vc_pending);
+ INIT_LIST_HEAD(&vcp->vc_processing);
+ init_waitqueue_head(&vcp->vc_waitq);
+ vcp->vc_sb = NULL;
+ vcp->vc_seq = 0;
+ }
+
+ file->private_data = vcp;
+
+ unlock_kernel();
+ return 0;
+}
+
+
+static int coda_psdev_release(struct inode * inode, struct file * file)
+{
+ struct venus_comm *vcp = (struct venus_comm *) file->private_data;
+ struct upc_req *req, *tmp;
+
+ lock_kernel();
+ if ( !vcp->vc_inuse ) {
+ unlock_kernel();
+ printk("psdev_release: Not open.\n");
+ return -1;
+ }
+
+ if (--vcp->vc_inuse) {
+ unlock_kernel();
+ return 0;
+ }
+
+ /* Wakeup clients so they can return. */
+ list_for_each_entry_safe(req, tmp, &vcp->vc_pending, uc_chain) {
+ /* Async requests need to be freed here */
+ if (req->uc_flags & REQ_ASYNC) {
+ CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
+ upc_free(req);
+ continue;
+ }
+ req->uc_flags |= REQ_ABORT;
+ wake_up(&req->uc_sleep);
+ }
+
+ list_for_each_entry(req, &vcp->vc_processing, uc_chain) {
+ req->uc_flags |= REQ_ABORT;
+ wake_up(&req->uc_sleep);
+ }
+
+ unlock_kernel();
+ return 0;
+}
+
+
+static struct file_operations coda_psdev_fops = {
+ .owner = THIS_MODULE,
+ .read = coda_psdev_read,
+ .write = coda_psdev_write,
+ .poll = coda_psdev_poll,
+ .ioctl = coda_psdev_ioctl,
+ .open = coda_psdev_open,
+ .release = coda_psdev_release,
+};
+
+static int init_coda_psdev(void)
+{
+ int i, err = 0;
+ if (register_chrdev(CODA_PSDEV_MAJOR, "coda", &coda_psdev_fops)) {
+ printk(KERN_ERR "coda_psdev: unable to get major %d\n",
+ CODA_PSDEV_MAJOR);
+ return -EIO;
+ }
+ coda_psdev_class = class_simple_create(THIS_MODULE, "coda");
+ if (IS_ERR(coda_psdev_class)) {
+ err = PTR_ERR(coda_psdev_class);
+ goto out_chrdev;
+ }
+ devfs_mk_dir ("coda");
+ for (i = 0; i < MAX_CODADEVS; i++) {
+ class_simple_device_add(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR,i),
+ NULL, "cfs%d", i);
+ err = devfs_mk_cdev(MKDEV(CODA_PSDEV_MAJOR, i),
+ S_IFCHR|S_IRUSR|S_IWUSR, "coda/%d", i);
+ if (err)
+ goto out_class;
+ }
+ coda_sysctl_init();
+ goto out;
+
+out_class:
+ for (i = 0; i < MAX_CODADEVS; i++)
+ class_simple_device_remove(MKDEV(CODA_PSDEV_MAJOR, i));
+ class_simple_destroy(coda_psdev_class);
+out_chrdev:
+ unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
+out:
+ return err;
+}
+
+
+MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>");
+MODULE_LICENSE("GPL");
+
+extern int coda_init_inodecache(void);
+extern void coda_destroy_inodecache(void);
+static int __init init_coda(void)
+{
+ int status;
+ int i;
+ printk(KERN_INFO "Coda Kernel/Venus communications, "
+#ifdef CONFIG_CODA_FS_OLD_API
+ "v5.3.20"
+#else
+ "v6.0.0"
+#endif
+ ", coda@cs.cmu.edu\n");
+
+ status = coda_init_inodecache();
+ if (status)
+ goto out2;
+ status = init_coda_psdev();
+ if ( status ) {
+ printk("Problem (%d) in init_coda_psdev\n", status);
+ goto out1;
+ }
+
+ status = register_filesystem(&coda_fs_type);
+ if (status) {
+ printk("coda: failed to register filesystem!\n");
+ goto out;
+ }
+ return 0;
+out:
+ for (i = 0; i < MAX_CODADEVS; i++) {
+ class_simple_device_remove(MKDEV(CODA_PSDEV_MAJOR, i));
+ devfs_remove("coda/%d", i);
+ }
+ class_simple_destroy(coda_psdev_class);
+ devfs_remove("coda");
+ unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
+ coda_sysctl_clean();
+out1:
+ coda_destroy_inodecache();
+out2:
+ return status;
+}
+
+static void __exit exit_coda(void)
+{
+ int err, i;
+
+ err = unregister_filesystem(&coda_fs_type);
+ if ( err != 0 ) {
+ printk("coda: failed to unregister filesystem\n");
+ }
+ for (i = 0; i < MAX_CODADEVS; i++) {
+ class_simple_device_remove(MKDEV(CODA_PSDEV_MAJOR, i));
+ devfs_remove("coda/%d", i);
+ }
+ class_simple_destroy(coda_psdev_class);
+ devfs_remove("coda");
+ unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
+ coda_sysctl_clean();
+ coda_destroy_inodecache();
+}
+
+module_init(init_coda);
+module_exit(exit_coda);
+
diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c
new file mode 100644
index 000000000000..b35e5bbd9c99
--- /dev/null
+++ b/fs/coda/symlink.c
@@ -0,0 +1,55 @@
+/*
+ * Symlink inode operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/pagemap.h>
+#include <linux/smp_lock.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_proc.h>
+
+static int coda_symlink_filler(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ int error;
+ struct coda_inode_info *cii;
+ unsigned int len = PAGE_SIZE;
+ char *p = kmap(page);
+
+ lock_kernel();
+ cii = ITOC(inode);
+ coda_vfs_stat.follow_link++;
+
+ error = venus_readlink(inode->i_sb, &cii->c_fid, p, &len);
+ unlock_kernel();
+ if (error)
+ goto fail;
+ SetPageUptodate(page);
+ kunmap(page);
+ unlock_page(page);
+ return 0;
+
+fail:
+ SetPageError(page);
+ kunmap(page);
+ unlock_page(page);
+ return error;
+}
+
+struct address_space_operations coda_symlink_aops = {
+ .readpage = coda_symlink_filler,
+};
diff --git a/fs/coda/sysctl.c b/fs/coda/sysctl.c
new file mode 100644
index 000000000000..f0b10757288f
--- /dev/null
+++ b/fs/coda/sysctl.c
@@ -0,0 +1,254 @@
+/*
+ * Sysctl operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ *
+ * CODA operation statistics
+ * (c) March, 1998 Zhanyong Wan <zhanyong.wan@yale.edu>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/ctype.h>
+#include <linux/bitops.h>
+#include <asm/uaccess.h>
+#include <linux/utsname.h>
+#include <linux/module.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cache.h>
+#include <linux/coda_proc.h>
+
+static struct ctl_table_header *fs_table_header;
+
+#define FS_CODA 1 /* Coda file system */
+
+#define CODA_TIMEOUT 3 /* timeout on upcalls to become intrble */
+#define CODA_HARD 5 /* mount type "hard" or "soft" */
+#define CODA_VFS 6 /* vfs statistics */
+#define CODA_CACHE_INV 9 /* cache invalidation statistics */
+#define CODA_FAKE_STATFS 10 /* don't query venus for actual cache usage */
+
+struct coda_vfs_stats coda_vfs_stat;
+static struct coda_cache_inv_stats coda_cache_inv_stat;
+
+static void reset_coda_vfs_stats( void )
+{
+ memset( &coda_vfs_stat, 0, sizeof( coda_vfs_stat ) );
+}
+
+static void reset_coda_cache_inv_stats( void )
+{
+ memset( &coda_cache_inv_stat, 0, sizeof( coda_cache_inv_stat ) );
+}
+
+static int do_reset_coda_vfs_stats( ctl_table * table, int write,
+ struct file * filp, void __user * buffer,
+ size_t * lenp, loff_t * ppos )
+{
+ if ( write ) {
+ reset_coda_vfs_stats();
+
+ *ppos += *lenp;
+ } else {
+ *lenp = 0;
+ }
+
+ return 0;
+}
+
+static int do_reset_coda_cache_inv_stats( ctl_table * table, int write,
+ struct file * filp,
+ void __user * buffer,
+ size_t * lenp, loff_t * ppos )
+{
+ if ( write ) {
+ reset_coda_cache_inv_stats();
+
+ *ppos += *lenp;
+ } else {
+ *lenp = 0;
+ }
+
+ return 0;
+}
+
+static int coda_vfs_stats_get_info( char * buffer, char ** start,
+ off_t offset, int length)
+{
+ int len=0;
+ off_t begin;
+ struct coda_vfs_stats * ps = & coda_vfs_stat;
+
+ /* this works as long as we are below 1024 characters! */
+ len += sprintf( buffer,
+ "Coda VFS statistics\n"
+ "===================\n\n"
+ "File Operations:\n"
+ "\topen\t\t%9d\n"
+ "\tflush\t\t%9d\n"
+ "\trelease\t\t%9d\n"
+ "\tfsync\t\t%9d\n\n"
+ "Dir Operations:\n"
+ "\treaddir\t\t%9d\n\n"
+ "Inode Operations\n"
+ "\tcreate\t\t%9d\n"
+ "\tlookup\t\t%9d\n"
+ "\tlink\t\t%9d\n"
+ "\tunlink\t\t%9d\n"
+ "\tsymlink\t\t%9d\n"
+ "\tmkdir\t\t%9d\n"
+ "\trmdir\t\t%9d\n"
+ "\trename\t\t%9d\n"
+ "\tpermission\t%9d\n",
+
+ /* file operations */
+ ps->open,
+ ps->flush,
+ ps->release,
+ ps->fsync,
+
+ /* dir operations */
+ ps->readdir,
+
+ /* inode operations */
+ ps->create,
+ ps->lookup,
+ ps->link,
+ ps->unlink,
+ ps->symlink,
+ ps->mkdir,
+ ps->rmdir,
+ ps->rename,
+ ps->permission);
+
+ begin = offset;
+ *start = buffer + begin;
+ len -= begin;
+
+ if ( len > length )
+ len = length;
+ if ( len < 0 )
+ len = 0;
+
+ return len;
+}
+
+static int coda_cache_inv_stats_get_info( char * buffer, char ** start,
+ off_t offset, int length)
+{
+ int len=0;
+ off_t begin;
+ struct coda_cache_inv_stats * ps = & coda_cache_inv_stat;
+
+ /* this works as long as we are below 1024 characters! */
+ len += sprintf( buffer,
+ "Coda cache invalidation statistics\n"
+ "==================================\n\n"
+ "flush\t\t%9d\n"
+ "purge user\t%9d\n"
+ "zap_dir\t\t%9d\n"
+ "zap_file\t%9d\n"
+ "zap_vnode\t%9d\n"
+ "purge_fid\t%9d\n"
+ "replace\t\t%9d\n",
+ ps->flush,
+ ps->purge_user,
+ ps->zap_dir,
+ ps->zap_file,
+ ps->zap_vnode,
+ ps->purge_fid,
+ ps->replace );
+
+ begin = offset;
+ *start = buffer + begin;
+ len -= begin;
+
+ if ( len > length )
+ len = length;
+ if ( len < 0 )
+ len = 0;
+
+ return len;
+}
+
+static ctl_table coda_table[] = {
+ {CODA_TIMEOUT, "timeout", &coda_timeout, sizeof(int), 0644, NULL, &proc_dointvec},
+ {CODA_HARD, "hard", &coda_hard, sizeof(int), 0644, NULL, &proc_dointvec},
+ {CODA_VFS, "vfs_stats", NULL, 0, 0644, NULL, &do_reset_coda_vfs_stats},
+ {CODA_CACHE_INV, "cache_inv_stats", NULL, 0, 0644, NULL, &do_reset_coda_cache_inv_stats},
+ {CODA_FAKE_STATFS, "fake_statfs", &coda_fake_statfs, sizeof(int), 0600, NULL, &proc_dointvec},
+ { 0 }
+};
+
+static ctl_table fs_table[] = {
+ {FS_CODA, "coda", NULL, 0, 0555, coda_table},
+ {0}
+};
+
+
+#ifdef CONFIG_PROC_FS
+
+/*
+ target directory structure:
+ /proc/fs (see linux/fs/proc/root.c)
+ /proc/fs/coda
+ /proc/fs/coda/{vfs_stats,
+
+*/
+
+static struct proc_dir_entry* proc_fs_coda;
+
+#endif
+
+#define coda_proc_create(name,get_info) \
+ create_proc_info_entry(name, 0, proc_fs_coda, get_info)
+
+void coda_sysctl_init(void)
+{
+ reset_coda_vfs_stats();
+ reset_coda_cache_inv_stats();
+
+#ifdef CONFIG_PROC_FS
+ proc_fs_coda = proc_mkdir("coda", proc_root_fs);
+ if (proc_fs_coda) {
+ proc_fs_coda->owner = THIS_MODULE;
+ coda_proc_create("vfs_stats", coda_vfs_stats_get_info);
+ coda_proc_create("cache_inv_stats", coda_cache_inv_stats_get_info);
+ }
+#endif
+
+#ifdef CONFIG_SYSCTL
+ if ( !fs_table_header )
+ fs_table_header = register_sysctl_table(fs_table, 0);
+#endif
+}
+
+void coda_sysctl_clean(void)
+{
+
+#ifdef CONFIG_SYSCTL
+ if ( fs_table_header ) {
+ unregister_sysctl_table(fs_table_header);
+ fs_table_header = NULL;
+ }
+#endif
+
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("cache_inv_stats", proc_fs_coda);
+ remove_proc_entry("vfs_stats", proc_fs_coda);
+ remove_proc_entry("coda", proc_root_fs);
+#endif
+}
diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c
new file mode 100644
index 000000000000..1bae99650a91
--- /dev/null
+++ b/fs/coda/upcall.c
@@ -0,0 +1,914 @@
+/*
+ * Mostly platform independent upcall operations to Venus:
+ * -- upcalls
+ * -- upcall routines
+ *
+ * Linux 2.0 version
+ * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk>,
+ * Michael Callahan <callahan@maths.ox.ac.uk>
+ *
+ * Redone for Linux 2.1
+ * Copyright (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon University encourages users of this code to contribute
+ * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
+ */
+
+#include <asm/system.h>
+#include <linux/signal.h>
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/vfs.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
+#include <linux/coda_proc.h>
+
+#define upc_alloc() kmalloc(sizeof(struct upc_req), GFP_KERNEL)
+#define upc_free(r) kfree(r)
+
+static int coda_upcall(struct coda_sb_info *mntinfo, int inSize, int *outSize,
+ union inputArgs *buffer);
+
+static void *alloc_upcall(int opcode, int size)
+{
+ union inputArgs *inp;
+
+ CODA_ALLOC(inp, union inputArgs *, size);
+ if (!inp)
+ return ERR_PTR(-ENOMEM);
+
+ inp->ih.opcode = opcode;
+ inp->ih.pid = current->pid;
+ inp->ih.pgid = process_group(current);
+#ifdef CONFIG_CODA_FS_OLD_API
+ memset(&inp->ih.cred, 0, sizeof(struct coda_cred));
+ inp->ih.cred.cr_fsuid = current->fsuid;
+#else
+ inp->ih.uid = current->fsuid;
+#endif
+ return (void*)inp;
+}
+
+#define UPARG(op)\
+do {\
+ inp = (union inputArgs *)alloc_upcall(op, insize); \
+ if (IS_ERR(inp)) { return PTR_ERR(inp); }\
+ outp = (union outputArgs *)(inp); \
+ outsize = insize; \
+} while (0)
+
+#define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
+#define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
+#define SIZE(tag) max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))
+
+
+/* the upcalls */
+int venus_rootfid(struct super_block *sb, struct CodaFid *fidp)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(root);
+ UPARG(CODA_ROOT);
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ if (error) {
+ printk("coda_get_rootfid: error %d\n", error);
+ } else {
+ *fidp = outp->coda_root.VFid;
+ }
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_getattr(struct super_block *sb, struct CodaFid *fid,
+ struct coda_vattr *attr)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(getattr);
+ UPARG(CODA_GETATTR);
+ inp->coda_getattr.VFid = *fid;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ *attr = outp->coda_getattr.attr;
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_setattr(struct super_block *sb, struct CodaFid *fid,
+ struct coda_vattr *vattr)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(setattr);
+ UPARG(CODA_SETATTR);
+
+ inp->coda_setattr.VFid = *fid;
+ inp->coda_setattr.attr = *vattr;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_lookup(struct super_block *sb, struct CodaFid *fid,
+ const char *name, int length, int * type,
+ struct CodaFid *resfid)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset;
+
+ offset = INSIZE(lookup);
+ insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup));
+ UPARG(CODA_LOOKUP);
+
+ inp->coda_lookup.VFid = *fid;
+ inp->coda_lookup.name = offset;
+ inp->coda_lookup.flags = CLU_CASE_SENSITIVE;
+ /* send Venus a null terminated string */
+ memcpy((char *)(inp) + offset, name, length);
+ *((char *)inp + offset + length) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ *resfid = outp->coda_lookup.VFid;
+ *type = outp->coda_lookup.vtype;
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_store(struct super_block *sb, struct CodaFid *fid, int flags,
+ vuid_t uid)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+#ifdef CONFIG_CODA_FS_OLD_API
+ struct coda_cred cred = { 0, };
+ cred.cr_fsuid = uid;
+#endif
+
+ insize = SIZE(store);
+ UPARG(CODA_STORE);
+
+#ifdef CONFIG_CODA_FS_OLD_API
+ memcpy(&(inp->ih.cred), &cred, sizeof(cred));
+#else
+ inp->ih.uid = uid;
+#endif
+
+ inp->coda_store.VFid = *fid;
+ inp->coda_store.flags = flags;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_release(struct super_block *sb, struct CodaFid *fid, int flags)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(release);
+ UPARG(CODA_RELEASE);
+
+ inp->coda_release.VFid = *fid;
+ inp->coda_release.flags = flags;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_close(struct super_block *sb, struct CodaFid *fid, int flags,
+ vuid_t uid)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+#ifdef CONFIG_CODA_FS_OLD_API
+ struct coda_cred cred = { 0, };
+ cred.cr_fsuid = uid;
+#endif
+
+ insize = SIZE(release);
+ UPARG(CODA_CLOSE);
+
+#ifdef CONFIG_CODA_FS_OLD_API
+ memcpy(&(inp->ih.cred), &cred, sizeof(cred));
+#else
+ inp->ih.uid = uid;
+#endif
+
+ inp->coda_close.VFid = *fid;
+ inp->coda_close.flags = flags;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_open(struct super_block *sb, struct CodaFid *fid,
+ int flags, struct file **fh)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(open_by_fd);
+ UPARG(CODA_OPEN_BY_FD);
+
+ inp->coda_open.VFid = *fid;
+ inp->coda_open.flags = flags;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ *fh = outp->coda_open_by_fd.fh;
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid,
+ const char *name, int length,
+ struct CodaFid *newfid, struct coda_vattr *attrs)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset;
+
+ offset = INSIZE(mkdir);
+ insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir));
+ UPARG(CODA_MKDIR);
+
+ inp->coda_mkdir.VFid = *dirfid;
+ inp->coda_mkdir.attr = *attrs;
+ inp->coda_mkdir.name = offset;
+ /* Venus must get null terminated string */
+ memcpy((char *)(inp) + offset, name, length);
+ *((char *)inp + offset + length) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ *attrs = outp->coda_mkdir.attr;
+ *newfid = outp->coda_mkdir.VFid;
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+
+int venus_rename(struct super_block *sb, struct CodaFid *old_fid,
+ struct CodaFid *new_fid, size_t old_length,
+ size_t new_length, const char *old_name,
+ const char *new_name)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset, s;
+
+ offset = INSIZE(rename);
+ insize = max_t(unsigned int, offset + new_length + old_length + 8,
+ OUTSIZE(rename));
+ UPARG(CODA_RENAME);
+
+ inp->coda_rename.sourceFid = *old_fid;
+ inp->coda_rename.destFid = *new_fid;
+ inp->coda_rename.srcname = offset;
+
+ /* Venus must receive an null terminated string */
+ s = ( old_length & ~0x3) +4; /* round up to word boundary */
+ memcpy((char *)(inp) + offset, old_name, old_length);
+ *((char *)inp + offset + old_length) = '\0';
+
+ /* another null terminated string for Venus */
+ offset += s;
+ inp->coda_rename.destname = offset;
+ s = ( new_length & ~0x3) +4; /* round up to word boundary */
+ memcpy((char *)(inp) + offset, new_name, new_length);
+ *((char *)inp + offset + new_length) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_create(struct super_block *sb, struct CodaFid *dirfid,
+ const char *name, int length, int excl, int mode,
+ struct CodaFid *newfid, struct coda_vattr *attrs)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset;
+
+ offset = INSIZE(create);
+ insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create));
+ UPARG(CODA_CREATE);
+
+ inp->coda_create.VFid = *dirfid;
+ inp->coda_create.attr.va_mode = mode;
+ inp->coda_create.excl = excl;
+ inp->coda_create.mode = mode;
+ inp->coda_create.name = offset;
+
+ /* Venus must get null terminated string */
+ memcpy((char *)(inp) + offset, name, length);
+ *((char *)inp + offset + length) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ *attrs = outp->coda_create.attr;
+ *newfid = outp->coda_create.VFid;
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid,
+ const char *name, int length)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset;
+
+ offset = INSIZE(rmdir);
+ insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir));
+ UPARG(CODA_RMDIR);
+
+ inp->coda_rmdir.VFid = *dirfid;
+ inp->coda_rmdir.name = offset;
+ memcpy((char *)(inp) + offset, name, length);
+ *((char *)inp + offset + length) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_remove(struct super_block *sb, struct CodaFid *dirfid,
+ const char *name, int length)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int error=0, insize, outsize, offset;
+
+ offset = INSIZE(remove);
+ insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove));
+ UPARG(CODA_REMOVE);
+
+ inp->coda_remove.VFid = *dirfid;
+ inp->coda_remove.name = offset;
+ memcpy((char *)(inp) + offset, name, length);
+ *((char *)inp + offset + length) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_readlink(struct super_block *sb, struct CodaFid *fid,
+ char *buffer, int *length)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int retlen;
+ char *result;
+
+ insize = max_t(unsigned int,
+ INSIZE(readlink), OUTSIZE(readlink)+ *length + 1);
+ UPARG(CODA_READLINK);
+
+ inp->coda_readlink.VFid = *fid;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ if (! error) {
+ retlen = outp->coda_readlink.count;
+ if ( retlen > *length )
+ retlen = *length;
+ *length = retlen;
+ result = (char *)outp + (long)outp->coda_readlink.data;
+ memcpy(buffer, result, retlen);
+ *(buffer + retlen) = '\0';
+ }
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+
+
+int venus_link(struct super_block *sb, struct CodaFid *fid,
+ struct CodaFid *dirfid, const char *name, int len )
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset;
+
+ offset = INSIZE(link);
+ insize = max_t(unsigned int, offset + len + 1, OUTSIZE(link));
+ UPARG(CODA_LINK);
+
+ inp->coda_link.sourceFid = *fid;
+ inp->coda_link.destFid = *dirfid;
+ inp->coda_link.tname = offset;
+
+ /* make sure strings are null terminated */
+ memcpy((char *)(inp) + offset, name, len);
+ *((char *)inp + offset + len) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_symlink(struct super_block *sb, struct CodaFid *fid,
+ const char *name, int len,
+ const char *symname, int symlen)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset, s;
+
+ offset = INSIZE(symlink);
+ insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink));
+ UPARG(CODA_SYMLINK);
+
+ /* inp->coda_symlink.attr = *tva; XXXXXX */
+ inp->coda_symlink.VFid = *fid;
+
+ /* Round up to word boundary and null terminate */
+ inp->coda_symlink.srcname = offset;
+ s = ( symlen & ~0x3 ) + 4;
+ memcpy((char *)(inp) + offset, symname, symlen);
+ *((char *)inp + offset + symlen) = '\0';
+
+ /* Round up to word boundary and null terminate */
+ offset += s;
+ inp->coda_symlink.tname = offset;
+ s = (len & ~0x3) + 4;
+ memcpy((char *)(inp) + offset, name, len);
+ *((char *)inp + offset + len) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_fsync(struct super_block *sb, struct CodaFid *fid)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize=SIZE(fsync);
+ UPARG(CODA_FSYNC);
+
+ inp->coda_fsync.VFid = *fid;
+ error = coda_upcall(coda_sbp(sb), sizeof(union inputArgs),
+ &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_access(struct super_block *sb, struct CodaFid *fid, int mask)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(access);
+ UPARG(CODA_ACCESS);
+
+ inp->coda_access.VFid = *fid;
+ inp->coda_access.flags = mask;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+
+int venus_pioctl(struct super_block *sb, struct CodaFid *fid,
+ unsigned int cmd, struct PioctlData *data)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int iocsize;
+
+ insize = VC_MAXMSGSIZE;
+ UPARG(CODA_IOCTL);
+
+ /* build packet for Venus */
+ if (data->vi.in_size > VC_MAXDATASIZE) {
+ error = -EINVAL;
+ goto exit;
+ }
+
+ if (data->vi.out_size > VC_MAXDATASIZE) {
+ error = -EINVAL;
+ goto exit;
+ }
+
+ inp->coda_ioctl.VFid = *fid;
+
+ /* the cmd field was mutated by increasing its size field to
+ * reflect the path and follow args. We need to subtract that
+ * out before sending the command to Venus. */
+ inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));
+ iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
+ inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) << 16;
+
+ /* in->coda_ioctl.rwflag = flag; */
+ inp->coda_ioctl.len = data->vi.in_size;
+ inp->coda_ioctl.data = (char *)(INSIZE(ioctl));
+
+ /* get the data out of user space */
+ if ( copy_from_user((char*)inp + (long)inp->coda_ioctl.data,
+ data->vi.in, data->vi.in_size) ) {
+ error = -EINVAL;
+ goto exit;
+ }
+
+ error = coda_upcall(coda_sbp(sb), SIZE(ioctl) + data->vi.in_size,
+ &outsize, inp);
+
+ if (error) {
+ printk("coda_pioctl: Venus returns: %d for %s\n",
+ error, coda_f2s(fid));
+ goto exit;
+ }
+
+ if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) {
+ error = -EINVAL;
+ goto exit;
+ }
+
+ /* Copy out the OUT buffer. */
+ if (outp->coda_ioctl.len > data->vi.out_size) {
+ error = -EINVAL;
+ goto exit;
+ }
+
+ /* Copy out the OUT buffer. */
+ if (copy_to_user(data->vi.out,
+ (char *)outp + (long)outp->coda_ioctl.data,
+ outp->coda_ioctl.len)) {
+ error = -EFAULT;
+ goto exit;
+ }
+
+ exit:
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_statfs(struct super_block *sb, struct kstatfs *sfs)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs));
+ UPARG(CODA_STATFS);
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ if (!error) {
+ sfs->f_blocks = outp->coda_statfs.stat.f_blocks;
+ sfs->f_bfree = outp->coda_statfs.stat.f_bfree;
+ sfs->f_bavail = outp->coda_statfs.stat.f_bavail;
+ sfs->f_files = outp->coda_statfs.stat.f_files;
+ sfs->f_ffree = outp->coda_statfs.stat.f_ffree;
+ } else {
+ printk("coda_statfs: Venus returns: %d\n", error);
+ }
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+/*
+ * coda_upcall and coda_downcall routines.
+ *
+ */
+
+static inline void coda_waitfor_upcall(struct upc_req *vmp,
+ struct venus_comm *vcommp)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ vmp->uc_posttime = jiffies;
+
+ add_wait_queue(&vmp->uc_sleep, &wait);
+ for (;;) {
+ if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE )
+ set_current_state(TASK_INTERRUPTIBLE);
+ else
+ set_current_state(TASK_UNINTERRUPTIBLE);
+
+ /* venus died */
+ if ( !vcommp->vc_inuse )
+ break;
+
+ /* got a reply */
+ if ( vmp->uc_flags & ( REQ_WRITE | REQ_ABORT ) )
+ break;
+
+ if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE && signal_pending(current) ) {
+ /* if this process really wants to die, let it go */
+ if ( sigismember(&(current->pending.signal), SIGKILL) ||
+ sigismember(&(current->pending.signal), SIGINT) )
+ break;
+ /* signal is present: after timeout always return
+ really smart idea, probably useless ... */
+ if ( jiffies - vmp->uc_posttime > coda_timeout * HZ )
+ break;
+ }
+ schedule();
+ }
+ remove_wait_queue(&vmp->uc_sleep, &wait);
+ set_current_state(TASK_RUNNING);
+
+ return;
+}
+
+
+/*
+ * coda_upcall will return an error in the case of
+ * failed communication with Venus _or_ will peek at Venus
+ * reply and return Venus' error.
+ *
+ * As venus has 2 types of errors, normal errors (positive) and internal
+ * errors (negative), normal errors are negated, while internal errors
+ * are all mapped to -EINTR, while showing a nice warning message. (jh)
+ *
+ */
+static int coda_upcall(struct coda_sb_info *sbi,
+ int inSize, int *outSize,
+ union inputArgs *buffer)
+{
+ struct venus_comm *vcommp;
+ union outputArgs *out;
+ struct upc_req *req;
+ int error = 0;
+
+ vcommp = sbi->sbi_vcomm;
+ if ( !vcommp->vc_inuse ) {
+ printk("No pseudo device in upcall comms at %p\n", vcommp);
+ return -ENXIO;
+ }
+
+ /* Format the request message. */
+ req = upc_alloc();
+ if (!req) {
+ printk("Failed to allocate upc_req structure\n");
+ return -ENOMEM;
+ }
+ req->uc_data = (void *)buffer;
+ req->uc_flags = 0;
+ req->uc_inSize = inSize;
+ req->uc_outSize = *outSize ? *outSize : inSize;
+ req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode;
+ req->uc_unique = ++vcommp->vc_seq;
+ init_waitqueue_head(&req->uc_sleep);
+
+ /* Fill in the common input args. */
+ ((union inputArgs *)buffer)->ih.unique = req->uc_unique;
+
+ /* Append msg to pending queue and poke Venus. */
+ list_add(&(req->uc_chain), vcommp->vc_pending.prev);
+
+ wake_up_interruptible(&vcommp->vc_waitq);
+ /* We can be interrupted while we wait for Venus to process
+ * our request. If the interrupt occurs before Venus has read
+ * the request, we dequeue and return. If it occurs after the
+ * read but before the reply, we dequeue, send a signal
+ * message, and return. If it occurs after the reply we ignore
+ * it. In no case do we want to restart the syscall. If it
+ * was interrupted by a venus shutdown (psdev_close), return
+ * ENODEV. */
+
+ /* Go to sleep. Wake up on signals only after the timeout. */
+ coda_waitfor_upcall(req, vcommp);
+
+ if (vcommp->vc_inuse) { /* i.e. Venus is still alive */
+ /* Op went through, interrupt or not... */
+ if (req->uc_flags & REQ_WRITE) {
+ out = (union outputArgs *)req->uc_data;
+ /* here we map positive Venus errors to kernel errors */
+ error = -out->oh.result;
+ *outSize = req->uc_outSize;
+ goto exit;
+ }
+ if ( !(req->uc_flags & REQ_READ) && signal_pending(current)) {
+ /* Interrupted before venus read it. */
+ list_del(&(req->uc_chain));
+ /* perhaps the best way to convince the app to
+ give up? */
+ error = -EINTR;
+ goto exit;
+ }
+ if ( (req->uc_flags & REQ_READ) && signal_pending(current) ) {
+ /* interrupted after Venus did its read, send signal */
+ union inputArgs *sig_inputArgs;
+ struct upc_req *sig_req;
+
+ list_del(&(req->uc_chain));
+ error = -ENOMEM;
+ sig_req = upc_alloc();
+ if (!sig_req) goto exit;
+
+ CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
+ if (!sig_req->uc_data) {
+ upc_free(sig_req);
+ goto exit;
+ }
+
+ error = -EINTR;
+ sig_inputArgs = (union inputArgs *)sig_req->uc_data;
+ sig_inputArgs->ih.opcode = CODA_SIGNAL;
+ sig_inputArgs->ih.unique = req->uc_unique;
+
+ sig_req->uc_flags = REQ_ASYNC;
+ sig_req->uc_opcode = sig_inputArgs->ih.opcode;
+ sig_req->uc_unique = sig_inputArgs->ih.unique;
+ sig_req->uc_inSize = sizeof(struct coda_in_hdr);
+ sig_req->uc_outSize = sizeof(struct coda_in_hdr);
+
+ /* insert at head of queue! */
+ list_add(&(sig_req->uc_chain), &vcommp->vc_pending);
+ wake_up_interruptible(&vcommp->vc_waitq);
+ } else {
+ printk("Coda: Strange interruption..\n");
+ error = -EINTR;
+ }
+ } else { /* If venus died i.e. !VC_OPEN(vcommp) */
+ printk("coda_upcall: Venus dead on (op,un) (%d.%d) flags %d\n",
+ req->uc_opcode, req->uc_unique, req->uc_flags);
+ error = -ENODEV;
+ }
+
+ exit:
+ upc_free(req);
+ return error;
+}
+
+/*
+ The statements below are part of the Coda opportunistic
+ programming -- taken from the Mach/BSD kernel code for Coda.
+ You don't get correct semantics by stating what needs to be
+ done without guaranteeing the invariants needed for it to happen.
+ When will be have time to find out what exactly is going on? (pjb)
+*/
+
+
+/*
+ * There are 7 cases where cache invalidations occur. The semantics
+ * of each is listed here:
+ *
+ * CODA_FLUSH -- flush all entries from the name cache and the cnode cache.
+ * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
+ * This call is a result of token expiration.
+ *
+ * The next arise as the result of callbacks on a file or directory.
+ * CODA_ZAPFILE -- flush the cached attributes for a file.
+
+ * CODA_ZAPDIR -- flush the attributes for the dir and
+ * force a new lookup for all the children
+ of this dir.
+
+ *
+ * The next is a result of Venus detecting an inconsistent file.
+ * CODA_PURGEFID -- flush the attribute for the file
+ * purge it and its children from the dcache
+ *
+ * The last allows Venus to replace local fids with global ones
+ * during reintegration.
+ *
+ * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */
+
+int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb)
+{
+ /* Handle invalidation requests. */
+ if ( !sb || !sb->s_root || !sb->s_root->d_inode)
+ return 0;
+
+ switch (opcode) {
+
+ case CODA_FLUSH : {
+ coda_cache_clear_all(sb);
+ shrink_dcache_sb(sb);
+ coda_flag_inode(sb->s_root->d_inode, C_FLUSH);
+ return(0);
+ }
+
+ case CODA_PURGEUSER : {
+ coda_cache_clear_all(sb);
+ return(0);
+ }
+
+ case CODA_ZAPDIR : {
+ struct inode *inode;
+ struct CodaFid *fid = &out->coda_zapdir.CodaFid;
+
+ inode = coda_fid_to_inode(fid, sb);
+ if (inode) {
+ coda_flag_inode_children(inode, C_PURGE);
+ coda_flag_inode(inode, C_VATTR);
+ iput(inode);
+ }
+
+ return(0);
+ }
+
+ case CODA_ZAPFILE : {
+ struct inode *inode;
+ struct CodaFid *fid = &out->coda_zapfile.CodaFid;
+ inode = coda_fid_to_inode(fid, sb);
+ if ( inode ) {
+ coda_flag_inode(inode, C_VATTR);
+ iput(inode);
+ }
+ return 0;
+ }
+
+ case CODA_PURGEFID : {
+ struct inode *inode;
+ struct CodaFid *fid = &out->coda_purgefid.CodaFid;
+ inode = coda_fid_to_inode(fid, sb);
+ if ( inode ) {
+ coda_flag_inode_children(inode, C_PURGE);
+
+ /* catch the dentries later if some are still busy */
+ coda_flag_inode(inode, C_PURGE);
+ d_prune_aliases(inode);
+
+ iput(inode);
+ }
+ return 0;
+ }
+
+ case CODA_REPLACE : {
+ struct inode *inode;
+ struct CodaFid *oldfid = &out->coda_replace.OldFid;
+ struct CodaFid *newfid = &out->coda_replace.NewFid;
+ inode = coda_fid_to_inode(oldfid, sb);
+ if ( inode ) {
+ coda_replace_fid(inode, oldfid, newfid);
+ iput(inode);
+ }
+ return 0;
+ }
+ }
+ return 0;
+}
+