#ifndef _BLURB_
#define _BLURB_
/*

            Coda: an Experimental Distributed File System
                             Release 3.1

          Copyright (c) 1987-1995 Carnegie Mellon University
                         All Rights Reserved

Permission  to  use, copy, modify and distribute this software and its
documentation is hereby granted,  provided  that  both  the  copyright
notice  and  this  permission  notice  appear  in  all  copies  of the
software, derivative works or  modified  versions,  and  any  portions
thereof, and that both notices appear in supporting documentation, and
that credit is given to Carnegie Mellon University  in  all  documents
and publicity pertaining to direct or indirect use of this code or its
derivatives.

CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS  KNOWN  TO  HAVE  BUGS,
SOME  OF  WHICH MAY HAVE SERIOUS CONSEQUENCES.  CARNEGIE MELLON ALLOWS
FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.   CARNEGIE  MELLON
DISCLAIMS  ANY  LIABILITY  OF  ANY  KIND  FOR  ANY  DAMAGES WHATSOEVER
RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE  OR  OF
ANY DERIVATIVE WORK.

Carnegie  Mellon  encourages  users  of  this  software  to return any
improvements or extensions that  they  make,  and  to  grant  Carnegie
Mellon the rights to redistribute these changes without encumbrance.
*/

static char *rcsid = "$Header: cfs_vfsops.c,v 3.3.2.1 95/10/11 10:03:35 raiff Exp $";
#endif /*_BLURB_*/

/* 
 * Mach Operating System
 * Copyright (c) 1989 Carnegie-Mellon University
 * All rights reserved.  The CMU software License Agreement specifies
 * the terms and conditions for use and redistribution.
 */

/*
 * This code was written for the Coda file system at Carnegie Mellon University.
 * Contributers include David Steere, James Kistler, and M. Satyanarayanan.
 */

/*
 * HISTORY
 * $Log:	cfs_vfsops.c,v $
 * Revision 3.3.2.1  95/10/11  10:03:35  raiff
 * Branch for release beta-11Oct1995_34901
 * 
 * Revision 3.3  95/10/09  19:25:01  satya
 * Reblurbed with new CMU and IBM notices for SOSP-15 CD-ROM
 * 
 * Revision 3.2  95/10/06  16:36:20  dcs
 * Added printfs to catch a bug.
 * 
 * Revision 3.1  95/06/08  16:03:22  satya
 * *** empty log message ***
 * 
 * Revision 3.1.4.1  95/05/11  11:13:55  raiff
 * Branch for release beta-11May1995_36561
 * 
 * Revision 3.1  95/03/04  19:08:01  bnoble
 * Bump to major revision 3 to prepare for NetBSD port
 * 
 * Revision 2.4  1995/02/17  16:25:22  dcs
 * These versions represent several changes:
 * 1. Allow venus to restart even if outstanding references exist.
 * 2. Have only one ctlvp per client, as opposed to one per mounted cfs device.d
 * 3. Allow ody_expand to return many members, not just one.
 *
 * Revision 2.3  94/10/14  09:58:21  dcs
 * Made changes 'cause sun4s have braindead compilers
 * 
 * Revision 2.2  94/10/12  16:46:33  dcs
 * Cleaned kernel/venus interface by removing XDR junk, plus
 * so cleanup to allow this code to be more easily ported.
 * 
 * Revision 1.3  93/05/28  16:24:29  bnoble
 * *** empty log message ***
 * 
 * Revision 1.2  92/10/27  17:58:24  lily
 * merge kernel/latest and alpha/src/cfs
 * 
 * Revision 2.3  92/09/30  14:16:32  mja
 * 	Added call to cfs_flush to cfs_unmount.
 * 	[90/12/15            dcs]
 * 
 * 	Added contributors blurb.
 * 	[90/12/13            jjk]
 * 
 * Revision 2.2  90/07/05  11:26:40  mrt
 * 	Created for the Coda File System.
 * 	[90/05/23            dcs]
 * 
 * Revision 1.3  90/05/31  17:01:42  dcs
 * Prepare for merge with facilities kernel.
 * 
 * 
 */ 

#include <mach_cfs.h>
#if	NMACH_CFS

#include <sys/param.h>
#include <sys/conf.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <vfs/vfs.h>
#include <vfs/vnode.h>
#include <cfs/cfs.h>
#include <cfs/cnode.h>
#include <cfs/cfs_opstats.h>

int cfsdebug = 0;
struct vnode *makecfsnode();

/* structure to keep statistics of internally generated/satisfied calls */

struct cfs_op_stats cfs_vfsopstats[CFS_VFSOPS_SIZE];

#define MARK_ENTRY(op) (cfs_vfsopstats[op].entries++)
#define MARK_INT_SAT(op) (cfs_vfsopstats[op].sat_intrn++)
#define MARK_INT_FAIL(op) (cfs_vfsopstats[op].unsat_intrn++)
#define MRAK_INT_GEN(op) (cfs_vfsopstats[op].gen_intrn++)

/*
 * cfs vfs operations.
 */
int cfs_mount();
int cfs_unmount();
int cfs_root();
int cfs_statfs();
int cfs_sync();
int cfs_vget();
extern int cfs_badop();
extern int cfsnc_initialized;     /* Set if cache has been initialized */

extern struct cdevsw cdevsw[];    /* For sanity check in cfs_mount */
extern int vcopen();

struct vfsops cfs_vfsops = {
	cfs_mount,
	cfs_unmount,
	cfs_root,
	cfs_statfs,
	cfs_sync,
	cfs_vget
};

print_cred(cred)
	struct ucred *cred;
{
	int i;

	printf("ref %d uid %d ruid %d gid %d rgid %d pag %d\n",
		cred->id_ref,cred->id_uid,cred->id_ruid,
		cred->id_gid,cred->id_rgid,cred->id_pag);

	for (i=0; i < 16; i++)
		printf("%d groups %d ",i,cred->id_groups[i]);
	printf("\n");
}

cfs_vfsopstats_init()
{
	register int i;
	
	for (i=0;i<CFS_VFSOPS_SIZE;i++) {
		cfs_vfsopstats[i].opcode = i;
		cfs_vfsopstats[i].entries = 0;
		cfs_vfsopstats[i].sat_intrn = 0;
		cfs_vfsopstats[i].unsat_intrn = 0;
		cfs_vfsopstats[i].gen_intrn = 0;
	}
	
	return 0;
}
	

/*
 * cfs mount vfsop
 * Set up mount info record and attach it to vfs struct.
 */
/*ARGSUSED*/
cfs_mount(vfsp, path, data)
	struct vfs *vfsp;
	char *path;
	caddr_t data;
{
	struct vnode *dvp;
	dev_t dev;
	struct cfs_mntinfo *mi;
	struct vnode *rootvp;
	ViceFid rootfid;
	int error;

	CFSDEBUG(CFS_ROOT, printf("cfs_mount(0x%x, %s, 0x%x)\n", vfsp, path, data););
	
	if (!cfsnc_initialized) {
		cfs_vfsopstats_init();
		cfs_vnodeopstats_init();
		cfsnc_init();
	}

	MARK_ENTRY(CFS_MOUNT_STATS);
	if (CFS_MOUNTED(vfsp)) {
		MARK_INT_FAIL(CFS_MOUNT_STATS);
		return(EBUSY);
	}

	/* Validate mount device.  Similar to getmdev(). */
	error = lookupname(data, UIO_USERSPACE, FOLLOW_LINK,
			   (struct vnode **)0, &dvp);
	if (error) {
		MARK_INT_FAIL(CFS_MOUNT_STATS);
		return (error);
	}
	if ((dvp->v_mode & VFMT) != VCHR) {
		MARK_INT_FAIL(CFS_MOUNT_STATS);
		VN_RELE(dvp);
		return(ENXIO);
	}
	dev = dvp->v_rdev;
	VN_RELE(dvp);
	if (major(dev) >= nchrdev || major(dev) < 0) {
		MARK_INT_FAIL(CFS_MOUNT_STATS);
		return(ENXIO);
	}

	/*
 	 * See if the device table matches our expectations.
	 */
	if (cdevsw[major(dev)].d_open != vcopen) {
		MARK_INT_FAIL(CFS_MOUNT_STATS);
		return(ENXIO);
	}

	if (minor(dev) >= NMACH_CFS || minor(dev) < 0) {
		MARK_INT_FAIL(CFS_MOUNT_STATS);
		return(ENXIO);
	}

	/*
	 * Initialize the mount record and link it to the vfs struct
	 */
	mi = &cfs_mnttbl[minor(dev)];

	if (!VC_OPEN(&mi->mi_vcomm)) {
	    MARK_INT_FAIL(CFS_MOUNT_STATS);
	    return(ENODEV);
	}
	
	mi->mi_refct = 0;

	/* No initialization (here) of mi_vcomm! */
	vfsp->vfs_data = (caddr_t)mi;
	vfsp->vfs_fsid.val[0] = 0;
	vfsp->vfs_fsid.val[1] = MOUNT_CFS;
	mi->mi_vfschain.vfsp = vfsp;
	mi->mi_vfschain.next = NULL;

	/*
	 * Make a root vnode to placate the Vnode interface, but don't
	 * actually make the CFS_ROOT call to venus until the first call
	 * to cfs_root in case a server is down while venus is starting.
	 */
	rootfid.Volume = 0;
	rootfid.Vnode = 0;
	rootfid.Unique = 0;
	rootvp = makecfsnode(&rootfid, vfsp, VDIR);
	rootvp->v_flag |= VROOT;

	/* Add vfs and rootvp to chain of vfs hanging off mntinfo */
	ADD_VFS_TO_MNTINFO(mi, vfsp, rootvp);

	/* set filesystem block size */
	vfsp->vfs_bsize	= 8192;	    /* XXX -JJK */

	/* error is currently guaranteed to be zero, but in case some
	   code changes... */
	if (error)
		MARK_INT_FAIL(CFS_MOUNT_STATS);
	else
		MARK_INT_SAT(CFS_MOUNT_STATS);

	return(error);
}

cfs_unmount(vfsp)
	struct vfs *vfsp;
{
        struct cfs_mntinfo *mi = vftomi(vfsp);
	struct ody_mntinfo *op, *pre;
	int active, error = 0;

	CFSDEBUG(CFS_ROOT, printf("cfs_unmount(0x%x)\n", vfsp););
	
	MARK_ENTRY(CFS_UMOUNT_STATS);
	if (!CFS_MOUNTED(vfsp)) {
	    error = EINVAL;
	}
	else if (mi->mi_refct == 0) {
	    /* Someone already unmounted this device. */
	    printf("Ackk! unmount called on ody-style vfsp!\n");
	    error = EINVAL;
	}
	

	for (pre = NULL, op = &mi->mi_vfschain; error == 0 && op; pre = op, op = op->next) {
	    if (op->vfsp == vfsp) {	/* We found the victim */
		if (!IS_DYING(VTOC(op->rootvp))) {
		    error = EBUSY;	/* Venus is still running */
		    break;
		}

		active = cfs_kill(vfsp);

		if (active > 1) {	/* 1 is for rootvp */
		    error = EBUSY;

		    /* HACK! Just for fun, if venus is dying/dead, let's fake the 
		     * unmount to allow a new venus to start. All operations on 
		     * outstanding cnodes will effectively fail, but shouldn't crash 
		     * either the kernel or venus. One to blaim: -- DCS 11/29/94
		     */
		    FAKE_UNMOUNT(vfsp);
		    printf("CFS_UNMOUNT: faking unmount, vfsp %x active == %d\n", vfsp, active);
		} else {
		    /* Do nothing, caller of cfs_unmount will do the unmounting */
		}

		VN_RELE(op->rootvp);

		/* I'm going to take this out to allow lookups to go through. I'm
		 * not sure it's important anyway. -- DCS 2/2/94
		 */
		/* vfsp->vfs_data = NULL; */

		/* Remove the vfs from our list of valid coda-like vfs */
		if (pre) {
		    pre->next = op->next;
		    kfree(op, sizeof(struct ody_mntinfo));
		} else
		    if (mi->mi_vfschain.next) {
			mi->mi_vfschain.vfsp = (mi->mi_vfschain.next)->vfsp;
			mi->mi_vfschain.rootvp = (mi->mi_vfschain.next)->rootvp;
			mi->mi_vfschain.next = (mi->mi_vfschain.next)->next;
		    } else {
			/* No more vfsp's to hold onto */
			mi->mi_vfschain.vfsp = NULL;
			mi->mi_vfschain.rootvp = NULL;
		    }

		break;
	    }
	} 

	if (error == 0 && op == NULL) 
	    error = EINVAL;

	if (error) {
	    MARK_INT_FAIL(CFS_UMOUNT_STATS);
	    printf("cfs_unmount exiting with %d\n", error);
	} else
	    MARK_INT_SAT(CFS_UMOUNT_STATS);
	return error;
}

/*
 * find root of cfs
 */
int
cfs_root(vfsp, vpp)
	struct vfs *vfsp;
	struct vnode **vpp;
{
    struct cfs_mntinfo *mi = vftomi(vfsp);
    struct ody_mntinfo *op;
    struct vnode *rvp, **result;
    struct inputArgs in;
    struct outputArgs *out = (struct outputArgs *)&in;
    int error, size;

    CFSDEBUG(CFS_ROOT, printf("cfs_root(0x%x, 0x%x)\n", vfsp, vpp););
    
    MARK_ENTRY(CFS_ROOT_STATS);
    result = NULL;
    
    for (op = &mi->mi_vfschain; op; op = op->next) {
	/* Look for a match between vfsp and op->vfsp */
	if (vfsp == op->vfsp) {
	    if ((VTOC(op->rootvp)->c_fid.Volume != 0) ||
		(VTOC(op->rootvp)->c_fid.Vnode != 0) ||
		(VTOC(op->rootvp)->c_fid.Unique != 0))
		{ /* Found valid root. */
		    *vpp = op->rootvp;
		    VN_HOLD(*vpp);
		    MARK_INT_SAT(CFS_ROOT_STATS);
		    return(0);
		}
	    else	/* Found the vfs, but the vnode not inited yet. */
		break;
	}
    }

    if (op == NULL) {
	/* Huh, didn't find the vfsp. Noone called cfs_mount? Should we panic? */
	return (EINVAL);
    }
    
    /* Didn't find the root, try sending up to a warden for it. */
    INIT_IN(&in, CFS_ROOT, u.u_cred);

    size = sizeof(struct inputArgs);
    error = cfscall(vftomi(vfsp), VC_IN_NO_DATA, &size, (char *)&in);
    if (!error)
	error = out->result;

    if (!error) {
	/*
	 * Save the new rootfid in the cnode, and rehash the cnode into the
	 * cnode hash with the new fid key.
	 */
	cfs_unsave(VTOC(op->rootvp));
	VTOC(op->rootvp)->c_fid = out->d.cfs_root.VFid;
	cfs_save(VTOC(op->rootvp));

	*vpp = op->rootvp;
	VN_HOLD(*vpp);
	MARK_INT_SAT(CFS_ROOT_STATS);
	return(0);
    } else if (error == ENODEV) {
	/* Gross hack here! */
	/*
	 * If Venus fails to respond to the CFS_ROOT call, cfscall returns
	 * ENODEV. Return the uninitialized root vnode to allow vfs
	 * operations such as unmount to continue. Without this hack,
	 * there is no way to do an unmount if Venus dies before a 
	 * successful CFS_ROOT call is done. All vnode operations 
	 * will fail.
	 */
	*vpp = op->rootvp;
	VN_HOLD(*vpp);
	MARK_INT_FAIL(CFS_ROOT_STATS);
	return(0);
    } else {
	CFSDEBUG( CFS_ROOT, printf("error %d in CFS_ROOT\n", error); );
	MARK_INT_FAIL(CFS_ROOT_STATS);
		
	return(error);
    }

    /* Shouldn't reach here */
}

/* Locate a type-specific FS based on a name (tome) */
int findTome(tome, data, coveredvp, vpp)
     char *tome; char *data; struct vnode *coveredvp; struct vnode **vpp;
{
    struct vnode *rootvp;
    char buf[VC_INSIZE(ody_mount_in) + CFS_MAXPATHLEN];
    struct inputArgs *in;
    struct outputArgs *out;
    struct vfs *vfsp;
    int i, size, error;

    CFSDEBUG(CFS_ROOT, printf("findTome(%s, %s, 0x%x, 0x%x)\n", tome, data, coveredvp, vpp););
    
    *vpp = 0;
    
    if (!cfsnc_initialized) {
	cfs_vfsopstats_init();
	cfs_vnodeopstats_init();
	cfsnc_init();
    }
    
    for (i = 0; i < NMACH_CFS; i++) {
	if (cfs_mnttbl[i].mi_name) {
	    if (strcmp(cfs_mnttbl[i].mi_name, tome) == 0) {
		CFSDEBUG( CFS_ROOT, printf("Mounting a tome for %s.%s\n", tome, data); )
		/* We need to do the work of both a mount and a root call here.
		 * Since these funny fs's are never explicity "mounted", but 
		 * we want to treat them as if they are.
		 */

		/* Setup enough of vfs to allow ODY_MOUNT to go through */
		ZALLOC(vfs_vfs_zone, vfsp, struct vfs *);
		VFS_INIT(vfsp, vfssw[MOUNT_CFS], (caddr_t)0);

		/* Initialize the cfs part of the vfs */
		vfsp->vfs_data = (caddr_t)&cfs_mnttbl[i];
		vfsp->vfs_fsid.val[0] = 0;
		vfsp->vfs_fsid.val[1] = MOUNT_CFS;

		/* set filesystem block size */
		vfsp->vfs_bsize	= 8192;	    /* XXX -JJK */

		/* Do the work to mount this thing */
		FAKE_MOUNT(vfsp, coveredvp);
		
		/* Get a root vnode for the "volume" (like cfs_root) */
		in = (struct inputArgs *)buf;
		out = (struct outputArgs *)buf;

		INIT_IN(in, ODY_MOUNT, u.u_cred);
		in->d.ody_mount.name = (char*)VC_INSIZE(ody_mount_in);
		size = strlen(data) + 1;
		strncpy((char*)in + (int)in->d.ody_mount.name, data, size);

		size +=  VC_INSIZE(ody_mount_in);
		error = cfscall(vftomi(vfsp), size, &size, (char *)in);

		if (error || out->result) {
		    CFSDEBUG(CFS_ROOT, printf("error %d in ODY_MOUNT\n",
					      error? error : out->result); )
		    return( error? error : out->result );
		}

		CFSDEBUG( CFS_ROOT, printf("ODY_MOUNT returns %x.%x.%x\n",
					  out->d.ody_mount.VFid.Volume,
					  out->d.ody_mount.VFid.Vnode,
					  out->d.ody_mount.VFid.Unique); )
		    
		rootvp = makecfsnode(&out->d.ody_mount.VFid, vfsp, VDIR);
		rootvp->v_flag |= VROOT;
		*vpp = rootvp;
		VN_HOLD(*vpp);

		/* Add this to the list of mounted things. */
		ADD_VFS_TO_MNTINFO(vftomi(vfsp), vfsp, rootvp);

		return (0);
	    }	
	}
    }
    return ENOENT;	/* Indicate that no matching warden was found */
}

/*
 * Get file system statistics.
 */
int
cfs_statfs(vfsp, sbp)
register struct vfs *vfsp;
struct statfs *sbp;
{
	MARK_ENTRY(CFS_STATFS_STATS);
	sbp->f_type = 0;
	sbp->f_bsize = 8192;	/* XXX -JJK */
	sbp->f_blocks = -1;
	sbp->f_bfree = -1;
	sbp->f_bavail = -1;
	sbp->f_files = -1;
	sbp->f_ffree = -1;
	bcopy((caddr_t)&vfsp->vfs_fsid, (caddr_t)&sbp->f_fsid, sizeof (fsid_t));
	
	MARK_INT_SAT(CFS_STATFS_STATS);
	return(0);
}

/*
 * Flush any pending I/O.
 */
int
cfs_sync(vfsp)
	struct vfs *vfsp;
{
	MARK_ENTRY(CFS_SYNC_STATS);
	MARK_INT_SAT(CFS_SYNC_STATS);
	return(0);
}


int
cfs_vget(vfsp, vpp, fidp)
	struct vfs *vfsp;
	struct vnode **vpp;
	struct fid *fidp;
{
	struct cfid *cfid = (struct cfid *)fidp;
	struct cnode *cp = 0;
	struct inputArgs in;
	struct outputArgs *out = (struct outputArgs *)&in; /* Reuse space */
 	int error, size;

	MARK_ENTRY(CFS_VGET_STATS);
	/* Check for vget of control object. */
	if (IS_CTL_FID(&cfid->cfid_fid)) {
	    *vpp = CFS_CTL_VP;
	    VN_HOLD(CFS_CTL_VP);
	    MARK_INT_SAT(CFS_VGET_STATS);
	    return(0);
	}

	INIT_IN(&in, CFS_VGET, u.u_cred);
	in.d.cfs_vget.VFid = cfid->cfid_fid;

	size = VC_INSIZE(cfs_vget_in);
	error = cfscall(vftomi(vfsp), size, &size, (char *)&in);

	if (!error) 
	    error = out->result;

	if (error) {
	    CFSDEBUG(CFS_VGET, printf("vget error %d\n",error);)
	      *vpp = (struct vnode *)0;
	}
	else {
	    CFSDEBUG(CFS_VGET, 
		     printf("vget: vol %u vno %d uni %d type %d result %d\n",
			    out->d.cfs_vget.VFid.Volume,
			    out->d.cfs_vget.VFid.Vnode,
			    out->d.cfs_vget.VFid.Unique,
			    out->d.cfs_vget.vtype,
			    out->result); )

	      *vpp = makecfsnode(&out->d.cfs_vget.VFid, vfsp,out->d.cfs_vget.vtype);
	}
	return(error);
}

/*
 * To allow for greater ease of use, some vnodes may be orphaned when Venus dies.
 * Certain operations should still be allowed to go through, but without propagating
 * ophan-ness.  So this function will get a new vnode for the file from the current
 * run of Venus.
 */
 
int getNewVnode(cp)
     struct cnode **cp;
{
    struct ody_mntinfo *op;
    struct cfid cfid;
    struct cfs_mntinfo *mi = vftomi(CTOV(*cp)->v_vfsp);
    
    for (op = &mi->mi_vfschain; op; op = op->next) {
	/* Look for a match between vfsp and op->vfsp */
	if (CTOV(*cp)->v_vfsp == op->vfsp) {
	    break;
	}
    }

    if (op)
	return EINVAL;

    cfid.cfid_len = (short)sizeof(ViceFid);
    cfid.cfid_fid = (*cp)->c_fid;	/* Structure assignment. */

    /* We're guessing that if set, the 1st element on the list is a
     * valid vnode to use. If not, return ENODEV as venus is dead.
     */
    if (mi->mi_vfschain.vfsp == NULL)
	return ENODEV;
    
    return cfs_vget(mi->mi_vfschain.vfsp, cp, &cfid);
}
     
#endif	/* NMACH_CFS */



