static char rcsid[] = "$Header: dfs_nami.c,v 820.1 86/12/04 19:44:24 root Exp $";
static char sccsid[]="%W% %Y% %Q% %G%";

/************************************************************************
*									*
*				Copyright 1984				*
*			VALID LOGIC SYSTEMS INCORPORATED		*
*									*
*	This listing contains confidential proprietary information	*
*	which is not to be disclosed to unauthorized persons without	*
*	written consent of an officer of Valid Logic Systems 		*
*	Incorporated.							*
*									*
*	The copyright notice appearing above is included to provide	*
*	statutory protection in the event of unauthorized or 		*
*	unintentional public disclosure.				*
*									*
************************************************************************/

/*
 * DFS namei interface
 *
 *	Bakul Shah
 *
 * bvs 840313 -- original version
 * jht 841115,16,26-29 -- complete conversion to use prpc.
 * jht 841221 -- use descriptors to access the two inodes + dirent. pkt-fields.
 * jht 850213 -- add dfsPktHdr_t containing version/functionality info to pkts.
 * jht 850222 -- inode write-counts to prevent execution of ITEXT open for write
 * jht 850301 -- conversion to use new errno.h error-code mnemonics.
 * jht 850313 -- fix irele(0Xc000000), an "illegal" inode.
 * jht 850314 -- allow access to a BDEV inode, in prep. for BULKDATA,
 *		 and to circumvent u_error==EREMOTEBDEV msg.
 * jht 850401 -- fill out the gathering of statistics.
 * jht 850414 -- differentiate between no remote struct and one not locked.
 */

#include "../h/param.h"
#include "../h/ioctl.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"

#include "../vnet/vnet.h"
#include "../conn/conn.h"
#include "../rpc/rpc.h"
#include "../dfs/dfs.h"

#include "../h/inode.h"
#include "../h/socket.h"
#include "../h/kernel.h"
#include "../net/if.h"
#include "../h/mbuf.h"
#include "../h/nami.h"

/*
 * Namei related operations
 */
typedef struct dfsc_namei {	/* Pkt from client --> server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	caddr_t		startdir;	/* Start namei in this directory */
	int		uid;		/* UID of caller */
	int		gid;		/* GID of caller */
	int		flag; 		/* Passed through from namei()	 */
	int		follow;		/* Passed through from namei()	 */
	int		groups[NGROUPS];/* Groups we have permission for */
	/* Pathname follows the header */
} dfsc_namei_t;


typedef struct dfsr_namei {	/* Pkt from client <-- server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	int		error;		/* Error number or 0 on success	    */
	long		offset;		/* Offset in parent for a new entry */
	long		count;		/* Size of hole for a new entry	    */
	caddr_t 	ip;		/* Inode			    */
	caddr_t		parentIp;	/* Parent inode			    */
#ifdef	DFS_DATA_DESCRIPTORS
	desc_t		inode;		/* Descriptor for inode lookedup    */
	desc_t		parentinode;	/* Descriptor for parent inode      */
	desc_t		dent;		/* Descriptor for directory entry   */
	/* Both of the inodes and the directory entry follow this header */
#else	DFS_DATA_DESCRIPTORS
	struct inode	inode;		/* Contents of the inode lookedup   */
	struct inode	parentinode;	/* Contents of the parent	    */
	/* Directory entry  follows the header */
#endif	DFS_DATA_DESCRIPTORS
} dfsr_namei_t;

 int	sizeofDfsClientResultPkt = sizeof(dfsr_namei_t);
 extern	caddr_t	  m_field();

/*
 * Remote namei.
 * Pack the path, start-dir, flag, follow, uid, gid, group-set
 * in the call packet and make a remote call.
 * Result should be interpreted as follows.
 * u.u_error != 0 =>	return error.
 *
 *	u.u_dent points to component assoc'd with the error. -- BUG: True?
 *
 * u.u_error == 0 =>
 *
 *	u.u_dent points to last component
 *
 *	flag == LOOKUP =>	ip points to locked agent.
 *	flag == CREATE =>	ip NULL or a locked inode.
 *				u.u_pdir points to locked parent dir agent
 *				u.u_offset, u.u_count to empty dir slot.
 *	flag == DELETE =>	ip points to locked agent.
 *				u.u_pdir points to (locked) parent dir agent
 *				u.u_offset, u.u_count to entry dir slot.
 */
 struct inode *
dfsClient_namei(cp, flag, follow, conn, startdir)
	register char		* cp;
	register int		  flag;
	int			  follow;
	connection_t		* conn;
	struct inode		* startdir;
{
	register struct inode	* ip = NULL;
	register dfsc_namei_t	* call;
	register dfsr_namei_t	* result;
	register long		  size;
#ifdef	DFS_WCOUNT
	register int		  userFWRITE	= flag & DFS_NAMEI_FWRITE;

	flag	&= ~DFS_NAMEI_FWRITE;
#endif	DFS_WCOUNT

	dfs_incClient(namei);
	if (!dfs_enabled) {			/* DFS is not enabled */
		u.u_error = DFS_ENOTENABLED;
		return NULL;
	}
	if (ip->i_flag & IDEADSERVER) {		/* Server for agent is dead */
		u.u_error = DFS_ECRASHED;
		return NULL;
	}
#ifdef	DFS_ECYCLE_RETRY
	/*
	 * Client attempting server-side work
	 * is tantamount to a compound reference
	 * in the network pathname digraph.
	 *
	 * This is where we normally expect
	 * to intercept multiple-host reference anominalies.
	 */
	if (u.u_procp->p_rpcflags & RPCFLAG_AGENT) {
		/*
		 * u_dirp is NULL at this juncture,
		 * so we use it to pass back the (ROOTED) "hint"
		 * for the clientSide -- we're an AGENT/serverSide.
		 */
		*(u.u_dirp = (caddr_t) cp-1) = '/'; /* Fix dfs_root()'s sins */
		u.u_error  = DFS_EMULTIHOSTREF;
		DFS_DEBUG(10,("dfsClient_namei/I-0: DFS_EMULTIHOSTREF u_dirp=0x%X='%s'\n", u.u_dirp, u.u_dirp));
		return NULL;	/* ...to namei(), perhaps via dfs_root() */
	}
#endif	DFS_ECYCLE_RETRY

	/*
	 * Obtain a call packet.
	 */
	size = strlen(cp) + 1;
	call = DFSC_VPACKET(namei, size);
	if (!call)
		return NULL;
	/*
	 * Fill in the call packet
	 * with the pathname, etc.
	 */
	mtombuf(m_field(call, DFS_DATA(call)), cp, size);
	call->uid      = u.u_uid;
	call->gid      = u.u_gid;
#ifdef	DFS_WCOUNT
	call->flag     = flag|userFWRITE;
#else	DFS_WCOUNT
	call->flag     = flag;
#endif	DFS_WCOUNT
	call->follow   = follow;
	call->startdir = startdir ? startdir->i_rmt_ip : NULL;
	groupstombuf(m_field(call, call->groups), u.u_groups);

	/*
	 * Export the work to the server-side.
	 */
	result = DFS_CALL(conn,namei,call);
	if (!result)
		return NULL;
	u.u_error = result->error;
	/*
	 * Examine the results returned
	 * from the serverSide.
	 */
#ifdef	DFS_ECYCLE_RETRY
	if (result->error) {
		/*
		 * Check if we had a multiple host reference
		 * detected over on the serverSide.
		 *
		 * We already know that RPCFLAG_AGENT is NOT asserted.
		 * I.e., we are running on the clientSide.
		 */
		if (result->error == DFS_EMULTIHOSTREF) {
			/*
			 * The server-side dfsClient_namei() detected
			 * a cycle in the network pathname digraph.
			 *
			 * We pass the "hint"
			 * (that the serverSide dfsServer_namei() returned)
			 * using u_dirp to pass the address of the
			 * packet CONTAINING the "hint."
			 * That way we can DELAY releasing the pkt
			 * until we have safely consumed the "hint".
			 *
			 * Context:
			 * u_dirp, having already been "consumed" prior
			 * to this, usually points into the user's stack
			 * just beyond a command argument
			 * pathname; or some part of user stack
			 * or data space just past a pathname.
			 *
			 * We forget about the inode-s and dirent
			 * usually in the results pkt.
			 *
			 * Recycling the b_addr buffer should keep '(*func)()'
			 * used in namei() alright,
			 * provided the buffer is not in user-space.
			 */
			u.u_dirp = (caddr_t)result;
			DFS_DEBUG(10,("dfsClient_namei/I-3: DFS_EMULTIHOSTREF new u_dirp=0x%x='%s'\n", u.u_dirp, u.u_dirp));
			return	NULL; /* ...to namei() perhaps via dfs_root() */
		} else {
			/*
			 * Not something we know how to fix -- punt.
			 */
			goto free;
		}
	}
#else	DFS_ECYCLE_RETRY
	if (result->error)
		goto free;
#endif	DFS_ECYCLE_RETRY

#ifdef	DFS_DATA_DESCRIPTORS
	/*
	 * If we get a smaller inode-structure
	 * than we expected, we probably have a
	 * DFS version out-of-sync-ness.
	 */
	if (result->inode.d_length != sizeof(struct inode)) {
		dfs_badVersion(	NULL,	/* callPktp	*/
				result,	/* resultPktp	*/
				"namei",
				"dfsClient_namei/E-4: inode is wrong size: expected %d byte inode; got %d\n",
				sizeof(struct inode),
				result->inode.d_length);
		goto bad;
	}
	mbuftodent(&u.u_dent, M_VFIELD(result,dent));
#else	DFS_DATA_DESCRIPTORS
	mbuftodent(&u.u_dent, m_field(result, DFS_DATA(result)));
#endif	DFS_DATA_DESCRIPTORS
	u.u_offset = result->offset;
	u.u_count  = result->count;
	if (result->parentIp) {
		/*
		 * Create a locked agent for the remote inode.
		 * dfs_mkagent() calls iget() to get the agent
		 * and iget() returns a locked inode.
		 * The parent is left locked if we are
		 * creating or deleting a file.
		 */
		u.u_pdir = dfs_mkagent(conn, 
#ifdef	DFS_DATA_DESCRIPTORS
				       M_VFIELD(result,parentinode),
				       result->parentIp);
#else	DFS_DATA_DESCRIPTORS
				       m_field(result, &result->parentinode),
				       result->parentIp);
#endif	DFS_DATA_DESCRIPTORS
		if (flag == LOOKUP)
			IUNLOCK(u.u_pdir);
	}
	if (result->ip) {
		/*
		 * Namei is a messy routine.
		 * When the parent and child are the same inode,
		 * usually we get two references to the inode.
		 * The only exception is when we are deleting
		 * a file without locking the parent, in which 
		 * case we get one reference.
		 */
		if (result->ip == result->parentIp) {
			ip = u.u_pdir;
			if (!(flag & DELETE) || flag & LOCKPARENT)
				ip->i_count++;
		 } else
#ifdef	DFS_DATA_DESCRIPTORS
			ip = dfs_mkagent(conn,
					 M_VFIELD(result,inode),
					 result->ip);
#else	DFS_DATA_DESCRIPTORS
			ip = dfs_mkagent(conn, m_field(result, &result->inode),
					 result->ip);
#endif	DFS_DATA_DESCRIPTORS
	}
free:
	DFS_FREE_RESULTS(result);
	return ip;
bad:
	DFS_FREE_RESULTS(result);
	return NULL;
}

/*
 * Remote namei.
 * Copy globals like u.u_rdir (used in namei)
 * from the packet before calling namei.
 * If the path is local, namei returns
 * the inode or a NULL.
 * If the path contains another reference to a
 * remote inode, the rest of the path is returned.
 * The caller should take that path and continue namei.
 *
 * We try to make THIS routine as bullet-proof as we can.
 * We try to insure that this routine provides ONLY good data.
 */
dfsr_namei_t *
dfsServer_namei(clientConn, clientId, operation, call)
	connection_t	*	clientConn;	/* Host we are serving	*/
	u_long			clientId;	/* Specific client@host	*/
	u_short			operation;	/* Redundant: what to do*/
	register dfsc_namei_t * call;		/* Call pkt -- params	*/
{
	register dfsr_namei_t * result;
	register struct inode * ip;
	register struct inode * dp;
	register int		flag;
#ifdef	DFS_WCOUNT
	register int		userFWRITE;
#endif	DFS_WCOUNT
	register dfs_remote_t * rp;
	extern	 dfs_remote_t *	dfs_rmt_add();

	dfs_incServer(namei);
	if (!dfsServer_validContext(operation, call, "namei")) {
		DFS_FREE_PARAMS(call);
		/*
		 * Since we don't know the return-packet format
		 * that our incompatible caller expects,
		 * we really cannot give her a result->error.
		 * Therefore we pretend that we just
		 * could not "find" what she wanted.
		 */
		return NULL;
	}
	u.u_uid = DFS_UID(call->uid, clientConn);
	u.u_gid = DFS_GID(call->gid, clientConn);
	if (call->startdir) {
		rp = dfs_rmt_find(DFS_NODE(clientConn), call->startdir);
		u.u_cdir = rp->ip;
	} else
		u.u_cdir = rootdir;
	/*
	 * "Can never happen"...
	 */
	if (!(u.u_procp->p_rpcflags & RPCFLAG_AGENT)) {
		panic("dfsServer_namei/E-0:  not RPCFLAG_AGENT");
		/*NOTREACHED*/
	}
	/*
	 * Assume the remote request
	 * can always start at our (serverSide) root.
	 */
	u.u_rdir = NULL;
#ifdef	DFS_WCOUNT
	userFWRITE = call->flag &   DFS_NAMEI_FWRITE;
	flag	   = call->flag & (~DFS_NAMEI_FWRITE);
#else	DFS_WCOUNT
	flag	 = call->flag;
#endif	DFS_WCOUNT
	mbuftogroups(u.u_groups, m_field(call, call->groups));
	u.u_dirp = (caddr_t)m_field(call, DFS_DATA(call));
#ifdef	DFS_WCOUNT
	ip = namei(mbufchar, flag|userFWRITE, call->follow);
#else	DFS_WCOUNT
	ip = namei(mbufchar, flag, call->follow);
#endif	DFS_WCOUNT
	dp = u.u_pdir;

#ifndef	DFS_DATA_DESCRIPTORS
	result = DFSR_VPACKET(namei, DIRSIZ(&u.u_dent));
#else	DFS_DATA_DESCRIPTORS
	/*
	 * Variable portion of the pkt contains
	 * two inodes and the directory entry.
	 * Also included is some small amount of padding
	 * for version compatibility.
	 */
	result = DFSR_VPACKET(namei,
			      DIRSIZ(&u.u_dent)
			    + (sizeof(struct inode)+DFS_INODE_PADDING) << 1);
#endif	DFS_DATA_DESCRIPTORS
	if (!result)
		goto bad;
	if (result->error = u.u_error) {
		/*
		 * In the case of errors,
		 * report what little additional information
		 * that we did get from namei().
		 */
		result->ip	 = (caddr_t) ip;
		result->parentIp = (caddr_t) dp;
#ifdef	DFS_ECYCLE_RETRY
		/*
		 * Check if we can fix up the problem.
		 */
		if (result->error == DFS_EMULTIHOSTREF) {
			/*
			 * We are in the server-side dfsServer_namei().
			 *
			 * The server-side dfsClient_namei() detected
			 * a multiple host reference
			 * in the network pathname digraph.
			 * dfsClient_namei() returned DFS_EMULTIHOSTREF
			 * to the serverSide namei().
			 * namei() then set u_dirp
			 * to point at the "hint"
			 * and return/reported the error to us here.
			 *
			 * Pass the pathname "hint" back
			 * to the clientSide dfsClient_namei().
			 * (We forget about the inode-s and dirent
			 * usually in the results pkt.)
			 */
			DFS_DEBUG(10,("dfsServer_namei/I-1: returning DFS_EMULTIHOSTREF u_dirpx=0x%X='%s'\n", u.u_dirp, u.u_dirp));
			mtombuf(m_field(result, DFS_DATA(result)),
				u.u_dirp,
				strlen(u.u_dirp)+1);
			goto	bad;
		}
#endif	DFS_ECYCLE_RETRY
		goto bad;
	}

#ifdef	DFS_ECYCLE_RETRY
	/*
	 * If we have an AGENT inode,
	 * we have a cycle in the pathname
	 * that we must unwind.
	 */
	if (ip) {				/* Paranoia */
	  if (ip != DFS_ILLEGAL_INODE) {
	    if (ip->i_host) {
		/*
		 * Have serverSide referencing an agent inode.
		 * This was NOT anticipated -- and should NOT happen!
		 */
		 DFS_DEBUG(0,("dfsServer_namei/E-2: serverSide referencing AGENT inode=0x%X\n", ip));
		 u.u_error	= DFS_EMULTIHOSTREF;
		 result->error	= u.u_error;
		 panic("dfsServer_namei/E-2: AGENT inode referenced on serverSide");
		/*NOTREACHED*/
	    }
	  } else DFS_DEBUG(8,("dfsServer_namei/E-3: unexpected ip=0x%X\n",
				DFS_ILLEGAL_INODE));
	}
#endif	DFS_ECYCLE_RETRY

#ifdef	DFS_DATA_DESCRIPTORS
	/*
	 * Construct the descriptors in the result pkt.
	 * for the two inodes
	 * and any corresponding directory entry.
	 */
	{
		register offset;
		register length = sizeof(struct inode);

		/*
		 * Define receiving location for 1st inode.
		 * (The order or relative locations
		 * don't matter.)
		 */
		offset			     = (u_long)DFS_DATA(result)
					     - (u_long)result;
		result->parentinode.d_offset = offset;
		result->parentinode.d_length = length;
		/*
		 * 2nd inode.
		 */
		offset			     = offset+length+DFS_INODE_PADDING;
		result->      inode.d_offset = offset;
		result->      inode.d_length = length;
		/*
		 * Directory entry.
		 */
		offset			     = offset+length+DFS_INODE_PADDING;
		result->       dent.d_offset = offset;
		result->       dent.d_length = DIRSIZ(&u.u_dent);
	}
#endif	DFS_DATA_DESCRIPTORS
	/*
	 * If u.u_pdir is set, we have a valid parent
	 */
	if (dp && dp != DFS_ILLEGAL_INODE) {
		rp = dfs_rmt_add(DFS_NODE(clientConn), dp);
		/*
		 * Now actually fill in the parent inode.
		 */
#ifndef	DFS_DATA_DESCRIPTORS
		inodetombuf(m_field(result, &result->parentinode), dp);
#else	DFS_DATA_DESCRIPTORS
		inodetombuf(M_VFIELD(result,parentinode), dp);
#endif	DFS_DATA_DESCRIPTORS
		result->parentIp = (caddr_t)dp;
	} else
		result->parentIp = NULL;
	if (ip) {
		register u_short mode = ip->i_mode & IFMT;
		/*
		 * Can not return a socket or block device inode ptr.
		 */
		if (mode == IFSOCK) {
			result->error = DFS_EREMOTESOCK;
			goto bad;
#ifdef	DFS_NOREMOTEBDEV
		} else if (mode == IFBLK) {
			result->error = DFS_EREMOTEBDEV;
			goto bad;
#endif	DFS_NOREMOTEBDEV
		}
#ifdef	DFS_WCOUNT
		/*
		 * namei()==>iget() is tantamount to a copen(), and hence
		 * is the best marriage of necessity with opportunity
		 * to manage
		 *		(struct inode *)ip->i_wcount
		 * and		(dfs_remote_t *)rp->wcount
		 */
		if (userFWRITE) {
			ip->i_flag |= IOPENWRITE;
			ip->i_wcount++;
		}
#endif	DFS_WCOUNT
		/*
		 * Add a new entry in dfs_remote table
		 */
		if (ip == dp) {
			/*
			 * Namei is  a messy routine -- see
			 * the comment in dfsClient_namei above.
			 * We must keep remote ref count in sync.
			 * with the actual number of references.
			 */
			if (!(flag & DELETE) || flag & LOCKPARENT)
				rp->count++;
		} else {
			rp = dfs_rmt_add(DFS_NODE(clientConn), ip);
#ifndef	DFS_DATA_DESCRIPTORS
			inodetombuf(m_field(result, &result->inode), ip);
#else	DFS_DATA_DESCRIPTORS
			inodetombuf(M_VFIELD(result,inode), ip);
#endif	DFS_DATA_DESCRIPTORS
		}
#ifdef	DFS_WCOUNT
		if (userFWRITE)
			rp->wcount++;
#endif	DFS_WCOUNT
		result->ip = (caddr_t)ip;
	} else
		result->ip = NULL;
	result->offset = u.u_offset;
	result->count  = u.u_count;
#ifndef	DFS_DATA_DESCRIPTORS
	denttombuf(m_field(result, DFS_DATA(result)), &u.u_dent);
#else	DFS_DATA_DESCRIPTORS
	denttombuf(M_VFIELD(result, dent), &u.u_dent);
#endif	DFS_DATA_DESCRIPTORS
	result->error = u.u_error;
	/*
	 * We force the abort handling to be synchronous,
	 * which avoids races; and we always start from a known state.
	 */
	if (u.u_procp->p_rpcflags & RPCFLAG_ABORTED) {
		dfsServer_nameiAbort(clientConn, result->ip, result->parentIp);
		result->error = u.u_error;
	}
	DFS_FREE_PARAMS(call);
	DFS_RETURN(result);
bad:

	if (ip) {
#ifdef	DFS_LOCK_FIX
		ip->i_rmt_lock = NULL;
#endif	DFS_LOCK_FIX
		iput(ip);
	}
	/*
	 * Now work on the parent.
	 * In this case, the out-of-bound values
	 * do not merit reporting.
	 */
	if ( dp
	 && (dp != ip)			/* Already did the work   */
	 && (dp != DFS_ILLEGAL_INODE)	/* namei: no parent inode */
	   ) {
#ifdef	DFS_LOCK_FIX
		dp->i_rmt_lock = NULL;
#endif	DFS_LOCK_FIX
		if (dp->i_flag & ILOCKED)
			iput(dp);
		else
			irele(dp);
	}
	/*
	 * We force the abort handling to be synchronous,
	 * which avoids races; and we always start from a known state.
	 */
	if (u.u_procp->p_rpcflags & RPCFLAG_ABORTED) {
		dfsServer_nameiAbort(clientConn, result->ip, result->parentIp);
		result->error = u.u_error;
	}
ret:
	DFS_FREE_PARAMS(call);
	DFS_RETURN(result);
}

/*
 * Release any locked resources -- both parent and child.
 * We are on the server-side.
 * This is synchronous processing.
 *
 * We try to be as bullet-proof as possible!
 * We don't report discovered anomolies -- just fix them.
 *
 * BUG: There can be races with dfs[Client|Server]_exception().
 */
dfsServer_nameiAbort(clientConn, ip, parentIp)
	register connection_t * clientConn;
	register struct inode * ip;
	register struct inode * parentIp;
{
	register dfs_remote_t * rp;

	dfs_incServerAbort(namei);
	DFS_DEBUG(4,("dfsServer_nameiAbort/I-0: clientConn=0x%X (%s) ip=0x%X dev=%d/%d i#=%D i_count=%d parentIp=0x%X\n",
		clientConn,
		(clientConn	? clientConn->name
				: "unknown"),
		ip,
		major(ip->i_dev),
		minor(ip->i_dev),
		ip->i_number, ip->i_count,
		parentIp));
	/*
	 * Look for the remote structure
	 * with an unvalidated ip,
	 * since we don't validate ip at dfs_rmt_add() time.
	 */
	if (rp = dfs_rmt_find(DFS_NODE(clientConn), ip)) {
		/*
		 * Attempt to remove (decrement) the reference (count)
		 * even if a bogus ip.
		 */
		ip = rp->ip;
		dfs_rmt_remove(DFS_NODE(clientConn), ip);
		/*
		 * Now we must be more stringent.
		 */
		if (ip && (ip!=DFS_ILLEGAL_INODE)) {
#ifdef	DFS_LOCK_FIX
			ip->i_rmt_lock = NULL;
#endif	DFS_LOCK_FIX
			if (ip->i_flag & ILOCKED)
				iput(ip);
			else
				irele(ip);
		}
		if (ip = parentIp) {
			dfs_rmt_remove(DFS_NODE(clientConn), ip);
			if (ip && (ip!=DFS_ILLEGAL_INODE))
#ifdef	DFS_LOCK_FIX
				ip->i_rmt_lock = NULL;
#endif	DFS_LOCK_FIX
				if (ip->i_flag & ILOCKED)
					iput(ip);
				else
					irele(ip);
		}
	}
	else	DFS_DEBUG(4,("dfsServer_nameiAbort/E-1:  No rp for ip: clientConn=0x%X ip=0x%X parentIp=0x%X\n",
			clientConn, ip, parentIp));
	u.u_error = DFS_EDFSABORT;
}

typedef struct dfsc_direnter {	/* Pkt from client --> server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	caddr_t		ip;		/* Inode to be direnter'ed	*/
	caddr_t		directoryIp;	/* ...in this directory		*/
	long		offset;		/* ...at this offset		*/
	long		count;		/* Size of the hole		*/
	/* Directory entry  follows the header */
} dfsc_direnter_t;

typedef struct dfsr_direnter {	/* Pkt from client <-- server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	int		error;		/* Error number or 0 on success */
} dfsr_direnter_t;

/*
 * Remote direnter.
 * u.u_pdir is released upon return
 * and the u.u_dent entry is created in it.
 */
dfsClient_direnter(ip)
	register struct inode		* ip;
{
	register dfsc_direnter_t	* call;
	register dfsr_direnter_t	* result;
	register struct inode		* dp = u.u_pdir;

	dfs_incClient(direnter);
	if (!dfsClient_validContext(ip, "direnter"))
		goto ret;
	if (!dp) {
		u.u_error = DFS_EINVAL;
		goto ret;
	}
	if ( dp != DFS_ILLEGAL_INODE
	 && (dp->i_flag & IDEADSERVER)) {	/* Dead server for parent */
		DFS_DEBUG(4,("dfsClient_direnter/I-0: dp=0x%X\n", dp));
		u.u_error = DFS_ECRASHED;
		goto ret;
	}
	if (!ip->i_rmt_ip) {
		DFS_DEBUG(4,("dfsClient_direnter/I-1: ip=0x%X i_rmt_ip=0\n", ip));
		u.u_error = DFS_EINVAL;
		goto ret;
	}
	if (!dp->i_rmt_ip) {
		DFS_DEBUG(4,("dfsClient_direnter/I-2: dp=0x%X i_rmt_ip=0\n", ip));
		u.u_error = DFS_EINVAL;
		goto ret;
	}
	call = DFSC_VPACKET(direnter, DIRSIZ(&u.u_dent));
	if (!call)
		goto ret;
	call->ip		= ip->i_rmt_ip;
	call->directoryIp	= dp->i_rmt_ip;
	call->offset		= u.u_offset;
	call->count		= u.u_count;
	denttombuf(m_field(call, DFS_DATA(call)), &u.u_dent);
	result = DFS_CALL(dp->i_host,direnter,call);
	if (result) {
		u.u_error = result->error;
		DFS_FREE_RESULTS(result);
	}
ret:
	/*
	 * Only the agent needs to be iput.
	 * The remote inode has already been
	 * iput by the server.
	 */
	IUNLOCK(dp);
	dfs_irele(dp);
	return u.u_error;
}

/*
 * Remote installation of a directory entry.
 */
dfsr_direnter_t *
dfsServer_direnter(clientConn, clientId, operation, call)
	connection_t	*	clientConn;	/* Host we are serving	*/
	u_long			clientId;	/* Specific client@host	*/
	u_short			operation;	/* Redundant: what to do*/
	register dfsc_direnter_t * call;	/* Call pkt -- params	*/
{
	register dfsr_direnter_t * result;
	register struct inode * ip;
	register dfs_remote_t * rp;

	dfs_incServer(direnter);
	if (!dfsServer_validContext(operation, call, "direnter")) {
		DFS_FREE_PARAMS(call);
		return NULL;
	}
	result = DFSR_PACKET(direnter);
	if (!result) {
		DFS_FREE_PARAMS(call);
		return NULL;		/* Say "no result pkt" */
	}
	/*
	 * First process the inode
	 */
	rp = dfs_rmt_find(DFS_NODE(clientConn), call->ip);
	if (!rp) {
		u.u_error = DFS_EBADRMTDESC;
		goto ret;
	}
	if (rp->flags & DFS_RMT_LOCKED)
		rp->locktime = UPTIME();
	if (!(ip = rp->ip)) {
		u.u_error = DFS_EINVAL;
		goto ret;
	}
	/*
	 * Then process the directory inode
	 */
	if (!call->directoryIp) {
		u.u_error = DFS_EINVAL;
		goto ret;
	}
	rp = dfs_rmt_find(DFS_NODE(clientConn), call->directoryIp);
	/*
	 * call->directoryIp must be locked and referenced
	 * by the remote node.
	 */
	if (!rp || !(rp->flags & DFS_RMT_LOCKED)) {
		u.u_error = !rp ? DFS_EBADRMTDESC : DFS_ERMTNOTLOCKED;
		goto ret;
	}
	rp->locktime = UPTIME();
	u.u_pdir = rp->ip;

	u.u_offset = call->offset;
	u.u_count = call->count;
	mbuftodent(&u.u_dent, m_field(call, DFS_DATA(call)));
	/*
	 * Now add the directory entry for the inode ip
	 * to the directory specified by u_pdir.
	 */
	u.u_error = direnter(ip);

	/*
	 * Cleanup the parent directory
	 */
	rp->flags &= ~DFS_RMT_LOCKED;
	if (u.u_pdir) {
		dfs_rmt_remove(DFS_NODE(clientConn), u.u_pdir);
#ifdef	DFS_LOCK_FIX
		u.u_pdir->i_rmt_lock = NULL;
#endif	DFS_LOCK_FIX
	}
ret:
	result->error = u.u_error;
	DFS_FREE_PARAMS(call);
	DFS_RETURN(result);
}

typedef struct dfsc_dirremove {	/* Pkt from client --> server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	caddr_t		ip;		/* Inode to be dirremove'd */
	caddr_t 	parentIp;	/* ...in this directory */
	long		offset;		/* ...at this offset */
	long		count;		/* Size of the hole */
	/* Directory entry  follows the header */
} dfsc_dirremove_t;

typedef struct dfsr_dirremove {	/* Pkt from client <-- server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	int		error;		/* Error number or 0 on success */
} dfsr_dirremove_t;

/*
 * Remove an entry from a directory
 *
 * Return	    1 if OK;
 * otherwise return 0 if an error.
 */
dfsClient_dirremove()
{
	register dfsc_dirremove_t * call;
	register dfsr_dirremove_t * result;
	register struct	inode	  * dp = u.u_pdir;

	dfs_incClient(dirremove);
	if (!dfsClient_validContext(dp, "dirremove"))	/* NOTE we pass dp! */
		return 0;
	if (!dp)
		panic("dfsClient_dirremove: u_pdir=0");
		/*NOTREACHED*/
	if ( dp != DFS_ILLEGAL_INODE
	 && (dp->i_flag & IDEADSERVER)) {	/* Dead server for parent */
		u.u_error = DFS_ECRASHED;
		return 0;
	}
	if (!dp->i_rmt_ip) {
		u.u_error = DFS_EINVAL;
		return 0;
	}
	call = DFSC_VPACKET(dirremove, DIRSIZ(&u.u_dent));
	if (!call)
		return 0;
	call->parentIp	= dp->i_rmt_ip;
	call->count	= u.u_count;
	call->offset	= u.u_offset;
	denttombuf(m_field(call, DFS_DATA(call)), &u.u_dent);
	result = DFS_CALL(dp->i_host,dirremove,call);
	if (!result)
		return 0;
	/*
	 * BUG: do we change u.u_error if already non-zero?
	 */
	u.u_error = result->error;
	DFS_FREE_RESULTS(result);
	return u.u_error == 0;
}

/*
 * Remove a directory entry
 * for the client.
 */
dfsr_dirremove_t *
dfsServer_dirremove(clientConn, clientId, operation, call)
	connection_t	*	clientConn;	/* Host we are serving	*/
	u_long			clientId;	/* Specific client@host	*/
	u_short			operation;	/* Redundant: what to do*/
	register dfsc_dirremove_t * call;	/* Call pkt -- params	*/
{
	register dfsr_dirremove_t * result;
	register dfs_remote_t * rp;

	dfs_incServer(dirremove);
	if (!dfsServer_validContext(operation, call, "dirremove")) {
		DFS_FREE_PARAMS(call);
		return NULL;
	}
	result = DFSR_PACKET(dirremove);
	if (!result) {
		DFS_FREE_PARAMS(call);
		return NULL;		/* Say "no result pkt" */
	}
	if (!call->parentIp) {
		u.u_error = DFS_EINVAL;
		goto ret;
	}
	rp = dfs_rmt_find(DFS_NODE(clientConn), call->parentIp);
	/*
	 * call->parentIp must be locked and referenced
	 * by the remote node.
	 */
	if (!rp || !(rp->flags & DFS_RMT_LOCKED)) {
		u.u_error = !rp ? DFS_EBADRMTDESC : DFS_ERMTNOTLOCKED;
		goto ret;
	}
	rp->locktime = UPTIME();
	u.u_pdir = rp->ip;

	u.u_count = call->count;
	u.u_offset = call->offset;
	mbuftodent(&u.u_dent, m_field(call, DFS_DATA(call)));
	if (dirremove())
		u.u_error = 0;
ret:
	result->error = u.u_error;
	DFS_FREE_PARAMS(call);
	DFS_RETURN(result);
}
