/*
 * Write to an EFS file using the bulk
 * data transfer protocol.
 *
 * jam 850222
 * jht 850829 -- ifdef'd the bulk transfer facility with EFS_BULKDATA.
 */

#ifdef	EFS_BULKDATA
#include "../h/param.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/file.h"
#include "../h/buf.h"
#include "../h/uio.h"
#include "../h/vmparam.h"
#include "../vnet/vnet.h"
#include "../conn/conn.h"
#include "../rpc/rpc.h"
#include "../efs/efs.h"
#include "../vnet/bulkdata.h"

extern bulk_connection_t *bulk_create();
extern bulk_connection_t *bulk_connect();

struct efs_bulkwriteCall {
	u_long		rfd;		/* Remote file descriptor */
	long		offset;		/* Offset into remote file */
	int		size;		/* Number of bytes being written */
	bulk_id_t	bulkid;		/* ID of local BULK connection */
};

struct efs_bulkwriteReturn {
	char		junk;
	char		interrupted;	/* Call was interrupted */
	char		eosys;		/* How to handle system call return */
	char		error;		/* Error number or 0 on success */
	int		size;		/* Number of bytes actually written */
};

int efs_bulkwriteCopy();
int efs_bulkwriteRecv();

bulk_client_t efs_bwcclient = { 0, efs_bulkwriteCopy, 0, 0 };
bulk_client_t efs_bwsclient = { efs_bulkwriteRecv, 0, 0, 0 };

int
efs_bulkwrite(uio, remote, addr, size, offset, rsizep)
   struct uio *uio;
   register struct efs_remoteFile *remote;
   caddr_t addr;
   int size;
   int offset;
   int *rsizep;
{
	register bulk_connection_t *bulkconn;
	register struct efs_bulkwriteCall *params;
	register struct efs_bulkwriteReturn *results;
	int error;

	/*
	 * Lock the memory from which the transfer
	 * is going to take place.
	 */
	if (!useracc(addr, size, B_READ)) {
		*rsizep = 0;
		return(EFAULT);
	}
	vslock(addr, size);
	remote->procp = u.u_procp;
	remote->addr = addr;

	/*
	 * Make sure that the remote end has the
	 * correct environment for this transfer.
	 */
	efs_checkenv(remote);

	/*
	 * Create the sending end of the connection.
	 */
	bulkconn = bulk_create(remote->conn, &efs_bwcclient, (caddr_t)remote);

	/*
	 * Queue the send request with this
	 * end of the connection.
	 */
	bulk_send(bulkconn, size);

	/*
	 * Send the RPC request to the server
	 * which will connect the other end; the
	 * data will be transferred, and the RPC
	 * will return.
	 */
	params = efs_makePacket(bulkwrite,0);
	params->rfd = remote->rfd;
	params->offset = offset;
	params->size = size;
	params->bulkid = bulk_getid(bulkconn);
	results = (struct efs_bulkwriteReturn *)
			efs_call(remote->conn,EFS_BULKWRITE,params);

	/*
	 * Unlock the memory that we locked and
	 * destroy the connection.
	 */
	pcopy_unlock(u.u_procp);
	vsunlock(addr, size, B_WRITE);
	bulk_destroy(bulkconn);

	if (results == NULL) {
		*rsizep = 0;
		return(u.u_error);
	}

	error = results->error;
	*rsizep = results->size;
	uioflush(results->size, uio);

	/*
	 * 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 (results->interrupted) {
		u.u_error = error;
		u.u_eosys = results->eosys;
		rpc_freeResults(results);
		longjmp(&u.u_qsave);
	}
	rpc_freeResults(results);
	return(error);
}

caddr_t
efs_remoteBulkwrite(clientConn, clientId, operation, params)
   connection_t *clientConn;
   u_long clientId;
   u_short operation;
   struct efs_bulkwriteCall *params;
{
	register struct efs_bulkwriteReturn *results;
	register efs_localFile_t *local;

	results = efs_makeReturnPacket(params,bulkwrite, 0);
	if ((local = efs_rfdToLocal(params->rfd)) == NULL) {
		results->error = EBADF;
		results->interrupted = 0;
		results->size = 0;
	} else {
		bulk_connection_t *bulkconn;
		struct uio uuio;
		struct iovec uiov;
		caddr_t addr;

		/*
		 * Grab the correct environment for this
		 * file descriptor.
		 */
		efs_takeenv(local);

		/*
		 * Allocate and lock the memory into
		 * which the transfer is going to take
		 * place.
		 */
		prpc_memsize(params->size);
		addr = (caddr_t)USRTEXT;
		vslock(addr, params->size);

		/*
		 * Create the receiving end of the
		 * bulk data connection.
		 */
		local->procp = u.u_procp;
		local->addr = addr;
		local->resid = params->size;
		bulkconn = bulk_connect(local->conn, params->bulkid,
					&efs_bwsclient, (caddr_t)local, 1);

		/*
		 * Wait for all of the data to be
		 * received.
		 */
		/* BUG should be at BULKSPL */
		while (local->resid > 0)
			/* BUG should sleep at PRIxxx */
			sleep((caddr_t)local, PZERO);

		/*
		 * For now we always destroy the bulk data
		 * connection.
		 */
		bulk_destroy(bulkconn);

		/*
		 * Unlock the memory and do the actual
		 * write transfer from the memory allocated
		 * above.
		 */
		pcopy_unlock(u.u_procp);
		vsunlock(addr, params->size, B_READ);
		uiofilluser(&uuio, &uiov, params->offset, addr, params->size);
		EFS_SETJMP();
		u.u_error = rwip((struct inode *)local->fp->f_data, &uuio,
				 UIO_WRITE);
		EFS_UNSETJMP();
		results->size = params->size - uuio.uio_resid;

		prpc_memsize(0);
	}
	rpc_freeParams(params);
	return((caddr_t)results);
}

efs_bulkwriteCopy(remote, buffer, count)
   register struct efs_remoteFile *remote;
   caddr_t buffer;
   register int count;
{
	pcopy_lock(remote->procp);
	pcopyin(remote->procp, remote->addr, buffer, count);
	remote->addr += count;
}

efs_bulkwriteRecv(local, buffer, count)
   register efs_localFile_t *local;
   caddr_t buffer;
   register int count;
{
	pcopy_lock(local->procp);
	pcopyout(local->procp, buffer, local->addr, count);
	if ((local->resid -= count) == 0) {
		/*
		 * The entire write buffer has been
		 * received.  Wakeup the server process
		 * so that it can perform the actual
		 * write and send the RPC return.
		 */
		wakeup((caddr_t)local);
	}
	else
		local->addr += count;
}
#endif	EFS_BULKDATA
