static char rcsid[] = "$Header: efs_write.c,v 820.1 86/12/04 19:47:51 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.				*
*									*
************************************************************************/

/*
 * Write on a remote file.  Break it up
 * into packets no larger than EFS_WRITESIZE.
 *
 * jam 840207-09-11-22-0424
 * sas 840217 -- Added references to offset so as not to change the value of
 *               fp->f_offset which is managed by rwuio()
 * jht 850829 -- ifdef'd the bulk transfer facility with EFS_BULKDATA.
 */

#include "../h/param.h"
#include "../h/dir.h"
#include "../h/file.h"
#include "../h/user.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"

struct efs_writeCall {
	u_long		rfd;		/* Remote file descriptor */
	long		offset;		/* Offset into remote file */
	int		size;		/* Number of bytes being written */
	/* Data follows this structure */
};

struct efs_writeReturn {
	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 */
};

efs_write(dev, uio)
   dev_t dev;
   struct uio *uio;
{
	register int error;
	struct file *fp = getf(u.u_ap[0]);
	struct efs_remoteFile *remote = fp->f_remote;
	register int offset = fp->f_offset;

	/*
	 * A write to the character device is actually
	 * a return packet to be sent.
	 */
	if (remote == NULL)
		return(EIO);

	/*
	 * Writes on remote files must be fragmented.
	 * If an error occurs return the error to the
	 * caller without writing any more data.
	 */
	while (uio->uio_resid) {
		register struct iovec *iov = uio->uio_iov;
		register int size = iov->iov_len;
		register struct efs_writeCall *params;
		register struct efs_writeReturn *results;
		int rsize;
		struct uio auio;
		struct iovec aiov[20];

		if (remote->state == EFS_FILE_CRASHED)
			return(ESERVERCRASHED);

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

#ifdef	EFS_BULKDATA
		/*
		 * If the transfer is bigger than a single
		 * EFS_WRITE request can be then use the
		 * bulk data protocol.  We must do this to
		 * ensure atomicity of the transfer (e.g.
		 * when writing to tape).  If the server
		 * does not support bulk transfers then we
		 * flag the remote descriptor and use the
		 * normal (non-bulk) mechanism.
		 */
		if (!remote->nobulk && size > EFS_WRITESIZE) {
			error = efs_bulkwrite(uio, remote, iov->iov_base, size,
					      offset, &rsize);
			if (error != EREMOTEOPERATION) {
				offset += rsize;
				if (error || rsize != size)
					break;
				continue;
			}
			/*
			 * We had an EREMOTEOPERATION.
			 * Prevent further use of bulk transfers.
			 */
			remote->nobulk = 1;
		}
#else	EFS_BULKDATA
		/*
		 * While the bulkdata facility is still extant,
		 * we decline to use the bulk transfer facility.
		 */
		remote->nobulk = 1;
#endif	EFS_BULKDATA

		/*
		 * Create the call packet and make
		 * the call.
		 */
		if (size > EFS_WRITESIZE)
			size = EFS_WRITESIZE;
		params = efs_makePacket(write,size);
		params->rfd = remote->rfd;
		params->offset = offset;
		params->size = size;
		auio.uio_iov = &aiov[0];
		mbuftouio(efs_data(params), size, &auio, 20);
		error = uiocopy(uio, &auio, size);
		if (error) {
			rpc_freeParams(params);
			break;
		}
		efs_incClient(writes);
		results = (struct efs_writeReturn *)
					efs_call(remote->conn,EFS_WRITE,params);

		/*
		 * Get the information from the
		 * results.
		 */
		if (results == NULL)
			return(u.u_error);
		rsize = results->size;
		error = results->error;
		efs_stats.writeBytes.client += rsize;

		/*
		 * 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);
		if (error || rsize != size)
			break;
		offset += rsize;
	}
	return(error);
}

caddr_t
efs_remoteWrite(clientConn, clientId, operation, params)
   connection_t *clientConn;
   u_long clientId;
   u_short operation;
   struct efs_writeCall *params;
{
	register struct efs_writeReturn *results;
	register efs_localFile_t *local;

	efs_incServer(writes);
	results = efs_makeReturnPacket(params,write, 0);
	if ((local = efs_rfdToLocal(params->rfd)) == NULL) {
		results->error = EBADF;
		results->interrupted = 0;
		results->size = 0;
	} else {
		struct uio uuio;
		struct iovec uiov;

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

		/*
		 * Allocate prpc (user) memory and copy
		 * the data from the packet into it.
		 */
		prpc_memsize(params->size);
		mbuftouser(efs_data(params), USRTEXT, params->size);

		/*
		 * Write the data from the prpc memory into
		 * the file.
		 */
		uiofilluser(&uuio, &uiov, params->offset, USRTEXT,
			    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);
	}
	efs_stats.writeBytes.server += results->size;
	rpc_freeParams(params);
	return((caddr_t)results);
}
