915 lines
		
	
	
	
		
			24 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			915 lines
		
	
	
	
		
			24 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * Mostly platform independent upcall operations to Venus: | ||
|  |  *  -- upcalls | ||
|  |  *  -- upcall routines | ||
|  |  * | ||
|  |  * Linux 2.0 version | ||
|  |  * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk>,  | ||
|  |  * Michael Callahan <callahan@maths.ox.ac.uk>  | ||
|  |  *  | ||
|  |  * Redone for Linux 2.1 | ||
|  |  * Copyright (C) 1997 Carnegie Mellon University | ||
|  |  * | ||
|  |  * Carnegie Mellon University encourages users of this code to contribute | ||
|  |  * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>. | ||
|  |  */ | ||
|  | 
 | ||
|  | #include <asm/system.h>
 | ||
|  | #include <linux/signal.h>
 | ||
|  | 
 | ||
|  | #include <linux/types.h>
 | ||
|  | #include <linux/kernel.h>
 | ||
|  | #include <linux/mm.h>
 | ||
|  | #include <linux/time.h>
 | ||
|  | #include <linux/fs.h>
 | ||
|  | #include <linux/file.h>
 | ||
|  | #include <linux/stat.h>
 | ||
|  | #include <linux/errno.h>
 | ||
|  | #include <linux/string.h>
 | ||
|  | #include <asm/uaccess.h>
 | ||
|  | #include <linux/vmalloc.h>
 | ||
|  | #include <linux/vfs.h>
 | ||
|  | 
 | ||
|  | #include <linux/coda.h>
 | ||
|  | #include <linux/coda_linux.h>
 | ||
|  | #include <linux/coda_psdev.h>
 | ||
|  | #include <linux/coda_fs_i.h>
 | ||
|  | #include <linux/coda_cache.h>
 | ||
|  | #include <linux/coda_proc.h> 
 | ||
|  | 
 | ||
|  | #define upc_alloc() kmalloc(sizeof(struct upc_req), GFP_KERNEL)
 | ||
|  | #define upc_free(r) kfree(r)
 | ||
|  | 
 | ||
|  | static int coda_upcall(struct coda_sb_info *mntinfo, int inSize, int *outSize,  | ||
|  | 		       union inputArgs *buffer); | ||
|  | 
 | ||
|  | static void *alloc_upcall(int opcode, int size) | ||
|  | { | ||
|  | 	union inputArgs *inp; | ||
|  | 
 | ||
|  | 	CODA_ALLOC(inp, union inputArgs *, size); | ||
|  |         if (!inp) | ||
|  | 		return ERR_PTR(-ENOMEM); | ||
|  | 
 | ||
|  |         inp->ih.opcode = opcode; | ||
|  | 	inp->ih.pid = current->pid; | ||
|  | 	inp->ih.pgid = process_group(current); | ||
|  | #ifdef CONFIG_CODA_FS_OLD_API
 | ||
|  | 	memset(&inp->ih.cred, 0, sizeof(struct coda_cred)); | ||
|  | 	inp->ih.cred.cr_fsuid = current->fsuid; | ||
|  | #else
 | ||
|  | 	inp->ih.uid = current->fsuid; | ||
|  | #endif
 | ||
|  | 	return (void*)inp; | ||
|  | } | ||
|  | 
 | ||
|  | #define UPARG(op)\
 | ||
|  | do {\ | ||
|  | 	inp = (union inputArgs *)alloc_upcall(op, insize); \ | ||
|  |         if (IS_ERR(inp)) { return PTR_ERR(inp); }\ | ||
|  |         outp = (union outputArgs *)(inp); \ | ||
|  |         outsize = insize; \ | ||
|  | } while (0) | ||
|  | 
 | ||
|  | #define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
 | ||
|  | #define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
 | ||
|  | #define SIZE(tag)  max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))
 | ||
|  | 
 | ||
|  | 
 | ||
|  | /* the upcalls */ | ||
|  | int venus_rootfid(struct super_block *sb, struct CodaFid *fidp) | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int insize, outsize, error; | ||
|  | 
 | ||
|  |         insize = SIZE(root); | ||
|  |         UPARG(CODA_ROOT); | ||
|  | 
 | ||
|  | 	error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 	 | ||
|  | 	if (error) { | ||
|  | 	        printk("coda_get_rootfid: error %d\n", error); | ||
|  | 	} else { | ||
|  | 		*fidp = outp->coda_root.VFid; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  | 	return error; | ||
|  | } | ||
|  | 
 | ||
|  | int venus_getattr(struct super_block *sb, struct CodaFid *fid,  | ||
|  | 		     struct coda_vattr *attr)  | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int insize, outsize, error; | ||
|  | 
 | ||
|  |         insize = SIZE(getattr);  | ||
|  | 	UPARG(CODA_GETATTR); | ||
|  |         inp->coda_getattr.VFid = *fid; | ||
|  | 
 | ||
|  |         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 	 | ||
|  | 	*attr = outp->coda_getattr.attr; | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  |         return error; | ||
|  | } | ||
|  | 
 | ||
|  | int venus_setattr(struct super_block *sb, struct CodaFid *fid,  | ||
|  | 		  struct coda_vattr *vattr) | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int insize, outsize, error; | ||
|  | 	 | ||
|  | 	insize = SIZE(setattr); | ||
|  | 	UPARG(CODA_SETATTR); | ||
|  | 
 | ||
|  |         inp->coda_setattr.VFid = *fid; | ||
|  | 	inp->coda_setattr.attr = *vattr; | ||
|  | 
 | ||
|  |         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 
 | ||
|  |         CODA_FREE(inp, insize); | ||
|  |         return error; | ||
|  | } | ||
|  | 
 | ||
|  | int venus_lookup(struct super_block *sb, struct CodaFid *fid,  | ||
|  | 		    const char *name, int length, int * type,  | ||
|  | 		    struct CodaFid *resfid) | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int insize, outsize, error; | ||
|  | 	int offset; | ||
|  | 
 | ||
|  | 	offset = INSIZE(lookup); | ||
|  |         insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup)); | ||
|  | 	UPARG(CODA_LOOKUP); | ||
|  | 
 | ||
|  |         inp->coda_lookup.VFid = *fid; | ||
|  | 	inp->coda_lookup.name = offset; | ||
|  | 	inp->coda_lookup.flags = CLU_CASE_SENSITIVE; | ||
|  |         /* send Venus a null terminated string */ | ||
|  |         memcpy((char *)(inp) + offset, name, length); | ||
|  |         *((char *)inp + offset + length) = '\0'; | ||
|  | 
 | ||
|  |         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 
 | ||
|  | 	*resfid = outp->coda_lookup.VFid; | ||
|  | 	*type = outp->coda_lookup.vtype; | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  | 	return error; | ||
|  | } | ||
|  | 
 | ||
|  | int venus_store(struct super_block *sb, struct CodaFid *fid, int flags, | ||
|  |                 vuid_t uid) | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int insize, outsize, error; | ||
|  | #ifdef CONFIG_CODA_FS_OLD_API
 | ||
|  | 	struct coda_cred cred = { 0, }; | ||
|  | 	cred.cr_fsuid = uid; | ||
|  | #endif
 | ||
|  | 	 | ||
|  | 	insize = SIZE(store); | ||
|  | 	UPARG(CODA_STORE); | ||
|  | 	 | ||
|  | #ifdef CONFIG_CODA_FS_OLD_API
 | ||
|  | 	memcpy(&(inp->ih.cred), &cred, sizeof(cred)); | ||
|  | #else
 | ||
|  | 	inp->ih.uid = uid; | ||
|  | #endif
 | ||
|  | 	 | ||
|  |         inp->coda_store.VFid = *fid; | ||
|  |         inp->coda_store.flags = flags; | ||
|  | 
 | ||
|  |         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  |         return error; | ||
|  | } | ||
|  | 
 | ||
|  | int venus_release(struct super_block *sb, struct CodaFid *fid, int flags) | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int insize, outsize, error; | ||
|  | 	 | ||
|  | 	insize = SIZE(release); | ||
|  | 	UPARG(CODA_RELEASE); | ||
|  | 	 | ||
|  | 	inp->coda_release.VFid = *fid; | ||
|  | 	inp->coda_release.flags = flags; | ||
|  | 
 | ||
|  | 	error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  | 	return error; | ||
|  | } | ||
|  | 
 | ||
|  | int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, | ||
|  |                 vuid_t uid) | ||
|  | { | ||
|  | 	union inputArgs *inp; | ||
|  | 	union outputArgs *outp; | ||
|  | 	int insize, outsize, error; | ||
|  | #ifdef CONFIG_CODA_FS_OLD_API
 | ||
|  | 	struct coda_cred cred = { 0, }; | ||
|  | 	cred.cr_fsuid = uid; | ||
|  | #endif
 | ||
|  | 	 | ||
|  | 	insize = SIZE(release); | ||
|  | 	UPARG(CODA_CLOSE); | ||
|  | 	 | ||
|  | #ifdef CONFIG_CODA_FS_OLD_API
 | ||
|  | 	memcpy(&(inp->ih.cred), &cred, sizeof(cred)); | ||
|  | #else
 | ||
|  | 	inp->ih.uid = uid; | ||
|  | #endif
 | ||
|  | 	 | ||
|  |         inp->coda_close.VFid = *fid; | ||
|  |         inp->coda_close.flags = flags; | ||
|  | 
 | ||
|  |         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  |         return error; | ||
|  | } | ||
|  | 
 | ||
|  | int venus_open(struct super_block *sb, struct CodaFid *fid, | ||
|  | 		  int flags, struct file **fh) | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int insize, outsize, error; | ||
|  |         | ||
|  | 	insize = SIZE(open_by_fd); | ||
|  | 	UPARG(CODA_OPEN_BY_FD); | ||
|  | 
 | ||
|  |         inp->coda_open.VFid = *fid; | ||
|  |         inp->coda_open.flags = flags; | ||
|  | 
 | ||
|  |         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 
 | ||
|  | 	*fh = outp->coda_open_by_fd.fh; | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  | 	return error; | ||
|  | }	 | ||
|  | 
 | ||
|  | int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid,  | ||
|  | 		   const char *name, int length,  | ||
|  | 		   struct CodaFid *newfid, struct coda_vattr *attrs) | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int insize, outsize, error; | ||
|  |         int offset; | ||
|  | 
 | ||
|  | 	offset = INSIZE(mkdir); | ||
|  | 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir)); | ||
|  | 	UPARG(CODA_MKDIR); | ||
|  | 
 | ||
|  |         inp->coda_mkdir.VFid = *dirfid; | ||
|  |         inp->coda_mkdir.attr = *attrs; | ||
|  | 	inp->coda_mkdir.name = offset; | ||
|  |         /* Venus must get null terminated string */ | ||
|  |         memcpy((char *)(inp) + offset, name, length); | ||
|  |         *((char *)inp + offset + length) = '\0'; | ||
|  |          | ||
|  |         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 
 | ||
|  | 	*attrs = outp->coda_mkdir.attr; | ||
|  | 	*newfid = outp->coda_mkdir.VFid; | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  | 	return error;         | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int venus_rename(struct super_block *sb, struct CodaFid *old_fid,  | ||
|  | 		 struct CodaFid *new_fid, size_t old_length,  | ||
|  | 		 size_t new_length, const char *old_name,  | ||
|  | 		 const char *new_name) | ||
|  | { | ||
|  | 	union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int insize, outsize, error;  | ||
|  | 	int offset, s; | ||
|  | 	 | ||
|  | 	offset = INSIZE(rename); | ||
|  | 	insize = max_t(unsigned int, offset + new_length + old_length + 8, | ||
|  | 		     OUTSIZE(rename));  | ||
|  |  	UPARG(CODA_RENAME); | ||
|  | 
 | ||
|  |         inp->coda_rename.sourceFid = *old_fid; | ||
|  |         inp->coda_rename.destFid =  *new_fid; | ||
|  |         inp->coda_rename.srcname = offset; | ||
|  | 
 | ||
|  |         /* Venus must receive an null terminated string */ | ||
|  |         s = ( old_length & ~0x3) +4; /* round up to word boundary */ | ||
|  |         memcpy((char *)(inp) + offset, old_name, old_length); | ||
|  |         *((char *)inp + offset + old_length) = '\0'; | ||
|  | 
 | ||
|  |         /* another null terminated string for Venus */ | ||
|  |         offset += s; | ||
|  |         inp->coda_rename.destname = offset; | ||
|  |         s = ( new_length & ~0x3) +4; /* round up to word boundary */ | ||
|  |         memcpy((char *)(inp) + offset, new_name, new_length); | ||
|  |         *((char *)inp + offset + new_length) = '\0'; | ||
|  | 
 | ||
|  |         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  | 	return error; | ||
|  | } | ||
|  | 
 | ||
|  | int venus_create(struct super_block *sb, struct CodaFid *dirfid,  | ||
|  | 		 const char *name, int length, int excl, int mode, | ||
|  | 		 struct CodaFid *newfid, struct coda_vattr *attrs)  | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int insize, outsize, error; | ||
|  |         int offset; | ||
|  | 
 | ||
|  |         offset = INSIZE(create); | ||
|  | 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create)); | ||
|  | 	UPARG(CODA_CREATE); | ||
|  | 
 | ||
|  |         inp->coda_create.VFid = *dirfid; | ||
|  |         inp->coda_create.attr.va_mode = mode; | ||
|  | 	inp->coda_create.excl = excl; | ||
|  |         inp->coda_create.mode = mode; | ||
|  |         inp->coda_create.name = offset; | ||
|  | 
 | ||
|  |         /* Venus must get null terminated string */ | ||
|  |         memcpy((char *)(inp) + offset, name, length); | ||
|  |         *((char *)inp + offset + length) = '\0'; | ||
|  |                  | ||
|  |         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 
 | ||
|  | 	*attrs = outp->coda_create.attr; | ||
|  | 	*newfid = outp->coda_create.VFid; | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  | 	return error;         | ||
|  | } | ||
|  | 
 | ||
|  | int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid,  | ||
|  | 		    const char *name, int length) | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int insize, outsize, error; | ||
|  |         int offset; | ||
|  | 
 | ||
|  |         offset = INSIZE(rmdir); | ||
|  | 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir)); | ||
|  | 	UPARG(CODA_RMDIR); | ||
|  | 
 | ||
|  |         inp->coda_rmdir.VFid = *dirfid; | ||
|  |         inp->coda_rmdir.name = offset; | ||
|  |         memcpy((char *)(inp) + offset, name, length); | ||
|  | 	*((char *)inp + offset + length) = '\0'; | ||
|  |          | ||
|  |         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  | 	return error; | ||
|  | } | ||
|  | 
 | ||
|  | int venus_remove(struct super_block *sb, struct CodaFid *dirfid,  | ||
|  | 		    const char *name, int length) | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int error=0, insize, outsize, offset; | ||
|  | 
 | ||
|  |         offset = INSIZE(remove); | ||
|  | 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove)); | ||
|  | 	UPARG(CODA_REMOVE); | ||
|  | 
 | ||
|  |         inp->coda_remove.VFid = *dirfid; | ||
|  |         inp->coda_remove.name = offset; | ||
|  |         memcpy((char *)(inp) + offset, name, length); | ||
|  | 	*((char *)inp + offset + length) = '\0'; | ||
|  |          | ||
|  |         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  | 	return error; | ||
|  | } | ||
|  | 
 | ||
|  | int venus_readlink(struct super_block *sb, struct CodaFid *fid,  | ||
|  | 		      char *buffer, int *length) | ||
|  | {  | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int insize, outsize, error; | ||
|  |         int retlen; | ||
|  |         char *result; | ||
|  |          | ||
|  | 	insize = max_t(unsigned int, | ||
|  | 		     INSIZE(readlink), OUTSIZE(readlink)+ *length + 1); | ||
|  | 	UPARG(CODA_READLINK); | ||
|  | 
 | ||
|  |         inp->coda_readlink.VFid = *fid; | ||
|  |      | ||
|  |         error =  coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 	 | ||
|  | 	if (! error) { | ||
|  |                 retlen = outp->coda_readlink.count; | ||
|  | 		if ( retlen > *length ) | ||
|  | 		        retlen = *length; | ||
|  | 		*length = retlen; | ||
|  | 		result =  (char *)outp + (long)outp->coda_readlink.data; | ||
|  | 		memcpy(buffer, result, retlen); | ||
|  | 		*(buffer + retlen) = '\0'; | ||
|  | 	} | ||
|  |          | ||
|  |         CODA_FREE(inp, insize); | ||
|  |         return error; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | int venus_link(struct super_block *sb, struct CodaFid *fid,  | ||
|  | 		  struct CodaFid *dirfid, const char *name, int len ) | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int insize, outsize, error; | ||
|  |         int offset; | ||
|  | 
 | ||
|  | 	offset = INSIZE(link); | ||
|  | 	insize = max_t(unsigned int, offset  + len + 1, OUTSIZE(link)); | ||
|  |         UPARG(CODA_LINK); | ||
|  | 
 | ||
|  |         inp->coda_link.sourceFid = *fid; | ||
|  |         inp->coda_link.destFid = *dirfid; | ||
|  |         inp->coda_link.tname = offset; | ||
|  | 
 | ||
|  |         /* make sure strings are null terminated */ | ||
|  |         memcpy((char *)(inp) + offset, name, len); | ||
|  |         *((char *)inp + offset + len) = '\0'; | ||
|  |          | ||
|  |         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  |         return error; | ||
|  | } | ||
|  | 
 | ||
|  | int venus_symlink(struct super_block *sb, struct CodaFid *fid, | ||
|  | 		     const char *name, int len, | ||
|  | 		     const char *symname, int symlen) | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int insize, outsize, error; | ||
|  |         int offset, s; | ||
|  | 
 | ||
|  |         offset = INSIZE(symlink); | ||
|  | 	insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink)); | ||
|  | 	UPARG(CODA_SYMLINK); | ||
|  |          | ||
|  |         /*        inp->coda_symlink.attr = *tva; XXXXXX */  | ||
|  |         inp->coda_symlink.VFid = *fid; | ||
|  | 
 | ||
|  | 	/* Round up to word boundary and null terminate */ | ||
|  |         inp->coda_symlink.srcname = offset; | ||
|  |         s = ( symlen  & ~0x3 ) + 4;  | ||
|  |         memcpy((char *)(inp) + offset, symname, symlen); | ||
|  |         *((char *)inp + offset + symlen) = '\0'; | ||
|  |          | ||
|  | 	/* Round up to word boundary and null terminate */ | ||
|  |         offset += s; | ||
|  |         inp->coda_symlink.tname = offset; | ||
|  |         s = (len & ~0x3) + 4; | ||
|  |         memcpy((char *)(inp) + offset, name, len); | ||
|  |         *((char *)inp + offset + len) = '\0'; | ||
|  | 
 | ||
|  | 	error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  |         return error; | ||
|  | } | ||
|  | 
 | ||
|  | int venus_fsync(struct super_block *sb, struct CodaFid *fid) | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp;  | ||
|  | 	int insize, outsize, error; | ||
|  | 	 | ||
|  | 	insize=SIZE(fsync); | ||
|  | 	UPARG(CODA_FSYNC); | ||
|  | 
 | ||
|  |         inp->coda_fsync.VFid = *fid; | ||
|  |         error = coda_upcall(coda_sbp(sb), sizeof(union inputArgs),  | ||
|  |                             &outsize, inp); | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  | 	return error; | ||
|  | } | ||
|  | 
 | ||
|  | int venus_access(struct super_block *sb, struct CodaFid *fid, int mask) | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp;  | ||
|  | 	int insize, outsize, error; | ||
|  | 
 | ||
|  | 	insize = SIZE(access); | ||
|  | 	UPARG(CODA_ACCESS); | ||
|  | 
 | ||
|  |         inp->coda_access.VFid = *fid; | ||
|  |         inp->coda_access.flags = mask; | ||
|  | 
 | ||
|  | 	error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 
 | ||
|  | 	CODA_FREE(inp, insize); | ||
|  | 	return error; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int venus_pioctl(struct super_block *sb, struct CodaFid *fid, | ||
|  | 		 unsigned int cmd, struct PioctlData *data) | ||
|  | { | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp;   | ||
|  | 	int insize, outsize, error; | ||
|  | 	int iocsize; | ||
|  | 
 | ||
|  | 	insize = VC_MAXMSGSIZE; | ||
|  | 	UPARG(CODA_IOCTL); | ||
|  | 
 | ||
|  |         /* build packet for Venus */ | ||
|  |         if (data->vi.in_size > VC_MAXDATASIZE) { | ||
|  | 		error = -EINVAL; | ||
|  | 		goto exit; | ||
|  |         } | ||
|  | 
 | ||
|  |         if (data->vi.out_size > VC_MAXDATASIZE) { | ||
|  | 		error = -EINVAL; | ||
|  | 		goto exit; | ||
|  | 	} | ||
|  | 
 | ||
|  |         inp->coda_ioctl.VFid = *fid; | ||
|  |      | ||
|  |         /* the cmd field was mutated by increasing its size field to
 | ||
|  |          * reflect the path and follow args. We need to subtract that | ||
|  |          * out before sending the command to Venus.  */ | ||
|  |         inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));	 | ||
|  |         iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int); | ||
|  |         inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) <<	16;	 | ||
|  |      | ||
|  |         /* in->coda_ioctl.rwflag = flag; */ | ||
|  |         inp->coda_ioctl.len = data->vi.in_size; | ||
|  |         inp->coda_ioctl.data = (char *)(INSIZE(ioctl)); | ||
|  |       | ||
|  |         /* get the data out of user space */ | ||
|  |         if ( copy_from_user((char*)inp + (long)inp->coda_ioctl.data, | ||
|  | 			    data->vi.in, data->vi.in_size) ) { | ||
|  | 		error = -EINVAL; | ||
|  | 	        goto exit; | ||
|  | 	} | ||
|  | 
 | ||
|  |         error = coda_upcall(coda_sbp(sb), SIZE(ioctl) + data->vi.in_size, | ||
|  |                             &outsize, inp); | ||
|  |          | ||
|  |         if (error) { | ||
|  | 	        printk("coda_pioctl: Venus returns: %d for %s\n",  | ||
|  | 		       error, coda_f2s(fid)); | ||
|  | 		goto exit;  | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) { | ||
|  | 		error = -EINVAL; | ||
|  | 		goto exit; | ||
|  | 	} | ||
|  |          | ||
|  | 	/* Copy out the OUT buffer. */ | ||
|  |         if (outp->coda_ioctl.len > data->vi.out_size) { | ||
|  | 		error = -EINVAL; | ||
|  | 		goto exit; | ||
|  |         } | ||
|  | 
 | ||
|  | 	/* Copy out the OUT buffer. */ | ||
|  | 	if (copy_to_user(data->vi.out, | ||
|  | 			 (char *)outp + (long)outp->coda_ioctl.data, | ||
|  | 			 outp->coda_ioctl.len)) { | ||
|  | 		error = -EFAULT; | ||
|  | 		goto exit; | ||
|  | 	} | ||
|  | 
 | ||
|  |  exit: | ||
|  | 	CODA_FREE(inp, insize); | ||
|  | 	return error; | ||
|  | } | ||
|  | 
 | ||
|  | int venus_statfs(struct super_block *sb, struct kstatfs *sfs)  | ||
|  | {  | ||
|  |         union inputArgs *inp; | ||
|  |         union outputArgs *outp; | ||
|  |         int insize, outsize, error; | ||
|  |          | ||
|  | 	insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs)); | ||
|  | 	UPARG(CODA_STATFS); | ||
|  | 
 | ||
|  |         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
|  | 	 | ||
|  |         if (!error) { | ||
|  | 		sfs->f_blocks = outp->coda_statfs.stat.f_blocks; | ||
|  | 		sfs->f_bfree  = outp->coda_statfs.stat.f_bfree; | ||
|  | 		sfs->f_bavail = outp->coda_statfs.stat.f_bavail; | ||
|  | 		sfs->f_files  = outp->coda_statfs.stat.f_files; | ||
|  | 		sfs->f_ffree  = outp->coda_statfs.stat.f_ffree; | ||
|  | 	} else { | ||
|  | 		printk("coda_statfs: Venus returns: %d\n", error); | ||
|  | 	} | ||
|  | 
 | ||
|  |         CODA_FREE(inp, insize); | ||
|  |         return error; | ||
|  | } | ||
|  | 
 | ||
|  | /*
 | ||
|  |  * coda_upcall and coda_downcall routines. | ||
|  |  *  | ||
|  |  */ | ||
|  | 
 | ||
|  | static inline void coda_waitfor_upcall(struct upc_req *vmp, | ||
|  | 				       struct venus_comm *vcommp) | ||
|  | { | ||
|  | 	DECLARE_WAITQUEUE(wait, current); | ||
|  | 
 | ||
|  | 	vmp->uc_posttime = jiffies; | ||
|  | 
 | ||
|  | 	add_wait_queue(&vmp->uc_sleep, &wait); | ||
|  | 	for (;;) { | ||
|  | 		if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE )  | ||
|  | 			set_current_state(TASK_INTERRUPTIBLE); | ||
|  | 		else | ||
|  | 			set_current_state(TASK_UNINTERRUPTIBLE); | ||
|  | 
 | ||
|  |                 /* venus died */ | ||
|  |                 if ( !vcommp->vc_inuse ) | ||
|  |                         break; | ||
|  | 
 | ||
|  | 		/* got a reply */ | ||
|  | 		if ( vmp->uc_flags & ( REQ_WRITE | REQ_ABORT ) ) | ||
|  | 			break; | ||
|  | 
 | ||
|  | 		if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE && signal_pending(current) ) { | ||
|  | 			/* if this process really wants to die, let it go */ | ||
|  | 			if ( sigismember(&(current->pending.signal), SIGKILL) || | ||
|  | 			     sigismember(&(current->pending.signal), SIGINT) ) | ||
|  | 				break; | ||
|  | 			/* signal is present: after timeout always return 
 | ||
|  | 			   really smart idea, probably useless ... */ | ||
|  | 			if ( jiffies - vmp->uc_posttime > coda_timeout * HZ ) | ||
|  | 				break;  | ||
|  | 		} | ||
|  | 		schedule(); | ||
|  | 	} | ||
|  | 	remove_wait_queue(&vmp->uc_sleep, &wait); | ||
|  | 	set_current_state(TASK_RUNNING); | ||
|  | 
 | ||
|  | 	return; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* 
 | ||
|  |  * coda_upcall will return an error in the case of  | ||
|  |  * failed communication with Venus _or_ will peek at Venus | ||
|  |  * reply and return Venus' error. | ||
|  |  * | ||
|  |  * As venus has 2 types of errors, normal errors (positive) and internal | ||
|  |  * errors (negative), normal errors are negated, while internal errors | ||
|  |  * are all mapped to -EINTR, while showing a nice warning message. (jh) | ||
|  |  *  | ||
|  |  */ | ||
|  | static int coda_upcall(struct coda_sb_info *sbi,  | ||
|  | 		int inSize, int *outSize,  | ||
|  | 		union inputArgs *buffer)  | ||
|  | { | ||
|  | 	struct venus_comm *vcommp; | ||
|  | 	union outputArgs *out; | ||
|  | 	struct upc_req *req; | ||
|  | 	int error = 0; | ||
|  | 
 | ||
|  | 	vcommp = sbi->sbi_vcomm; | ||
|  | 	if ( !vcommp->vc_inuse ) { | ||
|  | 		printk("No pseudo device in upcall comms at %p\n", vcommp); | ||
|  |                 return -ENXIO; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* Format the request message. */ | ||
|  | 	req = upc_alloc(); | ||
|  | 	if (!req) { | ||
|  | 		printk("Failed to allocate upc_req structure\n"); | ||
|  | 		return -ENOMEM; | ||
|  | 	} | ||
|  | 	req->uc_data = (void *)buffer; | ||
|  | 	req->uc_flags = 0; | ||
|  | 	req->uc_inSize = inSize; | ||
|  | 	req->uc_outSize = *outSize ? *outSize : inSize; | ||
|  | 	req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode; | ||
|  | 	req->uc_unique = ++vcommp->vc_seq; | ||
|  | 	init_waitqueue_head(&req->uc_sleep); | ||
|  | 	 | ||
|  | 	/* Fill in the common input args. */ | ||
|  | 	((union inputArgs *)buffer)->ih.unique = req->uc_unique; | ||
|  | 
 | ||
|  | 	/* Append msg to pending queue and poke Venus. */ | ||
|  | 	list_add(&(req->uc_chain), vcommp->vc_pending.prev); | ||
|  |          | ||
|  | 	wake_up_interruptible(&vcommp->vc_waitq); | ||
|  | 	/* We can be interrupted while we wait for Venus to process
 | ||
|  | 	 * our request.  If the interrupt occurs before Venus has read | ||
|  | 	 * the request, we dequeue and return. If it occurs after the | ||
|  | 	 * read but before the reply, we dequeue, send a signal | ||
|  | 	 * message, and return. If it occurs after the reply we ignore | ||
|  | 	 * it. In no case do we want to restart the syscall.  If it | ||
|  | 	 * was interrupted by a venus shutdown (psdev_close), return | ||
|  | 	 * ENODEV.  */ | ||
|  | 
 | ||
|  | 	/* Go to sleep.  Wake up on signals only after the timeout. */ | ||
|  | 	coda_waitfor_upcall(req, vcommp); | ||
|  | 
 | ||
|  | 	if (vcommp->vc_inuse) {      /* i.e. Venus is still alive */ | ||
|  | 	    /* Op went through, interrupt or not... */ | ||
|  | 	    if (req->uc_flags & REQ_WRITE) { | ||
|  | 		out = (union outputArgs *)req->uc_data; | ||
|  | 		/* here we map positive Venus errors to kernel errors */ | ||
|  | 		error = -out->oh.result; | ||
|  | 		*outSize = req->uc_outSize; | ||
|  | 		goto exit; | ||
|  | 	    } | ||
|  | 	    if ( !(req->uc_flags & REQ_READ) && signal_pending(current)) {  | ||
|  | 		/* Interrupted before venus read it. */ | ||
|  | 		list_del(&(req->uc_chain)); | ||
|  | 		/* perhaps the best way to convince the app to
 | ||
|  | 		   give up? */ | ||
|  | 		error = -EINTR; | ||
|  | 		goto exit; | ||
|  | 	    }  | ||
|  | 	    if ( (req->uc_flags & REQ_READ) && signal_pending(current) ) { | ||
|  | 		    /* interrupted after Venus did its read, send signal */ | ||
|  | 		    union inputArgs *sig_inputArgs; | ||
|  | 		    struct upc_req *sig_req; | ||
|  | 		     | ||
|  | 		    list_del(&(req->uc_chain)); | ||
|  | 		    error = -ENOMEM; | ||
|  | 		    sig_req = upc_alloc(); | ||
|  | 		    if (!sig_req) goto exit; | ||
|  | 
 | ||
|  | 		    CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr)); | ||
|  | 		    if (!sig_req->uc_data) { | ||
|  | 			upc_free(sig_req); | ||
|  | 			goto exit; | ||
|  | 		    } | ||
|  | 		     | ||
|  | 		    error = -EINTR; | ||
|  | 		    sig_inputArgs = (union inputArgs *)sig_req->uc_data; | ||
|  | 		    sig_inputArgs->ih.opcode = CODA_SIGNAL; | ||
|  | 		    sig_inputArgs->ih.unique = req->uc_unique; | ||
|  | 		     | ||
|  | 		    sig_req->uc_flags = REQ_ASYNC; | ||
|  | 		    sig_req->uc_opcode = sig_inputArgs->ih.opcode; | ||
|  | 		    sig_req->uc_unique = sig_inputArgs->ih.unique; | ||
|  | 		    sig_req->uc_inSize = sizeof(struct coda_in_hdr); | ||
|  | 		    sig_req->uc_outSize = sizeof(struct coda_in_hdr); | ||
|  | 		     | ||
|  | 		    /* insert at head of queue! */ | ||
|  | 		    list_add(&(sig_req->uc_chain), &vcommp->vc_pending); | ||
|  | 		    wake_up_interruptible(&vcommp->vc_waitq); | ||
|  | 	    } else { | ||
|  | 		    printk("Coda: Strange interruption..\n"); | ||
|  | 		    error = -EINTR; | ||
|  | 	    } | ||
|  | 	} else {	/* If venus died i.e. !VC_OPEN(vcommp) */ | ||
|  | 	        printk("coda_upcall: Venus dead on (op,un) (%d.%d) flags %d\n", | ||
|  | 		       req->uc_opcode, req->uc_unique, req->uc_flags); | ||
|  | 		error = -ENODEV; | ||
|  | 	} | ||
|  | 
 | ||
|  |  exit: | ||
|  | 	upc_free(req); | ||
|  | 	return error; | ||
|  | } | ||
|  | 
 | ||
|  | /*  
 | ||
|  |     The statements below are part of the Coda opportunistic | ||
|  |     programming -- taken from the Mach/BSD kernel code for Coda.  | ||
|  |     You don't get correct semantics by stating what needs to be | ||
|  |     done without guaranteeing the invariants needed for it to happen. | ||
|  |     When will be have time to find out what exactly is going on?  (pjb) | ||
|  | */ | ||
|  | 
 | ||
|  | 
 | ||
|  | /* 
 | ||
|  |  * There are 7 cases where cache invalidations occur.  The semantics | ||
|  |  *  of each is listed here: | ||
|  |  * | ||
|  |  * CODA_FLUSH     -- flush all entries from the name cache and the cnode cache. | ||
|  |  * CODA_PURGEUSER -- flush all entries from the name cache for a specific user | ||
|  |  *                  This call is a result of token expiration. | ||
|  |  * | ||
|  |  * The next arise as the result of callbacks on a file or directory. | ||
|  |  * CODA_ZAPFILE   -- flush the cached attributes for a file. | ||
|  | 
 | ||
|  |  * CODA_ZAPDIR    -- flush the attributes for the dir and | ||
|  |  *                  force a new lookup for all the children | ||
|  |                     of this dir. | ||
|  | 
 | ||
|  |  * | ||
|  |  * The next is a result of Venus detecting an inconsistent file. | ||
|  |  * CODA_PURGEFID  -- flush the attribute for the file | ||
|  |  *                  purge it and its children from the dcache | ||
|  |  * | ||
|  |  * The last  allows Venus to replace local fids with global ones | ||
|  |  * during reintegration. | ||
|  |  * | ||
|  |  * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */ | ||
|  | 
 | ||
|  | int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) | ||
|  | { | ||
|  | 	/* Handle invalidation requests. */ | ||
|  |           if ( !sb || !sb->s_root || !sb->s_root->d_inode) | ||
|  | 		  return 0;  | ||
|  | 
 | ||
|  | 	  switch (opcode) { | ||
|  | 
 | ||
|  | 	  case CODA_FLUSH : { | ||
|  | 		   coda_cache_clear_all(sb); | ||
|  | 		   shrink_dcache_sb(sb); | ||
|  | 		   coda_flag_inode(sb->s_root->d_inode, C_FLUSH); | ||
|  | 		   return(0); | ||
|  | 	  } | ||
|  | 
 | ||
|  | 	  case CODA_PURGEUSER : { | ||
|  | 		   coda_cache_clear_all(sb); | ||
|  | 		   return(0); | ||
|  | 	  } | ||
|  | 
 | ||
|  | 	  case CODA_ZAPDIR : { | ||
|  | 	          struct inode *inode; | ||
|  | 		  struct CodaFid *fid = &out->coda_zapdir.CodaFid; | ||
|  | 
 | ||
|  | 		  inode = coda_fid_to_inode(fid, sb); | ||
|  | 		  if (inode) { | ||
|  | 			  coda_flag_inode_children(inode, C_PURGE); | ||
|  | 	                  coda_flag_inode(inode, C_VATTR); | ||
|  | 			  iput(inode); | ||
|  | 		  } | ||
|  | 		   | ||
|  | 		  return(0); | ||
|  | 	  } | ||
|  | 
 | ||
|  | 	  case CODA_ZAPFILE : { | ||
|  | 	          struct inode *inode; | ||
|  | 		  struct CodaFid *fid = &out->coda_zapfile.CodaFid; | ||
|  | 		  inode = coda_fid_to_inode(fid, sb); | ||
|  | 		  if ( inode ) { | ||
|  | 	                  coda_flag_inode(inode, C_VATTR); | ||
|  | 			  iput(inode); | ||
|  | 		  } | ||
|  | 		  return 0; | ||
|  | 	  } | ||
|  | 
 | ||
|  | 	  case CODA_PURGEFID : { | ||
|  | 	          struct inode *inode; | ||
|  | 		  struct CodaFid *fid = &out->coda_purgefid.CodaFid; | ||
|  | 		  inode = coda_fid_to_inode(fid, sb); | ||
|  | 		  if ( inode ) {  | ||
|  | 			coda_flag_inode_children(inode, C_PURGE); | ||
|  | 
 | ||
|  | 			/* catch the dentries later if some are still busy */ | ||
|  | 			coda_flag_inode(inode, C_PURGE); | ||
|  | 			d_prune_aliases(inode); | ||
|  | 
 | ||
|  | 			iput(inode); | ||
|  | 		  } | ||
|  | 		  return 0; | ||
|  | 	  } | ||
|  | 
 | ||
|  | 	  case CODA_REPLACE : { | ||
|  | 	          struct inode *inode; | ||
|  | 		  struct CodaFid *oldfid = &out->coda_replace.OldFid; | ||
|  | 		  struct CodaFid *newfid = &out->coda_replace.NewFid; | ||
|  | 		  inode = coda_fid_to_inode(oldfid, sb); | ||
|  | 		  if ( inode ) {  | ||
|  | 			  coda_replace_fid(inode, oldfid, newfid); | ||
|  | 			  iput(inode); | ||
|  | 		  } | ||
|  | 		  return 0; | ||
|  | 	  } | ||
|  | 	  } | ||
|  | 	  return 0; | ||
|  | } | ||
|  | 
 |