summaryrefslogtreecommitdiffstats
path: root/fs/coda
diff options
context:
space:
mode:
Diffstat (limited to 'fs/coda')
-rw-r--r--fs/coda/.cvsignore1
-rw-r--r--fs/coda/Makefile15
-rw-r--r--fs/coda/cnode.c196
-rw-r--r--fs/coda/coda_linux.c70
-rw-r--r--fs/coda/dir.c769
-rw-r--r--fs/coda/file.c240
-rw-r--r--fs/coda/inode.c34
-rw-r--r--fs/coda/namecache.c832
-rw-r--r--fs/coda/pioctl.c154
-rw-r--r--fs/coda/psdev.c444
-rw-r--r--fs/coda/super.c612
-rw-r--r--fs/coda/symlink.c82
-rw-r--r--fs/coda/sysctl.c168
-rw-r--r--fs/coda/upcall.c747
14 files changed, 4364 insertions, 0 deletions
diff --git a/fs/coda/.cvsignore b/fs/coda/.cvsignore
new file mode 100644
index 000000000..4671378ae
--- /dev/null
+++ b/fs/coda/.cvsignore
@@ -0,0 +1 @@
+.depend
diff --git a/fs/coda/Makefile b/fs/coda/Makefile
new file mode 100644
index 000000000..4118fdc61
--- /dev/null
+++ b/fs/coda/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux Coda-filesystem routines.
+#
+
+O_TARGET := coda.o
+O_OBJS := psdev.o upcall.o cnode.o super.o dir.o coda_linux.o symlink.o pioctl.o file.o namecache.o\
+ sysctl.o
+M_OBJS := $(O_TARGET)
+
+# If you want debugging output, please uncomment the following line
+
+# EXTRA_CFLAGS += -DDEBUG -DDEBUG_SMB_MALLOC=1
+
+include $(TOPDIR)/Rules.make
+
diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c
new file mode 100644
index 000000000..abf8a855f
--- /dev/null
+++ b/fs/coda/cnode.c
@@ -0,0 +1,196 @@
+/* cnode related routines for the coda kernel code
+ Peter Braam, Sep 1996.
+ */
+
+#include <linux/types.h>
+#include <linux/time.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cnode.h>
+
+extern int coda_debug;
+extern int coda_print_entry;
+extern int coda_fetch_inode(struct inode *inode, struct coda_vattr *attr);
+
+/* cnode.c */
+struct cnode *coda_cnode_alloc(void);
+void coda_cnode_free(struct cnode *);
+int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb);
+
+/* return pointer to new empty cnode */
+struct cnode *coda_cnode_alloc(void)
+{
+ struct cnode *result = NULL;
+
+ CODA_ALLOC(result, struct cnode *, sizeof(struct cnode));
+ if ( !result ) {
+ printk("coda_cnode_alloc: kmalloc returned NULL.\n");
+ return result;
+ }
+
+ memset(result, 0, (int) sizeof(struct cnode));
+ return result;
+}
+
+/* release cnode memory */
+void coda_cnode_free(struct cnode *cinode)
+{
+ CODA_FREE(cinode, sizeof(struct cnode));
+}
+
+/* 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, ViceFid *fid, struct super_block *sb)
+{
+ struct cnode *cnp;
+ struct coda_vattr attr;
+ int error;
+ ino_t ino;
+ char str[50];
+
+ ENTRY;
+
+ /*
+ * We get inode numbers from Venus -- see venus source
+ */
+
+ error = venus_getattr(sb, fid, &attr);
+ if ( error ) {
+ printk("coda_cnode_make: coda_getvattr returned %d for %s.\n",
+ error, coda_f2s(fid, str));
+ *inode = NULL;
+ return error;
+ }
+
+ ino = attr.va_fileid;
+ *inode = iget(sb, ino);
+ if ( !*inode ) {
+ printk("coda_cnode_make: iget failed\n");
+ return -ENOMEM;
+ }
+
+ /* link the cnode and the vfs inode
+ if this inode is not linked yet
+ */
+ if ( !(*inode)->u.generic_ip ) {
+ cnp = coda_cnode_alloc();
+ if ( !cnp ) {
+ printk("coda_cnode_make: coda_cnode_alloc failed.\n");
+ clear_inode(*inode);
+ return -ENOMEM;
+ }
+ cnp->c_fid = *fid;
+ cnp->c_magic = CODA_CNODE_MAGIC;
+ cnp->c_flags = C_VATTR;
+ cnp->c_vnode = *inode;
+ (*inode)->u.generic_ip = (void *) cnp;
+ CDEBUG(D_CNODE, "LINKING: ino %ld, count %d at 0x%x with cnp 0x%x, cnp->c_vnode 0x%x, in->u.generic_ip 0x%x\n", (*inode)->i_ino, (*inode)->i_count, (int) (*inode), (int) cnp, (int)cnp->c_vnode, (int) (*inode)->u.generic_ip);
+ } else {
+ cnp = (struct cnode *)(*inode)->u.generic_ip;
+ CDEBUG(D_CNODE, "FOUND linked: ino %ld, count %d, at 0x%x with cnp 0x%x, cnp->c_vnode 0x%x\n", (*inode)->i_ino, (*inode)->i_count, (int) (*inode), (int) cnp, (int)cnp->c_vnode);
+ }
+ CHECK_CNODE(cnp);
+
+ /* refresh the attributes */
+ error = coda_fetch_inode(*inode, &attr);
+ if ( error ) {
+ printk("coda_cnode_make: fetch_inode returned %d\n", error);
+ clear_inode(*inode);
+ coda_cnode_free(cnp);
+ return -error;
+ }
+ CDEBUG(D_CNODE, "Done linking: ino %ld, at 0x%x with cnp 0x%x, cnp->c_vnode 0x%x\n", (*inode)->i_ino, (int) (*inode), (int) cnp, (int)cnp->c_vnode);
+
+ EXIT;
+ return 0;
+}
+
+
+inline int coda_fideq(ViceFid *fid1, ViceFid *fid2)
+{
+ int eq;
+ eq = ( (fid1->Vnode == fid2->Vnode) &&
+ (fid1->Volume == fid2->Volume) &&
+ (fid1->Unique == fid2->Unique) );
+ return eq;
+}
+
+
+/* compute the inode number from the FID
+ same routine as in vproc.cc (venus)
+ XXX look at the exceptional case of root fids etc
+*/
+static ino_t
+coda_fid2ino(ViceFid *fid)
+{
+ u_long ROOT_VNODE = 1;
+ u_long ROOT_UNIQUE = 1;
+ ViceFid nullfid = { 0, 0, 0};
+
+ if ( coda_fideq(fid, &nullfid) ) {
+ printk("coda_fid2ino: called with NULL Fid!\n");
+ return 0;
+ }
+
+ /* what do we return for the root fid */
+
+ /* Other volume root. We need the relevant mount point's
+ fid, but we don't know what that is! */
+ if (fid->Vnode == ROOT_VNODE && fid->Unique == ROOT_UNIQUE) {
+ return(0);
+ }
+
+ /* Non volume root. */
+ return(fid->Unique + (fid->Vnode << 10) + (fid->Volume << 20));
+}
+
+/* convert a fid to an inode. Avoids having a hash table
+ such as present in the Mach minicache */
+struct inode *
+coda_fid2inode(ViceFid *fid, struct super_block *sb) {
+ ino_t nr;
+ struct inode *inode;
+ struct cnode *cnp;
+
+ nr = coda_fid2ino(fid);
+ inode = iget(sb, nr);
+
+ /* check if this inode is linked to a cnode */
+ cnp = (struct cnode *) inode->u.generic_ip;
+ if ( cnp == NULL ) {
+ iput(inode);
+ return NULL;
+ }
+ /* make sure fid is the one we want */
+ if ( !coda_fideq(fid, &(cnp->c_fid)) ) {
+ printk("coda_fid2inode: bad cnode! Tell Peter.\n");
+ iput(inode);
+ return NULL;
+ }
+
+ iput(inode);
+ 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 = 0;
+
+ *inode = iget(sb, CTL_INO);
+ if ( *inode ) {
+ (*inode)->i_op = &coda_ioctl_inode_operations;
+ (*inode)->i_mode = 00444;
+ error = 0;
+ } else {
+ error = -ENOMEM;
+ }
+
+ return error;
+}
diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c
new file mode 100644
index 000000000..ea595c35b
--- /dev/null
+++ b/fs/coda/coda_linux.c
@@ -0,0 +1,70 @@
+/*
+ * 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/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.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_cnode.h>
+#include <linux/coda_namecache.h>
+
+/* initialize the debugging variables */
+int coda_debug =815;
+int coda_print_entry = 1;
+int coda_access_cache = 1;
+
+/* caller must allocate 36 byte string ! */
+char * coda_f2s(ViceFid *f, char *s)
+{
+ if ( f ) {
+ sprintf(s, "(%-#10lx,%-#10lx,%-#10lx)",
+ f->Volume, f->Vnode, f->Unique);
+ }
+ return s;
+}
+
+int coda_isroot(struct inode *i)
+{
+ if ( i->i_sb->s_root->d_inode == i ) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void coda_load_creds(struct CodaCred *cred)
+{
+ int i;
+
+ cred->cr_uid = (vuid_t) current->uid;
+ cred->cr_euid = (vuid_t) current->euid;
+ cred->cr_suid = (vuid_t) current->suid;
+ cred->cr_fsuid = (vuid_t) current->fsuid;
+
+ cred->cr_gid = (vgid_t) current->gid;
+ cred->cr_egid = (vgid_t) current->egid;
+ cred->cr_sgid = (vgid_t) current->sgid;
+ cred->cr_fsgid = (vgid_t) current->fsgid;
+
+ for ( i = 0 ; i < NGROUPS ; ++i ) {
+ cred->cr_groups[i] = (vgid_t) current->groups[i];
+ }
+
+}
+
diff --git a/fs/coda/dir.c b/fs/coda/dir.c
new file mode 100644
index 000000000..cfc10dc83
--- /dev/null
+++ b/fs/coda/dir.c
@@ -0,0 +1,769 @@
+/*
+ * Direcotry 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/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.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_cnode.h>
+#include <linux/coda_namecache.h>
+
+/* dir inode-ops */
+static int coda_create(struct inode *dir, struct dentry *new, int mode);
+static int coda_lookup(struct inode *dir, struct dentry *target);
+static int coda_link(struct inode *old_inode, 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);
+
+/* support routines */
+static int coda_venus_readdir(struct file *filp, void *dirent,
+ filldir_t filldir);
+
+
+
+struct inode_operations coda_dir_inode_operations =
+{
+ &coda_dir_operations,
+ coda_create, /* create */
+ coda_lookup, /* lookup */
+ coda_link, /* link */
+ coda_unlink, /* unlink */
+ coda_symlink, /* symlink */
+ coda_mkdir, /* mkdir */
+ coda_rmdir, /* rmdir */
+ NULL, /* mknod */
+ coda_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ coda_permission, /* permission */
+ NULL, /* smap */
+ NULL, /* update page */
+ NULL /* revalidate */
+};
+
+struct file_operations coda_dir_operations = {
+ NULL, /* lseek */
+ NULL, /* read -- bad */
+ NULL, /* write */
+ coda_readdir, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ coda_open, /* open */
+ coda_release, /* release */
+ NULL, /* fsync */
+};
+
+
+/* inode operations for directories */
+/* acces routines: lookup, readlink, permission */
+static int coda_lookup(struct inode *dir, struct dentry *entry)
+{
+ struct cnode *dircnp, *savedcnp;
+ struct inode *res_inode = NULL;
+ struct ViceFid resfid;
+ int type;
+ int error = 0;
+ const char *name = entry->d_name.name;
+ size_t length = entry->d_name.len;
+ char str[50];
+
+ ENTRY;
+ CDEBUG(D_INODE, "name %s, len %d in ino %ld\n",
+ name, length, dir->i_ino);
+
+ if (!dir || !S_ISDIR(dir->i_mode)) {
+ printk("coda_lookup: inode is NULL or not a directory\n");
+ return -ENOENT;
+ }
+
+ dircnp = ITOC(dir);
+ CHECK_CNODE(dircnp);
+
+ if ( length > CFS_MAXNAMLEN ) {
+ printk("name too long: lookup, %s (%s)\n",
+ coda_f2s(&dircnp->c_fid, str), name);
+ return -ENAMETOOLONG;
+ }
+
+ CDEBUG(D_INODE, "lookup: %s in %s\n", name,
+ coda_f2s(&dircnp->c_fid, str));
+
+ /* control object, create inode on the fly */
+ if ( coda_isroot(dir) && (CFS_CONTROLLEN == length) &&
+ (strncmp(name, CFS_CONTROL, CFS_CONTROLLEN) == 0) ) {
+ error = coda_cnode_makectl(&res_inode, dir->i_sb);
+ CDEBUG(D_SPECIAL,
+ "Lookup on CTL object; iput of ino %ld, count %d\n",
+ dir->i_ino, dir->i_count);
+ goto exit;
+ }
+
+ /* do we have it already in name cache */
+ if ( (savedcnp = cfsnc_lookup(dircnp, name, length)) != NULL ) {
+ CHECK_CNODE(savedcnp);
+ res_inode = CTOI(savedcnp);
+ iget(res_inode->i_sb, res_inode->i_ino);
+ CDEBUG(D_INODE, "cache hit for ino: %ld, count: %d!\n",
+ res_inode->i_ino, res_inode->i_count);
+ goto exit;
+ }
+ CDEBUG(D_INODE, "name not found in cache!\n");
+
+ /* name not cached */
+ error = venus_lookup(dir->i_sb, &(dircnp->c_fid),
+ (const char *)name, length, &type, &resfid);
+
+ res_inode = NULL;
+ if (!error) {
+ error = coda_cnode_make(&res_inode, &resfid, dir->i_sb);
+ if (error)
+ return -EACCES;
+ /* put the thing in the name cache */
+ savedcnp = ITOC(res_inode);
+ CHECK_CNODE(savedcnp);
+ CDEBUG(D_INODE, "ABOUT to enter into cache.\n");
+ cfsnc_enter(dircnp, name, length, savedcnp);
+ CDEBUG(D_INODE, "entered in cache\n");
+ } else if (error != -ENOENT) {
+ CDEBUG(D_INODE, "error for %s(%s)%d\n",
+ coda_f2s(&dircnp->c_fid, str), name, error);
+ return error;
+ }
+ CDEBUG(D_INODE, "lookup: %s is (%s) type %d result %d\n",
+ name, coda_f2s(&resfid, str), type, error);
+
+ /* at last we have our inode number from Venus,
+ now allocate storage for
+ the cnode and do iget, and fill in the attributes */
+
+
+exit:
+ entry->d_time = 0;
+ entry->d_op = NULL;
+ d_add(entry, res_inode);
+ EXIT;
+ return 0;
+}
+
+
+
+int coda_permission(struct inode *inode, int mask)
+{
+ struct cnode *cp;
+ int error;
+ int mode = inode->i_mode;
+ char str[50];
+
+ ENTRY;
+
+ if ( mask == 0 ) {
+ EXIT;
+ return 0;
+ }
+
+ /* we should be able to trust what is in the mode
+ although Venus should be told to return the
+ correct modes to the kernel */
+ if ( coda_access_cache == 1 ) {
+ if (current->fsuid == inode->i_uid)
+ mode >>= 6;
+ else if (in_group_p(inode->i_gid))
+ mode >>= 3;
+ if (((mode & mask & 0007) == mask) )
+ return 0;
+ }
+
+ cp = ITOC(inode);
+ CHECK_CNODE(cp);
+
+ CDEBUG(D_INODE, "mask is %o\n", mask);
+ error = venus_access(inode->i_sb, &(cp->c_fid), mask);
+
+ CDEBUG(D_INODE, "fid: %s, ino: %ld (mask: %o) error: %d\n",
+ coda_f2s(&(cp->c_fid), str), inode->i_ino, mask, error);
+
+ return error;
+}
+
+
+
+/* creation routines: create, mkdir, link, symlink */
+
+static int coda_create(struct inode *dir, struct dentry *de, int mode)
+{
+ int error=0;
+ struct cnode *dircnp;
+ const char *name=de->d_name.name;
+ int length=de->d_name.len;
+ struct inode *result = NULL;
+ struct ViceFid newfid;
+ struct coda_vattr attrs;
+
+ CDEBUG(D_INODE, "name: %s, length %d, mode %o\n",name, length, mode);
+
+ if (!dir || !S_ISDIR(dir->i_mode)) {
+ printk("coda_create: inode is null or not a directory\n");
+ return -ENOENT;
+ }
+ dircnp = ITOC(dir);
+ CHECK_CNODE(dircnp);
+
+ if ( length > CFS_MAXNAMLEN ) {
+ char str[50];
+ printk("name too long: create, %s(%s)\n",
+ coda_f2s(&dircnp->c_fid, str), name);
+ return -ENAMETOOLONG;
+ }
+
+ error = venus_create(dir->i_sb, &(dircnp->c_fid), name, length,
+ 0, mode, &newfid, &attrs);
+
+ if ( error ) {
+ char str[50];
+ CDEBUG(D_INODE, "create: %s, result %d\n",
+ coda_f2s(&newfid, str), error);
+ return error;
+ }
+
+ error = coda_cnode_make(&result, &newfid, dir->i_sb);
+ if ( error ) {
+ result = NULL;
+ return error;
+ }
+
+ /* invalidate the directory cnode's attributes */
+ dircnp->c_flags &= ~C_VATTR;
+ cfsnc_zapfid(&(dircnp->c_fid));
+
+ d_instantiate(de, result);
+ return 0;
+}
+
+static int coda_mkdir(struct inode *dir, struct dentry *de, int mode)
+{
+ struct cnode *dircnp;
+ struct inode *inode;
+ struct coda_vattr attr;
+ const char *name = de->d_name.name;
+ int len = de->d_name.len;
+ int error;
+ struct ViceFid newfid;
+ char fidstr[50];
+
+ if (!dir || !S_ISDIR(dir->i_mode)) {
+ printk("coda_mkdir: inode is NULL or not a directory\n");
+ return -ENOENT;
+ }
+ dircnp = ITOC(dir);
+ CHECK_CNODE(dircnp);
+
+ if ( len > CFS_MAXNAMLEN )
+ return -ENAMETOOLONG;
+
+ CDEBUG(D_INODE, "mkdir %s (len %d) in %s, mode %o.\n",
+ name, len, coda_f2s(&(dircnp->c_fid), fidstr), mode);
+
+ attr.va_mode = mode;
+ error = venus_mkdir(dir->i_sb, &(dircnp->c_fid),
+ name, len, &newfid, &attr);
+
+ if ( error ) {
+ CDEBUG(D_INODE, "mkdir error: %s result %d\n",
+ coda_f2s(&newfid, fidstr), error);
+ return error;
+ }
+
+ CDEBUG(D_INODE, "mkdir: new dir has fid %s.\n",
+ coda_f2s(&newfid, fidstr));
+
+ error = coda_cnode_make(&inode, &newfid, dir->i_sb);
+ if ( error )
+ return error;
+
+ /* invalidate the directory cnode's attributes */
+ dircnp->c_flags &= ~C_VATTR;
+ cfsnc_zapfid(&(dircnp->c_fid));
+
+ dir->i_nlink++;
+ d_instantiate(de, inode);
+ return 0;
+}
+
+static int coda_link(struct inode *inode, struct inode *dir_inode,
+ struct dentry *de)
+{
+ const char * name = de->d_name.name;
+ int len = de->d_name.len;
+ struct cnode *dir_cnp, *cnp;
+ char str[50];
+ int error;
+
+ ENTRY;
+
+ dir_cnp = ITOC(dir_inode);
+ CHECK_CNODE(dir_cnp);
+ cnp = ITOC(inode);
+ CHECK_CNODE(cnp);
+
+ CDEBUG(D_INODE, "old: fid: %s\n", coda_f2s(&(cnp->c_fid), str));
+ CDEBUG(D_INODE, "directory: %s\n", coda_f2s(&(dir_cnp->c_fid), str));
+
+ if ( len > CFS_MAXNAMLEN ) {
+ printk("coda_link: name too long. \n");
+ return -ENAMETOOLONG;
+ }
+
+ /* Check for link to/from control object. */
+
+ error = venus_link(dir_inode->i_sb,&(cnp->c_fid), &(dir_cnp->c_fid),
+ (const char *)name, len);
+
+ if ( ! error ) {
+ dir_cnp->c_flags &= ~C_VATTR;
+ cfsnc_zapfid(&(dir_cnp->c_fid));
+ cfsnc_zapfid(&(cnp->c_fid));
+
+ inode->i_nlink++;
+ d_instantiate(de, inode);
+ }
+ CDEBUG(D_INODE, "link result %d\n",error);
+ EXIT;
+ 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;
+ struct cnode *dir_cnp = ITOC(dir_inode);
+ int symlen;
+ int error=0;
+
+ ENTRY;
+
+ error = -ENAMETOOLONG;
+ if ( len > CFS_MAXNAMLEN ) {
+ return error;
+ }
+
+ symlen = strlen(symname);
+ if ( symlen > CFS_MAXNAMLEN ) {
+ return error;
+ }
+ CDEBUG(D_INODE, "symname: %s, length: %d\n", symname, symlen);
+
+ error = venus_symlink(dir_inode->i_sb, &(dir_cnp->c_fid), name, len,
+ symname, symlen);
+
+ if ( !error ) {
+ d_drop(de);
+ }
+
+ CDEBUG(D_INODE, "in symlink result %d\n",error);
+ EXIT;
+ return error;
+}
+
+/* destruction routines: unlink, rmdir */
+
+int coda_unlink(struct inode *dir, struct dentry *de)
+{
+ struct cnode *dircnp;
+ int error;
+ const char *name = de->d_name.name;
+ int len = de->d_name.len;
+ char fidstr[50];
+
+ ENTRY;
+
+ dircnp = ITOC(dir);
+ CHECK_CNODE(dircnp);
+
+ CDEBUG(D_INODE, " %s in %s, ino %ld\n", name ,
+ coda_f2s(&(dircnp->c_fid), fidstr), dir->i_ino);
+
+ /* this file should no longer be in the namecache! */
+ cfsnc_zapfile(dircnp, (const char *)name, len);
+
+ error = venus_remove(dir->i_sb, &(dircnp->c_fid), name, len);
+
+ if ( error ) {
+ CDEBUG(D_INODE, "upc returned error %d\n", error);
+ return error;
+ }
+
+ /* cache management */
+ dircnp->c_flags &= ~C_VATTR;
+ cfsnc_zapfid(&(dircnp->c_fid));
+
+ de->d_inode->i_nlink--;
+ d_delete(de);
+
+ return 0;
+}
+
+int coda_rmdir(struct inode *dir, struct dentry *de)
+{
+ struct cnode *dircnp;
+ const char *name = de->d_name.name;
+ int len = de->d_name.len;
+ int error;
+
+ if (!dir || !S_ISDIR(dir->i_mode)) {
+ printk("coda_rmdir: inode is NULL or not a directory\n");
+ return -ENOENT;
+ }
+ dircnp = ITOC(dir);
+ CHECK_CNODE(dircnp);
+
+ if (len > CFS_MAXNAMLEN)
+ return -ENAMETOOLONG;
+
+ /* this directory name should no longer be in the namecache */
+ cfsnc_zapfile(dircnp, (const char *)name, len);
+
+ error = venus_rmdir(dir->i_sb, &(dircnp->c_fid), name, len);
+
+ if ( error ) {
+ CDEBUG(D_INODE, "upc returned error %d\n", error);
+ return error;
+ }
+
+ dircnp->c_flags &= ~C_VATTR;
+ cfsnc_zapfid(&(dircnp->c_fid));
+
+ dir->i_nlink--;
+ d_delete(de);
+
+ 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;
+ struct inode *old_inode = old_dentry->d_inode;
+ struct inode *new_inode = new_dentry->d_inode;
+ struct cnode *new_cnp, *old_cnp;
+ int error, rehash = 0, update = 1;
+ENTRY;
+ old_cnp = ITOC(old_dir);
+ CHECK_CNODE(old_cnp);
+ new_cnp = ITOC(new_dir);
+ CHECK_CNODE(new_cnp);
+
+ CDEBUG(D_INODE, "old: %s, (%d length, %d strlen), new: %s (%d length, %d strlen).\n", old_name, old_length, strlen(old_name), new_name, new_length, strlen(new_name));
+
+ if ( (old_length > CFS_MAXNAMLEN) || new_length > CFS_MAXNAMLEN ) {
+ return -ENAMETOOLONG;
+ }
+
+ /* the old file should go from the namecache */
+ cfsnc_zapfile(old_cnp, (const char *)old_name, old_length);
+ cfsnc_zapfile(new_cnp, (const char *)new_name, new_length);
+
+ /* cross directory moves */
+ if (new_dir != old_dir &&
+ S_ISDIR(old_inode->i_mode) &&
+ old_dentry->d_count > 1)
+ shrink_dcache_parent(old_dentry);
+
+ /* We must prevent any new references to the
+ * target while the rename is in progress, so
+ * we unhash the dentry. */
+ if (!list_empty(&new_dentry->d_hash)) {
+ d_drop(new_dentry);
+ rehash = 1;
+ }
+
+ error = venus_rename(old_dir->i_sb, &(old_cnp->c_fid),
+ &(new_cnp->c_fid), old_length, new_length,
+ (const char *) old_name, (const char *)new_name);
+
+ if (rehash) {
+ d_add(new_dentry, new_inode);
+ }
+
+ if ( error ) {
+ CDEBUG(D_INODE, "returned error %d\n", error);
+ return error;
+ }
+ /* Update the dcache if needed */
+ if (update)
+ d_move(old_dentry, new_dentry);
+
+ CDEBUG(D_INODE, "result %d\n", error);
+
+ EXIT;
+ return 0;
+}
+
+
+
+/* file operations for directories */
+int coda_readdir(struct file *file, void *dirent, filldir_t filldir)
+{
+ int result = 0;
+ struct cnode *cnp;
+ struct file open_file;
+ struct dentry open_dentry;
+ struct inode *inode=file->f_dentry->d_inode;
+
+ ENTRY;
+
+ if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode)) {
+ printk("coda_readdir: inode is NULL or not a directory\n");
+ return -EBADF;
+ }
+
+ cnp = ITOC(inode);
+ CHECK_CNODE(cnp);
+
+ if ( !cnp->c_ovp ) {
+ CDEBUG(D_FILE, "open inode pointer = NULL.\n");
+ return -ENODEV;
+ }
+
+ coda_prepare_openfile(inode, file, cnp->c_ovp, &open_file,
+ &open_dentry);
+ if ( S_ISREG(cnp->c_ovp->i_mode) ) {
+ /* Venus: we must read Venus dirents from the file */
+ result = coda_venus_readdir(&open_file, dirent, filldir);
+ } else {
+ /* potemkin case: we are handed a directory inode */
+ result = open_file.f_op->readdir(&open_file, dirent, filldir);
+ }
+ coda_restore_codafile(inode, file, cnp->c_ovp, &open_file);
+ return result;
+ EXIT;
+}
+
+/* ask venus to cache the file and return the inode of the container file,
+ put this inode pointer in the cnode for future read/writes */
+int coda_open(struct inode *i, struct file *f)
+{
+ ino_t ino;
+ dev_t dev;
+ struct cnode *cnp;
+ int error = 0;
+ struct inode *cont_inode = NULL;
+ unsigned short flags = f->f_flags;
+
+ ENTRY;
+
+ CDEBUG(D_SPECIAL, "OPEN inode number: %ld, flags %o.\n",
+ f->f_dentry->d_inode->i_ino, flags);
+
+ if ( flags & O_CREAT ) {
+ flags &= ~O_EXCL; /* taken care of by coda_create ?? */
+ }
+ cnp = ITOC(i);
+ CHECK_CNODE(cnp);
+
+ error = venus_open(i->i_sb, &(cnp->c_fid), flags, &ino, &dev);
+ if (error) {
+ CDEBUG(D_FILE, "venus: dev %d, inode %ld, out->result %d\n",
+ dev, ino, error);
+ return error;
+ }
+
+ /* coda_upcall returns ino number of cached object, get inode */
+ CDEBUG(D_FILE, "cache file dev %d, ino %ld\n", dev, ino);
+
+ if ( ! cnp->c_ovp ) {
+ error = coda_inode_grab(dev, ino, &cont_inode);
+
+ if ( error ){
+ printk("coda_open: coda_inode_grab error %d.", error);
+ if (cont_inode) iput(cont_inode);
+ return error;
+ }
+ CDEBUG(D_FILE, "GRAB: coda_inode_grab: ino %ld, ops at %x\n",
+ cont_inode->i_ino, (int)cont_inode->i_op);
+ cnp->c_ovp = cont_inode;
+ cnp->c_odentry.d_inode = cont_inode;
+ }
+ cnp->c_ocount++;
+
+ /* if opened for writing flush cache entry. */
+ if ( flags & (O_WRONLY | O_RDWR) ) {
+ cfsnc_zapfid(&(cnp->c_fid));
+ }
+
+ CDEBUG(D_FILE, "result %d, coda i->i_count is %d for ino %ld\n",
+ error, i->i_count, i->i_ino);
+ CDEBUG(D_FILE, "cache ino: %ld, count %d, ops %x\n",
+ cnp->c_ovp->i_ino, cnp->c_ovp->i_count,
+ (int)(cnp->c_ovp->i_op));
+ EXIT;
+ return 0;
+}
+
+int coda_release(struct inode *i, struct file *f)
+{
+ struct cnode *cnp;
+ int error;
+ unsigned short flags = f->f_flags;
+
+ ENTRY;
+
+ cnp =ITOC(i);
+ CHECK_CNODE(cnp);
+ CDEBUG(D_FILE,
+ "RELEASE coda (ino %ld, ct %d) cache (ino %ld, ct %d)\n",
+ i->i_ino, i->i_count, (cnp->c_ovp ? cnp->c_ovp->i_ino : 0),
+ (cnp->c_ovp ? cnp->c_ovp->i_count : -99));
+
+
+ /* even when c_ocount=0 we cannot put c_ovp to
+ * NULL since the file may be mmapped.
+ * See code in inode.c (coda_put_inode) for
+ * further handling of close.
+ */
+
+ --cnp->c_ocount;
+
+ if (flags & (O_WRONLY | O_RDWR)) {
+ --cnp->c_owrite;
+ }
+
+ error = venus_release(i->i_sb, &(cnp->c_fid), flags);
+
+ CDEBUG(D_FILE, "coda_release: result: %d\n", error);
+ return error;
+}
+
+/* support routines */
+/*
+ * this structure is manipulated by filldir in vfs layer.
+ * the count holds the remaining amount of space in the getdents buffer,
+ * beyond the current_dir pointer.
+ */
+
+struct getdents_callback {
+ struct linux_dirent * current_dir;
+ struct linux_dirent * previous;
+ int count;
+ int error;
+};
+
+static int coda_venus_readdir(struct file *filp, void *getdent,
+ filldir_t filldir)
+{
+ int result = 0, offset, count, pos, error = 0;
+ int errfill;
+ caddr_t buff = NULL;
+ struct venus_dirent *vdirent;
+ struct getdents_callback *dents_callback;
+ int string_offset;
+ char debug[255];
+
+ ENTRY;
+
+ /* we also need the ofset of the string in the dirent struct */
+ string_offset = sizeof ( char )* 2 + sizeof(unsigned int) +
+ sizeof(unsigned short);
+
+ dents_callback = (struct getdents_callback *) getdent;
+
+ count = dents_callback->count;
+ CODA_ALLOC(buff, void *, count);
+ if ( ! buff ) {
+ printk("coda_venus_readdir: out of memory.\n");
+ return -ENOMEM;
+ }
+
+ /* we use this routine to read the file into our buffer */
+ result = read_exec(filp->f_dentry, filp->f_pos, buff, count, 1);
+ if ( result < 0) {
+ printk("coda_venus_readdir: cannot read directory %d.\n",
+ result);
+ error = result;
+ goto exit;
+ }
+ if ( result == 0) {
+ error = result;
+ goto exit;
+ }
+
+ /* Parse and write into user space. Filldir tells us when done! */
+ offset = filp->f_pos;
+ pos = 0;
+ CDEBUG(D_FILE, "offset %d, count %d.\n", offset, count);
+
+ while ( pos + string_offset < result ) {
+ vdirent = (struct venus_dirent *) (buff + pos);
+
+ /* test if the name is fully in the buffer */
+ if ( pos + string_offset + (int) vdirent->d_namlen >= result ){
+ break;
+ }
+
+ /* now we are certain that we can read the entry from buff */
+
+ /* for debugging, get the string out */
+ memcpy(debug, vdirent->d_name, vdirent->d_namlen);
+ *(debug + vdirent->d_namlen) = '\0';
+
+ /* if we don't have a null entry, copy it */
+ if ( vdirent->d_fileno ) {
+ int namlen = vdirent->d_namlen;
+ off_t offs = filp->f_pos;
+ ino_t ino = vdirent->d_fileno;
+ char *name = vdirent->d_name;
+ /* adjust count */
+ count = dents_callback->count;
+
+ errfill = filldir(dents_callback, name, namlen,
+ offs, ino);
+CDEBUG(D_FILE, "ino %ld, namlen %d, reclen %d, type %d, pos %d, string_offs %d, name %s, offset %d, count %d.\n", vdirent->d_fileno, vdirent->d_namlen, vdirent->d_reclen, vdirent->d_type, pos, string_offset, debug, (u_int) offs, dents_callback->count);
+
+ /* errfill means no space for filling in this round */
+ if ( errfill < 0 ) break;
+ }
+ /* next one */
+ filp->f_pos += (unsigned int) vdirent->d_reclen;
+ pos += (unsigned int) vdirent->d_reclen;
+ }
+
+exit:
+ CODA_FREE(buff, count);
+ return error;
+}
diff --git a/fs/coda/file.c b/fs/coda/file.c
new file mode 100644
index 000000000..225ca881a
--- /dev/null
+++ b/fs/coda/file.c
@@ -0,0 +1,240 @@
+/*
+ * 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/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+
+#include <linux/coda_namecache.h>
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_psdev.h>
+
+/* file operations */
+static int coda_readpage(struct inode * inode, struct page * page);
+static ssize_t coda_file_read(struct file *f, char *buf, size_t count, loff_t *off);
+static ssize_t coda_file_write(struct file *f, const char *buf, size_t count, loff_t *off);
+static int coda_file_mmap(struct file * file, struct vm_area_struct * vma);
+
+/* exported from this file */
+struct inode_operations coda_file_inode_operations = {
+ &coda_file_operations, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ coda_readpage, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ coda_permission, /* permission */
+ NULL, /* smap */
+ NULL, /* update page */
+ NULL /* revalidate */
+};
+
+struct file_operations coda_file_operations = {
+ NULL, /* lseek - default should work for coda */
+ coda_file_read, /* read */
+ coda_file_write, /* write */
+ NULL, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl */
+ coda_file_mmap, /* mmap */
+ coda_open, /* open */
+ coda_release, /* release */
+ NULL, /* fsync */
+};
+
+/* File file operations */
+static int coda_readpage(struct inode * inode, struct page * page)
+{
+ struct inode *open_inode;
+ struct cnode *cnp;
+
+ ENTRY;
+
+ cnp = ITOC(inode);
+ CHECK_CNODE(cnp);
+
+ if ( ! cnp->c_ovp ) {
+ printk("coda_readpage: no open inode for ino %ld\n", inode->i_ino);
+ return -ENXIO;
+ }
+
+ open_inode = cnp->c_ovp;
+
+ CDEBUG(D_INODE, "coda ino: %ld, cached ino %ld, page offset: %lx\n", inode->i_ino, open_inode->i_ino, page->offset);
+
+ generic_readpage(open_inode, page);
+ EXIT;
+ return 0;
+}
+
+static int coda_file_mmap(struct file * file, struct vm_area_struct * vma)
+{
+ struct cnode *cnp;
+ cnp = ITOC(file->f_dentry->d_inode);
+ cnp->c_mmcount++;
+
+ return generic_file_mmap(file, vma);
+}
+
+static ssize_t coda_file_read(struct file *coda_file, char *buff,
+ size_t count, loff_t *ppos)
+{
+ struct cnode *cnp;
+ struct inode *coda_inode = coda_file->f_dentry->d_inode;
+ struct inode *cont_inode = NULL;
+ struct file cont_file;
+ struct dentry cont_dentry;
+ int result = 0;
+
+ ENTRY;
+
+ cnp = ITOC(coda_inode);
+ CHECK_CNODE(cnp);
+
+ cont_inode = cnp->c_ovp;
+ if ( cont_inode == NULL ) {
+ printk("coda_file_read: cached inode is 0!\n");
+ return -1;
+ }
+
+ coda_prepare_openfile(coda_inode, coda_file, cont_inode,
+ &cont_file, &cont_dentry);
+
+ if (!cont_file.f_op || ! cont_file.f_op->read) {
+ printk( "container file has no read in file operations.\n");
+ return -1;
+ }
+
+ result = cont_file.f_op->read(&cont_file , buff, count,
+ &(cont_file.f_pos));
+
+ CDEBUG(D_FILE, "ops at %x result %d, count %d, position: %d\n",
+ (int)cont_file.f_op, result, count, (int)cont_file.f_pos);
+
+ coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file);
+ return result;
+}
+
+
+static ssize_t coda_file_write(struct file *coda_file, const char *buff,
+ size_t count, loff_t *ppos)
+{
+ struct cnode *cnp;
+ struct inode *coda_inode = coda_file->f_dentry->d_inode;
+ struct inode *cont_inode = NULL;
+ struct file cont_file;
+ struct dentry cont_dentry;
+ int result = 0;
+
+ ENTRY;
+
+ cnp = ITOC(coda_inode);
+ CHECK_CNODE(cnp);
+
+ cont_inode = cnp->c_ovp;
+ if ( cont_inode == NULL ) {
+ printk("coda_file_write: cached inode is 0!\n");
+ return -1;
+ }
+
+ coda_prepare_openfile(coda_inode, coda_file, cont_inode,
+ &cont_file, &cont_dentry);
+
+ if (!cont_file.f_op || !cont_file.f_op->write) {
+ printk("coda_file_write: container file has no file ops.\n");
+ return -1;
+ }
+
+ cnp->c_flags &= ~C_VATTR;
+
+ down(&cont_inode->i_sem);
+ result = cont_file.f_op->write(&cont_file , buff, count,
+ &(cont_file.f_pos));
+ up(&cont_inode->i_sem);
+ coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file);
+
+ return result;
+}
+
+
+
+/*
+ * support routines
+ */
+
+/* instantiate the container file and dentry object to do io */
+void coda_prepare_openfile(struct inode *i, struct file *coda_file,
+ struct inode *cont_inode, struct file *cont_file,
+ struct dentry *cont_dentry)
+{
+ cont_file->f_pos = coda_file->f_pos;
+ cont_file->f_mode = coda_file->f_mode;
+ cont_file->f_flags = coda_file->f_flags;
+ cont_file->f_count = coda_file->f_count;
+ cont_file->f_owner = coda_file->f_owner;
+ cont_file->f_op = cont_inode->i_op->default_file_ops;
+ cont_file->f_dentry = cont_dentry;
+ cont_file->f_dentry->d_inode = cont_inode;
+ return ;
+}
+
+/* update the Coda file & inode after I/O */
+void coda_restore_codafile(struct inode *coda_inode, struct file *coda_file,
+ struct inode *open_inode, struct file *open_file)
+{
+ coda_file->f_pos = open_file->f_pos;
+ /* XXX what about setting the mtime here too? */
+ coda_inode->i_mtime = open_inode->i_mtime;
+ coda_inode->i_size = open_inode->i_size;
+ return;
+}
+
+/* grab the ext2 inode of the container file */
+int coda_inode_grab(dev_t dev, ino_t ino, struct inode **ind)
+{
+ struct super_block *sbptr;
+
+ sbptr = get_super(dev);
+
+ if ( !sbptr ) {
+ printk("coda_inode_grab: coda_find_super returns NULL.\n");
+ return -ENXIO;
+ }
+
+ *ind = NULL;
+ *ind = iget(sbptr, ino);
+
+ if ( *ind == NULL ) {
+ printk("coda_inode_grab: iget(dev: %d, ino: %ld)
+ returns NULL.\n", dev, ino);
+ return -ENOENT;
+ }
+ CDEBUG(D_FILE, "ino: %ld, ops at %x\n", ino, (int)(*ind)->i_op);
+ return 0;
+}
+
diff --git a/fs/coda/inode.c b/fs/coda/inode.c
new file mode 100644
index 000000000..b295470fa
--- /dev/null
+++ b/fs/coda/inode.c
@@ -0,0 +1,34 @@
+/*
+ * 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/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.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_cnode.h>
+#include <linux/coda_namecache.h>
+
+/* prototypes */
+static int coda_readpage(struct inode *inode, struct page *page);
+static int coda_cnode_makectl(struct inode **inode, struct super_block *sb);
+
+
+
+
+
diff --git a/fs/coda/namecache.c b/fs/coda/namecache.c
new file mode 100644
index 000000000..08f1ee9e7
--- /dev/null
+++ b/fs/coda/namecache.c
@@ -0,0 +1,832 @@
+/*
+ * Cache 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>.
+ */
+
+/*
+ * This module contains the routines to implement the CFS name cache. The
+ * purpose of this cache is to reduce the cost of translating pathnames
+ * into Vice FIDs. Each entry in the cache contains the name of the file,
+ * the vnode (FID) of the parent directory, and the cred structure of the
+ * user accessing the file.
+ *
+ * The first time a file is accessed, it is looked up by the local Venus
+ * which first insures that the user has access to the file. In addition
+ * we are guaranteed that Venus will invalidate any name cache entries in
+ * case the user no longer should be able to access the file. For these
+ * reasons we do not need to keep access list information as well as a
+ * cred structure for each entry.
+ *
+ * The table can be accessed through the routines cnc_init(), cnc_enter(),
+ * cnc_lookup(), cnc_rmfidcred(), cnc_rmfid(), cnc_rmcred(), and cnc_purge().
+ * There are several other routines which aid in the implementation of the
+ * hash table.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/string.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_namecache.h>
+
+int cfsnc_use;
+
+static struct cfscache * cfsnc_find(struct cnode *dcp, const char * name, int namelen, int hash);
+static void cfsnc_remove(struct cfscache *cncp);
+static inline int nchash(const char *, int, struct cnode *);
+static inline int ncmatch(struct cfscache *, const char *, int,
+ struct cnode *);
+static inline void hashins(struct cfscache *a, struct cfscache *pred);
+static inline void hashrem(struct cfscache *a);
+static inline void hashnull(struct cfscache *);
+static inline void lrurem(struct cfscache *a);
+static inline void lruins(struct cfscache *a, struct cfscache *pred);
+static void cfsnc_gather_stats(void);
+
+
+/* externals */
+extern int coda_fideq(ViceFid *fid1, ViceFid *fid2);
+extern int coda_debug;
+extern int coda_print_entry;
+extern struct super_block *coda_super_block;
+
+
+
+/*
+ * Declaration of the name cache data structure.
+ */
+
+int cfsnc_use = 0; /* Indicate use of CFS Name Cache */
+int cfsnc_size = CFSNC_CACHESIZE; /* size of the cache */
+int cfsnc_hashsize = CFSNC_HASHSIZE; /* size of the primary hash */
+int cfsnc_flushme = 0;
+int cfsnc_procsize = 0;
+static int cfsnc_force = 0;
+
+struct cfshash {
+ struct cfscache *hash_next, *hash_prev;
+ int length;
+};
+
+struct cfslruhead {
+ struct cfscache *dummy1, *dummy2;
+ struct cfscache *lru_next, *lru_prev;
+};
+
+struct cfscache *cfsncheap; /* pointer to the cache entries */
+struct cfshash *cfsnchash; /* hash table of cfscache pointers */
+struct cfslruhead cfsnc_lru; /* head of lru chain; prev = lru */
+
+struct cfsnc_statistics cfsnc_stat; /* Keep various stats */
+
+#define TOTAL_CACHE_SIZE (sizeof(struct cfscache) * cfsnc_size)
+#define TOTAL_HASH_SIZE (sizeof(struct cfshash) * cfsnc_hashsize)
+int cfsnc_initialized = 0; /* Initially the cache has not been initialized */
+
+/*
+ * for testing purposes
+ */
+int cfsnc_debug = 1;
+
+
+/*
+ * Auxillary routines -- shouldn't be entry points
+ */
+
+
+/*
+ * Hash function for the primary hash.
+ * First try -- (first + last letters + length + (int)cp) mod size
+ * 2nd try -- same, except dir fid.vnode instead of cp
+ */
+static inline int
+nchash(const char *name, int namelen, struct cnode *cp)
+{
+ return ((name[0] + name[namelen-1] +
+ namelen + (int)(cp)) & (cfsnc_hashsize-1));
+}
+
+/* matching function */
+static inline int ncmatch(struct cfscache *cp, const char *name, int namelen,
+ struct cnode *dcp)
+{
+ return ((namelen == cp->namelen) && (dcp == cp->dcp) &&
+ (memcmp(cp->name,name,namelen) == 0));
+}
+
+/* insert a behind pred */
+static inline void hashins(struct cfscache *a, struct cfscache *pred)
+{
+ a->hash_next = pred->hash_next;
+ pred->hash_next->hash_prev= a;
+ pred->hash_next = a;
+ a->hash_prev = pred;
+}
+
+static inline void hashrem(struct cfscache *a)
+{
+ a->hash_prev->hash_next = a->hash_next;
+ a->hash_next->hash_prev = a->hash_prev;
+}
+
+static inline void hashnull(struct cfscache *elem) {
+ elem->hash_next = elem;
+ elem->hash_prev = elem;
+}
+
+static inline void lrurem(struct cfscache *a)
+{
+ a->lru_prev->lru_next = a->lru_next;
+ a->lru_next->lru_prev = a->lru_prev;
+}
+
+static inline void lruins(struct cfscache *a, struct cfscache *pred)
+{
+ pred->lru_next->lru_prev= a;
+ a->lru_next = pred->lru_next;
+
+ a->lru_prev = pred;
+ pred->lru_next = a;
+}
+
+static struct cfscache *
+cfsnc_find(struct cnode *dcp, const char * name, int namelen, int hash)
+{
+ /*
+ * hash to find the appropriate bucket, look through the chain
+ * for the right entry
+ */
+ register struct cfscache *cncp;
+ int count = 1;
+
+ CDEBUG(D_CACHE, "dcp 0x%x, name %s, len %d, hash %d\n",
+ (int)dcp, name, namelen, hash);
+
+ for (cncp = cfsnchash[hash].hash_next;
+ cncp != (struct cfscache *)&cfsnchash[hash];
+ cncp = cncp->hash_next, count++)
+ {
+
+ if (ncmatch(cncp, name, namelen, dcp))
+ {
+ cfsnc_stat.Search_len += count;
+ CDEBUG(D_CACHE, "dcp 0x%x,found.\n", (int) dcp);
+ return(cncp);
+
+ }
+ }
+ CDEBUG(D_CACHE, "dcp 0x%x,not found.\n", (int) dcp);
+ return((struct cfscache *)0);
+}
+
+static void
+cfsnc_remove(struct cfscache *cncp)
+{
+ /*
+ * remove an entry -- VN_RELE(cncp->dcp, cp), crfree(cred),
+ * remove it from it's hash chain, and
+ * place it at the head of the lru list.
+ */
+ CDEBUG(D_CACHE, "remove %s from parent %lx.%lx.%lx\n",
+ cncp->name, (cncp->dcp)->c_fid.Volume,
+ (cncp->dcp)->c_fid.Vnode, (cncp->dcp)->c_fid.Unique);
+
+ hashrem(cncp);
+ hashnull(cncp); /* have it be a null chain */
+
+ /* VN_RELE(CTOV(cncp->dcp)); */
+ iput(CTOI(cncp->cp));
+ /* crfree(cncp->cred); */
+
+ memset(DATA_PART(cncp), 0 ,DATA_SIZE);
+ cncp->cp = NULL;
+ cncp->dcp = (struct cnode *) 0;
+
+ /* Put the null entry just after the least-recently-used entry */
+ lrurem(cncp);
+ lruins(cncp, cfsnc_lru.lru_prev);
+}
+
+
+/*
+ * Entry points for the CFS Name Cache
+ */
+
+/*
+ * Initialize the cache, the LRU structure and the Hash structure(s)
+ */
+void
+cfsnc_init(void)
+{
+ register int i;
+
+ /* zero the statistics structure */
+ cfsnc_procsize = 10000 * cfsnc_hashsize + cfsnc_size;
+ memset(&cfsnc_stat, 0, (sizeof(struct cfsnc_statistics)));
+
+ CODA_ALLOC(cfsncheap, struct cfscache *, TOTAL_CACHE_SIZE);
+ CODA_ALLOC(cfsnchash, struct cfshash *, TOTAL_HASH_SIZE);
+
+ cfsnc_lru.lru_next = cfsnc_lru.lru_prev = (struct cfscache *)&cfsnc_lru;
+
+ /* initialize the heap */
+ for (i=0; i < cfsnc_size; i++) {
+ lruins(&cfsncheap[i], (struct cfscache *) &cfsnc_lru);
+ hashnull(&cfsncheap[i]);
+ cfsncheap[i].cp = cfsncheap[i].dcp = (struct cnode *)0;
+ }
+
+ for (i=0; i < cfsnc_hashsize; i++) { /* initialize the hashtable */
+ hashnull((struct cfscache *)&cfsnchash[i]);
+ cfsnchash[i].length=0; /* bucket length */
+ }
+
+ cfsnc_initialized = 1;
+ CDEBUG(D_CACHE, "cfsnc_initialized is now 1.\n");
+}
+
+/*
+ * Enter a new (dir cnode, name) pair into the cache, updating the
+ * LRU and Hash as needed.
+ */
+
+void
+cfsnc_enter(struct cnode *dcp, register const char *name, int namelen, struct cnode *cp)
+{
+ register struct cfscache *cncp;
+ register int hash;
+
+ if (cfsnc_use == 0) /* Cache is off */
+ return;
+
+ CDEBUG(D_CACHE, "dcp 0x%x cp 0x%x name %s, ind 0x%x \n",
+ (int)dcp, (int)cp, name, (int)cp->c_vnode);
+
+ if (namelen > CFSNC_NAMELEN) {
+ CDEBUG(D_CACHE, "long name enter %s\n",name);
+ cfsnc_stat.long_name_enters++; /* record stats */
+ return;
+ }
+
+ hash = nchash(name, namelen, dcp);
+ CDEBUG(D_CACHE, "Calling find with name %s, dcp %d, hash %d\n",
+ name, (int) dcp, (int) hash);
+
+ cncp = cfsnc_find(dcp, name, namelen, hash);
+ if (cncp != (struct cfscache *) 0) {
+ printk("cfsnc_enter: Duplicate cache entry; tell Peter.\n");
+ cfsnc_stat.dbl_enters++; /* duplicate entry */
+ return;
+ }
+
+ cfsnc_stat.enters++; /* record the enters statistic */
+
+ /* Grab the lru element in the lru chain */
+ cncp = cfsnc_lru.lru_prev;
+
+ lrurem(cncp); /* remove it from the lists */
+
+ /* if cncp is on hash list remove it */
+ if ( cncp->dcp != (struct cnode *) 0 ) {
+ /* We have to decrement the appropriate hash bucket length
+ here, so we have to find the hash bucket */
+ cfsnchash[nchash(cncp->name, cncp->namelen, cncp->dcp)].length--;
+ cfsnc_stat.lru_rm++; /* zapped a valid entry */
+ hashrem(cncp);
+ iput(CTOI(cncp->cp));
+ /* VN_RELE(CTOV(cncp->dcp)); */
+ /* crfree(cncp->cred); */
+ }
+ /*
+ * Put a hold on the current vnodes and fill in the cache entry.
+ */
+ iget((CTOI(cp))->i_sb, CTOI(cp)->i_ino);
+ /* VN_HOLD(CTOV(dcp)); */
+ /* XXXX crhold(cred); */
+ cncp->dcp = dcp;
+ cncp->cp = cp;
+ cncp->namelen = namelen;
+ /* cncp->cred = cred; */
+
+ memcpy(cncp->name, name, (unsigned)namelen);
+
+ /* Insert into the lru and hash chains. */
+
+ lruins(cncp, (struct cfscache *) &cfsnc_lru);
+ hashins(cncp, (struct cfscache *)&cfsnchash[hash]);
+ cfsnchash[hash].length++; /* Used for tuning */
+ CDEBUG(D_CACHE, "Entering:\n");
+ coda_print_ce(cncp);
+}
+
+/*
+ * Find the (dir cnode, name) pair in the cache, if it's cred
+ * matches the input, return it, otherwise return 0
+ */
+
+struct cnode *
+cfsnc_lookup(struct cnode *dcp, register const char *name, int namelen)
+{
+ register int hash;
+ register struct cfscache *cncp;
+ /* this should go into a callback funcntion for /proc/sys
+ don't know how at the moment? */
+ if (cfsnc_flushme == 1) {
+ cfsnc_flush();
+ cfsnc_flushme = 0;
+ }
+
+ if (cfsnc_procsize != 10000*cfsnc_hashsize + cfsnc_size ) {
+ int hsh = cfsnc_procsize/10000;
+ int siz = cfsnc_procsize%10000;
+ int rc;
+
+ if ( (hsh > 1) && (siz > 2) ) {
+ rc = cfsnc_resize(hsh, siz);
+ if ( !rc ) {
+ printk("Coda:cache size (hash,size) (%d,%d)\n",
+ hsh, siz);
+ } else {
+ printk("Coda: cache resize failed\n");
+ }
+ }
+ }
+
+ if (cfsnc_use == 0) /* Cache is off */
+ return((struct cnode *) 0);
+
+ if (namelen > CFSNC_NAMELEN) {
+ CDEBUG(D_CACHE,"long name lookup %s\n",name);
+ cfsnc_stat.long_name_lookups++; /* record stats */
+ return((struct cnode *) 0);
+ }
+
+ /* Use the hash function to locate the starting point,
+ then the search routine to go down the list looking for
+ the correct cred.
+ */
+
+ hash = nchash(name, namelen, dcp);
+ CDEBUG(D_CACHE, "Calling find with name %s, dcp %d, hash %d\n",
+ name, (int) dcp, (int) hash);
+ cncp = cfsnc_find(dcp, name, namelen, hash);
+ if (cncp == (struct cfscache *) 0) {
+ cfsnc_stat.misses++; /* record miss */
+ return((struct cnode *) 0);
+ }
+
+ cfsnc_stat.hits++;
+
+ /* put this entry at the mru end of the LRU */
+ lrurem(cncp);
+ lruins(cncp, (struct cfscache *) &cfsnc_lru);
+
+ /* move it to the front of the hash chain */
+ /* don't need to change the hash bucket length */
+ hashrem(cncp);
+ hashins(cncp, (struct cfscache *) &cfsnchash[hash]);
+
+ CDEBUG(D_CACHE, "lookup: dcp 0x%x, name %s, cp 0x%x\n",
+ (int) dcp, name, (int) cncp->cp);
+
+ return(cncp->cp);
+}
+
+/*
+ * Remove all entries with a parent which has the input fid.
+ */
+
+void
+cfsnc_zapParentfid(ViceFid *fid)
+{
+ /* To get to a specific fid, we might either have another hashing
+ function or do a sequential search through the cache for the
+ appropriate entries. The later may be acceptable since I don't
+ think callbacks or whatever Case 1 covers are frequent occurences.
+ */
+ register struct cfscache *cncp, *ncncp;
+ register int i;
+
+ if (cfsnc_use == 0) /* Cache is off */
+ return;
+
+ CDEBUG(D_CACHE, " fid 0x%lx, 0x%lx, 0x%lx \n",
+ fid->Volume, fid->Vnode, fid->Unique);
+
+ cfsnc_stat.zapPfids++;
+
+ for (i = 0; i < cfsnc_hashsize; i++) {
+
+ /*
+ * Need to save the hash_next pointer in case we remove the
+ * entry. remove causes hash_next to point to itself.
+ */
+
+ for (cncp = cfsnchash[i].hash_next;
+ cncp != (struct cfscache *) &cfsnchash[i];
+ cncp = ncncp) {
+ ncncp = cncp->hash_next;
+ if ( coda_fideq(&cncp->dcp->c_fid, fid) ) {
+ cfsnchash[i].length--; /* Used for tuning */
+ cfsnc_remove(cncp);
+ }
+ }
+ }
+}
+
+/*
+ * Remove all entries which have the same fid as the input
+ */
+void
+cfsnc_zapfid(ViceFid *fid)
+{
+ /* See comment for zapParentfid. This routine will be used
+ if attributes are being cached.
+ */
+ register struct cfscache *cncp, *ncncp;
+ register int i;
+
+ if (cfsnc_use == 0) /* Cache is off */
+ return;
+
+ CDEBUG(D_CACHE, "Zapfid: fid 0x%lx, 0x%lx, 0x%lx \n",
+ fid->Volume, fid->Vnode, fid->Unique);
+
+ cfsnc_stat.zapFids++;
+
+ for (i = 0; i < cfsnc_hashsize; i++) {
+ for (cncp = cfsnchash[i].hash_next;
+ cncp != (struct cfscache *) &cfsnchash[i];
+ cncp = ncncp) {
+ ncncp = cncp->hash_next;
+ if (coda_fideq(&(cncp->cp->c_fid), fid)) {
+ CDEBUG(D_CACHE, "Found cncp: name %s\n", cncp->name);
+ cfsnchash[i].length--; /* Used for tuning */
+ cfsnc_remove(cncp);
+ }
+ }
+ }
+}
+
+
+/*
+ * Remove all entries which have the (dir vnode, name) pair
+ */
+void
+cfsnc_zapfile(struct cnode *dcp, register const char *name, int length)
+{
+ /* use the hash function to locate the file, then zap all
+ entries of it regardless of the cred.
+ */
+ register struct cfscache *cncp;
+ int hash;
+
+ if (cfsnc_use == 0) /* Cache is off */
+ return;
+
+ CDEBUG(D_CACHE,"Zapfile: dcp 0x%x name %s \n",
+ (int) dcp, name);
+
+ if (length > CFSNC_NAMELEN) {
+ cfsnc_stat.long_remove++; /* record stats */
+ return;
+ }
+
+ cfsnc_stat.zapFile++;
+
+ hash = nchash(name, length, dcp);
+ /* remove entries: remember they might exist for more than a
+ single cred */
+ while ( (cncp = cfsnc_find(dcp, name, length, hash)) != NULL ) {
+ cfsnchash[hash].length--;
+ cfsnc_remove(cncp);
+ }
+}
+
+/*
+ * Remove all the entries for a particular user. Used when tokens expire.
+ * A user is determined by his/her effective user id (id_uid).
+ */
+
+void
+cfsnc_purge_user(struct CodaCred *cred)
+{
+ /* I think the best approach is to go through the entire cache
+ via HASH or whatever and zap all entries which match the
+ input cred. Or just flush the whole cache.
+ It might be best to go through on basis of LRU since cache
+ will almost always be full and LRU is more straightforward.
+ */
+
+ register struct cfscache *cncp;
+ int hash;
+
+ if (cfsnc_use == 0) /* Cache is off */
+ return;
+
+ CDEBUG(D_CACHE,"ZapDude: uid %ld\n",cred->cr_uid);
+ cfsnc_stat.zapUsers++;
+
+ for (cncp = cfsnc_lru.lru_next;
+ cncp != (struct cfscache *) &cfsnc_lru;
+ cncp = cncp->lru_next) {
+
+ if ((CFSNC_VALID(cncp)) &&
+ ((cncp->cred)->cr_uid == cred->cr_uid)) {
+ /* Seems really ugly, but we have to decrement the appropriate
+ hash bucket length here, so we have to find the hash bucket
+ */
+ hash = nchash(cncp->name, cncp->namelen, cncp->dcp);
+ cfsnchash[hash].length--; /* For performance tuning */
+
+ cfsnc_remove(cncp);
+ }
+ }
+}
+
+/*
+ * Flush the entire name cache. In response to a flush of the Venus cache.
+ */
+
+void
+cfsnc_flush(void)
+{
+ /* One option is to deallocate the current name cache and
+ call init to start again. Or just deallocate, then rebuild.
+ Or again, we could just go through the array and zero the
+ appropriate fields.
+ */
+
+ /*
+ * Go through the whole lru chain and kill everything as we go.
+ * I don't use remove since that would rebuild the lru chain
+ * as it went and that seemed unneccesary.
+ */
+ register struct cfscache *cncp;
+ int i;
+
+ if ((cfsnc_use == 0 || cfsnc_initialized == 0) && (cfsnc_force == 0) )
+ return;
+
+ cfsnc_stat.Flushes++;
+
+ for (cncp = cfsnc_lru.lru_next;
+ cncp != (struct cfscache *) &cfsnc_lru;
+ cncp = cncp->lru_next) {
+ if ( cncp->cp ) {
+ hashrem(cncp); /* only zero valid nodes */
+ hashnull(cncp);
+ iput(CTOI(cncp->cp));
+ /* crfree(cncp->cred); */
+ memset(DATA_PART(cncp), 0, DATA_SIZE);
+ }
+ }
+
+ for (i = 0; i < cfsnc_hashsize; i++)
+ cfsnchash[i].length = 0;
+}
+
+/*
+ * This routine replaces a ViceFid in the name cache with another.
+ * It is added to allow Venus during reintegration to replace
+ * locally allocated temp fids while disconnected with global fids
+ * even when the reference count on those fids are not zero.
+ */
+void
+cfsnc_replace(ViceFid *f1, ViceFid *f2)
+{
+ /*
+ * Replace f1 with f2 throughout the name cache
+ */
+ int hash;
+ register struct cfscache *cncp;
+
+ CDEBUG(D_CACHE,
+ "cfsnc_replace fid_1 = (%lx.%lx.%lx) and fid_2 = (%lx.%lx.%lx)\n",
+ f1->Volume, f1->Vnode, f1->Unique,
+ f2->Volume, f2->Vnode, f2->Unique);
+
+ for (hash = 0; hash < cfsnc_hashsize; hash++) {
+ for (cncp = cfsnchash[hash].hash_next;
+ cncp != (struct cfscache *) &cfsnchash[hash];
+ cncp = cncp->hash_next) {
+ if (!memcmp(&cncp->cp->c_fid, f1, sizeof(ViceFid))) {
+ memcpy(&cncp->cp->c_fid, f2, sizeof(ViceFid));
+ continue; /* no need to check cncp->dcp now */
+ }
+ if (!memcmp(&cncp->dcp->c_fid, f1, sizeof(ViceFid)))
+ memcpy(&cncp->dcp->c_fid, f2, sizeof(ViceFid));
+ }
+ }
+}
+
+/*
+ * Debugging routines
+ */
+
+/*
+ * This routine should print out all the hash chains to the console.
+ */
+
+void
+print_cfsnc(void)
+{
+ int hash;
+ register struct cfscache *cncp;
+
+ for (hash = 0; hash < cfsnc_hashsize; hash++) {
+ printk("\nhash %d\n",hash);
+
+ for (cncp = cfsnchash[hash].hash_next;
+ cncp != (struct cfscache *)&cfsnchash[hash];
+ cncp = cncp->hash_next) {
+ printk("cp 0x%x dcp 0x%x cred 0x%x name %s ino %d count %d dev %d\n",
+ (int)cncp->cp, (int)cncp->dcp,
+ (int)cncp->cred, cncp->name, CTOI(cncp->cp)->i_count, CTOI(cncp->cp)->i_count, CTOI(cncp->cp)->i_dev);
+ }
+ }
+}
+
+int
+cfsnc_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int hash;
+ int len=0;
+ off_t pos=0;
+ off_t begin;
+ struct cfscache *cncp;
+ char tmpbuf[80];
+
+ if (offset < 80)
+ len += sprintf(buffer, "%-79s\n",
+ "hash len volume vnode unique name ino pino ct");
+ if ( !cfsnc_initialized ) {
+ *start = buffer;
+ return len;
+ }
+ pos = 80;
+ for (hash = 0; hash < cfsnc_hashsize; hash++) {
+ for (cncp = cfsnchash[hash].hash_next;
+ cncp != (struct cfscache *)&cfsnchash[hash];
+ cncp = cncp->hash_next) {
+ pos += 80;
+ if (pos < offset)
+ continue;
+ sprintf(tmpbuf, "%4d %3d %8x %8x %8x %16s %10ld %10ld %2d",
+ hash, cfsnchash[hash].length, (int) cncp->cp->c_fid.Volume,
+ (int) cncp->cp->c_fid.Vnode, (int) cncp->cp->c_fid.Unique , cncp->name,
+ CTOI(cncp->cp)->i_ino,
+ CTOI(cncp->dcp)->i_ino,
+ CTOI(cncp->cp)->i_count);
+ len += sprintf(buffer+len, "%-79s\n", tmpbuf);
+ if(len >= length)
+ break;
+ }
+ if(len>= length)
+ break;
+ }
+ begin = len - (pos - offset);
+ *start = buffer + begin;
+ len -= begin;
+ if(len>length)
+ len = length;
+ return len;
+}
+
+int
+cfsnc_nc_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len=0;
+ off_t begin;
+
+ cfsnc_gather_stats();
+
+ /* this works as long as we are below 1024 characters! */
+ len += sprintf(buffer,"Coda minicache statistics\n\n");
+ len += sprintf(buffer+len, "cfsnc_hits : %d\n", cfsnc_stat.hits);
+ len += sprintf(buffer+len, "cfsnc_misses : %d\n", cfsnc_stat.misses);
+ len += sprintf(buffer+len, "cfsnc_enters : %d\n", cfsnc_stat.enters);
+ len += sprintf(buffer+len, "cfsnc_dbl_enters : %d\n", cfsnc_stat.dbl_enters);
+ len += sprintf(buffer+len, "cfsnc_long_name_enters : %d\n", cfsnc_stat.long_name_enters);
+ len += sprintf(buffer+len, "cfsnc_long_name_lookups : %d\n", cfsnc_stat.long_name_lookups);
+ len += sprintf(buffer+len, "cfsnc_long_remove : %d\n", cfsnc_stat.long_remove);
+ len += sprintf(buffer+len, "cfsnc_lru_rm : %d\n", cfsnc_stat.lru_rm);
+ len += sprintf(buffer+len, "cfsnc_zapPfids : %d\n", cfsnc_stat.zapPfids);
+ len += sprintf(buffer+len, "cfsnc_zapFids : %d\n", cfsnc_stat.zapFids);
+ len += sprintf(buffer+len, "cfsnc_zapFile : %d\n", cfsnc_stat.zapFile);
+ len += sprintf(buffer+len, "cfsnc_zapUsers : %d\n", cfsnc_stat.zapUsers);
+ len += sprintf(buffer+len, "cfsnc_Flushes : %d\n", cfsnc_stat.Flushes);
+ len += sprintf(buffer+len, "cfsnc_SumLen : %d\n", cfsnc_stat.Sum_bucket_len);
+ len += sprintf(buffer+len, "cfsnc_Sum2Len : %d\n", cfsnc_stat.Sum2_bucket_len);
+ len += sprintf(buffer+len, "cfsnc_# 0 len : %d\n", cfsnc_stat.Num_zero_len);
+ len += sprintf(buffer+len, "cfsnc_MaxLen : %d\n", cfsnc_stat.Max_bucket_len);
+ len += sprintf(buffer+len, "cfsnc_SearchLen : %d\n", cfsnc_stat.Search_len);
+ begin = offset;
+ *start = buffer + begin;
+ len -= begin;
+
+ if(len>length)
+ len = length;
+ if (len< 0)
+ len = 0;
+ return len;
+}
+
+
+
+void
+coda_print_ce(struct cfscache *ce)
+{
+CDEBUG(D_CACHE, "cp 0x%x, dcp 0x%x, name %s, inod 0x%x, ino %d, count %d, dev %d\n",
+ (int)ce->cp, (int)ce->dcp, ce->name, (int)CTOI(ce->cp),(int)CTOI(ce->cp)->i_ino, CTOI(ce->cp)->i_count, CTOI(ce->cp)->i_dev);
+}
+
+static void
+cfsnc_gather_stats(void)
+{
+ int i, max = 0, sum = 0, temp, zeros = 0, ave, n;
+
+ for (i = 0; i < cfsnc_hashsize; i++) {
+ if (cfsnchash[i].length) {
+ sum += cfsnchash[i].length;
+ } else {
+ zeros++;
+ }
+
+ if (cfsnchash[i].length > max)
+ max = cfsnchash[i].length;
+ }
+
+/*
+ * When computing the Arithmetic mean, only count slots which
+ * are not empty in the distribution.
+ */
+ cfsnc_stat.Sum_bucket_len = sum;
+ cfsnc_stat.Num_zero_len = zeros;
+ cfsnc_stat.Max_bucket_len = max;
+
+ if ((n = cfsnc_hashsize - zeros) > 0)
+ ave = sum / n;
+ else
+ ave = 0;
+
+ sum = 0;
+ for (i = 0; i < cfsnc_hashsize; i++) {
+ if (cfsnchash[i].length) {
+ temp = cfsnchash[i].length - ave;
+ sum += temp * temp;
+ }
+ }
+ cfsnc_stat.Sum2_bucket_len = sum;
+}
+
+/*
+ * The purpose of this routine is to allow the hash and cache sizes to be
+ * changed dynamically. This should only be used in controlled environments,
+ * it makes no effort to lock other users from accessing the cache while it
+ * is in an improper state (except by turning the cache off).
+ */
+int
+cfsnc_resize(int hashsize, int heapsize)
+{
+ if ( !cfsnc_use )
+ return 0;
+
+ if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */
+ return(EINVAL);
+ }
+
+ cfsnc_use = 0; /* Turn the cache off */
+ cfsnc_force = 1; /* otherwise we can't flush */
+
+ cfsnc_flush(); /* free any cnodes in the cache */
+ cfsnc_force = 0;
+
+ /* WARNING: free must happen *before* size is reset */
+ CODA_FREE(cfsncheap,TOTAL_CACHE_SIZE);
+ CODA_FREE(cfsnchash,TOTAL_HASH_SIZE);
+
+ cfsnc_hashsize = hashsize;
+ cfsnc_size = heapsize;
+
+ cfsnc_init(); /* Set up a cache with the new size */
+
+ cfsnc_use = 1; /* Turn the cache back on */
+ return(0);
+}
+
+
+
diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c
new file mode 100644
index 000000000..c24008cd6
--- /dev/null
+++ b/fs/coda/pioctl.c
@@ -0,0 +1,154 @@
+/*
+ * 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/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+
+#include <linux/coda_namecache.h>
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_psdev.h>
+
+/* pioctl ops */
+static int coda_ioctl_permission(struct inode *inode, int mask);
+static int coda_ioctl_open(struct inode *i, struct file *f);
+static int coda_ioctl_release(struct inode *i, struct file *f);
+static int coda_pioctl(struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long arg);
+
+/* exported from this file */
+struct inode_operations coda_ioctl_inode_operations =
+{
+ &coda_ioctl_operations,
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ coda_ioctl_permission, /* permission */
+ NULL, /* smap */
+ NULL, /* update page */
+ NULL /* revalidate */
+};
+
+struct file_operations coda_ioctl_operations = {
+ NULL, /* lseek - default should work for coda */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select - default */
+ coda_pioctl, /* ioctl */
+ NULL, /* mmap */
+ coda_ioctl_open, /* open */
+ coda_ioctl_release, /* release */
+ NULL, /* fsync */
+};
+
+/* the coda pioctl inode ops */
+static int coda_ioctl_permission(struct inode *inode, int mask)
+{
+ ENTRY;
+
+ return 0;
+}
+
+/* The pioctl file ops*/
+int coda_ioctl_open(struct inode *i, struct file *f)
+{
+
+ ENTRY;
+
+ CDEBUG(D_PIOCTL, "File inode number: %ld\n",
+ f->f_dentry->d_inode->i_ino);
+
+ EXIT;
+ return 0;
+}
+
+int coda_ioctl_release(struct inode *i, struct file *f)
+{
+ return 0;
+}
+
+
+static int coda_pioctl(struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long user_data)
+{
+ struct dentry *target_de;
+ int error;
+ struct PioctlData data;
+ struct inode *target_inode = NULL;
+ struct cnode *cnp;
+
+ ENTRY;
+ /* get the Pioctl data arguments from user space */
+ if (copy_from_user(&data, (int *)user_data, sizeof(data))) {
+ return -EINVAL;
+ }
+
+ /*
+ * Look up the pathname. Note that the pathname is in
+ * user memory, and namei takes care of this
+ */
+ CDEBUG(D_PIOCTL, "namei, data.follow = %d\n", data.follow);
+ if ( data.follow ) {
+ target_de = namei(data.path);
+ } else {
+ target_de = lnamei(data.path);
+ }
+
+ if (!target_de) {
+ CDEBUG(D_PIOCTL, "error: lookup fails.\n");
+ return -EINVAL;
+ } else {
+ target_inode = target_de->d_inode;
+ }
+
+ CDEBUG(D_PIOCTL, "target ino: 0x%ld, dev: 0x%d\n",
+ target_inode->i_ino, target_inode->i_dev);
+
+ /* return if it is not a Coda inode */
+ if ( target_inode->i_sb != inode->i_sb ) {
+ if ( target_de )
+ dput(target_de);
+ return -EINVAL;
+ }
+
+ /* now proceed to make the upcall */
+ cnp = ITOC(target_inode);
+ CHECK_CNODE(cnp);
+
+ error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data);
+
+ CDEBUG(D_PIOCTL, "ioctl on inode %ld\n", target_inode->i_ino);
+
+ if ( target_de )
+ dput(target_de);
+ return error;
+}
+
diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c
new file mode 100644
index 000000000..cd591d3e9
--- /dev/null
+++ b/fs/coda/psdev.c
@@ -0,0 +1,444 @@
+/*
+ * An implementation of a loadable kernel mode driver providing
+ * multiple kernel/user space bidirectional communications links.
+ *
+ * Author: Alan Cox <alan@cymru.net>
+ *
+ * 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/sched.h>
+#include <linux/lp.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_namecache.h>
+#include <linux/coda_sysctl.h>
+
+
+/*
+ * Coda stuff
+ */
+extern struct file_system_type coda_fs_type;
+extern int coda_downcall(int opcode, struct outputArgs *out);
+extern int init_coda_fs(void);
+extern int cfsnc_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
+extern int cfsnc_nc_info(char *buffer, char **start, off_t offset, int length, int dummy);
+
+/* statistics */
+struct coda_upcallstats coda_callstats;
+
+extern struct coda_sb_info coda_super_info[MAX_CODADEVS];
+struct vcomm psdev_vcomm[MAX_CODADEVS];
+
+/*
+ * Device operations
+ */
+
+
+static struct vcomm *coda_psdev2vcomm(struct file *file)
+{
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ struct vcomm *vcp = NULL;
+
+ if ( (minor >= 0) && (minor < MAX_CODADEVS) )
+ vcp = &psdev_vcomm[minor];
+ return vcp;
+}
+
+
+static unsigned int coda_psdev_poll(struct file *file, poll_table * wait)
+{
+ struct vcomm *vcp = coda_psdev2vcomm(file);
+ unsigned int mask = POLLOUT | POLLWRNORM;
+
+ if ( !vcp )
+ return -ENXIO;
+
+ poll_wait(&(vcp->vc_waitq), wait);
+ if (!EMPTY(vcp->vc_pending))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+
+/*
+ * Receive a message written by Venus to the psdev
+ */
+
+static ssize_t coda_psdev_write(struct file *file, const char *buf,
+ size_t count, loff_t *off)
+{
+ struct vcomm *vcp = coda_psdev2vcomm(file);
+ struct vmsg *vmp;
+ struct outputArgs *out;
+ int error = 0;
+ int size;
+ u_long uniq;
+ u_long opcode;
+ u_long opcodebuf[2];
+
+ if (!vcp)
+ return -ENXIO;
+
+ /* Peek at the opcode, unique id */
+ if (copy_from_user(opcodebuf, buf, 2 * sizeof(u_long)))
+ return -EFAULT;
+ opcode = opcodebuf[0];
+ uniq = opcodebuf[1];
+
+ CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%ld,%ld)\n",
+ current->pid, opcode, uniq);
+
+ if (DOWNCALL(opcode)) {
+ struct outputArgs pbuf;
+
+ CDEBUG(D_PSDEV, "handling downcall\n");
+
+ /* get the rest of the data. */
+ size = sizeof(pbuf);
+ if ( count < sizeof(pbuf) ) {
+ printk("Coda: downcall opc %ld, uniq %ld, not enough!\n",
+ opcode, uniq);
+ size =count;
+ } else if ( count > sizeof(pbuf) ) {
+ printk("Coda: downcall opc %ld, uniq %ld, too much!",
+ opcode, uniq);
+ size = sizeof(pbuf);
+ }
+ if (copy_from_user(&pbuf, buf, size))
+ return -EFAULT;
+
+ /* what errors for coda_downcall should be
+ * sent to Venus ?
+ */
+ error = coda_downcall(opcode, &pbuf);
+ if ( error) {
+ printk("psdev_write: coda_downcall error: %d\n",
+ error);
+ return 0;
+ }
+ return count;
+ }
+
+
+ /* Look for the message on the processing queue. */
+ for (vmp = (struct vmsg *)GETNEXT(vcp->vc_processing);
+ !EOQ(vmp, vcp->vc_processing);
+ vmp = (struct vmsg *)GETNEXT(vmp->vm_chain)) {
+ if (vmp->vm_unique == uniq) break;
+ CDEBUG(D_PSDEV,"Eureka: uniq %ld on queue!\n", uniq);
+ }
+ if (EOQ(vmp, vcp->vc_processing)) {
+ printk("psdev_write: msg (%ld, %ld) not found\n",
+ opcode, uniq);
+ return(-ESRCH);
+ }
+
+ /* Remove the message from the processing queue */
+ REMQUE(vmp->vm_chain);
+
+ /* move data into response buffer. */
+ /* Don't need to copy opcode and uniquifier. */
+ out = (struct outputArgs *)vmp->vm_data;
+ /* get the rest of the data. */
+ if (vmp->vm_outSize < count) {
+ printk("Coda write: too much outs: %d, cnt: %d, opc: %ld, uniq: %ld.\n",
+ vmp->vm_outSize, count, opcode, uniq);
+ wake_up_interruptible(&vmp->vm_sleep);
+ return -EINVAL;
+ } else if (vmp->vm_outSize > count) {
+ printk("Coda write: too much outs: %d, cnt: %d, opc: %ld, uniq: %ld.\n",
+ vmp->vm_outSize, count, opcode, uniq);
+ }
+ if (copy_from_user(out, buf, count))
+ return -EFAULT;
+
+ /* adjust outsize. is this usefull ?? */
+ vmp->vm_outSize = count;
+ vmp->vm_flags |= VM_WRITE;
+
+ CDEBUG(D_PSDEV,
+ "Found! Count %d for (opc,uniq)=(%ld,%ld), vmsg at %x\n",
+ count, opcode, uniq, (int)&vmp);
+
+ wake_up_interruptible(&vmp->vm_sleep);
+ return(count);
+}
+
+/*
+ * Read a message from the kernel to Venus
+ */
+
+static ssize_t coda_psdev_read(struct file * file, char * buf,
+ size_t count, loff_t *off)
+{
+ struct vcomm *vcp = coda_psdev2vcomm(file);
+ struct vmsg *vmp;
+ int result = count ;
+
+ if (!vcp)
+ return -ENXIO;
+
+ /* Get message at head of request queue. */
+ if (EMPTY(vcp->vc_pending)) {
+ return 0; /* Nothing to read */
+ }
+
+ vmp = (struct vmsg *)GETNEXT(vcp->vc_pending);
+ REMQUE(vmp->vm_chain);
+
+ /* Move the input args into userspace */
+
+ if (vmp->vm_inSize <= count)
+ result = vmp->vm_inSize;
+
+ if (count < vmp->vm_inSize) {
+ printk ("psdev_read: warning: venus read %d bytes of %d long
+ message\n",count, vmp->vm_inSize);
+ }
+
+ if ( copy_to_user(buf, vmp->vm_data, result))
+ return -EFAULT;
+
+ if (vmp->vm_chain.forw == 0 || vmp->vm_chain.back == 0)
+ coda_panic("coda_psdev_read: bad chain");
+
+ /* If request was a signal, free up the message and don't
+ enqueue it in the reply queue. */
+ if (vmp->vm_opcode == CFS_SIGNAL) {
+ CDEBUG(D_PSDEV, "vcread: signal msg (%d, %d)\n",
+ vmp->vm_opcode, vmp->vm_unique);
+ CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
+ CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
+ return count;
+ }
+
+ vmp->vm_flags |= VM_READ;
+ INSQUE(vmp->vm_chain, vcp->vc_processing);
+
+ return result;
+}
+
+
+static int coda_psdev_open(struct inode * inode, struct file * file)
+{
+ register struct vcomm *vcp = NULL;
+
+ ENTRY;
+
+ vcp = coda_psdev2vcomm(file);
+
+ if (!vcp)
+ return -ENODEV;
+ memset(vcp, 0, sizeof(struct vcomm));
+
+ MOD_INC_USE_COUNT;
+
+ INIT_QUEUE(vcp->vc_pending);
+ INIT_QUEUE(vcp->vc_processing);
+
+ cfsnc_init();
+ CDEBUG(D_PSDEV, "Name cache initialized.\n");
+
+ memset(&coda_callstats, 0, sizeof(struct coda_upcallstats));
+ EXIT;
+ return 0;
+}
+
+
+static int
+coda_psdev_release(struct inode * inode, struct file * file)
+{
+ struct vcomm *vcp;
+ struct vmsg *vmp;
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+
+ vcp = coda_psdev2vcomm(file);
+
+ if ( !vcp || !vcomm_open(vcp) ) {
+ printk("psdev_release: not open");
+ return 0;
+ }
+
+
+ /* flush the name cache so that we can unmount */
+ CDEBUG(D_PSDEV, "Flushing the cache.\n");
+ cfsnc_flush();
+ cfsnc_use = 0;
+ CDEBUG(D_PSDEV, "Done.\n");
+
+ /* prevent future operations on this vfs from succeeding by
+ * auto- unmounting any vfs mounted via this device. This
+ * frees user or sysadm from having to remember where all
+ * mount points are located. Put this before WAKEUPs to avoid
+ * queuing new messages between the WAKEUP and the unmount
+ * (which can happen if we're unlucky) */
+
+ if (coda_super_info[minor].sbi_root) {
+ struct cnode *cnp = ITOC(coda_super_info[minor].sbi_root);
+ /* Let unmount know this is for real */
+ cnp->c_flags |= C_DYING;
+ /* XXX Could we force an unmount here? */
+ }
+
+
+ /* Wakeup clients so they can return. */
+ for (vmp = (struct vmsg *)GETNEXT(vcp->vc_pending);
+ !EOQ(vmp, vcp->vc_pending);
+ vmp = (struct vmsg *)GETNEXT(vmp->vm_chain)) {
+ /* Free signal request messages and don't wakeup cause
+ no one is waiting. */
+ if (vmp->vm_opcode == CFS_SIGNAL) {
+ CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
+ CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
+ continue;
+ }
+
+ wake_up_interruptible(&vmp->vm_sleep);
+ }
+
+ for (vmp = (struct vmsg *)GETNEXT(vcp->vc_processing);
+ !EOQ(vmp, vcp->vc_processing);
+ vmp = (struct vmsg *)GETNEXT(vmp->vm_chain)) {
+ wake_up_interruptible(&vmp->vm_sleep);
+ }
+
+ mark_vcomm_closed(vcp);
+ cfsnc_use = 0;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+
+static struct file_operations coda_psdev_fops = {
+ NULL, /* llseek */
+ coda_psdev_read, /* read */
+ coda_psdev_write, /* write */
+ NULL, /* coda_psdev_readdir */
+ coda_psdev_poll, /* poll */
+ NULL, /* ioctl */
+ NULL, /* coda_psdev_mmap */
+ coda_psdev_open, /* open */
+ coda_psdev_release, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL /* lock */
+};
+
+int init_coda_psdev(void)
+{
+
+ if(register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev", &coda_psdev_fops)) {
+ printk(KERN_ERR "coda_psdev: unable to get major %d\n",
+ CODA_PSDEV_MAJOR);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PROC_FS
+
+struct proc_dir_entry proc_coda = {
+ 0, 4, "coda",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+ 0, &proc_net_inode_operations,
+
+};
+
+struct proc_dir_entry proc_coda_cache = {
+ 0 , 10, "coda-cache",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ cfsnc_get_info
+ };
+
+struct proc_dir_entry proc_coda_ncstats = {
+ 0 , 12, "coda-ncstats",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ cfsnc_nc_info
+ };
+
+#endif
+
+#ifdef MODULE
+int init_module(void)
+{
+ int status;
+ printk(KERN_INFO "Coda Kernel/User communications module 0.04\n");
+
+#ifdef CONFIG_PROC_FS
+ proc_register(&proc_root,&proc_coda);
+ proc_register(&proc_coda, &proc_coda_cache);
+ proc_register(&proc_coda, &proc_coda_ncstats);
+ coda_sysctl_init();
+#endif
+
+ init_coda_psdev();
+
+ if ((status = init_coda_fs()) != 0)
+ {
+ printk("coda: failed in init_coda_fs!\n");
+ }
+ return status;
+}
+
+
+void cleanup_module(void)
+{
+ int err;
+
+ ENTRY;
+
+ unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev");
+
+ if ( (err = unregister_filesystem(&coda_fs_type)) != 0 ) {
+ printk("coda: failed to unregister filesystem\n");
+ }
+#if CONFIG_PROC_FS
+ coda_sysctl_clean();
+ proc_unregister(&proc_coda, proc_coda_cache.low_ino);
+ proc_unregister(&proc_coda, proc_coda_ncstats.low_ino);
+ proc_unregister(&proc_root, proc_coda.low_ino);
+#endif
+}
+
+#endif
+
+
+
diff --git a/fs/coda/super.c b/fs/coda/super.c
new file mode 100644
index 000000000..85a5ccbb3
--- /dev/null
+++ b/fs/coda/super.c
@@ -0,0 +1,612 @@
+/*
+ * Super block/filesystem wide operations
+ *
+ * Peter J. Braam <braam@maths.ox.ac.uk>,
+ * Michael Callahan <callahan@maths.ox.ac.uk> Aug 1996
+ * Rewritten for Linux 2.1.57 Peter Braam <braam@cs.cmu.edu>
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#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/locks.h>
+#include <linux/unistd.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+#include <asm/segment.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_namecache.h>
+
+
+/* VFS super_block ops */
+static struct super_block *coda_read_super(struct super_block *, void *, int);
+static void coda_read_inode(struct inode *);
+static int coda_notify_change(struct inode *inode, struct iattr *attr);
+static void coda_put_inode(struct inode *);
+static void coda_delete_inode(struct inode *);
+static void coda_put_super(struct super_block *);
+static int coda_statfs(struct super_block *sb, struct statfs *buf,
+ int bufsiz);
+
+
+/* helper functions */
+void print_vattr( struct coda_vattr *attr );
+static inline struct coda_sb_info *coda_psinode2sb(struct inode *inode);
+static inline struct vcomm *coda_psinode2vcomm(struct inode *inode);
+static int coda_get_psdev(void *, struct inode **);
+static void coda_vattr_to_iattr(struct inode *, struct coda_vattr *);
+static void coda_iattr_to_vattr(struct iattr *, struct coda_vattr *);
+int coda_fetch_inode(struct inode *, struct coda_vattr *);
+
+extern inline struct vcomm *coda_psdev_vcomm(struct inode *inode);
+extern int coda_cnode_make(struct inode **inode, ViceFid *fid,
+ struct super_block *sb);
+extern struct cnode *coda_cnode_alloc(void);
+extern void coda_cnode_free(struct cnode *);
+char *coda_f2s(struct ViceFid *, char *);
+
+extern int cfsnc_initialized;
+extern int coda_debug;
+extern int coda_print_entry;
+
+extern struct inode_operations coda_file_inode_operations;
+extern struct inode_operations coda_dir_inode_operations;
+extern struct inode_operations coda_ioctl_inode_operations;
+extern struct inode_operations coda_symlink_inode_operations;
+/* exported operations */
+struct super_operations coda_super_operations =
+{
+ coda_read_inode, /* read_inode */
+ NULL, /* write_inode */
+ coda_put_inode, /* put_inode */
+ coda_delete_inode, /* delete_inode */
+ coda_notify_change, /* notify_change */
+ coda_put_super, /* put_super */
+ NULL, /* write_super */
+ coda_statfs, /* statfs */
+ NULL /* remount_fs */
+};
+
+/*
+ * globals
+ */
+struct coda_sb_info coda_super_info[MAX_CODADEVS];
+
+
+
+static struct super_block * coda_read_super(struct super_block *sb,
+ void *data, int silent)
+{
+ struct inode *psdev = 0, *root = 0;
+ struct coda_sb_info *sbi = NULL;
+ struct vcomm *vc;
+ ViceFid fid;
+ kdev_t dev = sb->s_dev;
+ int error;
+ char str[50];
+
+ENTRY;
+ MOD_INC_USE_COUNT;
+ if (coda_get_psdev(data, &psdev))
+ goto exit;
+
+ vc = coda_psinode2vcomm(psdev);
+ if ( !vc )
+ goto exit;
+
+ sbi = coda_psinode2sb(psdev);
+ if ( !sbi )
+ goto exit;
+
+ sbi->sbi_psdev = psdev;
+ sbi->sbi_vcomm = vc;
+
+ lock_super(sb);
+ sb->u.generic_sbp = sbi;
+ sb->s_blocksize = 1024; /* XXXXX */
+ sb->s_blocksize_bits = 10;
+ sb->s_magic = CODA_SUPER_MAGIC;
+ sb->s_dev = dev;
+ sb->s_op = &coda_super_operations;
+
+ /* get root fid from Venus: this needs the root inode */
+ error = venus_rootfid(sb, &fid);
+
+ if ( error ) {
+ unlock_super(sb);
+ printk("coda_read_super: coda_get_rootfid failed with %d\n",
+ error);
+ goto exit;
+ }
+ printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid, str));
+
+ error = coda_cnode_make(&root, &fid, sb);
+ if ( error || !root ) {
+ printk("Failure of coda_cnode_make for root: error %d\n", error);
+ unlock_super(sb);
+ sb->s_dev = 0;
+ root = NULL;
+ goto exit;
+ }
+
+ printk("coda_read_super: rootinode is %ld dev %d\n",
+ root->i_ino, root->i_dev);
+ sbi->sbi_root = root;
+ sb->s_root = d_alloc_root(root, NULL);
+ unlock_super(sb);
+ return sb;
+EXIT;
+
+exit:
+ MOD_DEC_USE_COUNT;
+ sbi->sbi_vcomm = NULL;
+ sbi->sbi_root = NULL;
+ if (root) {
+ iput(root);
+ coda_cnode_free(ITOC(root));
+ }
+ sb->s_dev = 0;
+ return NULL;
+}
+
+
+static void coda_put_super(struct super_block *sb)
+{
+ struct coda_sb_info *sb_info;
+
+ ENTRY;
+
+ lock_super(sb);
+
+ sb->s_dev = 0;
+ sb_info = coda_sbp(sb);
+ memset(sb_info, 0, sizeof(* sb_info));
+
+ unlock_super(sb);
+ MOD_DEC_USE_COUNT;
+EXIT;
+}
+/* all filling in of inodes postponed until lookup */
+static void coda_read_inode(struct inode *inode)
+{
+ inode->u.generic_ip = NULL;
+ /* inode->i_blksize = inode->i_sb->s_blocksize;
+ inode->i_mode = 0;
+ inode->i_op = NULL;
+ NFS_CACHEINV(inode); */
+}
+
+static void coda_put_inode(struct inode *inode)
+{
+ CDEBUG(D_INODE,"ino: %ld, cnp: %x\n", inode->i_ino,
+ (int) inode->u.generic_ip);
+}
+
+static void coda_delete_inode(struct inode *inode)
+{
+ struct cnode *cnp;
+ struct inode *open_inode;
+
+ ENTRY;
+ CDEBUG(D_SUPER, " inode->ino: %ld, count: %d\n",
+ inode->i_ino, inode->i_count);
+
+ if ( inode->i_ino == CTL_INO ) {
+ clear_inode(inode);
+ return;
+ }
+
+ cnp = ITOC(inode);
+
+ open_inode = cnp->c_ovp;
+ if ( open_inode ) {
+ CDEBUG(D_SUPER, "DELINO cached file: ino %ld count %d.\n",
+ open_inode->i_ino, open_inode->i_count);
+ cnp->c_ovp = NULL;
+ cnp->c_odentry.d_inode = NULL;
+ iput( open_inode );
+ }
+ inode->u.generic_ip = NULL;
+ coda_cnode_free(cnp);
+ clear_inode(inode);
+EXIT;
+}
+
+static int coda_notify_change(struct inode *inode, struct iattr *iattr)
+{
+ struct cnode *cnp;
+ struct coda_vattr vattr;
+ int error;
+ENTRY;
+ memset(&vattr, 0, sizeof(vattr));
+ cnp = ITOC(inode);
+ CHECK_CNODE(cnp);
+
+ coda_iattr_to_vattr(iattr, &vattr);
+ vattr.va_type = VNON; /* cannot set type */
+ CDEBUG(D_SUPER, "vattr.va_mode %o\n", vattr.va_mode);
+
+ error = venus_setattr(inode->i_sb, &cnp->c_fid, &vattr);
+
+ if ( !error ) {
+ coda_vattr_to_iattr(inode, &vattr);
+ cfsnc_zapfid(&(cnp->c_fid));
+ }
+ CDEBUG(D_SUPER, "inode.i_mode %o, error %d\n",
+ inode->i_mode, error);
+
+ EXIT;
+ return error;
+}
+
+/* we need _something_ */
+static int coda_statfs(struct super_block *sb, struct statfs *buf,
+ int bufsiz)
+{
+ struct statfs tmp;
+
+#define NB_SFS_SIZ 0x895440
+
+ tmp.f_type = CODA_SUPER_MAGIC;
+ tmp.f_bsize = 1024;
+ tmp.f_blocks = 9000000;
+ tmp.f_bfree = 9000000;
+ tmp.f_bavail = 9000000 ;
+ tmp.f_files = 9000000;
+ tmp.f_ffree = 9000000;
+ tmp.f_namelen = 0;
+ copy_to_user(buf, &tmp, bufsiz);
+ return 0;
+}
+
+
+/* init_coda: used by filesystems.c to register coda */
+
+struct file_system_type coda_fs_type = {
+ "coda", 0, coda_read_super, NULL
+};
+
+
+
+
+int init_coda_fs(void)
+{
+ return register_filesystem(&coda_fs_type);
+}
+
+
+/* utility functions below */
+static void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr)
+{
+ int inode_type;
+ /* inode's i_dev, i_flags, i_ino are set by iget
+ XXX: is this all we need ??
+ */
+ switch (attr->va_type) {
+ case VNON:
+ inode_type = 0;
+ break;
+ case VREG:
+ inode_type = S_IFREG;
+ break;
+ case VDIR:
+ inode_type = S_IFDIR;
+ break;
+ case 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;
+ /* XXX This needs further study */
+ /*
+ inode->i_blksize = attr->va_blocksize;
+ inode->i_blocks = attr->va_size/attr->va_blocksize
+ + (attr->va_size % attr->va_blocksize ? 1 : 0);
+ */
+ if (attr->va_atime.tv_sec != -1)
+ inode->i_atime = attr->va_atime.tv_sec;
+ if (attr->va_mtime.tv_sec != -1)
+ inode->i_mtime = attr->va_mtime.tv_sec;
+ if (attr->va_ctime.tv_sec != -1)
+ inode->i_ctime = attr->va_ctime.tv_sec;
+}
+/*
+ * 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)
+{
+ umode_t mode;
+ 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_mtime.tv_sec = (time_t) -1;
+ vattr->va_ctime.tv_sec = (time_t) -1;
+ vattr->va_atime.tv_nsec = (time_t) -1;
+ vattr->va_mtime.tv_nsec = (time_t) -1;
+ vattr->va_ctime.tv_nsec = (time_t) -1;
+ vattr->va_type = VNON;
+ vattr->va_fileid = (long)-1;
+ vattr->va_gen = (long)-1;
+ vattr->va_bytes = (long)-1;
+ vattr->va_fsid = (long)-1;
+ vattr->va_nlink = (short)-1;
+ vattr->va_blocksize = (long)-1;
+ vattr->va_rdev = (dev_t)-1;
+ vattr->va_flags = 0;
+
+ /* determine the type */
+ mode = iattr->ia_mode;
+ if ( S_ISDIR(mode) ) {
+ vattr->va_type = VDIR;
+ } else if ( S_ISREG(mode) ) {
+ vattr->va_type = VREG;
+ } else if ( S_ISLNK(mode) ) {
+ vattr->va_type = VLNK;
+ } else {
+ /* don't do others */
+ vattr->va_type = VNON;
+ }
+
+ /* 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.tv_sec = iattr->ia_atime;
+ vattr->va_atime.tv_nsec = 0;
+ }
+ if ( valid & ATTR_MTIME ) {
+ vattr->va_mtime.tv_sec = iattr->ia_mtime;
+ vattr->va_mtime.tv_nsec = 0;
+ }
+ if ( valid & ATTR_CTIME ) {
+ vattr->va_ctime.tv_sec = iattr->ia_ctime;
+ vattr->va_ctime.tv_nsec = 0;
+ }
+
+}
+
+
+void print_vattr(struct coda_vattr *attr)
+{
+ char *typestr;
+
+ switch (attr->va_type) {
+ case VNON:
+ typestr = "VNON";
+ break;
+ case VREG:
+ typestr = "VREG";
+ break;
+ case VDIR:
+ typestr = "VDIR";
+ break;
+ case VBLK:
+ typestr = "VBLK";
+ break;
+ case VCHR:
+ typestr = "VCHR";
+ break;
+ case VLNK:
+ typestr = "VLNK";
+ break;
+ case VSOCK:
+ typestr = "VSCK";
+ break;
+ case VFIFO:
+ typestr = "VFFO";
+ break;
+ case VBAD:
+ typestr = "VBAD";
+ break;
+ default:
+ typestr = "????";
+ break;
+ }
+
+
+ printk("attr: type %s (%o) mode %o uid %d gid %d fsid %d rdev %d\n",
+ typestr, (int)attr->va_type, (int)attr->va_mode, (int)attr->va_uid,
+ (int)attr->va_gid, (int)attr->va_fsid, (int)attr->va_rdev);
+
+ printk(" fileid %d nlink %d size %d blocksize %d bytes %d\n",
+ (int)attr->va_fileid, (int)attr->va_nlink,
+ (int)attr->va_size,
+ (int)attr->va_blocksize,(int)attr->va_bytes);
+ printk(" gen %ld flags %ld vaflags %d\n",
+ attr->va_gen, attr->va_flags, attr->va_vaflags);
+ printk(" atime sec %d nsec %d\n",
+ (int)attr->va_atime.tv_sec, (int)attr->va_atime.tv_nsec);
+ printk(" mtime sec %d nsec %d\n",
+ (int)attr->va_mtime.tv_sec, (int)attr->va_mtime.tv_nsec);
+ printk(" ctime sec %d nsec %d\n",
+ (int)attr->va_ctime.tv_sec, (int)attr->va_ctime.tv_nsec);
+}
+
+/* */
+int coda_fetch_inode (struct inode *inode, struct coda_vattr *attr)
+{
+ struct cnode *cp;
+ int ino, error=0;
+ CDEBUG(D_SUPER, "fetch for ino: %ld\n", inode->i_ino);
+
+ ino = inode->i_ino;
+ if (!ino)
+ printk("coda_fetch_inode: inode called with i_ino = 0 (don't worry)\n");
+
+ inode->i_op = NULL;
+ inode->i_mode = 0;
+
+ cp = ITOC(inode);
+ CHECK_CNODE(cp);
+
+ /* root inode */
+ if (cp->c_fid.Volume == 0 &&
+ cp->c_fid.Vnode == 0 &&
+ cp->c_fid.Unique == 0) {
+ inode->i_ino = 1;
+ inode->i_op = NULL;
+ return 0;
+ }
+
+ if (IS_CTL_FID( &(cp->c_fid) )) {
+ /* This is the special magic control file.
+ Venus doesn't want
+ to hear a GETATTR about this! */
+ inode->i_op = &coda_ioctl_inode_operations;
+ return 0;
+ }
+
+ if ( ! attr ) {
+ printk("coda_fetch_inode: called with NULL vattr, ino %ld\n",
+ inode->i_ino);
+ return -1; /* XXX */
+ }
+
+ if (coda_debug & D_SUPER ) print_vattr(attr);
+ coda_vattr_to_iattr(inode, attr);
+
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &coda_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &coda_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &coda_symlink_inode_operations;
+ else {
+ printk ("coda_read_inode: what kind of inode is this? i_mode = %o\n", inode->i_mode);
+ inode->i_op = NULL;
+ }
+ return error;
+}
+
+static inline struct vcomm *
+coda_psinode2vcomm(struct inode *inode)
+{
+
+ unsigned int minor = MINOR(inode->i_rdev);
+CDEBUG(D_PSDEV,"minor %d\n", minor);
+ if ( minor < MAX_CODADEVS )
+ return &(psdev_vcomm[minor]);
+ else
+ return NULL;
+}
+
+static inline struct coda_sb_info *
+coda_psinode2sb(struct inode *inode)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+
+CDEBUG(D_PSDEV,"minor %d\n", minor);
+ if ( minor < MAX_CODADEVS )
+ return &(coda_super_info[minor]);
+ else
+ return NULL;
+}
+
+static int
+coda_get_psdev(void *data, struct inode **res_dev)
+{
+ char **psdev_path;
+ struct inode *psdev = 0;
+ struct dentry *ent=NULL;
+
+
+ if ( ! data ) {
+ printk("coda_read_super: no data!\n");
+ goto error;
+ } else {
+ psdev_path = data;
+ }
+ ent = namei((char *) *psdev_path);
+ if (IS_ERR(ent)) {
+ printk("namei error %ld for %d\n", PTR_ERR(ent),
+ (int) psdev_path);
+ goto error;
+ }
+ psdev = ent->d_inode;
+
+
+ if (!S_ISCHR(psdev->i_mode)) {
+ printk("not a character device\n");
+ goto error;
+ }
+CDEBUG(D_PSDEV,"major %d, minor %d, count %d\n", MAJOR(psdev->i_rdev),
+ MINOR(psdev->i_rdev), psdev->i_count);
+
+ if (MAJOR(psdev->i_rdev) != CODA_PSDEV_MAJOR) {
+ printk("device %d not a Coda PSDEV device\n",
+ MAJOR(psdev->i_rdev));
+ goto error;
+ }
+
+ if (MINOR(psdev->i_rdev) >= MAX_CODADEVS) {
+ printk("minor %d not an allocated Coda PSDEV\n",
+ psdev->i_rdev);
+ goto error;
+ }
+
+ if (psdev->i_count < 1) {
+ printk("coda device minor %d not open (i_count = %d)\n",
+ MINOR(psdev->i_rdev), psdev->i_count);
+ goto error;
+ }
+
+ *res_dev = psdev;
+
+ return 0;
+
+EXIT;
+error:
+ return 1;
+}
diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c
new file mode 100644
index 000000000..432b806f8
--- /dev/null
+++ b/fs/coda/symlink.c
@@ -0,0 +1,82 @@
+/*
+ * 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/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.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_cnode.h>
+#include <linux/coda_namecache.h>
+
+static int coda_readlink(struct inode *inode, char *buffer, int length);
+
+struct inode_operations coda_symlink_inode_operations = {
+ NULL, /* no file-operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ coda_readlink, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* update page */
+ NULL /* revalidate */
+};
+
+static int coda_readlink(struct inode *inode, char *buffer, int length)
+{
+ int len;
+ int error;
+ char *buf;
+ struct cnode *cp;
+ ENTRY;
+
+ cp = ITOC(inode);
+ CHECK_CNODE(cp);
+
+ /* the maximum length we receive is len */
+ if ( length > CFS_MAXPATHLEN )
+ len = CFS_MAXPATHLEN;
+ else
+ len = length;
+ CODA_ALLOC(buf, char *, len);
+ if ( !buf )
+ return -ENOMEM;
+
+ error = venus_readlink(inode->i_sb, &(cp->c_fid), buf, &len);
+
+ CDEBUG(D_INODE, "result %s\n", buf);
+ if (! error) {
+ copy_to_user(buffer, buf, len);
+ put_user('\0', buffer + len);
+ error = len;
+ CODA_FREE(buf, len);
+ }
+ return error;
+}
diff --git a/fs/coda/sysctl.c b/fs/coda/sysctl.c
new file mode 100644
index 000000000..04523a383
--- /dev/null
+++ b/fs/coda/sysctl.c
@@ -0,0 +1,168 @@
+/*
+ * 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).
+ */
+/* sysctl entries for Coda! */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+#include <linux/swapctl.h>
+#include <linux/proc_fs.h>
+#include <linux/malloc.h>
+#include <linux/stat.h>
+#include <linux/ctype.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/utsname.h>
+
+#include <linux/coda_namecache.h>
+#include <linux/coda_sysctl.h>
+extern int coda_debug;
+extern int cfsnc_use;
+extern int coda_print_entry;
+extern int cfsnc_flushme;
+extern int cfsnc_procsize;
+extern void cfsnc_flush(void);
+void coda_sysctl_init(void);
+void coda_sysctl_clean(void);
+
+int coda_dointvec(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp);
+
+struct ctl_table_header *fs_table_header, *coda_table_header;
+#define FS_CODA 1 /* Coda file system */
+
+#define CODA_DEBUG 1 /* control debugging */
+#define CODA_ENTRY 2 /* control enter/leave pattern */
+#define CODA_FLUSH 3 /* flush the cache on next lookup */
+#define CODA_MC 4 /* use/do not use the minicache */
+#define CODA_PROCSIZE 5 /* resize the cache on next lookup */
+
+
+
+static ctl_table coda_table[] = {
+ {CODA_DEBUG, "debug", &coda_debug, sizeof(int), 0644, NULL, &coda_dointvec},
+ {CODA_ENTRY, "printentry", &coda_print_entry, sizeof(int), 0644, NULL, &coda_dointvec},
+ {CODA_MC, "minicache", &cfsnc_use, sizeof(int), 0644, NULL, &coda_dointvec},
+ {CODA_FLUSH, "flushme", &cfsnc_flushme, sizeof(int), 0644, NULL, &coda_dointvec},
+ {CODA_PROCSIZE, "resize", &cfsnc_procsize, sizeof(int), 0644, NULL, &coda_dointvec},
+ { 0 }
+};
+
+
+static ctl_table fs_table[] = {
+ {FS_CODA, "coda", NULL, 0, 0555, coda_table},
+ {0}
+};
+
+
+
+void coda_sysctl_init()
+{
+ fs_table_header = register_sysctl_table(fs_table, 0);
+/* coda_table_header = register_sysctl_table(coda_table, 0);*/
+}
+
+void coda_sysctl_clean() {
+ /*unregister_sysctl_table(coda_table_header);*/
+ unregister_sysctl_table(fs_table_header);
+}
+
+int coda_dointvec(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp)
+{
+ int *i, vleft, first=1, len, left, neg, val;
+ #define TMPBUFLEN 20
+ char buf[TMPBUFLEN], *p;
+
+ if (!table->data || !table->maxlen || !*lenp ||
+ (filp->f_pos && !write)) {
+ *lenp = 0;
+ return 0;
+ }
+
+ i = (int *) table->data;
+ vleft = table->maxlen / sizeof(int);
+ left = *lenp;
+
+ for (; left && vleft--; i++, first=0) {
+ if (write) {
+ while (left) {
+ char c;
+ if(get_user(c,(char *) buffer))
+ return -EFAULT;
+ if (!isspace(c))
+ break;
+ left--;
+ ((char *) buffer)++;
+ }
+ if (!left)
+ break;
+ neg = 0;
+ len = left;
+ if (len > TMPBUFLEN-1)
+ len = TMPBUFLEN-1;
+ if (copy_from_user(buf, buffer, len))
+ return -EFAULT;
+ buf[len] = 0;
+ p = buf;
+ if (*p == '-' && left > 1) {
+ neg = 1;
+ left--, p++;
+ }
+ if (*p < '0' || *p > '9')
+ break;
+ val = simple_strtoul(p, &p, 0);
+ len = p-buf;
+ if ((len < left) && *p && !isspace(*p))
+ break;
+ if (neg)
+ val = -val;
+ buffer += len;
+ left -= len;
+ *i = val;
+ } else {
+ p = buf;
+ if (!first)
+ *p++ = '\t';
+ sprintf(p, "%d", *i);
+ len = strlen(buf);
+ if (len > left)
+ len = left;
+ if (copy_to_user(buffer, buf, len))
+ return -EFAULT;
+ left -= len;
+ buffer += len;
+ }
+ }
+
+ if (!write && !first && left) {
+ if(put_user('\n', (char *) buffer))
+ return -EFAULT;
+ left--, buffer++;
+ }
+ if (write) {
+ p = (char *) buffer;
+ while (left) {
+ char c;
+ if(get_user(c, p++))
+ return -EFAULT;
+ if (!isspace(c))
+ break;
+ left--;
+ }
+ }
+ if (write && first)
+ return -EINVAL;
+ *lenp -= left;
+ filp->f_pos += *lenp;
+ return 0;
+}
+
+
diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c
new file mode 100644
index 000000000..43536f622
--- /dev/null
+++ b/fs/coda/upcall.c
@@ -0,0 +1,747 @@
+/*
+ * 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 <asm/segment.h>
+
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+#include <asm/segment.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_namecache.h>
+
+
+static vcsize = (sizeof(struct inputArgs) > sizeof(struct outputArgs)) ?
+ sizeof(struct inputArgs): sizeof(struct outputArgs);
+
+/* the upcalls */
+int venus_rootfid(struct super_block *sb, ViceFid *fidp)
+{
+ struct inputArgs *inp;
+ struct outputArgs *outp;
+ int error=0;
+ int size;
+ENTRY;
+
+ UPARG(vcsize, CFS_ROOT);
+ error = coda_upcall(coda_sbp(sb), VC_IN_NO_DATA, &size, inp);
+
+ if (error) {
+ printk("coda_get_rootfid: error %d\n", error);
+ } else {
+ *fidp = (ViceFid) outp->d.cfs_root.VFid;
+ CDEBUG(D_SUPER, "VolumeId: %lx, VnodeId: %lx.\n",
+ fidp->Volume, fidp->Vnode);
+ }
+
+ if (inp) CODA_FREE(inp, VC_IN_NO_DATA);
+ EXIT;
+ return -error;
+}
+
+int venus_getattr(struct super_block *sb, struct ViceFid *fid,
+ struct coda_vattr *attr)
+{
+ struct inputArgs *inp;
+ struct outputArgs *outp;
+ int size, error;
+ENTRY;
+
+ UPARG(vcsize, CFS_GETATTR);
+ inp->d.cfs_getattr.VFid = *fid;
+ error = coda_upcall(coda_sbp(sb), vcsize, &size, inp);
+
+ if ( !error )
+ *attr = (struct coda_vattr) outp->d.cfs_getattr.attr;
+
+ if (inp) CODA_FREE(inp, sizeof(struct inputArgs));
+ EXIT;
+ return -error;
+}
+
+int venus_setattr(struct super_block *sb, struct ViceFid *fid,
+ struct coda_vattr *vattr)
+{
+ struct inputArgs *inp;
+ struct outputArgs *outp;
+ int error, size;
+
+ UPARG(vcsize, CFS_SETATTR);
+
+ inp->d.cfs_setattr.VFid = *fid;
+ inp->d.cfs_setattr.attr = *vattr;
+
+ error = coda_upcall(coda_sbp(sb), vcsize, &size, inp);
+
+ CDEBUG(D_SUPER, " result %ld\n", outp->result);
+ if ( inp ) CODA_FREE(inp, vcsize);
+ return -error;
+}
+
+int venus_lookup(struct super_block *sb, struct ViceFid *fid,
+ const char *name, int length, int * type,
+ struct ViceFid *resfid)
+{
+ struct inputArgs *inp;
+ struct outputArgs *outp;
+ int insize, size, error=0, payload_offset;
+
+ insize = VC_INSIZE(cfs_lookup_in) + CFS_MAXNAMLEN +1;
+ UPARG(insize, CFS_LOOKUP);
+
+ inp->d.cfs_lookup.VFid = *fid;
+ /* send Venus a null terminated string */
+ payload_offset = VC_INSIZE(cfs_lookup_in);
+ inp->d.cfs_lookup.name = (char *) payload_offset;
+ memcpy((char *)inp + payload_offset, name, length);
+ *((char *)inp + payload_offset + length) = '\0';
+
+ size = payload_offset + length + 1;
+ error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+ if ( !error ) {
+ *resfid = outp->d.cfs_lookup.VFid;
+ *type = outp->d.cfs_lookup.vtype;
+ }
+ if (inp) CODA_FREE(inp, insize);
+
+ return -error;
+}
+
+
+int venus_release(struct super_block *sb, struct ViceFid *fid, int flags)
+{
+ struct inputArgs *inp;
+ struct outputArgs *outp;
+ int size = sizeof(struct outputArgs);
+ int error = 0;
+
+ CODA_ALLOC(inp, struct inputArgs *, sizeof(struct inputArgs));
+ outp = (struct outputArgs *)inp;
+ INIT_IN(inp, CFS_CLOSE);
+ coda_load_creds(&(inp->cred));
+
+ inp->d.cfs_close.VFid = *fid;
+ inp->d.cfs_close.flags = flags;
+
+ error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+ if (inp) CODA_FREE(inp, sizeof(struct inputArgs));
+ return -error;
+}
+
+int venus_open(struct super_block *sb, struct ViceFid *fid,
+ int flags, ino_t *ino, dev_t *dev)
+{
+ struct inputArgs *inp = NULL;
+ struct outputArgs *outp = NULL;
+ int size = sizeof(struct inputArgs);
+ int error = 0;
+
+ CODA_ALLOC(inp, struct inputArgs *, sizeof(struct inputArgs));
+ outp = (struct outputArgs *)inp;
+ INIT_IN(inp, CFS_OPEN);
+ coda_load_creds(&(inp->cred));
+
+ inp->d.cfs_open.VFid = *fid;
+ inp->d.cfs_open.flags = flags;
+
+ error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+ if ( !error ) {
+ *ino = outp->d.cfs_open.inode;
+ *dev = outp->d.cfs_open.dev;
+ } else {
+ *ino = 0;
+ *dev = 0;
+ }
+
+ if (inp)
+ CODA_FREE(inp, sizeof(struct inputArgs));
+
+ return -error;
+}
+
+int venus_mkdir(struct super_block *sb, struct ViceFid *dirfid,
+ const char *name, int length,
+ struct ViceFid *newfid, struct coda_vattr *attrs)
+{
+ struct inputArgs *inp;
+ struct outputArgs *outp;
+ int error=0, size, payload_offset;
+
+ payload_offset = VC_INSIZE(cfs_mkdir_in);
+ size = CFS_MAXNAMLEN + payload_offset;
+ UPARG(size, CFS_MKDIR);
+
+ inp->d.cfs_mkdir.VFid = *dirfid;
+ inp->d.cfs_mkdir.attr = *attrs;
+ inp->d.cfs_mkdir.name = (char *) payload_offset;
+
+ /* Venus must get null terminated string */
+ memcpy((char *)inp + payload_offset, name, length);
+ *((char *)inp + payload_offset + length) = '\0';
+ size = payload_offset + length + 1;
+
+ error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+ *attrs = outp->d.cfs_mkdir.attr;
+ *newfid = outp->d.cfs_mkdir.VFid;
+
+ if (inp)
+ CODA_FREE(inp, size);
+ return -error;
+}
+
+
+int venus_rename(struct super_block *sb, struct ViceFid *old_fid,
+ struct ViceFid *new_fid, size_t old_length,
+ size_t new_length, const char *old_name, const char *new_name)
+{
+ struct inputArgs *inp;
+ struct outputArgs *outp;
+ int error, offset, size, s;
+
+ size = 2*CFS_MAXNAMLEN + VC_INSIZE(cfs_rename_in) +8;
+ UPARG(size, CFS_RENAME);
+
+ inp->d.cfs_rename.sourceFid = *old_fid;
+ inp->d.cfs_rename.destFid = *new_fid;
+
+ offset = VC_INSIZE(cfs_rename_in);
+
+ /* Venus must receive an null terminated string */
+ inp->d.cfs_rename.srcname = (char *)offset;
+ 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->d.cfs_rename.destname = (char *)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';
+
+ size += s;
+ CDEBUG(D_INODE, "destname in packet: %s\n",
+ (char *)inp + (int) inp->d.cfs_rename.destname);
+ error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+ if (inp) CODA_FREE(inp, size);
+ return -error;
+}
+
+int venus_create(struct super_block *sb, struct ViceFid *dirfid,
+ const char *name, int length, int excl, int mode,
+ struct ViceFid *newfid, struct coda_vattr *attrs)
+{
+ struct inputArgs *inp;
+ struct outputArgs *outp;
+ int error=0, size, payload_offset;
+
+ payload_offset = VC_INSIZE(cfs_create_in);
+ size = CFS_MAXNAMLEN + payload_offset;
+ UPARG(size, CFS_CREATE);
+
+ inp->d.cfs_create.VFid = *dirfid;
+ inp->d.cfs_create.attr.va_mode = mode;
+ inp->d.cfs_create.excl = excl;
+ inp->d.cfs_create.mode = mode;
+ inp->d.cfs_create.name = (char *) payload_offset;
+
+ /* Venus must get null terminated string */
+ memcpy((char *)inp + payload_offset, name, length);
+ *((char *)inp + payload_offset + length) = '\0';
+ size = payload_offset + length + 1;
+
+ error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+ *attrs = outp->d.cfs_create.attr;
+ *newfid = outp->d.cfs_create.VFid;
+
+ if (inp)
+ CODA_FREE(inp, size);
+ return -error;
+}
+
+int venus_rmdir(struct super_block *sb, struct ViceFid *dirfid,
+ const char *name, int length)
+{
+ struct inputArgs *inp;
+ struct outputArgs *outp;
+ int error=0, size, payload_offset;
+
+ payload_offset = VC_INSIZE(cfs_rmdir_in);
+ size = CFS_MAXNAMLEN + payload_offset;
+ UPARG(size, CFS_RMDIR);
+
+ inp->d.cfs_rmdir.VFid = *dirfid;
+ inp->d.cfs_rmdir.name = (char *) payload_offset;
+ memcpy((char *)inp + payload_offset, name, size);
+
+ error = coda_upcall(coda_sbp(sb), size, &size, inp);
+ if ( inp )
+ CODA_FREE(inp, size);
+ return -error;
+}
+
+int venus_remove(struct super_block *sb, struct ViceFid *dirfid,
+ const char *name, int length)
+{
+ struct inputArgs *inp;
+ struct outputArgs *outp;
+ int error=0, size, payload_offset;
+
+ payload_offset = VC_INSIZE(cfs_remove_in);
+ size = CFS_MAXNAMLEN + payload_offset;
+ UPARG(size, CFS_REMOVE);
+
+ inp->d.cfs_remove.VFid = *dirfid;
+ inp->d.cfs_remove.name = (char *)payload_offset;
+ memcpy((char *)inp + payload_offset, name, size);
+
+ error = coda_upcall(coda_sbp(sb), size, &size, inp);
+ if ( inp )
+ CODA_FREE(inp, size);
+ return -error;
+}
+
+int venus_readlink(struct super_block *sb, struct ViceFid *fid,
+ char *buffer, int *length)
+{
+ int error, size, retlen;
+ char *result;
+ struct inputArgs *inp;
+ struct outputArgs *outp;
+ char *buf=NULL; /*[CFS_MAXNAMLEN + VC_INSIZE(cfs_readlink_in)];*/
+
+ size = CFS_MAXPATHLEN + VC_INSIZE(cfs_readlink_in);
+ UPARG(size, CFS_READLINK);
+ inp->d.cfs_readlink.VFid = *fid;
+
+ error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+ if (! error) {
+ retlen = outp->d.cfs_readlink.count;
+ if ( retlen > *length )
+ retlen = *length;
+ *length = retlen;
+ result = (char *)outp + (int)outp->d.cfs_readlink.data;
+ memcpy(buffer, result, retlen);
+ }
+
+ if (inp) CODA_FREE(buf, size);
+ CDEBUG(D_INODE, " result %d\n",error);
+ EXIT;
+ return -error;
+}
+
+int venus_link(struct super_block *sb, struct ViceFid *fid,
+ struct ViceFid *dirfid, const char *name, int len )
+{
+ int error, payload_offset, size;
+ struct inputArgs *inp;
+ struct outputArgs *outp;
+
+ size = CFS_MAXNAMLEN + sizeof(struct inputArgs);
+ UPARG(size, CFS_LINK);
+
+ payload_offset = (VC_INSIZE(cfs_link_in));
+ inp->d.cfs_link.sourceFid = *fid;
+ inp->d.cfs_link.destFid = *dirfid;
+ inp->d.cfs_link.tname = (char *)payload_offset;
+
+ /* make sure strings are null terminated */
+ memcpy((char *)inp + payload_offset, name, len);
+ *((char *)inp + payload_offset + len) = '\0';
+ size = payload_offset + len + 1;
+
+ error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+ if (inp)
+ CODA_FREE(inp, size);
+ CDEBUG(D_INODE, " result %d\n",error);
+ EXIT;
+ return -error;
+}
+
+int venus_symlink(struct super_block *sb, struct ViceFid *fid,
+ const char *name, int len,
+ const char *symname, int symlen)
+{
+ int error, payload_offset, size, s;
+ struct inputArgs *inp;
+ struct outputArgs *outp;
+
+
+ /*
+ * allocate space for regular input,
+ * plus 1 path and 1 name, plus padding
+ */
+ size = sizeof(struct inputArgs) + CFS_MAXNAMLEN + CFS_MAXNAMLEN + 8;
+ UPARG(size, CFS_SYMLINK);
+
+ /* inp->d.cfs_symlink.attr = *tva; XXXXXX */
+ inp->d.cfs_symlink.VFid = *fid;
+
+ payload_offset = VC_INSIZE(cfs_symlink_in);
+ inp->d.cfs_symlink.srcname =(char*) payload_offset;
+
+ s = ( symlen & ~0x3 ) + 4; /* Round up to word boundary. */
+
+ /* don't forget to copy out the null termination */
+ memcpy((char *)inp + payload_offset, symname, symlen);
+ *((char *)inp + payload_offset + symlen) = '\0';
+
+ payload_offset += s;
+ inp->d.cfs_symlink.tname = (char *) payload_offset;
+ s = (len & ~0x3) + 4; /* Round up to word boundary. */
+ memcpy((char *)inp + payload_offset, name, len);
+ *((char *)inp + payload_offset + len) = '\0';
+
+ size = payload_offset + s;
+ error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+ if (inp)
+ CODA_FREE(inp, size);
+ CDEBUG(D_INODE, " result %d\n",error);
+ EXIT;
+ return -error;
+}
+
+int venus_access(struct super_block *sb, struct ViceFid *fid, int mask)
+{
+ struct inputArgs *inp;
+ struct outputArgs *outp;
+ int size;
+ int error;
+
+ size = sizeof(struct inputArgs);
+ UPARG(size, CFS_ACCESS);
+
+ inp->d.cfs_access.VFid = *fid;
+ inp->d.cfs_access.flags = mask << 6;
+
+ error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+ if (inp) CODA_FREE(inp, sizeof(struct inputArgs));
+ EXIT;
+ return -error;
+}
+
+
+int venus_pioctl(struct super_block *sb, struct ViceFid *fid,
+ unsigned int cmd, struct PioctlData *data)
+{
+ struct inputArgs *inp;
+ struct outputArgs *outp;
+ int size, error = 0;
+ int iocsize;
+ char str[50];
+
+ size = VC_MAXMSGSIZE;
+ UPARG(size, CFS_IOCTL);
+
+ /* build packet for Venus */
+ if (data->vi.in_size > VC_DATASIZE) {
+ error = EINVAL;
+ goto exit;
+ }
+
+ inp->d.cfs_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->d.cfs_ioctl.cmd = (cmd & ~(IOCPARM_MASK << 16));
+ iocsize = ((cmd >> 16) & IOCPARM_MASK) - sizeof(char *) - sizeof(int);
+ inp->d.cfs_ioctl.cmd |= (iocsize & IOCPARM_MASK) << 16;
+
+ /* in->d.cfs_ioctl.rwflag = flag; */
+ inp->d.cfs_ioctl.len = data->vi.in_size;
+ inp->d.cfs_ioctl.data = (char *)(VC_INSIZE(cfs_ioctl_in));
+
+ /* get the data out of user space */
+ if ( copy_from_user((char*)inp + (int)inp->d.cfs_ioctl.data,
+ data->vi.in, data->vi.in_size) ) {
+ error = EINVAL;
+ goto exit;
+ }
+ error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+ if (error) {
+ printk("coda_pioctl: Venus returns: %d for %s\n",
+ error, coda_f2s(fid, str));
+ goto exit;
+ }
+
+ /* Copy out the OUT buffer. */
+ if (outp->d.cfs_ioctl.len > data->vi.out_size) {
+ CDEBUG(D_FILE, "return len %d <= request len %d\n",
+ outp->d.cfs_ioctl.len,
+ data->vi.out_size);
+ error = EINVAL;
+ } else {
+ if (copy_to_user(data->vi.out,
+ (char *)outp + (int)outp->d.cfs_ioctl.data,
+ data->vi.out_size)) {
+ error = EINVAL;
+ goto exit;
+ }
+ }
+
+ exit:
+ if (inp)
+ CODA_FREE(inp, VC_MAXMSGSIZE);
+ return -error;
+}
+
+/*
+ * coda_upcall and coda_downcall routines.
+ *
+ */
+
+/*
+ * coda_upcall will return a POSITIVE error in the case of
+ * failed communication with Venus _or_ will peek at Venus
+ * reply and return Venus' error, also POSITIVE.
+ *
+ */
+int coda_upcall(struct coda_sb_info *sbi, int inSize, int *outSize,
+ struct inputArgs *buffer)
+{
+ struct vcomm *vcommp;
+ struct outputArgs *out;
+ struct vmsg *vmp;
+ int error = 0;
+
+ENTRY;
+
+ if (sbi->sbi_vcomm == NULL) {
+ return ENODEV;
+ }
+ vcommp = sbi->sbi_vcomm;
+
+ clstats(((struct inputArgs *)buffer)->opcode);
+
+ if (!vcomm_open(vcommp))
+ return(ENODEV);
+
+ /* Format the request message. */
+ CODA_ALLOC(vmp,struct vmsg *,sizeof(struct vmsg));
+ vmp->vm_data = (void *)buffer;
+ vmp->vm_flags = 0;
+ vmp->vm_inSize = inSize;
+ vmp->vm_outSize = *outSize ? *outSize : inSize;
+ vmp->vm_opcode = ((struct inputArgs *)buffer)->opcode;
+ vmp->vm_unique = ++vcommp->vc_seq;
+ vmp->vm_sleep = NULL;
+
+ /* Fill in the common input args. */
+ ((struct inputArgs *)buffer)->unique = vmp->vm_unique;
+
+ /* Append msg to pending queue and poke Venus. */
+
+ INSQUE(vmp->vm_chain, vcommp->vc_pending);
+ CDEBUG(D_UPCALL,
+ "Proc %d wake Venus for(opc,uniq) =(%d,%d) msg at %x.zzz.\n",
+ current->pid, vmp->vm_opcode, vmp->vm_unique, (int)vmp);
+
+ 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 (vcclose), return
+ * ENODEV. */
+
+ /* Ignore return, We have to check anyway */
+
+
+ interruptible_sleep_on(&vmp->vm_sleep);
+ CDEBUG(D_UPCALL,
+ "..process %d woken up by Venus for vmp at 0x%x, data at %x\n",
+ current->pid, (int)vmp, (int)vmp->vm_data);
+ if (vcomm_open(vcommp)) { /* i.e. Venus is still alive */
+ /* Op went through, interrupt or not... */
+ if (vmp->vm_flags & VM_WRITE) {
+ error = 0;
+ out = (struct outputArgs *)vmp->vm_data;
+ error = out->result;
+ CDEBUG(D_UPCALL,
+ "upcall: (u,o,r) (%ld, %ld, %ld) out at %x\n",
+ out->unique, out->opcode, out->result, (int)out);
+ *outSize = vmp->vm_outSize;
+ goto exit;
+ }
+ if (!(vmp->vm_flags & VM_READ)) {
+ /* Interrupted before venus read it. */
+ CDEBUG(D_UPCALL,
+ "Interrupted before read:(op,un) (%d.%d), flags = %x\n",
+ vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
+ REMQUE(vmp->vm_chain);
+ error = ERESTARTSYS;
+ goto exit;
+ }
+ if ( vmp->vm_flags & VM_READ) {
+ /* interrupted after Venus did its read, send signal */
+ struct inputArgs *dog;
+ struct vmsg *svmp;
+
+ CDEBUG(D_UPCALL,
+ "Sending Venus a signal: op = %d.%d, flags = %x\n",
+ vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
+
+ REMQUE(vmp->vm_chain);
+ error = ERESTARTSYS;
+
+ CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
+ CODA_ALLOC((svmp->vm_data), char *, VC_IN_NO_DATA);
+
+ CDEBUG(D_UPCALL,
+ "coda_upcall: enqueing signal msg (%d, %d)\n",
+ svmp->vm_opcode, svmp->vm_unique);
+ dog = (struct inputArgs *)svmp->vm_data;
+ dog->opcode = CFS_SIGNAL;
+ dog->unique = vmp->vm_unique;
+
+ svmp->vm_flags = 0;
+ svmp->vm_opcode = dog->opcode;
+ svmp->vm_unique = dog->unique;
+ svmp->vm_inSize = VC_IN_NO_DATA;
+ svmp->vm_outSize = VC_IN_NO_DATA;
+
+ /* insert at head of queue! */
+ INSQUE(svmp->vm_chain, vcommp->vc_pending);
+ wake_up_interruptible(&vcommp->vc_waitq);
+ }
+ } else { /* If venus died i.e. !VC_OPEN(vcommp) */
+ printk("coda_upcall: Venus dead upon (op,un) (%d.%d) flags %d\n",
+ vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
+
+ /* if (! (vmp->vm_flags & VM_WRITE) ) */
+ error = ENODEV;
+ }
+
+ exit:
+ CODA_FREE(vmp, sizeof(struct vmsg));
+ if (error)
+ badclstats();
+ return error;
+}
+
+
+
+
+/*
+ * There are 7 cases where cache invalidations occur. The semantics
+ * of each is listed here:
+ *
+ * CFS_FLUSH -- flush all entries from the name cache and the cnode cache.
+ * CFS_PURGEUSER -- flush all entries from the name cache for a specific user
+ * This call is a result of token expiration.
+ * Linux does a cfsnc_flush since cred's are not maintained.
+ *
+ * The next arise as the result of callbacks on a file or directory.
+ * CFS_ZAPDIR -- flush the attributes for the dir from its cnode.
+ * Zap all children of this directory from the namecache.
+ * CFS_ZAPFILE -- flush the cached attributes for a file.
+ * CFS_ZAPVNODE -- in linux the same as zap file (no creds).
+ *
+ * The next is a result of Venus detecting an inconsistent file.
+ * CFS_PURGEFID -- flush the attribute for the file
+ * If it is a dir (odd vnode), purge its
+ * children from the namecache
+ * remove the file from the namecache.
+ *
+ * The last allows Venus to replace local fids with global ones
+ * during reintegration.
+ *
+ * CFS_REPLACE -- replace one ViceFid with another throughout the name cache */
+
+int coda_downcall(int opcode, struct outputArgs * out)
+{
+
+ /* Handle invalidate requests. */
+ switch (opcode) {
+ case CFS_FLUSH : {
+ clstats(CFS_FLUSH);
+ cfsnc_flush();
+ return(0);
+ }
+ case CFS_PURGEUSER : {
+ clstats(CFS_PURGEUSER);
+ cfsnc_flush();
+ return(0);
+ }
+ case CFS_ZAPDIR : {
+ ViceFid *fid = &out->d.cfs_zapdir.CodaFid;
+ clstats(CFS_ZAPDIR);
+ cfsnc_zapfid(fid);
+ cfsnc_zapParentfid(fid);
+ CDEBUG(D_UPCALL, "zapdir: fid = (%lx.%lx.%lx), \n",fid->Volume,
+ fid->Vnode,
+ fid->Unique);
+ return(0);
+ }
+ case CFS_ZAPVNODE : {
+ clstats(CFS_ZAPVNODE);
+ cfsnc_zapfid(&out->d.cfs_zapvnode.VFid);
+ return(0);
+ }
+ case CFS_ZAPFILE : {
+ clstats(CFS_ZAPFILE);
+ cfsnc_zapfid(&out->d.cfs_zapfile.CodaFid);
+ return 0;
+ }
+ case CFS_PURGEFID : {
+ ViceFid *fid = &out->d.cfs_purgefid.CodaFid;
+ clstats(CFS_PURGEFID);
+ cfsnc_zapfid(fid);
+ cfsnc_zapParentfid(fid);
+ CDEBUG(D_UPCALL, "purgefid: fid = (%lx.%lx.%lx)\n",
+ fid->Volume, fid->Vnode,
+ fid->Unique);
+ return 0;
+ }
+ case CFS_REPLACE : {
+ clstats(CFS_REPLACE);
+ cfsnc_replace(&out->d.cfs_replace.OldFid,
+ &out->d.cfs_replace.NewFid);
+ return (0);
+ }
+ }
+ return 0;
+}
+
+