mirror of
				https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-stable.git
				synced 2025-11-04 07:44:51 +10:00 
			
		
		
		
	This patch converts list_add(A, B.prev) to list_add_tail(A, &B) for readability. Acked-by: Karsten Keil <kkeil@suse.de> Cc: Jan Harkes <jaharkes@cs.cmu.edu> Acked-by: Jan Kara <jack@suse.cz> AOLed-by: David Woodhouse <dwmw2@infradead.org> Cc: Sridhar Samudrala <sri@us.ibm.com> Signed-off-by: Akinobu Mita <mita@miraclelinux.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
		
			
				
	
	
		
			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 dentry *dentry, 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(dentry->d_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_tail(&(req->uc_chain), &vcommp->vc_pending);
 | 
						|
        
 | 
						|
	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;
 | 
						|
}
 | 
						|
 |