static char rcsid[] = "$Header: dfs_inode.c,v 820.1 86/12/04 19:44:15 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 inode routines
 *
 *	Bakul Shah
 *
 * bvs 840321 -- original version
 * jht 841115,16,26-29 -- complete conversion to use prpc.
 * jht 850213 -- add dfsPktHdr_t containing version/functionality info to pkts.
 * jht 850401 -- fill out the gathering of statistics.
 * jht 850414 -- differentiate between no remote struct and one not locked.
 * jht 850414 -- Clear i_rmt_lock along with DFS_RMT_LOCKED flag.
 */

#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"


/*
 * Basic inode operations
 */

typedef struct dfsc_ilock {	/* Pkt from client --> server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	caddr_t 	ip;		/* Ptr to inode */
} dfsc_ilock_t;

typedef struct dfsr_ilock {	/* 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	*/
	caddr_t 	ip;		/* Ptr to inode			*/
	struct inode	inode;		/* Copy of the inode		*/
} dfsr_ilock_t;

/*
 * Remote ilock call.
 * Note that if the lock succeeds,
 * the inode is refreshed.
 */
dfsClient_ilock(ip)
	register struct inode * ip;
{
	register dfsc_ilock_t * call;
	register dfsr_ilock_t * result;

	dfs_incClient(ilock);
	if (!dfsClient_validContext(ip, "ilock"))
		goto release;
	call = DFSC_PACKET(ilock);
	if (!call)
		goto release;
	call->ip = ip->i_rmt_ip;
	result = DFS_CALL(ip->i_host,ilock,call);
	if (!result)
		goto release;
	if (result->error) {
		u.u_error = result->error;
		goto free;
	}
	/*
	 * copy contents of the remote inode into the agent
	 */
	mbuftoinode(ip, &result->inode);
	DFS_FREE_RESULTS(result);
	return;
free:
	DFS_FREE_RESULTS(result);
release:
	IUNLOCK(ip);
}

/*
 * Lock the local inode call->ip
 * and return a copy of the inode.
 */
dfsr_ilock_t *
dfsServer_ilock(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_ilock_t * call;		/* Call pkt -- params	*/
{
	register dfsr_ilock_t * result;
	register struct inode * ip;
	register dfs_remote_t * rp;

	dfs_incServer(ilock);
	if (!dfsServer_validContext(operation, call, "ilock")) {
		DFS_FREE_PARAMS(call);
		return NULL;
	}
	result = DFSR_PACKET(ilock);
	if (!result) {
		DFS_FREE_PARAMS(call);
		return NULL;		/* Say "no result pkt" */
	}
	rp = dfs_rmt_find(DFS_NODE(clientConn), call->ip);
#ifdef	AGENT_FIRST
	if (!rp || rp->flags&DFS_RMT_LOCKED) {
		u.u_error = !rp ? DFS_EBADRMTDESC : DFS_ERMTNOTLOCKED;
		goto bad;
	}
#else	AGENT_FIRST
	if (!rp) {
		u.u_error = DFS_EBADRMTDESC;
		goto bad;
	}
#endif	AGENT_FIRST
	if (!(ip = rp->ip)) {
		u.u_error = DFS_EINVAL;
		goto bad;
	}
	ILOCK(ip);
	rp->flags |= DFS_RMT_LOCKED;
	rp->locktime = UPTIME();
	inodetombuf(&result->inode, ip);
	if (u.u_procp->p_rpcflags & RPCFLAG_ABORTED) {
		/*
		 * Release the locked resources
		 */
		dfsServer_ilockAbort(rp);
	}
bad:
	result->error = u.u_error;
	DFS_FREE_PARAMS(call);
	DFS_RETURN(result);
}

/*
 * Release the locked resources
 */
dfsServer_ilockAbort(rp)
	register dfs_remote_t * rp;
{
	register struct inode * ip;

	dfs_incServerAbort(ilock);
	if (!rp) {
		u.u_error = DFS_EBADRMTDESC;
		return;
	}
	rp->flags &= ~DFS_RMT_LOCKED;
	if (!(ip = rp->ip)) {
		u.u_error = DFS_EINVAL;
		return;
	}
#ifdef	DFS_LOCK_FIX
	ip->i_rmt_lock = NULL;
#endif	DFS_LOCK_FIX
	IUNLOCK(ip);
}

typedef struct dfsc_iunlock {	/* Pkt from client --> server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	caddr_t 	ip;		/* Ptr to inode		*/
	struct inode	inode;		/* Copy of the inode	*/
} dfsc_iunlock_t;

typedef struct dfsr_iunlock {	/* 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_iunlock_t;

/*
 * Remote iunlock.
 * Note that once unlocked, the inode is no longer uptodate.
 * File mode, owner, group, size, data
 * can not be relied upon to be correct.
 */
dfsClient_iunlock(ip)
	register struct inode * ip;
{
	register dfsc_iunlock_t * call;
	register dfsr_iunlock_t * result;

	dfs_incClient(iunlock);
	if (!dfsClient_validContext(ip, "iunlock"))
		return;
	call = DFSC_PACKET(iunlock);
	if (!call) 
		return;
	call->ip = ip->i_rmt_ip;
	inodetombuf(&call->inode, ip);
	result = DFS_CALL(ip->i_host,iunlock,call);
	if (!result)
		return;
	if (result->error)
		u.u_error = result->error;
	DFS_FREE_RESULTS(result);
}

/*
 * If the node(call) has locked the inode,
 * make a local iunlock call.
 */
dfsr_iunlock_t *
dfsServer_iunlock(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_iunlock_t * call;		/* Call pkt -- params	*/
{
	register dfsr_iunlock_t * result;
	register struct inode * ip;
	register dfs_remote_t * rp;

	dfs_incServer(iunlock);
	if (!dfsServer_validContext(operation, call, "iunlock")) {
		DFS_FREE_PARAMS(call);
		return NULL;
	}
	result = DFSR_PACKET(iunlock);
	if (!result) {
		DFS_FREE_PARAMS(call);
		return NULL;		/* Say "no result pkt" */
	}
	rp = dfs_rmt_find(DFS_NODE(clientConn), call->ip);
	if (!rp || !(rp->flags & DFS_RMT_LOCKED)) {
		u.u_error = !rp ? DFS_EBADRMTDESC : DFS_ERMTNOTLOCKED;
		goto bad;
	}
	if (!(ip = rp->ip)) {
		u.u_error = DFS_EINVAL;
		goto bad;
	}
	mbuftoinode(ip, &call->inode);
	IUNLOCK(ip);
	rp->flags &= ~DFS_RMT_LOCKED;
#ifdef	DFS_LOCK_FIX
	ip->i_rmt_lock = NULL;
#endif	DFS_LOCK_FIX
bad:
	result->error = u.u_error;
	DFS_FREE_PARAMS(call);
	DFS_RETURN(result);
}

typedef struct dfsc_irele {	/* Pkt from client --> server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	caddr_t 	ip;		/* Ptr to inode */
} dfsc_irele_t;

typedef struct dfsr_irele {	/* 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_irele_t;

/*
 * Make a remote irele call.
 */
dfsClient_irele(ip)
	register struct inode * ip;
{
	register dfsc_irele_t * call;
	register dfsr_irele_t * result;

	dfs_incClient(irele);
	if (!dfsClient_validContext(ip, "irele"))
		return;
	call = DFSC_PACKET(irele);
	if (!call)
		return;
	call->ip = ip->i_rmt_ip;
	result = DFS_CALL(ip->i_host,irele,call);
	if (result->error)
		u.u_error = result->error;
	DFS_FREE_RESULTS(result);
}

dfsr_irele_t *
dfsServer_irele(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_irele_t *	call;		/* Call pkt -- params	*/
{
	register dfsr_irele_t * result;
	register dfs_remote_t * rp;
	register struct inode * ip;

	dfs_incServer(irele);
	if (!dfsServer_validContext(operation, call, "irele")) {
		DFS_FREE_PARAMS(call);
		return NULL;
	}
	result = DFSR_PACKET(irele);
	if (!result) {
		DFS_FREE_PARAMS(call);
		return NULL;		/* Say "no result pkt" */
	}
	rp = dfs_rmt_remove(DFS_NODE(clientConn), call->ip);
	if (!rp) {
		u.u_error = DFS_EBADRMTDESC;
		goto bad;
	}
	if (!(ip = rp->ip)) {
		u.u_error = DFS_EINVAL;
		goto bad;
	}
	/*
	 * We have to synchronize here
	 * as irele can be called when the inode
	 * is locked by a remote process.
	 */
	ILOCK(ip);
	IUNLOCK(ip);
	irele(ip);
bad:
	result->error = u.u_error;
	DFS_FREE_PARAMS(call);
	DFS_RETURN(result);
}

typedef struct dfsc_itrunc {	/* Pkt from client --> server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	caddr_t 	ip;		/* Ptr to inode		 */
	long		size;		/* Truncate to this size */
	struct inode	inode;		/* Copy of the inode	 */
} dfsc_itrunc_t;

typedef struct dfsr_itrunc {	/* 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	*/
	struct inode	inode;		/* Copy of the inode		*/
} dfsr_itrunc_t;

/*
 * Truncate a file
 */
dfsClient_itrunc(ip, size)
	register struct inode * ip;
	int size;
{
	register dfsc_itrunc_t * call;
	register dfsr_itrunc_t * result;

	dfs_incClient(itrunc);
	if (!dfsClient_validContext(ip, "itrunc"))
		return;
	call = DFSC_PACKET(itrunc);
	if (!call)
		return;
	call->ip = ip->i_rmt_ip;
	call->size = size;
	inodetombuf(&call->inode, ip);
	result = DFS_CALL(ip->i_host,itrunc,call);
	if (!result)
		return;
	if (result->error) {
		u.u_error = result->error;
		goto free;
	}
	mbuftoinode(ip, &result->inode);
free:
	DFS_FREE_RESULTS(result);
}

dfsr_itrunc_t *
dfsServer_itrunc(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_itrunc_t * call;		/* Call pkt -- params	*/
{
	register dfsr_itrunc_t * result;
	register struct inode * ip;
	register int size;
	register dfs_remote_t * rp;

	dfs_incServer(itrunc);
	if (!dfsServer_validContext(operation, call, "itrunc")) {
		DFS_FREE_PARAMS(call);
		return NULL;
	}
	result = DFSR_PACKET(itrunc);
	if (!result) {
		DFS_FREE_PARAMS(call);
		return NULL;		/* Say "no result pkt" */
	}
	rp = dfs_rmt_find(DFS_NODE(clientConn), call->ip);
	if (!rp) {
		u.u_error = DFS_EBADRMTDESC;
		goto bad;
	}
	if (!(ip = rp->ip)) {
		u.u_error = DFS_EINVAL;
		goto bad;
	}
	mbuftoinode(ip, &call->inode);
	size = call->size;
	itrunc(ip, size);
	inodetombuf(&result->inode, ip);
bad:
	result->error = u.u_error;
	DFS_FREE_PARAMS(call);
	DFS_RETURN(result);
}

typedef struct dfsc_iupdat {	/* Pkt from client --> server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	caddr_t 	ip;		/* Ptr to inode			*/
	struct timeval	ta;		/* New access time		*/
	struct timeval	tm;		/* New modify time		*/
	int		waitfor;	/* Wait for update compete	*/
	struct inode	inode;		/* Inode image			*/
} dfsc_iupdat_t;

typedef struct dfsr_iupdat {	/* 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_iupdat_t;

/*
 * Update an inode
 */
dfsClient_iupdat(ip, ta, tm, waitfor)
	register struct inode * ip;
	struct timeval * ta;
	struct timeval * tm;
{
	register dfsc_iupdat_t * call;
	register dfsr_iupdat_t * result;
	
	dfs_incClient(iupdat);
	if (!dfsClient_validContext(ip, "iupdat"))
		return;
	call = DFSC_PACKET(iupdat);
	if (!call)
		return;
	call->ip = ip->i_rmt_ip;
	call->ta = *ta;
	call->tm = *tm;
	call->waitfor = waitfor;
	inodetombuf(&call->inode, ip);
	result = DFS_CALL(ip->i_host,iupdat,call);
	if (!result)
		return;
	if (result->error)
		u.u_error = result->error;
	DFS_FREE_RESULTS(result);
}

dfsr_iupdat_t *
dfsServer_iupdat(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_iupdat_t * call;		/* Call pkt -- params	*/
{
	register dfsr_iupdat_t * result;
	register struct inode * ip;
	register dfs_remote_t * rp;

	dfs_incServer(iupdat);
	if (!dfsServer_validContext(operation, call, "iupdat")) {
		DFS_FREE_PARAMS(call);
		return NULL;
	}
	result = DFSR_PACKET(iupdat);
	if (!result) {
		DFS_FREE_PARAMS(call);
		return NULL;		/* Say "no result pkt" */
	}
	rp = dfs_rmt_find(DFS_NODE(clientConn), call->ip);
	if (!rp) {
		u.u_error = DFS_EBADRMTDESC;
		goto bad;
	}
	if (!(ip = rp->ip)) {
		u.u_error = DFS_EINVAL;
		goto bad;
	}
	mbuftoinode(ip, &call->inode);
	iupdat(ip, &call->ta, &call->tm, call->waitfor);
bad:
	result->error = u.u_error;
	DFS_FREE_PARAMS(call);
	DFS_RETURN(result);
}

typedef struct dfsc_ilink {	/* Pkt from client --> server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	caddr_t 	ip;		/* Ptr to inode	*/
	struct inode	inode;		/* Inode image	*/
} dfsc_ilink_t;

typedef struct dfsr_ilink {	/* 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_ilink_t;

/*
 * Link to a file
 */
dfsClient_ilink(ip)
	register struct inode * ip;
{
	register dfsc_ilink_t * call;
	register dfsr_ilink_t * result;

	dfs_incClient(ilink);
	if (!dfsClient_validContext(ip, "ilink"))
		return;
	call = DFSC_PACKET(ilink);
	if (!call)
		return;
	call->ip = ip->i_rmt_ip;
	result = DFS_CALL(ip->i_host,ilink,call);
	if (!result)
		return;
	if (result->error)
		u.u_error = result->error;
	else
		ip->i_count++;
	DFS_FREE_RESULTS(result);
}

dfsr_ilink_t *
dfsServer_ilink(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_ilink_t * call;		/* Call pkt -- params	*/
{
	register dfsr_ilink_t * result;
	register struct inode * ip;
	register dfs_remote_t * rp;

	dfs_incServer(ilink);
	if (!dfsServer_validContext(operation, call, "ilink")) {
		DFS_FREE_PARAMS(call);
		return NULL;
	}
	result = DFSR_PACKET(ilink);
	if (!result) {
		DFS_FREE_PARAMS(call);
		return NULL;		/* Say "no result pkt" */
	}
	rp = dfs_rmt_find(DFS_NODE(clientConn), call->ip);
	/*
	 * The inode must be locked prior
	 * to calling ilink.
	 */
	if (!rp || !(rp->flags & DFS_RMT_LOCKED)) {
		u.u_error = !rp ? DFS_EBADRMTDESC : DFS_ERMTNOTLOCKED;
		goto bad;
	}
	if (!(ip = rp->ip)) {
		u.u_error = DFS_EINVAL;
		goto bad;
	}
	rp->count++;
	ip->i_count++;
bad:
	result->error = u.u_error;
	DFS_FREE_PARAMS(call);
	DFS_RETURN(result);
}
