diff options
Diffstat (limited to 'fs/coda')
-rw-r--r-- | fs/coda/.cvsignore | 1 | ||||
-rw-r--r-- | fs/coda/Makefile | 4 | ||||
-rw-r--r-- | fs/coda/cache.c | 349 | ||||
-rw-r--r-- | fs/coda/cnode.c | 148 | ||||
-rw-r--r-- | fs/coda/coda_linux.c | 236 | ||||
-rw-r--r-- | fs/coda/dir.c | 278 | ||||
-rw-r--r-- | fs/coda/file.c | 74 | ||||
-rw-r--r-- | fs/coda/inode.c | 34 | ||||
-rw-r--r-- | fs/coda/namecache.c | 832 | ||||
-rw-r--r-- | fs/coda/pioctl.c | 15 | ||||
-rw-r--r-- | fs/coda/psdev.c | 303 | ||||
-rw-r--r-- | fs/coda/super.c | 444 | ||||
-rw-r--r-- | fs/coda/symlink.c | 53 | ||||
-rw-r--r-- | fs/coda/sysctl.c | 28 | ||||
-rw-r--r-- | fs/coda/upcall.c | 781 |
15 files changed, 1637 insertions, 1943 deletions
diff --git a/fs/coda/.cvsignore b/fs/coda/.cvsignore index 4671378ae..857dd22e9 100644 --- a/fs/coda/.cvsignore +++ b/fs/coda/.cvsignore @@ -1 +1,2 @@ .depend +.*.flags diff --git a/fs/coda/Makefile b/fs/coda/Makefile index 4118fdc61..0a4140745 100644 --- a/fs/coda/Makefile +++ b/fs/coda/Makefile @@ -3,8 +3,8 @@ # 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 +O_OBJS := psdev.o cache.o cnode.o super.o dir.o file.o upcall.o coda_linux.o\ + symlink.o pioctl.o sysctl.o M_OBJS := $(O_TARGET) # If you want debugging output, please uncomment the following line diff --git a/fs/coda/cache.c b/fs/coda/cache.c new file mode 100644 index 000000000..7673bfbdd --- /dev/null +++ b/fs/coda/cache.c @@ -0,0 +1,349 @@ +/* + * Cache operations for Coda. + * 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 <asm/uaccess.h> +#include <linux/string.h> +#include <linux/list.h> + +#include <linux/coda.h> +#include <linux/coda_linux.h> +#include <linux/coda_psdev.h> +#include <linux/coda_fs_i.h> +#include <linux/coda_cache.h> + +/* Keep various stats */ +struct cfsnc_statistics cfsnc_stat; + + +/* we need to call INIT_LIST_HEAD on cnp->c_cnhead and sbi->sbi_cchead */ + +void coda_ccinsert(struct coda_cache *el, struct super_block *sb) +{ + struct coda_sb_info *sbi = coda_sbp(sb); +ENTRY; + if ( !sbi || !el) { + printk("coda_ccinsert: NULL sbi or el!\n"); + return ; + } + + list_add(&el->cc_cclist, &sbi->sbi_cchead); +} + +void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cnp) +{ +ENTRY; + if ( !cnp || !el) { + printk("coda_cninsert: NULL cnp or el!\n"); + return ; + } + list_add(&el->cc_cnlist, &cnp->c_cnhead); +} + +void coda_ccremove(struct coda_cache *el) +{ + ENTRY; + if (el->cc_cclist.next && el->cc_cclist.prev) + list_del(&el->cc_cclist); + else + printk("coda_cnremove: trying to remove 0 entry!"); +} + +void coda_cnremove(struct coda_cache *el) +{ + ENTRY; + if (el->cc_cnlist.next && el->cc_cnlist.prev) + list_del(&el->cc_cnlist); + else + printk("coda_cnremove: trying to remove 0 entry!"); +} + + +void coda_cache_create(struct inode *inode, int mask) +{ + struct coda_inode_info *cnp = ITOC(inode); + struct super_block *sb = inode->i_sb; + struct coda_cache *cc = NULL; + ENTRY; + CODA_ALLOC(cc, struct coda_cache *, sizeof(*cc)); + + if ( !cc ) { + printk("Out of memory in coda_cache_enter!\n"); + return; + } + coda_load_creds(&cc->cc_cred); + cc->cc_mask = mask; + coda_cninsert(cc, cnp); + coda_ccinsert(cc, sb); +} + +struct coda_cache * coda_cache_find(struct inode *inode) +{ + struct coda_inode_info *cnp = ITOC(inode); + struct list_head *lh, *le; + struct coda_cache *cc = NULL; + + le = lh = &cnp->c_cnhead; + while( (le = le->next ) != lh ) { + /* compare name and creds */ + cc = list_entry(le, struct coda_cache, cc_cnlist); + if ( !coda_cred_ok(&cc->cc_cred) ) + continue; + CDEBUG(D_CACHE, "HIT for ino %ld\n", inode->i_ino ); + return cc; /* cache hit */ + } + return NULL; +} + +void coda_cache_enter(struct inode *inode, int mask) +{ + struct coda_cache *cc; + + cc = coda_cache_find(inode); + + if ( cc ) { + cc->cc_mask |= mask; + } else { + coda_cache_create(inode, mask); + } +} + +void coda_cache_clear_cnp(struct coda_inode_info *cnp) +{ + struct list_head *lh, *le; + struct coda_cache *cc; + + if ( !cnp ) { + printk("coda_cache_cnp_clear: NULL cnode\n"); + return; + } + + lh = le = &cnp->c_cnhead; + while ( (le = le->next ) != lh ) { + cc = list_entry(le, struct coda_cache, cc_cnlist); + coda_cnremove(cc); + coda_ccremove(cc); + CODA_FREE(cc, sizeof(*cc)); + } +} + +void coda_cache_clear_all(struct super_block *sb) +{ + struct list_head *lh, *le; + struct coda_cache *cc; + struct coda_sb_info *sbi = coda_sbp(sb); + + if ( !sbi ) { + printk("coda_cache_clear_all: NULL sbi\n"); + return; + } + + lh = le = &sbi->sbi_cchead; + while ( (le = le->next ) != lh ) { + cc = list_entry(le, struct coda_cache, cc_cclist); + coda_cnremove(cc); + coda_ccremove(cc); + CODA_FREE(cc, sizeof(*cc)); + } +} + +void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred) +{ + struct list_head *lh, *le; + struct coda_cache *cc; + struct coda_sb_info *sbi = coda_sbp(sb); + + if ( !sbi ) { + printk("coda_cache_clear_all: NULL sbi\n"); + return; + } + + lh = le = &sbi->sbi_cchead; + while ( (le = le->next ) != lh ) { + cc = list_entry(le, struct coda_cache, cc_cclist); + if ( coda_cred_eq(&cc->cc_cred, cred)) { + coda_cnremove(cc); + coda_ccremove(cc); + CODA_FREE(cc, sizeof(*cc)); + } + } +} + + +int coda_cache_check(struct inode *inode, int mask) +{ + struct coda_inode_info *cnp = ITOC(inode); + struct list_head *lh, *le; + struct coda_cache *cc = NULL; + + le = lh = &cnp->c_cnhead; + while( (le = le->next ) != lh ) { + /* compare name and creds */ + cc = list_entry(le, struct coda_cache, cc_cnlist); + if ( (cc->cc_mask & mask) != mask ) + continue; + if ( !coda_cred_ok(&cc->cc_cred) ) + continue; + CDEBUG(D_CACHE, "HIT for ino %ld\n", inode->i_ino ); + return 1; /* cache hit */ + } + CDEBUG(D_CACHE, "MISS for ino %ld\n", inode->i_ino ); + return 0; +} + + +/* DENTRY related stuff */ + +/* when the dentry count falls to 0 this is called. If Venus has + asked for it to be flushed, we take it out of the dentry hash + table with d_drop */ + +static void coda_flag_children(struct dentry *parent) +{ + struct list_head *child; + struct coda_inode_info *cnp; + struct dentry *de; + + child = parent->d_subdirs.next; + while ( child != &parent->d_subdirs ) { + de = list_entry(child, struct dentry, d_child); + cnp = ITOC(de->d_inode); + if (cnp) + cnp->c_flags |= C_ZAPFID; + CDEBUG(D_CACHE, "ZAPFID for %s\n", coda_f2s(&cnp->c_fid)); + + child = child->next; + } + return; +} + +/* flag dentry and possibly children of a dentry with C_ZAPFID */ +void coda_dentry_delete(struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + struct coda_inode_info *cnp = NULL; + ENTRY; + + if (inode) { + cnp = ITOC(inode); + if ( cnp ) + CHECK_CNODE(cnp); + } else { + CDEBUG(D_CACHE, "No inode for dentry_delete!\n"); + return; + } + + + if ( !cnp ) { + printk("No cnode for dentry_delete!\n"); + return; + } + + if ( cnp->c_flags & (C_ZAPFID | C_ZAPDIR) ) + d_drop(dentry); + if ( (cnp->c_flags & C_ZAPDIR) && S_ISDIR(inode->i_mode) ) { + coda_flag_children(dentry); + } + return; +} + +static void coda_zap_cnode(struct coda_inode_info *cnp, int flags) +{ + cnp->c_flags |= flags; + coda_cache_clear_cnp(cnp); +} + + + +/* the dache will notice the flags and drop entries (possibly with + children) the moment they are no longer in use */ +void coda_zapfid(struct ViceFid *fid, struct super_block *sb, int flag) +{ + struct inode *inode = NULL; + struct coda_inode_info *cnp; + + ENTRY; + + if ( !sb ) { + printk("coda_zapfid: no sb!\n"); + return; + } + + if ( !fid ) { + printk("coda_zapfid: no fid!\n"); + return; + } + + if ( coda_fid_is_volroot(fid) ) { + struct list_head *lh, *le; + struct coda_sb_info *sbi = coda_sbp(sb); + le = lh = &sbi->sbi_volroothead; + while ( (le = le->next) != lh ) { + cnp = list_entry(le, struct coda_inode_info, c_volrootlist); + if ( cnp->c_fid.Volume == fid->Volume) + coda_zap_cnode(cnp, flag); + } + return; + } + + + inode = coda_fid_to_inode(fid, sb); + if ( !inode ) { + CDEBUG(D_CACHE, "coda_zapfid: no inode!\n"); + return; + } + cnp = ITOC(inode); + coda_zap_cnode(cnp, flag); +} + + +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; +} diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index abf8a855f..67133f275 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -1,5 +1,5 @@ /* cnode related routines for the coda kernel code - Peter Braam, Sep 1996. + (C) 1996 Peter Braam */ #include <linux/types.h> @@ -7,39 +7,38 @@ #include <linux/coda.h> #include <linux/coda_linux.h> +#include <linux/coda_fs_i.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; + +static void coda_fill_inode (struct inode *inode, struct coda_vattr *attr) +{ + CDEBUG(D_SUPER, "ino: %ld\n", inode->i_ino); + + 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's this? i_mode = %o\n", + inode->i_mode); + inode->i_op = NULL; } - - 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 @@ -48,11 +47,11 @@ void coda_cnode_free(struct cnode *cinode) */ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb) { - struct cnode *cnp; + struct coda_inode_info *cnp; + struct coda_sb_info *sbi= coda_sbp(sb); struct coda_vattr attr; int error; ino_t ino; - char str[50]; ENTRY; @@ -63,7 +62,7 @@ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb) error = venus_getattr(sb, fid, &attr); if ( error ) { printk("coda_cnode_make: coda_getvattr returned %d for %s.\n", - error, coda_f2s(fid, str)); + error, coda_f2s(fid)); *inode = NULL; return error; } @@ -75,37 +74,26 @@ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb) 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 = ITOC(*inode); + if ( cnp->c_magic == 0 ) { + memset(cnp, 0, (int) sizeof(struct coda_inode_info)); + 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); + cnp->c_vnode = *inode; + INIT_LIST_HEAD(&(cnp->c_cnhead)); + INIT_LIST_HEAD(&(cnp->c_volrootlist)); } 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); + printk("coda_cnode make on initialized inode %ld, %s!\n", + (*inode)->i_ino, coda_f2s(&cnp->c_fid)); } - 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); + + /* fill in the inode attributes */ + if ( coda_fid_is_volroot(fid) ) + list_add(&cnp->c_volrootlist, &sbi->sbi_volroothead); + + coda_fill_inode(*inode, &attr); + 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; @@ -122,51 +110,35 @@ inline int coda_fideq(ViceFid *fid1, ViceFid *fid2) } -/* 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) { +struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) +{ ino_t nr; struct inode *inode; - struct cnode *cnp; - - nr = coda_fid2ino(fid); + struct coda_inode_info *cnp; +ENTRY; + + CDEBUG(D_INODE, "%s\n", coda_f2s(fid)); + + nr = coda_f2i(fid); inode = iget(sb, nr); + if ( !inode ) { + printk("coda_fid_to_inode: null from iget, sb %p, nr %ld.\n", + sb, nr); + return NULL; + } + /* check if this inode is linked to a cnode */ - cnp = (struct cnode *) inode->u.generic_ip; - if ( cnp == NULL ) { + cnp = ITOC(inode); + + if ( cnp->c_magic != CODA_CNODE_MAGIC ) { 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"); diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c index ea595c35b..e968f3add 100644 --- a/fs/coda/coda_linux.c +++ b/fs/coda/coda_linux.c @@ -21,24 +21,33 @@ #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_fs_i.h> +#include <linux/coda_cache.h> /* initialize the debugging variables */ -int coda_debug =815; -int coda_print_entry = 1; +int coda_debug = 0; +int coda_print_entry = 0; int coda_access_cache = 1; /* caller must allocate 36 byte string ! */ -char * coda_f2s(ViceFid *f, char *s) +char * coda_f2s(ViceFid *f) { + static char s[50]; if ( f ) { - sprintf(s, "(%-#10lx,%-#10lx,%-#10lx)", + sprintf(s, "(%10lx,%10lx,%10lx)", f->Volume, f->Vnode, f->Unique); } return s; } +int coda_iscontrol(const char *name, size_t length) +{ + if ((CFS_CONTROLLEN == length) && + (strncmp(name, CFS_CONTROL, CFS_CONTROLLEN) == 0)) + return 1; + return 0; +} + int coda_isroot(struct inode *i) { if ( i->i_sb->s_root->d_inode == i ) { @@ -47,11 +56,10 @@ int coda_isroot(struct inode *i) return 0; } } + -void coda_load_creds(struct CodaCred *cred) +void coda_load_creds(struct coda_cred *cred) { - int i; - cred->cr_uid = (vuid_t) current->uid; cred->cr_euid = (vuid_t) current->euid; cred->cr_suid = (vuid_t) current->suid; @@ -61,10 +69,216 @@ void coda_load_creds(struct CodaCred *cred) cred->cr_egid = (vgid_t) current->egid; cred->cr_sgid = (vgid_t) current->sgid; cred->cr_fsgid = (vgid_t) current->fsgid; +} + +int coda_cred_ok(struct coda_cred *cred) +{ + return(current->fsuid == cred->cr_fsuid); +} + +int coda_cred_eq(struct coda_cred *cred1, struct coda_cred *cred2) +{ + return (cred1->cr_fsuid == cred2->cr_fsuid); +} + +unsigned short coda_flags_to_cflags(unsigned short flags) +{ + unsigned short coda_flags = 0; + + if ( flags & (O_RDONLY | O_RDWR) ) + coda_flags |= C_O_READ; + + if ( flags & (O_WRONLY | O_RDWR) ) + coda_flags |= C_O_WRITE; + + if ( flags & O_TRUNC ) + coda_flags |= C_O_TRUNC; + + return coda_flags; +} + + +int coda_fid_is_volroot(struct ViceFid *fid) +{ + return ( (fid->Vnode == 1) && (fid->Unique == 1 ) ); +} - for ( i = 0 ; i < NGROUPS ; ++i ) { - cred->cr_groups[i] = (vgid_t) current->groups[i]; +/* utility functions below */ +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 C_VNON: + inode_type = 0; + break; + case C_VREG: + inode_type = S_IFREG; + break; + case C_VDIR: + inode_type = S_IFDIR; + break; + case C_VLNK: + inode_type = S_IFLNK; + break; + default: + inode_type = 0; } + inode->i_mode |= inode_type; + if (attr->va_mode != (u_short) -1) + inode->i_mode = attr->va_mode | inode_type; + if (attr->va_uid != -1) + inode->i_uid = (uid_t) attr->va_uid; + if (attr->va_gid != -1) + inode->i_gid = (gid_t) attr->va_gid; + if (attr->va_nlink != -1) + inode->i_nlink = attr->va_nlink; + if (attr->va_size != -1) + inode->i_size = attr->va_size; + /* 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) +{ + 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 = C_VNON; + vattr->va_fileid = -1; + vattr->va_gen = -1; + vattr->va_bytes = -1; + vattr->va_nlink = -1; + vattr->va_blocksize = -1; + vattr->va_rdev = -1; + vattr->va_flags = 0; + /* determine the type */ +#if 0 + mode = iattr->ia_mode; + if ( S_ISDIR(mode) ) { + vattr->va_type = C_VDIR; + } else if ( S_ISREG(mode) ) { + vattr->va_type = C_VREG; + } else if ( S_ISLNK(mode) ) { + vattr->va_type = C_VLNK; + } else { + /* don't do others */ + vattr->va_type = C_VNON; + } +#endif + + /* set those vattrs that need change */ + valid = iattr->ia_valid; + if ( valid & ATTR_MODE ) { + vattr->va_mode = iattr->ia_mode; + } + if ( valid & ATTR_UID ) { + vattr->va_uid = (vuid_t) iattr->ia_uid; + } + if ( valid & ATTR_GID ) { + vattr->va_gid = (vgid_t) iattr->ia_gid; + } + if ( valid & ATTR_SIZE ) { + vattr->va_size = iattr->ia_size; + } + if ( valid & ATTR_ATIME ) { + vattr->va_atime.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 C_VNON: + typestr = "C_VNON"; + break; + case C_VREG: + typestr = "C_VREG"; + break; + case C_VDIR: + typestr = "C_VDIR"; + break; + case C_VBLK: + typestr = "C_VBLK"; + break; + case C_VCHR: + typestr = "C_VCHR"; + break; + case C_VLNK: + typestr = "C_VLNK"; + break; + case C_VSOCK: + typestr = "C_VSCK"; + break; + case C_VFIFO: + typestr = "C_VFFO"; + break; + case C_VBAD: + typestr = "C_VBAD"; + break; + default: + typestr = "????"; + break; + } + + + printk("attr: type %s (%o) mode %o uid %d gid %d rdev %d\n", + typestr, (int)attr->va_type, (int)attr->va_mode, + (int)attr->va_uid, (int)attr->va_gid, (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\n", + attr->va_gen, attr->va_flags); + 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); +} diff --git a/fs/coda/dir.c b/fs/coda/dir.c index cfc10dc83..dd20499dc 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -1,5 +1,5 @@ /* - * Direcotry operations for Coda filesystem + * Directory operations for Coda filesystem * Original version: (C) 1996 P. Braam and M. Callahan * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University * @@ -21,13 +21,13 @@ #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_fs_i.h> +#include <linux/coda_cache.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, +static int coda_link(struct dentry *old_dentry, struct inode *dir_inode, struct dentry *entry); static int coda_unlink(struct inode *dir_inode, struct dentry *entry); static int coda_symlink(struct inode *dir_inode, struct dentry *entry, @@ -43,8 +43,15 @@ 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); +int coda_fsync(struct file *, struct dentry *dentry); - +struct dentry_operations coda_dentry_operations = +{ + NULL, /* revalidate */ + NULL, /* hash */ + NULL, + coda_dentry_delete +}; struct inode_operations coda_dir_inode_operations = { @@ -80,7 +87,10 @@ struct file_operations coda_dir_operations = { NULL, /* mmap */ coda_open, /* open */ coda_release, /* release */ - NULL, /* fsync */ + coda_fsync, /* fsync */ + NULL, + NULL, + NULL }; @@ -88,14 +98,14 @@ struct file_operations coda_dir_operations = { /* acces routines: lookup, readlink, permission */ static int coda_lookup(struct inode *dir, struct dentry *entry) { - struct cnode *dircnp, *savedcnp; + struct coda_inode_info *dircnp; struct inode *res_inode = NULL; struct ViceFid resfid; + int dropme = 0; /* to indicate entry should not be cached */ 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", @@ -110,79 +120,56 @@ static int coda_lookup(struct inode *dir, struct dentry *entry) CHECK_CNODE(dircnp); if ( length > CFS_MAXNAMLEN ) { - printk("name too long: lookup, %s (%s)\n", - coda_f2s(&dircnp->c_fid, str), name); + printk("name too long: lookup, %s (%*s)\n", + coda_f2s(&dircnp->c_fid), length, name); return -ENAMETOOLONG; } - CDEBUG(D_INODE, "lookup: %s in %s\n", name, - coda_f2s(&dircnp->c_fid, str)); + CDEBUG(D_INODE, "lookup: %*s in %s\n", length, name, + coda_f2s(&dircnp->c_fid)); /* control object, create inode on the fly */ - if ( coda_isroot(dir) && (CFS_CONTROLLEN == length) && - (strncmp(name, CFS_CONTROL, CFS_CONTROLLEN) == 0) ) { + if (coda_isroot(dir) && coda_iscontrol(name, length)) { error = coda_cnode_makectl(&res_inode, dir->i_sb); CDEBUG(D_SPECIAL, - "Lookup on CTL object; iput of ino %ld, count %d\n", + "Lookup on CTL object; dir 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); + (const char *)name, length, &type, &resfid); res_inode = NULL; - if (!error) { + if (!error || (error == -CFS_NOCACHE) ) { + if (error == -CFS_NOCACHE) + dropme = 1; 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"); + return -error; } else if (error != -ENOENT) { - CDEBUG(D_INODE, "error for %s(%s)%d\n", - coda_f2s(&dircnp->c_fid, str), name, error); + CDEBUG(D_INODE, "error for %s(%*s)%d\n", + coda_f2s(&dircnp->c_fid), length, 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 */ - + CDEBUG(D_INODE, "lookup: %s is (%s) type %d result %d, dropme %d\n", + name, coda_f2s(&resfid), type, error, dropme); exit: entry->d_time = 0; - entry->d_op = NULL; + entry->d_op = &coda_dentry_operations; d_add(entry, res_inode); + if ( dropme ) + d_drop(entry); EXIT; return 0; } - int coda_permission(struct inode *inode, int mask) { - struct cnode *cp; + struct coda_inode_info *cp; int error; - int mode = inode->i_mode; - char str[50]; ENTRY; @@ -191,16 +178,10 @@ int coda_permission(struct inode *inode, int mask) 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; + if ( coda_access_cache == 1 ) { + if ( coda_cache_check(inode, mask) ) { + return 0; + } } cp = ITOC(inode); @@ -210,7 +191,11 @@ int coda_permission(struct inode *inode, int 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); + coda_f2s(&(cp->c_fid)), inode->i_ino, mask, error); + + if ( error == 0 ) { + coda_cache_enter(inode, mask); + } return error; } @@ -222,7 +207,7 @@ int coda_permission(struct inode *inode, int mask) static int coda_create(struct inode *dir, struct dentry *de, int mode) { int error=0; - struct cnode *dircnp; + struct coda_inode_info *dircnp; const char *name=de->d_name.name; int length=de->d_name.len; struct inode *result = NULL; @@ -235,13 +220,17 @@ static int coda_create(struct inode *dir, struct dentry *de, int mode) printk("coda_create: inode is null or not a directory\n"); return -ENOENT; } + + if (coda_isroot(dir) && coda_iscontrol(name, length)) + return -EPERM; + 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); + coda_f2s(&dircnp->c_fid), name); return -ENAMETOOLONG; } @@ -251,47 +240,51 @@ static int coda_create(struct inode *dir, struct dentry *de, int mode) if ( error ) { char str[50]; CDEBUG(D_INODE, "create: %s, result %d\n", - coda_f2s(&newfid, str), error); + coda_f2s(&newfid), error); + d_drop(de); return error; } error = coda_cnode_make(&result, &newfid, dir->i_sb); if ( error ) { + d_drop(de); 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 coda_inode_info *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; + if (coda_isroot(dir) && coda_iscontrol(name, len)) + return -EPERM; + + dircnp = ITOC(dir); + CHECK_CNODE(dircnp); + CDEBUG(D_INODE, "mkdir %s (len %d) in %s, mode %o.\n", - name, len, coda_f2s(&(dircnp->c_fid), fidstr), mode); + name, len, coda_f2s(&(dircnp->c_fid)), mode); attr.va_mode = mode; error = venus_mkdir(dir->i_sb, &(dircnp->c_fid), @@ -299,97 +292,107 @@ static int coda_mkdir(struct inode *dir, struct dentry *de, int mode) if ( error ) { CDEBUG(D_INODE, "mkdir error: %s result %d\n", - coda_f2s(&newfid, fidstr), error); + coda_f2s(&newfid), error); + d_drop(de); return error; } CDEBUG(D_INODE, "mkdir: new dir has fid %s.\n", - coda_f2s(&newfid, fidstr)); + coda_f2s(&newfid)); error = coda_cnode_make(&inode, &newfid, dir->i_sb); - if ( error ) - return error; + if ( error ) { + d_drop(de); + 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, +/* try to make de an entry in dir_inodde linked to source_de */ +static int coda_link(struct dentry *source_de, struct inode *dir_inode, struct dentry *de) { + struct inode *inode = source_de->d_inode; const char * name = de->d_name.name; int len = de->d_name.len; - struct cnode *dir_cnp, *cnp; + struct coda_inode_info *dir_cnp, *cnp; char str[50]; int error; ENTRY; + if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) + return -EPERM; + 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)); + CDEBUG(D_INODE, "old: fid: %s\n", coda_f2s(&(cnp->c_fid))); + CDEBUG(D_INODE, "directory: %s\n", coda_f2s(&(dir_cnp->c_fid))); 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); + dir_cnp->c_flags &= ~C_VATTR; + inode->i_nlink++; + d_instantiate(de, inode); + } else { + d_drop(de); } + 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) +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); + struct coda_inode_info *dir_cnp = ITOC(dir_inode); int symlen; int error=0; ENTRY; - error = -ENAMETOOLONG; - if ( len > CFS_MAXNAMLEN ) { - return error; - } + if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) + return -EPERM; + + if ( len > CFS_MAXNAMLEN ) + return -ENAMETOOLONG; symlen = strlen(symname); - if ( symlen > CFS_MAXNAMLEN ) { - return error; - } + if ( symlen > CFS_MAXPATHLEN ) + return -ENAMETOOLONG; + CDEBUG(D_INODE, "symname: %s, length: %d\n", symname, symlen); + /* + * This entry is now negative. Since we do not create + * an inode for the entry we have to drop it. + */ + d_drop(de); + error = venus_symlink(dir_inode->i_sb, &(dir_cnp->c_fid), name, len, symname, symlen); if ( !error ) { - d_drop(de); + dir_cnp->c_flags |= C_VATTR; } CDEBUG(D_INODE, "in symlink result %d\n",error); @@ -401,7 +404,7 @@ coda_symlink(struct inode *dir_inode, struct dentry *de, int coda_unlink(struct inode *dir, struct dentry *de) { - struct cnode *dircnp; + struct coda_inode_info *dircnp; int error; const char *name = de->d_name.name; int len = de->d_name.len; @@ -413,10 +416,9 @@ int coda_unlink(struct inode *dir, struct dentry *de) CHECK_CNODE(dircnp); CDEBUG(D_INODE, " %s in %s, ino %ld\n", name , - coda_f2s(&(dircnp->c_fid), fidstr), dir->i_ino); + coda_f2s(&(dircnp->c_fid)), 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); @@ -427,7 +429,6 @@ int coda_unlink(struct inode *dir, struct dentry *de) /* cache management */ dircnp->c_flags &= ~C_VATTR; - cfsnc_zapfid(&(dircnp->c_fid)); de->d_inode->i_nlink--; d_delete(de); @@ -437,10 +438,10 @@ int coda_unlink(struct inode *dir, struct dentry *de) int coda_rmdir(struct inode *dir, struct dentry *de) { - struct cnode *dircnp; + struct coda_inode_info *dircnp; const char *name = de->d_name.name; int len = de->d_name.len; - int error; + int error, rehash = 0; if (!dir || !S_ISDIR(dir->i_mode)) { printk("coda_rmdir: inode is NULL or not a directory\n"); @@ -452,8 +453,24 @@ int coda_rmdir(struct inode *dir, struct dentry *de) if (len > CFS_MAXNAMLEN) return -ENAMETOOLONG; - /* this directory name should no longer be in the namecache */ - cfsnc_zapfile(dircnp, (const char *)name, len); + error = -EBUSY; + if (de->d_count > 1) { + /* Attempt to shrink child dentries ... */ + shrink_dcache_parent(de); + if (de->d_count > 1) + return error; + } + /* Drop the dentry to force a new lookup */ + if (!list_empty(&de->d_hash)) { + d_drop(de); + rehash = 1; + } + + /* update i_nlink and free the inode before unlinking; + if rmdir fails a new lookup set i_nlink right.*/ + if (de->d_inode->i_nlink) + de->d_inode->i_nlink --; + d_delete(de); error = venus_rmdir(dir->i_sb, &(dircnp->c_fid), name, len); @@ -462,11 +479,9 @@ int coda_rmdir(struct inode *dir, struct dentry *de) return error; } - dircnp->c_flags &= ~C_VATTR; - cfsnc_zapfid(&(dircnp->c_fid)); - - dir->i_nlink--; - d_delete(de); + if (rehash) + d_add(de, NULL); + /* XXX how can mtime be set? */ return 0; } @@ -481,7 +496,7 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, 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; + struct coda_inode_info *new_cnp, *old_cnp; int error, rehash = 0, update = 1; ENTRY; old_cnp = ITOC(old_dir); @@ -496,8 +511,8 @@ ENTRY; } /* 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); +/* 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 && @@ -541,7 +556,7 @@ ENTRY; int coda_readdir(struct file *file, void *dirent, filldir_t filldir) { int result = 0; - struct cnode *cnp; + struct coda_inode_info *cnp; struct file open_file; struct dentry open_dentry; struct inode *inode=file->f_dentry->d_inode; @@ -581,23 +596,21 @@ int coda_open(struct inode *i, struct file *f) { ino_t ino; dev_t dev; - struct cnode *cnp; + struct coda_inode_info *cnp; int error = 0; struct inode *cont_inode = NULL; unsigned short flags = f->f_flags; + unsigned short coda_flags = coda_flags_to_cflags(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); + error = venus_open(i->i_sb, &(cnp->c_fid), coda_flags, &ino, &dev); if (error) { CDEBUG(D_FILE, "venus: dev %d, inode %ld, out->result %d\n", dev, ino, error); @@ -618,14 +631,13 @@ int coda_open(struct inode *i, struct file *f) 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)); - } +/* 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); @@ -638,9 +650,10 @@ int coda_open(struct inode *i, struct file *f) int coda_release(struct inode *i, struct file *f) { - struct cnode *cnp; + struct coda_inode_info *cnp; int error; unsigned short flags = f->f_flags; + unsigned short cflags = coda_flags_to_cflags(flags); ENTRY; @@ -664,7 +677,7 @@ int coda_release(struct inode *i, struct file *f) --cnp->c_owrite; } - error = venus_release(i->i_sb, &(cnp->c_fid), flags); + error = venus_release(i->i_sb, &(cnp->c_fid), cflags); CDEBUG(D_FILE, "coda_release: result: %d\n", error); return error; @@ -693,6 +706,7 @@ static int coda_venus_readdir(struct file *filp, void *getdent, struct venus_dirent *vdirent; struct getdents_callback *dents_callback; int string_offset; + int size; char debug[255]; ENTRY; @@ -703,8 +717,8 @@ static int coda_venus_readdir(struct file *filp, void *getdent, dents_callback = (struct getdents_callback *) getdent; - count = dents_callback->count; - CODA_ALLOC(buff, void *, count); + size = count = dents_callback->count; + CODA_ALLOC(buff, void *, size); if ( ! buff ) { printk("coda_venus_readdir: out of memory.\n"); return -ENOMEM; @@ -764,6 +778,6 @@ CDEBUG(D_FILE, "ino %ld, namlen %d, reclen %d, type %d, pos %d, string_offs %d, } exit: - CODA_FREE(buff, count); + CODA_FREE(buff, size); return error; } diff --git a/fs/coda/file.c b/fs/coda/file.c index 225ca881a..b33680cc3 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -18,19 +18,21 @@ #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_fs_i.h> #include <linux/coda_psdev.h> +#include <linux/coda_cache.h> /* file operations */ -static int coda_readpage(struct inode * inode, struct page * page); +static int coda_readpage(struct file *file, 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 */ +int coda_fsync(struct file *, struct dentry *dentry); + struct inode_operations coda_file_inode_operations = { &coda_file_operations, /* default file operations */ NULL, /* create */ @@ -64,14 +66,21 @@ struct file_operations coda_file_operations = { coda_file_mmap, /* mmap */ coda_open, /* open */ coda_release, /* release */ - NULL, /* fsync */ + coda_fsync, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ }; /* File file operations */ -static int coda_readpage(struct inode * inode, struct page * page) +static int coda_readpage(struct file * file, struct page * page) { - struct inode *open_inode; - struct cnode *cnp; + struct dentry *de = file->f_dentry; + struct inode *inode = de->d_inode; + struct dentry cont_dentry; + struct inode *cont_inode; + struct coda_inode_info *cnp; ENTRY; @@ -83,18 +92,19 @@ static int coda_readpage(struct inode * inode, struct page * page) return -ENXIO; } - open_inode = cnp->c_ovp; + cont_inode = cnp->c_ovp; + cont_dentry.d_inode = cont_inode; - CDEBUG(D_INODE, "coda ino: %ld, cached ino %ld, page offset: %lx\n", inode->i_ino, open_inode->i_ino, page->offset); + CDEBUG(D_INODE, "coda ino: %ld, cached ino %ld, page offset: %lx\n", inode->i_ino, cont_inode->i_ino, page->offset); - generic_readpage(open_inode, page); + generic_readpage(&cont_dentry, page); EXIT; return 0; } static int coda_file_mmap(struct file * file, struct vm_area_struct * vma) { - struct cnode *cnp; + struct coda_inode_info *cnp; cnp = ITOC(file->f_dentry->d_inode); cnp->c_mmcount++; @@ -104,7 +114,7 @@ static int coda_file_mmap(struct file * file, struct vm_area_struct * vma) static ssize_t coda_file_read(struct file *coda_file, char *buff, size_t count, loff_t *ppos) { - struct cnode *cnp; + struct coda_inode_info *cnp; struct inode *coda_inode = coda_file->f_dentry->d_inode; struct inode *cont_inode = NULL; struct file cont_file; @@ -144,7 +154,7 @@ static ssize_t coda_file_read(struct file *coda_file, char *buff, static ssize_t coda_file_write(struct file *coda_file, const char *buff, size_t count, loff_t *ppos) { - struct cnode *cnp; + struct coda_inode_info *cnp; struct inode *coda_inode = coda_file->f_dentry->d_inode; struct inode *cont_inode = NULL; struct file cont_file; @@ -181,8 +191,44 @@ static ssize_t coda_file_write(struct file *coda_file, const char *buff, return result; } +int coda_fsync(struct file *coda_file, struct dentry *coda_dentry) +{ + struct coda_inode_info *cnp; + struct inode *coda_inode = coda_dentry->d_inode; + struct inode *cont_inode = NULL; + struct file cont_file; + struct dentry cont_dentry; + int result = 0; + ENTRY; + + if (!(S_ISREG(coda_inode->i_mode) || S_ISDIR(coda_inode->i_mode) || + S_ISLNK(coda_inode->i_mode))) + return -EINVAL; + 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); + + down(&cont_inode->i_sem); + + result = file_fsync(&cont_file ,&cont_dentry); + if ( result == 0 ) { + result = venus_fsync(coda_inode->i_sb, &(cnp->c_fid)); + } + + up(&cont_inode->i_sem); + + coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file); + return result; +} /* * support routines */ @@ -209,7 +255,7 @@ void coda_restore_codafile(struct inode *coda_inode, struct file *coda_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_mtime = open_inode->i_mtime; */ coda_inode->i_size = open_inode->i_size; return; } diff --git a/fs/coda/inode.c b/fs/coda/inode.c deleted file mode 100644 index b295470fa..000000000 --- a/fs/coda/inode.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 deleted file mode 100644 index 08f1ee9e7..000000000 --- a/fs/coda/namecache.c +++ /dev/null @@ -1,832 +0,0 @@ -/* - * 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 index c24008cd6..99c960f7d 100644 --- a/fs/coda/pioctl.c +++ b/fs/coda/pioctl.c @@ -18,10 +18,10 @@ #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_fs_i.h> +#include <linux/coda_cache.h> #include <linux/coda_psdev.h> /* pioctl ops */ @@ -103,7 +103,7 @@ static int coda_pioctl(struct inode * inode, struct file * filp, int error; struct PioctlData data; struct inode *target_inode = NULL; - struct cnode *cnp; + struct coda_inode_info *cnp; ENTRY; /* get the Pioctl data arguments from user space */ @@ -115,16 +115,17 @@ static int coda_pioctl(struct inode * inode, struct file * filp, * 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); + 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) { + + if ( PTR_ERR(target_de) == -ENOENT ) { CDEBUG(D_PIOCTL, "error: lookup fails.\n"); - return -EINVAL; + return PTR_ERR(target_de); } else { target_inode = target_de->d_inode; } diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index cd591d3e9..04333e046 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -17,6 +17,7 @@ * Copyright (c) 1997 Carnegie-Mellon University */ +#include <linux/config.h> /* for CONFIG_PROC_FS */ #include <linux/module.h> #include <linux/errno.h> #include <linux/kernel.h> @@ -35,35 +36,75 @@ #include <asm/io.h> #include <asm/segment.h> #include <asm/system.h> +#include <asm/poll.h> #include <asm/uaccess.h> #include <linux/coda.h> #include <linux/coda_linux.h> +#include <linux/coda_fs_i.h> #include <linux/coda_psdev.h> -#include <linux/coda_cnode.h> -#include <linux/coda_namecache.h> +#include <linux/coda_cache.h> #include <linux/coda_sysctl.h> +/* + * Where is the prototype? + */ + +int proc_register_dynamic(struct proc_dir_entry * dir, + struct proc_dir_entry * dp); + /* * 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; - +int coda_hard = 0; /* introduces a timeout on upcalls */ +unsigned long coda_timeout = 10; /* .. secs, then signals will dequeue */ extern struct coda_sb_info coda_super_info[MAX_CODADEVS]; struct vcomm psdev_vcomm[MAX_CODADEVS]; -/* - * Device operations - */ +/* queue stuff for the messages */ +static inline void init_queue(struct queue *head) +{ + head->forw = head; + head->back = head; +} + +static inline struct vmsg *q_getnext(struct queue *elt) +{ + return (struct vmsg *)(elt->forw); +} + +static inline int q_end(struct vmsg *msg, struct queue *queue) +{ + return (struct queue *)msg == queue; +} + +static inline int q_empty(struct queue *queue) +{ + return queue->forw == queue; +} + +/* insert before head, ie. at the tail */ +void coda_q_insert(struct queue *el, struct queue *head) +{ + el->forw = head->back->forw; + el->back = head->back; + head->back->forw = el; + head->back = el; +} +void coda_q_remove(struct queue *el) +{ + el->forw->back = el->back; + el->back->forw = el->forw; +} static struct vcomm *coda_psdev2vcomm(struct file *file) { @@ -75,6 +116,10 @@ static struct vcomm *coda_psdev2vcomm(struct file *file) return vcp; } +/* + * Device operations + */ + static unsigned int coda_psdev_poll(struct file *file, poll_table * wait) { @@ -84,8 +129,8 @@ static unsigned int coda_psdev_poll(struct file *file, poll_table * wait) if ( !vcp ) return -ENXIO; - poll_wait(&(vcp->vc_waitq), wait); - if (!EMPTY(vcp->vc_pending)) + poll_wait(file, &(vcp->vc_waitq), wait); + if (!q_empty(&(vcp->vc_pending))) mask |= POLLIN | POLLRDNORM; return mask; @@ -101,7 +146,6 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, { struct vcomm *vcp = coda_psdev2vcomm(file); struct vmsg *vmp; - struct outputArgs *out; int error = 0; int size; u_long uniq; @@ -111,7 +155,7 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, if (!vcp) return -ENXIO; - /* Peek at the opcode, unique id */ + /* Peek at the opcode, uniquefier */ if (copy_from_user(opcodebuf, buf, 2 * sizeof(u_long))) return -EFAULT; opcode = opcodebuf[0]; @@ -121,67 +165,69 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, current->pid, opcode, uniq); if (DOWNCALL(opcode)) { - struct outputArgs pbuf; - + struct super_block *sb = NULL; + union outputArgs *dcbuf; + size = sizeof(*dcbuf); + + sb = vcp->vc_sb; + if ( !sb ) { + printk("coda_psdev_write: downcall, no SB!\n"); + return count; + } 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", + if ( count < sizeof(struct cfs_out_hdr) ) { + printk("coda_downcall opc %ld uniq %ld, not enough!\n", opcode, uniq); - size =count; - } else if ( count > sizeof(pbuf) ) { + return count; + } + CODA_ALLOC(dcbuf, union outputArgs *, size); + if ( count > size ) { printk("Coda: downcall opc %ld, uniq %ld, too much!", opcode, uniq); - size = sizeof(pbuf); + count = size; } - if (copy_from_user(&pbuf, buf, size)) + if (copy_from_user(dcbuf, buf, count)) return -EFAULT; - /* what errors for coda_downcall should be - * sent to Venus ? - */ - error = coda_downcall(opcode, &pbuf); + /* what downcall errors does Venus handle ? */ + error = coda_downcall(opcode, dcbuf, sb); + if ( error) { printk("psdev_write: coda_downcall error: %d\n", error); return 0; } + CODA_FREE(dcbuf, size); 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); + for (vmp = q_getnext(&(vcp->vc_processing)); + !q_end(vmp, &(vcp->vc_processing)); + vmp = q_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)) { + if (q_end(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); + coda_q_remove(&(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", + printk("psdev_write: too much cnt: %d, cnt: %d, opc: %ld, uniq: %ld.\n", vmp->vm_outSize, count, opcode, uniq); + count = vmp->vm_outSize; /* don't have more space! */ } - if (copy_from_user(out, buf, count)) + if (copy_from_user(vmp->vm_data, buf, count)) return -EFAULT; /* adjust outsize. is this usefull ?? */ @@ -192,7 +238,7 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, "Found! Count %d for (opc,uniq)=(%ld,%ld), vmsg at %x\n", count, opcode, uniq, (int)&vmp); - wake_up_interruptible(&vmp->vm_sleep); + wake_up(&vmp->vm_sleep); return(count); } @@ -201,7 +247,7 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, */ static ssize_t coda_psdev_read(struct file * file, char * buf, - size_t count, loff_t *off) + size_t count, loff_t *off) { struct vcomm *vcp = coda_psdev2vcomm(file); struct vmsg *vmp; @@ -211,41 +257,39 @@ static ssize_t coda_psdev_read(struct file * file, char * buf, return -ENXIO; /* Get message at head of request queue. */ - if (EMPTY(vcp->vc_pending)) { - return 0; /* Nothing to read */ + if (q_empty(&(vcp->vc_pending))) { + return 0; } - vmp = (struct vmsg *)GETNEXT(vcp->vc_pending); - REMQUE(vmp->vm_chain); + vmp = q_getnext(&(vcp->vc_pending)); + coda_q_remove(&(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); + printk ("psdev_read: Venus read %d bytes of %d in 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"); + printk("coda_psdev_read: bad chain"); - /* If request was a signal, free up the message and don't - enqueue it in the reply queue. */ + /* If request was a signal, don't enqueue */ 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)); + CODA_FREE(vmp->vm_data, sizeof(struct cfs_in_hdr)); + CODA_FREE(vmp, sizeof(struct vmsg)); return count; } vmp->vm_flags |= VM_READ; - INSQUE(vmp->vm_chain, vcp->vc_processing); + coda_q_insert(&(vmp->vm_chain), &(vcp->vc_processing)); return result; } @@ -254,22 +298,22 @@ static ssize_t coda_psdev_read(struct file * file, char * buf, static int coda_psdev_open(struct inode * inode, struct file * file) { register struct vcomm *vcp = NULL; - ENTRY; - vcp = coda_psdev2vcomm(file); + vcp =coda_psdev2vcomm(file); if (!vcp) - return -ENODEV; - memset(vcp, 0, sizeof(struct vcomm)); + return -ENODEV; - MOD_INC_USE_COUNT; + if (vcp->vc_inuse) + return -EBUSY; - INIT_QUEUE(vcp->vc_pending); - INIT_QUEUE(vcp->vc_processing); + memset(vcp, 0, sizeof(struct vcomm)); + vcp->vc_inuse = 1; + MOD_INC_USE_COUNT; - cfsnc_init(); - CDEBUG(D_PSDEV, "Name cache initialized.\n"); + init_queue(&(vcp->vc_pending)); + init_queue(&(vcp->vc_processing)); memset(&coda_callstats, 0, sizeof(struct coda_upcallstats)); EXIT; @@ -283,6 +327,7 @@ 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); + ENTRY; vcp = coda_psdev2vcomm(file); @@ -294,49 +339,44 @@ coda_psdev_release(struct inode * inode, struct file * file) /* flush the name cache so that we can unmount */ CDEBUG(D_PSDEV, "Flushing the cache.\n"); - cfsnc_flush(); - cfsnc_use = 0; + /* 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 operations are in progress perhaps the kernel + can profit from setting the C_DYING flag on the root + cnode of Coda filesystems */ if (coda_super_info[minor].sbi_root) { - struct cnode *cnp = ITOC(coda_super_info[minor].sbi_root); - /* Let unmount know this is for real */ + struct coda_inode_info *cnp = + ITOC(coda_super_info[minor].sbi_root); cnp->c_flags |= C_DYING; - /* XXX Could we force an unmount here? */ - } + } else + vcp->vc_inuse = 0; /* 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)) { + for (vmp = q_getnext(&(vcp->vc_pending)); + !q_end(vmp, &(vcp->vc_pending)); + vmp = q_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)); + CODA_FREE(vmp->vm_data, sizeof(struct cfs_in_hdr)); + CODA_FREE(vmp, (u_int)sizeof(struct vmsg)); continue; } - - wake_up_interruptible(&vmp->vm_sleep); + wake_up(&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); + for (vmp = q_getnext(&(vcp->vc_processing)); + !q_end(vmp, &(vcp->vc_processing)); + vmp = q_getnext(&(vmp->vm_chain))) { + wake_up(&vmp->vm_sleep); } mark_vcomm_closed(vcp); - cfsnc_use = 0; - MOD_DEC_USE_COUNT; + MOD_DEC_USE_COUNT; + EXIT; return 0; } @@ -358,35 +398,16 @@ static struct file_operations coda_psdev_fops = { 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, + S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR, 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, @@ -396,25 +417,48 @@ struct proc_dir_entry proc_coda_ncstats = { #endif -#ifdef MODULE -int init_module(void) + +int init_coda_psdev(void) { - int status; - printk(KERN_INFO "Coda Kernel/User communications module 0.04\n"); + 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; + } + memset(psdev_vcomm, 0, sizeof(psdev_vcomm)); + memset(coda_super_info, 0, sizeof(coda_super_info)); + memset(&coda_callstats, 0, sizeof(coda_callstats)); #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(); + proc_register(&proc_root,&proc_coda); + proc_register(&proc_coda, &proc_coda_ncstats); + coda_sysctl_init(); #endif + return 0; +} + + +#ifdef MODULE + +EXPORT_NO_SYMBOLS; - init_coda_psdev(); - - if ((status = init_coda_fs()) != 0) - { - printk("coda: failed in init_coda_fs!\n"); - } +MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>"); + +int init_module(void) +{ + int status; + printk(KERN_INFO "Coda Kernel/User communications module 1.0\n"); + + status = init_coda_psdev(); + if ( status ) { + printk("Problem (%d) in init_coda_psdev\n", status); + return status; + } + + status = init_coda_fs(); + if (status) { + printk("coda: failed in init_coda_fs!\n"); + } return status; } @@ -425,14 +469,13 @@ void cleanup_module(void) ENTRY; - unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev"); - if ( (err = unregister_filesystem(&coda_fs_type)) != 0 ) { printk("coda: failed to unregister filesystem\n"); } + unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev"); + #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 @@ -440,5 +483,3 @@ void cleanup_module(void) #endif - - diff --git a/fs/coda/super.c b/fs/coda/super.c index 85a5ccbb3..5410fb50d 100644 --- a/fs/coda/super.c +++ b/fs/coda/super.c @@ -1,13 +1,14 @@ /* * 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> + * Copryright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk> and + * Michael Callahan <callahan@maths.ox.ac.uk> + * + * Rewritten for Linux 2.1.?? Peter Braam <braam@cs.cmu.edu> + * Copyright (C) Carnegie Mellon University */ #define __NO_VERSION__ -#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/mm.h> @@ -32,45 +33,25 @@ #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_fs_i.h> +#include <linux/coda_cache.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 int coda_notify_change(struct dentry *dentry, 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; +static struct coda_sb_info *coda_psinode2sbi(struct inode *inode); + /* exported operations */ struct super_operations coda_super_operations = { @@ -91,37 +72,39 @@ struct super_operations coda_super_operations = 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; + struct vcomm *vc = NULL; ViceFid fid; kdev_t dev = sb->s_dev; int error; char str[50]; -ENTRY; + ENTRY; MOD_INC_USE_COUNT; if (coda_get_psdev(data, &psdev)) - goto exit; + goto error; vc = coda_psinode2vcomm(psdev); if ( !vc ) - goto exit; + goto error; + vc->vc_sb = sb; + vc->vc_inuse = 1; - sbi = coda_psinode2sb(psdev); + sbi = coda_psinode2sbi(psdev); if ( !sbi ) - goto exit; - + goto error; sbi->sbi_psdev = psdev; sbi->sbi_vcomm = vc; + INIT_LIST_HEAD(&(sbi->sbi_cchead)); + INIT_LIST_HEAD(&(sbi->sbi_volroothead)); lock_super(sb); sb->u.generic_sbp = sbi; - sb->s_blocksize = 1024; /* XXXXX */ + sb->s_blocksize = 1024; /* XXXXX what do we put here?? */ sb->s_blocksize_bits = 10; sb->s_magic = CODA_SUPER_MAGIC; sb->s_dev = dev; @@ -129,22 +112,22 @@ ENTRY; /* 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; + error); + sb->s_dev = 0; + unlock_super(sb); + goto error; } - printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid, str)); + printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid)); + /* make root inode */ error = coda_cnode_make(&root, &fid, sb); if ( error || !root ) { printk("Failure of coda_cnode_make for root: error %d\n", error); - unlock_super(sb); sb->s_dev = 0; - root = NULL; - goto exit; + unlock_super(sb); + goto error; } printk("coda_read_super: rootinode is %ld dev %d\n", @@ -152,22 +135,27 @@ ENTRY; sbi->sbi_root = root; sb->s_root = d_alloc_root(root, NULL); unlock_super(sb); + EXIT; return sb; -EXIT; -exit: +error: +EXIT; MOD_DEC_USE_COUNT; - sbi->sbi_vcomm = NULL; - sbi->sbi_root = NULL; + if (sbi) { + sbi->sbi_vcomm = NULL; + sbi->sbi_root = NULL; + } + if ( vc ) { + vc->vc_sb = NULL; + vc->vc_inuse = 0; + } 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; @@ -177,78 +165,94 @@ static void coda_put_super(struct super_block *sb) lock_super(sb); sb->s_dev = 0; + coda_cache_clear_all(sb); sb_info = coda_sbp(sb); + sb_info->sbi_vcomm->vc_inuse = 0; + sb_info->sbi_vcomm->vc_sb = NULL; + printk("Coda: Bye bye.\n"); memset(sb_info, 0, sizeof(* sb_info)); unlock_super(sb); MOD_DEC_USE_COUNT; -EXIT; + 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); */ + struct coda_inode_info *cnp; + ENTRY; + cnp = ITOC(inode); + cnp->c_magic = 0; + return; } -static void coda_put_inode(struct inode *inode) +static void coda_put_inode(struct inode *in) { - CDEBUG(D_INODE,"ino: %ld, cnp: %x\n", inode->i_ino, - (int) inode->u.generic_ip); + ENTRY; + + CDEBUG(D_INODE,"ino: %ld, count %d\n", in->i_ino, in->i_count); + + if ( in->i_count == 1 ) + in->i_nlink = 0; + } static void coda_delete_inode(struct inode *inode) { - struct cnode *cnp; + struct coda_inode_info *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 ) { + cnp = ITOC(inode); + if ( inode->i_ino == CTL_INO || cnp->c_magic != CODA_CNODE_MAGIC ) { clear_inode(inode); return; } - cnp = ITOC(inode); + + if ( coda_fid_is_volroot(&cnp->c_fid) ) + list_del(&cnp->c_volrootlist); 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 ); + iput(open_inode); } + + coda_cache_clear_cnp(cnp); + inode->u.generic_ip = NULL; - coda_cnode_free(cnp); clear_inode(inode); -EXIT; + EXIT; } -static int coda_notify_change(struct inode *inode, struct iattr *iattr) +static int coda_notify_change(struct dentry *de, struct iattr *iattr) { - struct cnode *cnp; + struct inode *inode = de->d_inode; + struct coda_inode_info *cnp; struct coda_vattr vattr; int error; -ENTRY; + + 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 */ + vattr.va_type = C_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)); + coda_cache_clear_cnp(cnp); } CDEBUG(D_SUPER, "inode.i_mode %o, error %d\n", inode->i_mode, error); @@ -257,14 +261,12 @@ ENTRY; return error; } -/* we need _something_ */ +/* we need _something_ for this routine. Let's mimic AFS */ 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; @@ -284,278 +286,38 @@ struct file_system_type coda_fs_type = { "coda", 0, coda_read_super, NULL }; - - - int init_coda_fs(void) { return register_filesystem(&coda_fs_type); } +/* MODULE stuff is in psdev.c */ -/* 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) +/* helpers */ +static inline struct vcomm *coda_psinode2vcomm(struct inode *inode) { unsigned int minor = MINOR(inode->i_rdev); -CDEBUG(D_PSDEV,"minor %d\n", minor); + 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) +static struct coda_sb_info *coda_psinode2sbi(struct inode *inode) { unsigned int minor = MINOR(inode->i_rdev); -CDEBUG(D_PSDEV,"minor %d\n", minor); - if ( minor < MAX_CODADEVS ) + CDEBUG(D_PSDEV,"minor %d\n", minor); + if ( (minor >= 0) && (minor < MAX_CODADEVS)) return &(coda_super_info[minor]); else return NULL; } -static int -coda_get_psdev(void *data, struct inode **res_dev) +/* name lookup for psdev passed in by mount */ +static int coda_get_psdev(void *data, struct inode **res_dev) { char **psdev_path; struct inode *psdev = 0; @@ -563,50 +325,46 @@ coda_get_psdev(void *data, struct inode **res_dev) if ( ! data ) { - printk("coda_read_super: no data!\n"); - goto error; - } else { - psdev_path = data; - } + printk("coda_get_psdev: no data!\n"); + return 1; + } + + 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; + return 1; } psdev = ent->d_inode; - if (!S_ISCHR(psdev->i_mode)) { printk("not a character device\n"); - goto error; + return 1; } -CDEBUG(D_PSDEV,"major %d, minor %d, count %d\n", MAJOR(psdev->i_rdev), - MINOR(psdev->i_rdev), psdev->i_count); + 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; + return 1; } if (MINOR(psdev->i_rdev) >= MAX_CODADEVS) { printk("minor %d not an allocated Coda PSDEV\n", psdev->i_rdev); - goto error; + return 1; } 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; + return 1; } *res_dev = psdev; - + EXIT; return 0; - -EXIT; -error: - return 1; } diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c index 432b806f8..c6d770a20 100644 --- a/fs/coda/symlink.c +++ b/fs/coda/symlink.c @@ -21,10 +21,11 @@ #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_fs_i.h> +#include <linux/coda_cache.h> -static int coda_readlink(struct inode *inode, char *buffer, int length); +static int coda_readlink(struct dentry *de, char *buffer, int length); +static struct dentry *coda_follow_link(struct dentry *, struct dentry *); struct inode_operations coda_symlink_inode_operations = { NULL, /* no file-operations */ @@ -38,7 +39,7 @@ struct inode_operations coda_symlink_inode_operations = { NULL, /* mknod */ NULL, /* rename */ coda_readlink, /* readlink */ - NULL, /* follow_link */ + coda_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -49,12 +50,13 @@ struct inode_operations coda_symlink_inode_operations = { NULL /* revalidate */ }; -static int coda_readlink(struct inode *inode, char *buffer, int length) +static int coda_readlink(struct dentry *de, char *buffer, int length) { + struct inode *inode = de->d_inode; int len; int error; char *buf; - struct cnode *cp; + struct coda_inode_info *cp; ENTRY; cp = ITOC(inode); @@ -76,7 +78,44 @@ static int coda_readlink(struct inode *inode, char *buffer, int length) copy_to_user(buffer, buf, len); put_user('\0', buffer + len); error = len; - CODA_FREE(buf, len); } + if ( buf ) + CODA_FREE(buf, len); return error; } + +static struct dentry *coda_follow_link(struct dentry *de, + struct dentry *base) +{ + struct inode *inode = de->d_inode; + int error; + struct coda_inode_info *cnp; + unsigned int len; + char mem[CFS_MAXPATHLEN]; + char *path; +ENTRY; + CDEBUG(D_INODE, "(%x/%ld)\n", inode->i_dev, inode->i_ino); + + cnp = ITOC(inode); + CHECK_CNODE(cnp); + + len = CFS_MAXPATHLEN; + error = venus_readlink(inode->i_sb, &(cnp->c_fid), mem, &len); + + if (error) { + dput(base); + return ERR_PTR(error); + } + len = strlen(mem); + path = kmalloc(len + 1, GFP_KERNEL); + if (!path) { + dput(base); + return ERR_PTR(-ENOMEM); + } + memcpy(path, mem, len); + path[len] = 0; + + base = lookup_dentry(path, base, 1); + kfree(path); + return base; +} diff --git a/fs/coda/sysctl.c b/fs/coda/sysctl.c index 04523a383..c21016030 100644 --- a/fs/coda/sysctl.c +++ b/fs/coda/sysctl.c @@ -21,14 +21,18 @@ #include <asm/uaccess.h> #include <linux/utsname.h> -#include <linux/coda_namecache.h> +#include <linux/coda.h> +#include <linux/coda_linux.h> +#include <linux/coda_fs_i.h> +#include <linux/coda_psdev.h> +#include <linux/coda_cache.h> #include <linux/coda_sysctl.h> extern int coda_debug; -extern int cfsnc_use; +/* extern int cfsnc_use; */ extern int coda_print_entry; -extern int cfsnc_flushme; +/* extern int cfsnc_flushme; */ extern int cfsnc_procsize; -extern void cfsnc_flush(void); +/* extern void cfsnc_flush(void); */ void coda_sysctl_init(void); void coda_sysctl_clean(void); @@ -38,20 +42,20 @@ int coda_dointvec(ctl_table *table, int write, struct file *filp, 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 */ +#define CODA_DEBUG 1 /* control debugging */ +#define CODA_ENTRY 2 /* control enter/leave pattern */ +#define CODA_TIMEOUT 3 /* timeout on upcalls to become intrble */ +#define CODA_MC 4 /* use/do not use the access cache */ +#define CODA_HARD 5 /* mount type "hard" or "soft" */ 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}, + {CODA_MC, "accesscache", &coda_access_cache, sizeof(int), 0644, NULL, &coda_dointvec}, + {CODA_TIMEOUT, "timeout", &coda_timeout, sizeof(int), 0644, NULL, &coda_dointvec}, + {CODA_HARD, "hard", &coda_hard, sizeof(int), 0644, NULL, &coda_dointvec}, { 0 } }; diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 43536f622..ac625ad17 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -16,7 +16,8 @@ #include <asm/system.h> #include <asm/segment.h> - +#include <asm/signal.h> +#include <linux/signal.h> #include <linux/types.h> #include <linux/kernel.h> @@ -34,34 +35,54 @@ #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_fs_i.h> +#include <linux/coda_cache.h> + +#define UPARG(op)\ +do {\ + CODA_ALLOC(inp, union inputArgs *, insize);\ + outp = (union outputArgs *) (inp);\ + inp->ih.opcode = (op);\ + inp->ih.pid = current->pid;\ + inp->ih.pgid = current->pgrp;\ + coda_load_creds(&(inp->ih.cred));\ + outsize = insize;\ +} while (0) + +static inline int max(int a, int b) +{ + if ( a > b ) + return a; + else + return b; +} +#define INSIZE(tag) sizeof(struct cfs_ ## tag ## _in) +#define OUTSIZE(tag) sizeof(struct cfs_ ## tag ## _out) +#define SIZE(tag) max(INSIZE(tag), OUTSIZE(tag)) -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; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; ENTRY; - UPARG(vcsize, CFS_ROOT); - error = coda_upcall(coda_sbp(sb), VC_IN_NO_DATA, &size, inp); + insize = SIZE(root); + UPARG(CFS_ROOT); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if (error) { printk("coda_get_rootfid: error %d\n", error); } else { - *fidp = (ViceFid) outp->d.cfs_root.VFid; + *fidp = (ViceFid) outp->cfs_root.VFid; CDEBUG(D_SUPER, "VolumeId: %lx, VnodeId: %lx.\n", fidp->Volume, fidp->Vnode); } - if (inp) CODA_FREE(inp, VC_IN_NO_DATA); + if (inp) CODA_FREE(inp, insize); EXIT; return -error; } @@ -69,19 +90,19 @@ ENTRY; int venus_getattr(struct super_block *sb, struct ViceFid *fid, struct coda_vattr *attr) { - struct inputArgs *inp; - struct outputArgs *outp; - int size, error; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; ENTRY; - - UPARG(vcsize, CFS_GETATTR); - inp->d.cfs_getattr.VFid = *fid; - error = coda_upcall(coda_sbp(sb), vcsize, &size, inp); + insize = SIZE(getattr); + UPARG(CFS_GETATTR); + inp->cfs_getattr.VFid = *fid; + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if ( !error ) - *attr = (struct coda_vattr) outp->d.cfs_getattr.attr; + *attr = outp->cfs_getattr.attr; - if (inp) CODA_FREE(inp, sizeof(struct inputArgs)); + if (inp) CODA_FREE(inp, insize); EXIT; return -error; } @@ -89,19 +110,20 @@ ENTRY; int venus_setattr(struct super_block *sb, struct ViceFid *fid, struct coda_vattr *vattr) { - struct inputArgs *inp; - struct outputArgs *outp; - int error, size; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; - UPARG(vcsize, CFS_SETATTR); + insize= SIZE(setattr); + UPARG(CFS_SETATTR); - inp->d.cfs_setattr.VFid = *fid; - inp->d.cfs_setattr.attr = *vattr; + inp->cfs_setattr.VFid = *fid; + inp->cfs_setattr.attr = *vattr; - error = coda_upcall(coda_sbp(sb), vcsize, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - CDEBUG(D_SUPER, " result %ld\n", outp->result); - if ( inp ) CODA_FREE(inp, vcsize); + CDEBUG(D_SUPER, " result %d\n", error); + if ( inp ) CODA_FREE(inp, insize); return -error; } @@ -109,26 +131,26 @@ 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; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset; - insize = VC_INSIZE(cfs_lookup_in) + CFS_MAXNAMLEN +1; - UPARG(insize, CFS_LOOKUP); + offset = INSIZE(lookup); + insize = max(offset + length +1, OUTSIZE(lookup)); + UPARG(CFS_LOOKUP); - inp->d.cfs_lookup.VFid = *fid; + inp->cfs_lookup.VFid = *fid; + inp->cfs_lookup.name = offset; /* 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'; + memcpy((char *)(inp) + offset, name, length); + *((char *)inp + offset + length) = '\0'; - size = payload_offset + length + 1; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if ( !error ) { - *resfid = outp->d.cfs_lookup.VFid; - *type = outp->d.cfs_lookup.vtype; + *resfid = outp->cfs_lookup.VFid; + *type = outp->cfs_lookup.vtype; } if (inp) CODA_FREE(inp, insize); @@ -138,53 +160,48 @@ int venus_lookup(struct super_block *sb, struct ViceFid *fid, 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)); + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + + insize = SIZE(close); + UPARG(CFS_CLOSE); - inp->d.cfs_close.VFid = *fid; - inp->d.cfs_close.flags = flags; + inp->cfs_close.VFid = *fid; + inp->cfs_close.flags = flags; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if (inp) CODA_FREE(inp, sizeof(struct inputArgs)); + if (inp) + CODA_FREE(inp, insize); 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)); + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + + insize = SIZE(open); + UPARG(CFS_OPEN); - inp->d.cfs_open.VFid = *fid; - inp->d.cfs_open.flags = flags; + inp->cfs_open.VFid = *fid; + inp->cfs_open.flags = flags; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if ( !error ) { - *ino = outp->d.cfs_open.inode; - *dev = outp->d.cfs_open.dev; + *ino = outp->cfs_open.inode; + *dev = outp->cfs_open.dev; } else { *ino = 0; *dev = 0; } if (inp) - CODA_FREE(inp, sizeof(struct inputArgs)); + CODA_FREE(inp, insize); return -error; } @@ -193,69 +210,69 @@ 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; - + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset; + + offset = INSIZE(mkdir); + insize = max(offset + length + 1, OUTSIZE(mkdir)); + UPARG(CFS_MKDIR); + + inp->cfs_mkdir.VFid = *dirfid; + inp->cfs_mkdir.attr = *attrs; + inp->cfs_mkdir.name = 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; + memcpy((char *)(inp) + offset, name, length); + *((char *)inp + offset + length) = '\0'; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - *attrs = outp->d.cfs_mkdir.attr; - *newfid = outp->d.cfs_mkdir.VFid; + *attrs = outp->cfs_mkdir.attr; + *newfid = outp->cfs_mkdir.VFid; if (inp) - CODA_FREE(inp, size); + CODA_FREE(inp, insize); 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) + size_t new_length, const char *old_name, + const char *new_name) { - struct inputArgs *inp; - struct outputArgs *outp; - int error, offset, size, s; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset, 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 = INSIZE(rename); + insize = max(offset + new_length + old_length + 8, + OUTSIZE(rename)); + UPARG(CFS_RENAME); - offset = VC_INSIZE(cfs_rename_in); + inp->cfs_rename.sourceFid = *old_fid; + inp->cfs_rename.destFid = *new_fid; + inp->cfs_rename.srcname = offset; /* 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); + 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; + inp->cfs_rename.destname = offset; s = ( new_length & ~0x3) +4; /* round up to word boundary */ - memcpy((char *)inp + offset, new_name, new_length); + 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); + (char *)inp + (int) inp->cfs_rename.destname); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if (inp) CODA_FREE(inp, size); + if (inp) CODA_FREE(inp, insize); return -error; } @@ -263,131 +280,138 @@ 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; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset; - payload_offset = VC_INSIZE(cfs_create_in); - size = CFS_MAXNAMLEN + payload_offset; - UPARG(size, CFS_CREATE); + offset = INSIZE(create); + insize = max(offset + length + 1, OUTSIZE(create)); + UPARG(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; + inp->cfs_create.VFid = *dirfid; + inp->cfs_create.attr.va_mode = mode; + inp->cfs_create.excl = excl; + inp->cfs_create.mode = mode; + inp->cfs_create.name = 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); + memcpy((char *)(inp) + offset, name, length); + *((char *)inp + offset + length) = '\0'; + + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - *attrs = outp->d.cfs_create.attr; - *newfid = outp->d.cfs_create.VFid; + *attrs = outp->cfs_create.attr; + *newfid = outp->cfs_create.VFid; if (inp) - CODA_FREE(inp, size); + CODA_FREE(inp, insize); 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); + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset; + + offset = INSIZE(rmdir); + insize = max(offset + length + 1, OUTSIZE(rmdir)); + UPARG(CFS_RMDIR); + + inp->cfs_rmdir.VFid = *dirfid; + inp->cfs_rmdir.name = offset; + memcpy((char *)(inp) + offset, name, length); + *((char *)inp + offset + length) = '\0'; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if ( inp ) - CODA_FREE(inp, size); + CODA_FREE(inp, insize); 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); + union inputArgs *inp; + union outputArgs *outp; + int error=0, insize, outsize, offset; + + offset = INSIZE(remove); + insize = max(offset + length + 1, OUTSIZE(remove)); + UPARG(CFS_REMOVE); + + inp->cfs_remove.VFid = *dirfid; + inp->cfs_remove.name = offset; + memcpy((char *)(inp) + offset, name, length); + *((char *)inp + offset + length) = '\0'; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if ( inp ) - CODA_FREE(inp, size); + CODA_FREE(inp, insize); return -error; } int venus_readlink(struct super_block *sb, struct ViceFid *fid, char *buffer, int *length) { - int error, size, retlen; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int 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; + insize = max(INSIZE(readlink), OUTSIZE(readlink)+ *length + 1); + UPARG(CFS_READLINK); + + inp->cfs_readlink.VFid = *fid; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if (! error) { - retlen = outp->d.cfs_readlink.count; + retlen = outp->cfs_readlink.count; if ( retlen > *length ) retlen = *length; *length = retlen; - result = (char *)outp + (int)outp->d.cfs_readlink.data; + result = (char *)outp + (int)outp->cfs_readlink.data; memcpy(buffer, result, retlen); + *(buffer + retlen) = '\0'; } - if (inp) CODA_FREE(buf, size); + if (inp) CODA_FREE(inp, insize); 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; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset; - size = CFS_MAXNAMLEN + sizeof(struct inputArgs); - UPARG(size, CFS_LINK); + offset = INSIZE(link); + insize = max(offset + len + 1, OUTSIZE(link)); + UPARG(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; + inp->cfs_link.sourceFid = *fid; + inp->cfs_link.destFid = *dirfid; + inp->cfs_link.tname = 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; + memcpy((char *)(inp) + offset, name, len); + *((char *)inp + offset + len) = '\0'; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if (inp) - CODA_FREE(inp, size); + CODA_FREE(inp, insize); CDEBUG(D_INODE, " result %d\n",error); EXIT; return -error; @@ -397,62 +421,73 @@ 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); + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset, s; + + offset = INSIZE(symlink); + insize = max(offset + len + symlen + 8, OUTSIZE(symlink)); + UPARG(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'; + /* inp->cfs_symlink.attr = *tva; XXXXXX */ + inp->cfs_symlink.VFid = *fid; + + /* Round up to word boundary and null terminate */ + inp->cfs_symlink.srcname = offset; + s = ( symlen & ~0x3 ) + 4; + memcpy((char *)(inp) + offset, symname, symlen); + *((char *)inp + 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'; + /* Round up to word boundary and null terminate */ + offset += s; + inp->cfs_symlink.tname = offset; + s = (len & ~0x3) + 4; + memcpy((char *)(inp) + offset, name, len); + *((char *)inp + offset + len) = '\0'; - size = payload_offset + s; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if (inp) - CODA_FREE(inp, size); + CODA_FREE(inp, insize); CDEBUG(D_INODE, " result %d\n",error); EXIT; return -error; } +int venus_fsync(struct super_block *sb, struct ViceFid *fid) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + + insize=SIZE(fsync); + UPARG(CFS_FSYNC); + + inp->cfs_fsync.VFid = *fid; + error = coda_upcall(coda_sbp(sb), sizeof(union inputArgs), + &outsize, inp); + + if ( inp ) + CODA_FREE(inp, insize); + return -error; +} + int venus_access(struct super_block *sb, struct ViceFid *fid, int mask) { - struct inputArgs *inp; - struct outputArgs *outp; - int size; - int error; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; - size = sizeof(struct inputArgs); - UPARG(size, CFS_ACCESS); + insize = SIZE(access); + UPARG(CFS_ACCESS); - inp->d.cfs_access.VFid = *fid; - inp->d.cfs_access.flags = mask << 6; + inp->cfs_access.VFid = *fid; + inp->cfs_access.flags = mask; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if (inp) CODA_FREE(inp, sizeof(struct inputArgs)); + if (inp) CODA_FREE(inp, insize); EXIT; return -error; } @@ -461,66 +496,80 @@ int venus_access(struct super_block *sb, struct ViceFid *fid, int mask) 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; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; int iocsize; char str[50]; - size = VC_MAXMSGSIZE; - UPARG(size, CFS_IOCTL); + insize = VC_MAXMSGSIZE; + UPARG(CFS_IOCTL); /* build packet for Venus */ - if (data->vi.in_size > VC_DATASIZE) { + if (data->vi.in_size > VC_MAXDATASIZE) { error = EINVAL; goto exit; } - inp->d.cfs_ioctl.VFid = *fid; + inp->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; + inp->cfs_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16)); + iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int); + inp->cfs_ioctl.cmd |= (iocsize & PIOCPARM_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)); + /* in->cfs_ioctl.rwflag = flag; */ + inp->cfs_ioctl.len = data->vi.in_size; + inp->cfs_ioctl.data = (char *)(INSIZE(ioctl)); /* get the data out of user space */ - if ( copy_from_user((char*)inp + (int)inp->d.cfs_ioctl.data, +#ifdef L20 + memcpy_fromfs((char*)inp + (int)inp->cfs_ioctl.data, + data->vi.in, data->vi.in_size); +#else + if ( copy_from_user((char*)inp + (int)inp->cfs_ioctl.data, data->vi.in, data->vi.in_size) ) { error = EINVAL; goto exit; } - error = coda_upcall(coda_sbp(sb), size, &size, inp); +#endif + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if (error) { printk("coda_pioctl: Venus returns: %d for %s\n", - error, coda_f2s(fid, str)); + error, coda_f2s(fid)); goto exit; } /* Copy out the OUT buffer. */ - if (outp->d.cfs_ioctl.len > data->vi.out_size) { + if (outp->cfs_ioctl.len > data->vi.out_size) { CDEBUG(D_FILE, "return len %d <= request len %d\n", - outp->d.cfs_ioctl.len, + outp->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, + error = verify_area(VERIFY_WRITE, data->vi.out, + data->vi.out_size); + if ( error ) goto exit; +#ifdef L20 + memcpy_tofs(data->vi.out, + (char *)outp + (int)outp->cfs_ioctl.data, + data->vi.out_size); +#else + if (copy_to_user(data->vi.out, + (char *)outp + (int)outp->cfs_ioctl.data, data->vi.out_size)) { error = EINVAL; goto exit; } +#endif } exit: if (inp) - CODA_FREE(inp, VC_MAXMSGSIZE); + CODA_FREE(inp, insize); return -error; } @@ -535,11 +584,53 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid, * reply and return Venus' error, also POSITIVE. * */ +static inline void coda_waitfor_upcall(struct vmsg *vmp) +{ + struct wait_queue wait = { current, NULL }; + old_sigset_t pending; + + vmp->vm_posttime = jiffies; + + add_wait_queue(&vmp->vm_sleep, &wait); + for (;;) { + if ( coda_hard == 0 ) + current->state = TASK_INTERRUPTIBLE; + else + current->state = TASK_UNINTERRUPTIBLE; + + /* got a reply */ + if ( vmp->vm_flags & VM_WRITE ) + break; + + if ( ! signal_pending(current) ) + schedule(); + /* signal is present: after timeout always return */ + if ( jiffies > vmp->vm_posttime + coda_timeout * HZ ) + break; + + spin_lock_irq(¤t->sigmask_lock); + pending = current->blocked.sig[0] & current->signal.sig[0]; + spin_unlock_irq(¤t->sigmask_lock); + + /* if this process really wants to die, let it go */ + if ( sigismember(&pending, SIGKILL) || + sigismember(&pending, SIGINT) ) + break; + else + schedule(); + } + remove_wait_queue(&vmp->vm_sleep, &wait); + current->state = TASK_RUNNING; + + return; +} + + int coda_upcall(struct coda_sb_info *sbi, int inSize, int *outSize, - struct inputArgs *buffer) + union inputArgs *buffer) { struct vcomm *vcommp; - struct outputArgs *out; + union outputArgs *out; struct vmsg *vmp; int error = 0; @@ -550,7 +641,7 @@ ENTRY; } vcommp = sbi->sbi_vcomm; - clstats(((struct inputArgs *)buffer)->opcode); + clstats(((union inputArgs *)buffer)->ih.opcode); if (!vcomm_open(vcommp)) return(ENODEV); @@ -561,16 +652,15 @@ ENTRY; vmp->vm_flags = 0; vmp->vm_inSize = inSize; vmp->vm_outSize = *outSize ? *outSize : inSize; - vmp->vm_opcode = ((struct inputArgs *)buffer)->opcode; + vmp->vm_opcode = ((union inputArgs *)buffer)->ih.opcode; vmp->vm_unique = ++vcommp->vc_seq; vmp->vm_sleep = NULL; /* Fill in the common input args. */ - ((struct inputArgs *)buffer)->unique = vmp->vm_unique; + ((union inputArgs *)buffer)->ih.unique = vmp->vm_unique; /* Append msg to pending queue and poke Venus. */ - - INSQUE(vmp->vm_chain, vcommp->vc_pending); + coda_q_insert(&(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); @@ -582,13 +672,15 @@ ENTRY; * 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 + * was interrupted by a venus shutdown (psdev_close), return * ENODEV. */ - /* Ignore return, We have to check anyway */ - + /* Go to sleep. Wake up on signals only after the timeout. */ + coda_waitfor_upcall(vmp); - interruptible_sleep_on(&vmp->vm_sleep); + CDEBUG(D_TIMING, "opc: %d time: %ld uniq: %d size: %d\n", + vmp->vm_opcode, jiffies - vmp->vm_posttime, + vmp->vm_unique, vmp->vm_outSize); 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); @@ -596,60 +688,61 @@ ENTRY; /* Op went through, interrupt or not... */ if (vmp->vm_flags & VM_WRITE) { error = 0; - out = (struct outputArgs *)vmp->vm_data; - error = out->result; + out = (union outputArgs *)vmp->vm_data; + error = out->oh.result; CDEBUG(D_UPCALL, - "upcall: (u,o,r) (%ld, %ld, %ld) out at %x\n", - out->unique, out->opcode, out->result, (int)out); + "upcall: (u,o,r) (%ld, %ld, %ld) out at %p\n", + out->oh.unique, out->oh.opcode, out->oh.result, out); *outSize = vmp->vm_outSize; goto exit; - } - if (!(vmp->vm_flags & VM_READ)) { + } + if ( !(vmp->vm_flags & VM_READ) && signal_pending(current)) { /* 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; + coda_q_remove(&(vmp->vm_chain)); + error = ERESTARTNOHAND; goto exit; } - if ( vmp->vm_flags & VM_READ) { + if ( (vmp->vm_flags & VM_READ) && signal_pending(current) ) { /* interrupted after Venus did its read, send signal */ - struct inputArgs *dog; + union 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_q_remove(&(vmp->vm_chain)); + error = ERESTARTNOHAND; CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg)); - CODA_ALLOC((svmp->vm_data), char *, VC_IN_NO_DATA); + CODA_ALLOC((svmp->vm_data), char *, sizeof(struct cfs_in_hdr)); + dog = (union inputArgs *)svmp->vm_data; + dog->ih.opcode = CFS_SIGNAL; + dog->ih.unique = vmp->vm_unique; + + svmp->vm_flags = 0; + svmp->vm_opcode = dog->ih.opcode; + svmp->vm_unique = dog->ih.unique; + svmp->vm_inSize = sizeof(struct cfs_in_hdr); + svmp->vm_outSize = sizeof(struct cfs_in_hdr); 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); + coda_q_insert(&(svmp->vm_chain), vcommp->vc_pending.forw); wake_up_interruptible(&vcommp->vc_waitq); + } else { + printk("Coda: Strange interruption..\n"); + error = EINTR; } } else { /* If venus died i.e. !VC_OPEN(vcommp) */ - printk("coda_upcall: Venus dead upon (op,un) (%d.%d) flags %d\n", + printk("coda_upcall: Venus dead on (op,un) (%d.%d) flags %d\n", vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags); - - /* if (! (vmp->vm_flags & VM_WRITE) ) */ error = ENODEV; } @@ -670,78 +763,106 @@ ENTRY; * 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). + * CFS_ZAPVNODE -- intended to be a zapfile for just one cred. Not used? * * 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. + * purge it and its children from the dcache * * 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) +int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) { /* 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); - } + case CFS_FLUSH : { + clstats(CFS_FLUSH); + CDEBUG(D_DOWNCALL, "CFS_FLUSH\n"); + coda_cache_clear_all(sb); + shrink_dcache_sb(sb); + return(0); + } + case CFS_PURGEUSER : { + struct coda_cred *cred = &out->cfs_purgeuser.cred; + CDEBUG(D_DOWNCALL, "CFS_PURGEUSER\n"); + if ( !cred ) { + printk("PURGEUSER: null cred!\n"); + return 0; + } + clstats(CFS_PURGEUSER); + coda_cache_clear_cred(sb, cred); + return(0); + } + case CFS_ZAPDIR : { + ViceFid *fid = &out->cfs_zapdir.CodaFid; + char str[50]; + if ( !fid ) { + printk("ZAPDIR: Null fid\n"); + return 0; + } + CDEBUG(D_DOWNCALL, "zapdir: fid = %s\n", coda_f2s(fid)); + clstats(CFS_ZAPDIR); + coda_zapfid(fid, sb, C_ZAPDIR); + return(0); + } + case CFS_ZAPVNODE : { + ViceFid *fid = &out->cfs_zapvnode.VFid; + char str[50]; + struct coda_cred *cred = &out->cfs_zapvnode.cred; + if ( !fid || !cred ) { + printk("ZAPVNODE: Null fid or cred\n"); + return 0; + } + CDEBUG(D_DOWNCALL, "zapvnode: fid = %s\n", coda_f2s(fid)); + coda_zapfid(fid, sb, C_ZAPFID); + coda_cache_clear_cred(sb, cred); + clstats(CFS_ZAPVNODE); + return(0); + } + case CFS_ZAPFILE : { + struct ViceFid *fid = &out->cfs_zapfile.CodaFid; + char str[50]; + clstats(CFS_ZAPFILE); + if ( !fid ) { + printk("ZAPFILE: Null fid\n"); + return 0; + } + CDEBUG(D_DOWNCALL, "zapfile: fid = %s\n", coda_f2s(fid)); + coda_zapfid(fid, sb, C_ZAPFID); + return 0; + } + case CFS_PURGEFID : { + ViceFid *fid = &out->cfs_purgefid.CodaFid; + char str[50]; + if ( !fid ) { + printk("PURGEFID: Null fid\n"); + return 0; + } + CDEBUG(D_DOWNCALL, "purgefid: fid = %s\n", coda_f2s(fid)); + clstats(CFS_PURGEFID); + coda_zapfid(fid, sb, C_ZAPDIR); + return 0; + } + case CFS_REPLACE : { + printk("CFS_REPLACCE\n"); + clstats(CFS_REPLACE); + CDEBUG(D_DOWNCALL, "CFS_REPLACE\n"); + coda_cache_clear_all(sb); + shrink_dcache_sb(sb); + return (0); + } } return 0; } + |