diff options
Diffstat (limited to 'fs/coda')
-rw-r--r-- | fs/coda/.cvsignore | 1 | ||||
-rw-r--r-- | fs/coda/Makefile | 15 | ||||
-rw-r--r-- | fs/coda/cnode.c | 196 | ||||
-rw-r--r-- | fs/coda/coda_linux.c | 70 | ||||
-rw-r--r-- | fs/coda/dir.c | 769 | ||||
-rw-r--r-- | fs/coda/file.c | 240 | ||||
-rw-r--r-- | fs/coda/inode.c | 34 | ||||
-rw-r--r-- | fs/coda/namecache.c | 832 | ||||
-rw-r--r-- | fs/coda/pioctl.c | 154 | ||||
-rw-r--r-- | fs/coda/psdev.c | 444 | ||||
-rw-r--r-- | fs/coda/super.c | 612 | ||||
-rw-r--r-- | fs/coda/symlink.c | 82 | ||||
-rw-r--r-- | fs/coda/sysctl.c | 168 | ||||
-rw-r--r-- | fs/coda/upcall.c | 747 |
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; +} + + |