static char rcsid[] = "$Header: dfs_generic.c,v 820.1 86/12/04 19:44:10 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 routines used from  ../sys/sys_generic.c
 *
 * 	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 850301 -- conversion to use new errno.h error-code mnemonics.
 * jht 850401 -- fill out the gathering of statistics.
 * jht 850416 -- support for rwip() to do implicit ilock()/iunlock().
 * jht 850829 -- ifdef'd the bulk transfer facility with EFS_BULKDATA.
 */

#include "../h/param.h"
#include "../h/dir.h"
#include "../h/ioctl.h"
#include "../h/user.h"
#include "../h/tty.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/uio.h"
#include "../h/vmparam.h"
#include "../h/buf.h"
#ifdef	DFS_BULKDATA
#include "../vnet/bulkdata.h"

extern bulk_connection_t *bulk_create();
extern bulk_connection_t *bulk_connect();
#endif	DFS_BULKDATA


#define DFS_UIOSIZE	20	/* Large enough for DFS_DATASIZE */

typedef struct xferinfo
{
	struct proc	*procp;		/* Process involved in transfer */
	caddr_t		addr;		/* Address of next byte */
	int		resid;		/* Bytes left to transfer */
} xferinfo_t;

typedef struct dfsc_rwip {	/* Pkt from client --> server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	caddr_t 	ip;		/* Remote file descriptor	*/
	long		offset;		/* Offset into remote file	*/
	int		count;		/* Number of bytes being read	*/
	int		rw;		/* Read/write flag		*/
#ifdef	DFS_NEW_RWIP
	int		implicitLock;	/* !0 ==> serverSide ilock()/iunlock()*/
#endif	DFS_NEW_RWIP
	int		usebulk;	/* True to use bulk data	*/
	bulk_id_t	bulkid;		/* ID of local BULK connection	*/
	/* Data follows the header */
} dfsc_rwip_t;

typedef struct dfsr_rwip {	/* Pkt from client <-- server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	char		junk;
	char		interrupted;	/* Call was interrupted by signal */
	char		eosys;		/* How to return fro system call */
	char		error;		/* Error number of 0 on success	 */
	int		count;		/* Number of bytes actually read */
	/* Data follows the header */
} dfsr_rwip_t;

#ifdef	DFS_BULKDATA
int dfsClient_rwipRecv();
int dfsClient_rwipCopy();
int dfsServer_rwipRecv();
int dfsServer_rwipCopy();
int dfsServer_rwipSent();

bulk_client_t dfsClient_bulkinfo =
	{ dfsClient_rwipRecv, dfsClient_rwipCopy, 0 };
bulk_client_t dfsServer_bulkinfo =
	{ dfsServer_rwipRecv, dfsServer_rwipCopy, dfsServer_rwipSent };
#endif	DFS_BULKDATA

#ifndef	DFS_NEW_RWIP
dfsClient_rwip(ip, uio, rw)
#else	DFS_NEW_RWIP
dfsClient_rwip(ip, uio, rw, implicitLock)
#endif	DFS_NEW_RWIP
	register struct inode *ip;
	register struct uio *uio;
	enum uio_rw rw;
#ifdef	DFS_NEW_RWIP
	register implicitLock;	/* !0 ==> serverSide to do ilock()/iunlock() */
#endif	DFS_NEW_RWIP
{
	register dfsc_rwip_t * call;
	register dfsr_rwip_t * result;
	register int error = 0;
	register int resid;
	register int offset;
	register int count;
	register int rcount;
	struct uio auio;
	struct iovec aiov[DFS_UIOSIZE];

	dfs_incClient(rwip);
	if (!dfsClient_validContext(ip, "rwip"))
		return u.u_error;

	resid  = uio->uio_resid;
	offset = uio->uio_offset;
	/*
	 * Transfer as many bytes as possible in each call.
	 */
	do {
		struct mbuf *m;
		xferinfo_t *xferinfo;
#ifdef	DFS_BULKDATA
		bulk_connection_t *bulkconn;
		int usebulk;
#else	DFS_BULKDATA
		int usebulk = 0;	/* Specify that we won't */
#endif	DFS_BULKDATA

		if (!dfsClient_validContext(ip, "rwip"))
			break;
		count = uio->uio_iov->iov_len;
#ifdef	DFS_BULKDATA
		usebulk = (count > DFS_DATASIZE);
#endif	DFS_BULKDATA
		call = DFSC_VPACKET(rwip, usebulk ? 0 : count);
		if (!call)
			break;
		call->rw     = (int)rw;
		call->ip     = ip->i_rmt_ip;
		call->count  = count;
		call->offset = offset;
#ifdef	DFS_NEW_RWIP
		call->implicitLock = implicitLock;
#endif	DFS_NEW_RWIP
		call->usebulk = usebulk;
#ifdef	DFS_BULKDATA
		if (call->usebulk) {
			/*
			 * Lock the memory which will be involved
			 * in the transfer.
			 */
			if (!useracc(uio->uio_iov->iov_base, count,
				     rw == UIO_READ ? B_WRITE : B_READ)) {
				DFS_FREE_PARAMS(call);
				break;
			}
			vslock(uio->uio_iov->iov_base, count);
			MGET(m, M_WAIT, MT_DATA);
			xferinfo = mtod(m, xferinfo_t *);
			xferinfo->procp = u.u_procp;
			xferinfo->addr = uio->uio_iov->iov_base;
			xferinfo->resid = count;

			/* BUG send environment info ala efs_checkenv() */

			/*
			 * Create the bulk data connection and
			 * send the data if this is a write.
			 */
			bulkconn = bulk_create(ip->i_host, &dfsClient_bulkinfo,
					       (caddr_t)xferinfo);
			call->bulkid = bulk_getid(bulkconn);
			if (rw == UIO_WRITE)
				bulk_send(bulkconn, count);
		} else
#endif	DFS_BULKDATA
		{
			auio.uio_iov = &aiov[0];
			if (rw == UIO_WRITE) {
				mbuftouio(DFS_DATA(call), count, &auio,
					  DFS_UIOSIZE);
				error = uiocopy(uio, &auio, count);
				if (error) {
					DFS_FREE_PARAMS(call);
					break;
				}
			}
		}
		result = DFS_CALL(ip->i_host,rwip,call);
#ifdef	DFS_BULKDATA
		if (usebulk) {
			if (result && rw == UIO_READ &&
			    xferinfo->resid != count - result->count)
				panic("dfsClient_rwip");

			/*
			 * Unlock the memory involved in the
			 * transfer and free the resources
			 * associated with the bulk data
			 * connection.
			 */
			pcopy_unlock(u.u_procp);
			vsunlock(uio->uio_iov->iov_base, count,
				 rw == UIO_READ ? B_READ : B_WRITE);
			bulk_destroy(bulkconn);
			m_free(m);
		}
#endif	DFS_BULKDATA
		if (!result)
			break;
		rcount = result->count;

#ifdef	DFS_BULKDATA
		/*
		 * If the data was transferred via bulk
		 * data, then the uio must be explicitly
		 * modified here since we do no uiocopy's.
		 */
		if (usebulk) {
			uioflush(rcount, uio);

		/*
		 * Otherwise, if this was a read then the
		 * data must be transferred.
		 */
		} else
#endif	DFS_BULKDATA
		{
		   if (rw == UIO_READ && rcount > 0) {
			mbuftouio(DFS_DATA(result), rcount,
				  &auio, DFS_UIOSIZE);
			error = uiocopy(&auio, uio, rcount);
			if (error) {
				DFS_FREE_PARAMS(result);
				break;
			}
		   }
		}
		if (rw == UIO_READ) {
			if (rcount > 0)
				dfs_stats.readBytes.client += rcount;
			dfs_incClient(reads);
		} else {
			dfs_incClient(writes);
			dfs_stats.writeBytes.client += rcount;
			mbuftoinode(ip, DFS_DATA(result));
		}
		resid  -= rcount;
		offset += rcount;
		error = result->error;

		/*
		 * If the call was interrupted on the
		 * remote end then, we perform a longjmp()
		 * here, effecting the equivalent of a
		 * single longjmp() from the sleep() function
		 * on the remote machine to the previous
		 * setjmp() on this machine.
		 */
		if (result->interrupted) {
			u.u_error = error;
			u.u_eosys = result->eosys;
			DFS_FREE_RESULTS(result);
			longjmp(&u.u_qsave);
		}
		DFS_FREE_RESULTS(result);
	} while (!error && rcount == count && resid > 0);

	/* uio->uio_resid = resid;  these are no longer meaningful */
	/* uio->uio_offset = offset;  these are no longer meaningful */

	return error;
}

/*
 * Server interface to rwip
 */
dfsr_rwip_t *
dfsServer_rwip(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_rwip_t *	call;		/* Call pkt -- params	*/
{
	register dfsr_rwip_t * result;
	register struct inode * ip;
	struct uio uuio;
	struct iovec uiov;
	enum uio_rw rw = (enum uio_rw)call->rw;
	register dfs_remote_t * rp;
#ifdef	DFS_NEW_RWIP
	register implicitLock;	/* !0 ==> serverSide to do ilock()/iunlock() */
#endif	DFS_NEW_RWIP

	dfs_incServer(rwip);
	if (!dfsServer_validContext(operation, call, "rwip")) {
		DFS_FREE_PARAMS(call);
		return NULL;
	}
	result = DFSR_VPACKET(rwip,
			      rw == UIO_READ ? (call->usebulk ? 0
							      : call->count)
			      		     : sizeof *ip);
	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;
		result->count	= 0;
		goto ret;
	}
	if (rp->flags & DFS_RMT_LOCKED)
		rp->locktime = UPTIME();
	if (!(ip = rp->ip)) {
		u.u_error	= DFS_EINVAL;
		result->count	= 0;
		goto ret;
	}
#ifdef	DFS_NEW_RWIP
	/*
	 * Lock the inode iff we are
	 * using the "implicit-lock" protocol.
	 */
	if (call->implicitLock)
		ilock(ip);
#endif	DFS_NEW_RWIP

	/*
	 * Allocate prpc (user) memory.  If this
	 * is a write then copy the data from the
	 * mbuf into the allocated memory.
	 */
	prpc_memsize(call->count);
	if (rw == UIO_WRITE) {
#ifdef	DFS_BULKDATA
		if (call->usebulk) {
			struct mbuf *m;
			xferinfo_t *xferinfo;
			bulk_connection_t *bulkconn;

			vslock((caddr_t)USRTEXT, call->count);
			MGET(m, M_WAIT, MT_DATA);
			xferinfo = mtod(m, xferinfo_t *);
			xferinfo->procp = u.u_procp;
			xferinfo->addr = (caddr_t)USRTEXT;
			xferinfo->resid = call->count;
			bulkconn = bulk_connect(clientConn, call->bulkid,
						&dfsServer_bulkinfo,
						(caddr_t)xferinfo, 1);
			/* BUG should be at BULKSPL */
			while (xferinfo->resid > 0)
				/* BUG should sleep at PRIxxx */
				sleep((caddr_t)xferinfo, PZERO);
			bulk_destroy(bulkconn);
			pcopy_unlock(u.u_procp);
			vsunlock((caddr_t)USRTEXT, call->count, B_READ);
		} else
#endif	DFS_BULKDATA
			mbuftouser(DFS_DATA(call), USRTEXT, call->count);
	}
	uiofilluser(&uuio, &uiov, call->offset, USRTEXT, call->count);
	DFS_SETJMP();
#ifndef	DFS_NEW_RWIP
	u.u_error = rwip(ip, &uuio, rw);
#else	DFS_NEW_RWIP
	u.u_error = rwip(ip, &uuio, rw, call->implicitLock);
#endif	DFS_NEW_RWIP
	DFS_UNSETJMP();
	result->count = call->count - uuio.uio_resid;
	if (rw == UIO_READ) {
#ifdef	DFS_BULKDATA
		if (call->usebulk && result->count > 0) {
			struct mbuf *m;
			xferinfo_t *xferinfo;
			bulk_connection_t *bulkconn;

			vslock((caddr_t)USRTEXT, result->count);
			MGET(m, M_WAIT, MT_DATA);
			xferinfo = mtod(m, xferinfo_t *);
			xferinfo->procp = u.u_procp;
			xferinfo->addr = (caddr_t)USRTEXT;
			xferinfo->resid = result->count;
			bulkconn = bulk_connect(clientConn, call->bulkid,
						&dfsServer_bulkinfo,
						(caddr_t)xferinfo, 0);
			bulk_send(bulkconn, result->count);
			/* BUG should be at BULKSPL */
			while (xferinfo->resid != -1)
				/* BUG should sleep at PRIxxx */
				sleep((caddr_t)xferinfo, PZERO);
			bulk_destroy(bulkconn);
			pcopy_unlock(u.u_procp);
			vsunlock((caddr_t)USRTEXT, result->count, B_WRITE);
		} else
#endif	DFS_BULKDATA
		   if (!call->usebulk)
			usertombuf(USRTEXT, DFS_DATA(result), result->count);

		dfs_incServer(reads);
		dfs_stats.readBytes.server += result->count;
	} else {
		dfs_incServer(writes);
		dfs_stats.writeBytes.server += result->count;
		inodetombuf(DFS_DATA(result), ip);
	}
#ifdef	DFS_NEW_RWIP
	/*
	 * Unlock the inode iff we
	 * "implicitly" locked it upon entry.
	 */
	if (call->implicitLock)
		iunlock(ip);
#endif	DFS_NEW_RWIP
	/*
	 * Free the memory we allocated.
	 */
	prpc_memsize(0);
ret:
	result->error = u.u_error;
	DFS_FREE_PARAMS(call);
	DFS_RETURN(result);
}

/*
 * The following three functions are used
 * when the operation is a read of the inode.
 */
dfsClient_rwipRecv(xferinfo, buffer, count)
   register xferinfo_t *xferinfo;
   caddr_t buffer;
   register int count;
{
	pcopy_lock(xferinfo->procp);
	pcopyout(xferinfo->procp, buffer, xferinfo->addr, count);
	xferinfo->addr += count;
	xferinfo->resid -= count;
}

dfsServer_rwipCopy(xferinfo, buffer, count)
   register xferinfo_t *xferinfo;
   caddr_t buffer;
   int count;
{
	pcopy_lock(xferinfo->procp);
	pcopyin(xferinfo->procp, xferinfo->addr, buffer, count);
	xferinfo->addr += count;
	xferinfo->resid -= count;
}

dfsServer_rwipSent(xferinfo)
   register xferinfo_t *xferinfo;
{
	if (xferinfo->resid != 0)
		panic("dfsServer_rwipSent");
	xferinfo->resid = -1;
	wakeup((caddr_t)xferinfo);
}

/*
 * The following two functions are used then
 * the operation is a write to the inode.
 */
dfsClient_rwipCopy(xferinfo, buffer, count)
   register xferinfo_t *xferinfo;
   caddr_t buffer;
   int count;
{
	pcopy_lock(xferinfo->procp);
	pcopyout(xferinfo->procp, buffer, xferinfo->addr, count);
	xferinfo->addr += count;
	xferinfo->resid -= count;
}

dfsServer_rwipRecv(xferinfo, buffer, count)
   register xferinfo_t *xferinfo;
   caddr_t buffer;
   register int count;
{
	pcopy_lock(xferinfo->procp);
	pcopyout(xferinfo->procp, buffer, xferinfo->addr, count);
	if ((xferinfo->resid -= count) == 0)
		wakeup((caddr_t)xferinfo);
	else
		xferinfo->addr += count;
}

/*
 * Ioctl file operations
 */
typedef struct dfsc_ioctl {	/* Pkt from client --> server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	caddr_t 	ip;		/* Remote file descriptor	*/
	int		size;		/* Size of the data portion	*/
	int		command;	/* Ioctl command		*/
	int		flag;		/* fp->f_flag			*/
	int		offset;		/* fp->f_offset			*/
	/* data follows the header */
} dfsc_ioctl_t;

typedef struct dfsr_ioctl {	/* 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 */
	/* data follows the header */
} dfsr_ioctl_t;

/*
 * Remote ioctl call.
 */
dfsClient_ioctl(ip, command, data, offset, flag)
	struct inode * ip;
	long command;
	long data;
{
	register dfsc_ioctl_t * call;
	register dfsr_ioctl_t * result;
	register size;
	register isize;

	dfs_incClient(ioctl);
	if (!dfsClient_validContext(ip, "ioctl"))
		goto ret;
	/*
	 * Interpret high order word to find
	 * amount of data to be copied to/from the
	 * user's address space.
	 */
	size = (command &~ (IOC_INOUT|IOC_VOID)) >> 16;
	isize = size ? size : sizeof(long);
	call = DFSC_VPACKET(ioctl, isize);
	if (!call)
		goto ret;
	call->ip = ip->i_rmt_ip;
	call->size = size;
	call->command = command;
	call->flag = flag;
	call->offset = offset;
	if (command & IOC_IN || command & IOC_VOID)
		mtombuf(DFS_DATA(call), data, isize);
	result = DFS_CALL(ip->i_host,ioctl,call);
	if (!result)
		goto ret;
	if (u.u_error = result->error) 
		goto free;
	if ((command & IOC_OUT) && size > 0)
		mbuftom(data, DFS_DATA(result), size);
free:
	DFS_FREE_RESULTS(result);
ret:
	return u.u_error;
}

/*
 * Remote ioctl.
 */
dfsr_ioctl_t *
dfsServer_ioctl(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_ioctl_t * call;		/* Call pkt -- params	*/
{
	register dfsr_ioctl_t * result;
	register int command = call->command;
	register struct inode * ip;
	register dfs_remote_t * rp;
	register int size = call->size;
	char data[IOCPARM_MASK+1];

	dfs_incServer(ioctl);
	if (!dfsServer_validContext(operation, call, "ioctl")) {
		DFS_FREE_PARAMS(call);
		return NULL;
	}
	result = DFSR_VPACKET(ioctl, call->size);
	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 ret;
	}
	if (!(ip = rp->ip)) {
		u.u_error = DFS_EINVAL;
		goto ret;
	}
	if (command & IOC_IN)
		mbuftom(data, DFS_DATA(call), size);
	else if (command & IOC_VOID)
		mbuftom(data, DFS_DATA(call), 4);
	u.u_error = ioctl1(ip, command, data, call->offset, call->flag);
	if ((command & IOC_OUT) && size)
		mtombuf(DFS_DATA(result), data, size);
ret:
	result->error = u.u_error;
	DFS_FREE_PARAMS(call);
	DFS_RETURN(result);
}
