Fix checkpatch warning WARNING: Use #include <linux/uaccess.h> instead of <asm/uaccess.h> Signed-off-by: Fabian Frederick <fabf@skynet.be> Cc: Jan Harkes <jaharkes@cs.cmu.edu> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
		
			
				
	
	
		
			882 lines
		
	
	
	
		
			23 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			882 lines
		
	
	
	
		
			23 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 <linux/signal.h>
 | 
						|
#include <linux/sched.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 <linux/slab.h>
 | 
						|
#include <linux/mutex.h>
 | 
						|
#include <linux/uaccess.h>
 | 
						|
#include <linux/vmalloc.h>
 | 
						|
#include <linux/vfs.h>
 | 
						|
 | 
						|
#include <linux/coda.h>
 | 
						|
#include <linux/coda_psdev.h>
 | 
						|
#include "coda_linux.h"
 | 
						|
#include "coda_cache.h"
 | 
						|
 | 
						|
#include "coda_int.h"
 | 
						|
 | 
						|
static int coda_upcall(struct venus_comm *vc, 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 = task_pid_nr_ns(current, &init_pid_ns);
 | 
						|
	inp->ih.pgid = task_pgrp_nr_ns(current, &init_pid_ns);
 | 
						|
	inp->ih.uid = from_kuid(&init_user_ns, current_fsuid());
 | 
						|
 | 
						|
	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_vcp(sb), insize, &outsize, inp);
 | 
						|
	if (!error)
 | 
						|
		*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_vcp(sb), insize, &outsize, inp);
 | 
						|
	if (!error)
 | 
						|
		*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_vcp(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_vcp(sb), insize, &outsize, inp);
 | 
						|
	if (!error) {
 | 
						|
		*resfid = outp->coda_lookup.VFid;
 | 
						|
		*type = outp->coda_lookup.vtype;
 | 
						|
	}
 | 
						|
 | 
						|
	CODA_FREE(inp, insize);
 | 
						|
	return error;
 | 
						|
}
 | 
						|
 | 
						|
int venus_close(struct super_block *sb, struct CodaFid *fid, int flags,
 | 
						|
		kuid_t uid)
 | 
						|
{
 | 
						|
	union inputArgs *inp;
 | 
						|
	union outputArgs *outp;
 | 
						|
	int insize, outsize, error;
 | 
						|
	
 | 
						|
	insize = SIZE(release);
 | 
						|
	UPARG(CODA_CLOSE);
 | 
						|
	
 | 
						|
	inp->ih.uid = from_kuid(&init_user_ns, uid);
 | 
						|
        inp->coda_close.VFid = *fid;
 | 
						|
        inp->coda_close.flags = flags;
 | 
						|
 | 
						|
	error = coda_upcall(coda_vcp(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_by_fd.VFid = *fid;
 | 
						|
	inp->coda_open_by_fd.flags = flags;
 | 
						|
 | 
						|
	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
 | 
						|
	if (!error)
 | 
						|
		*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_vcp(sb), insize, &outsize, inp);
 | 
						|
	if (!error) {
 | 
						|
		*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_vcp(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_vcp(sb), insize, &outsize, inp);
 | 
						|
	if (!error) {
 | 
						|
		*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_vcp(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_vcp(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_vcp(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_vcp(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_vcp(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_vcp(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_vcp(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_vcp(sb), SIZE(ioctl) + data->vi.in_size,
 | 
						|
			    &outsize, inp);
 | 
						|
 | 
						|
        if (error) {
 | 
						|
		pr_warn("%s: Venus returns: %d for %s\n",
 | 
						|
			__func__, 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_vcp(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;
 | 
						|
	}
 | 
						|
 | 
						|
        CODA_FREE(inp, insize);
 | 
						|
        return error;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * coda_upcall and coda_downcall routines.
 | 
						|
 */
 | 
						|
static void coda_block_signals(sigset_t *old)
 | 
						|
{
 | 
						|
	spin_lock_irq(¤t->sighand->siglock);
 | 
						|
	*old = current->blocked;
 | 
						|
 | 
						|
	sigfillset(¤t->blocked);
 | 
						|
	sigdelset(¤t->blocked, SIGKILL);
 | 
						|
	sigdelset(¤t->blocked, SIGSTOP);
 | 
						|
	sigdelset(¤t->blocked, SIGINT);
 | 
						|
 | 
						|
	recalc_sigpending();
 | 
						|
	spin_unlock_irq(¤t->sighand->siglock);
 | 
						|
}
 | 
						|
 | 
						|
static void coda_unblock_signals(sigset_t *old)
 | 
						|
{
 | 
						|
	spin_lock_irq(¤t->sighand->siglock);
 | 
						|
	current->blocked = *old;
 | 
						|
	recalc_sigpending();
 | 
						|
	spin_unlock_irq(¤t->sighand->siglock);
 | 
						|
}
 | 
						|
 | 
						|
/* Don't allow signals to interrupt the following upcalls before venus
 | 
						|
 * has seen them,
 | 
						|
 * - CODA_CLOSE or CODA_RELEASE upcall  (to avoid reference count problems)
 | 
						|
 * - CODA_STORE				(to avoid data loss)
 | 
						|
 */
 | 
						|
#define CODA_INTERRUPTIBLE(r) (!coda_hard && \
 | 
						|
			       (((r)->uc_opcode != CODA_CLOSE && \
 | 
						|
				 (r)->uc_opcode != CODA_STORE && \
 | 
						|
				 (r)->uc_opcode != CODA_RELEASE) || \
 | 
						|
				(r)->uc_flags & CODA_REQ_READ))
 | 
						|
 | 
						|
static inline void coda_waitfor_upcall(struct venus_comm *vcp,
 | 
						|
				       struct upc_req *req)
 | 
						|
{
 | 
						|
	DECLARE_WAITQUEUE(wait, current);
 | 
						|
	unsigned long timeout = jiffies + coda_timeout * HZ;
 | 
						|
	sigset_t old;
 | 
						|
	int blocked;
 | 
						|
 | 
						|
	coda_block_signals(&old);
 | 
						|
	blocked = 1;
 | 
						|
 | 
						|
	add_wait_queue(&req->uc_sleep, &wait);
 | 
						|
	for (;;) {
 | 
						|
		if (CODA_INTERRUPTIBLE(req))
 | 
						|
			set_current_state(TASK_INTERRUPTIBLE);
 | 
						|
		else
 | 
						|
			set_current_state(TASK_UNINTERRUPTIBLE);
 | 
						|
 | 
						|
		/* got a reply */
 | 
						|
		if (req->uc_flags & (CODA_REQ_WRITE | CODA_REQ_ABORT))
 | 
						|
			break;
 | 
						|
 | 
						|
		if (blocked && time_after(jiffies, timeout) &&
 | 
						|
		    CODA_INTERRUPTIBLE(req))
 | 
						|
		{
 | 
						|
			coda_unblock_signals(&old);
 | 
						|
			blocked = 0;
 | 
						|
		}
 | 
						|
 | 
						|
		if (signal_pending(current)) {
 | 
						|
			list_del(&req->uc_chain);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		mutex_unlock(&vcp->vc_mutex);
 | 
						|
		if (blocked)
 | 
						|
			schedule_timeout(HZ);
 | 
						|
		else
 | 
						|
			schedule();
 | 
						|
		mutex_lock(&vcp->vc_mutex);
 | 
						|
	}
 | 
						|
	if (blocked)
 | 
						|
		coda_unblock_signals(&old);
 | 
						|
 | 
						|
	remove_wait_queue(&req->uc_sleep, &wait);
 | 
						|
	set_current_state(TASK_RUNNING);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * 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 venus_comm *vcp,
 | 
						|
		       int inSize, int *outSize,
 | 
						|
		       union inputArgs *buffer)
 | 
						|
{
 | 
						|
	union outputArgs *out;
 | 
						|
	union inputArgs *sig_inputArgs;
 | 
						|
	struct upc_req *req = NULL, *sig_req;
 | 
						|
	int error;
 | 
						|
 | 
						|
	mutex_lock(&vcp->vc_mutex);
 | 
						|
 | 
						|
	if (!vcp->vc_inuse) {
 | 
						|
		pr_notice("Venus dead, not sending upcall\n");
 | 
						|
		error = -ENXIO;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Format the request message. */
 | 
						|
	req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
 | 
						|
	if (!req) {
 | 
						|
		error = -ENOMEM;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	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 = ++vcp->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, &vcp->vc_pending);
 | 
						|
 | 
						|
	wake_up_interruptible(&vcp->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(vcp, req);
 | 
						|
 | 
						|
	/* Op went through, interrupt or not... */
 | 
						|
	if (req->uc_flags & CODA_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;
 | 
						|
	}
 | 
						|
 | 
						|
	error = -EINTR;
 | 
						|
	if ((req->uc_flags & CODA_REQ_ABORT) || !signal_pending(current)) {
 | 
						|
		pr_warn("Unexpected interruption.\n");
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Interrupted before venus read it. */
 | 
						|
	if (!(req->uc_flags & CODA_REQ_READ))
 | 
						|
		goto exit;
 | 
						|
 | 
						|
	/* Venus saw the upcall, make sure we can send interrupt signal */
 | 
						|
	if (!vcp->vc_inuse) {
 | 
						|
		pr_info("Venus dead, not sending signal.\n");
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	error = -ENOMEM;
 | 
						|
	sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
 | 
						|
	if (!sig_req) goto exit;
 | 
						|
 | 
						|
	CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
 | 
						|
	if (!sig_req->uc_data) {
 | 
						|
		kfree(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 = CODA_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), &vcp->vc_pending);
 | 
						|
	wake_up_interruptible(&vcp->vc_waitq);
 | 
						|
 | 
						|
exit:
 | 
						|
	kfree(req);
 | 
						|
	mutex_unlock(&vcp->vc_mutex);
 | 
						|
	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(struct venus_comm *vcp, int opcode, union outputArgs *out)
 | 
						|
{
 | 
						|
	struct inode *inode = NULL;
 | 
						|
	struct CodaFid *fid = NULL, *newfid;
 | 
						|
	struct super_block *sb;
 | 
						|
 | 
						|
	/* Handle invalidation requests. */
 | 
						|
	mutex_lock(&vcp->vc_mutex);
 | 
						|
	sb = vcp->vc_sb;
 | 
						|
	if (!sb || !sb->s_root)
 | 
						|
		goto unlock_out;
 | 
						|
 | 
						|
	switch (opcode) {
 | 
						|
	case CODA_FLUSH:
 | 
						|
		coda_cache_clear_all(sb);
 | 
						|
		shrink_dcache_sb(sb);
 | 
						|
		if (sb->s_root->d_inode)
 | 
						|
			coda_flag_inode(sb->s_root->d_inode, C_FLUSH);
 | 
						|
		break;
 | 
						|
 | 
						|
	case CODA_PURGEUSER:
 | 
						|
		coda_cache_clear_all(sb);
 | 
						|
		break;
 | 
						|
 | 
						|
	case CODA_ZAPDIR:
 | 
						|
		fid = &out->coda_zapdir.CodaFid;
 | 
						|
		break;
 | 
						|
 | 
						|
	case CODA_ZAPFILE:
 | 
						|
		fid = &out->coda_zapfile.CodaFid;
 | 
						|
		break;
 | 
						|
 | 
						|
	case CODA_PURGEFID:
 | 
						|
		fid = &out->coda_purgefid.CodaFid;
 | 
						|
		break;
 | 
						|
 | 
						|
	case CODA_REPLACE:
 | 
						|
		fid = &out->coda_replace.OldFid;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	if (fid)
 | 
						|
		inode = coda_fid_to_inode(fid, sb);
 | 
						|
 | 
						|
unlock_out:
 | 
						|
	mutex_unlock(&vcp->vc_mutex);
 | 
						|
 | 
						|
	if (!inode)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	switch (opcode) {
 | 
						|
	case CODA_ZAPDIR:
 | 
						|
		coda_flag_inode_children(inode, C_PURGE);
 | 
						|
		coda_flag_inode(inode, C_VATTR);
 | 
						|
		break;
 | 
						|
 | 
						|
	case CODA_ZAPFILE:
 | 
						|
		coda_flag_inode(inode, C_VATTR);
 | 
						|
		break;
 | 
						|
 | 
						|
	case CODA_PURGEFID:
 | 
						|
		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);
 | 
						|
		break;
 | 
						|
 | 
						|
	case CODA_REPLACE:
 | 
						|
		newfid = &out->coda_replace.NewFid;
 | 
						|
		coda_replace_fid(inode, fid, newfid);
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	iput(inode);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 |