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

/*
 * Read on a remote file.  Break it up
 * into packets no larger than EFS_READSIZE.
 *
 * jam 840207-09-22-0424
 * sas 840217 -- Added references to offset so as not to change value of
 *               fp->f_offset which is managed in 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_readCall {
	u_long		rfd;		/* Remote file descriptor */
	long		offset;		/* Offset into remote file */
	int		size;		/* Number of bytes being read */
};

struct efs_readReturn {
	char		junk;
	char		interrupted;	/* Call was interrupted by signal */
	char		eosys;		/* How to return from system call */
	char		error;		/* Error number or 0 on success */
	int		size;		/* Number of bytes actually read */
	/* Data follows this structure */
};

efs_read(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;

	/*
	 * Reads of the character device itself will
	 * block until a packet has been received and
	 * will return the packet as the data.
	 */
	if (remote == NULL)
		return(EIO);

	/*
	 * Reads on remote files will be fragmented and
	 * sent to the server.  An error at any point will
	 * cause termination of the read and an error to
	 * be returned to the caller.
	 */
	while (uio->uio_resid) {
		register struct iovec *iov = uio->uio_iov;
		register int size = iov->iov_len;
		register struct efs_readCall *params;
		register struct efs_readReturn *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_READ request can be, then use the
		 * bulk data protocol.  We must do this to
		 * ensure atomicity of the transfer (e.g.
		 * when reading from tape).
		 */
		if (!remote->nobulk && size > EFS_READSIZE) {
			error = efs_bulkread(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_READSIZE)
			size = EFS_READSIZE;
		params = efs_makePacket(read, 0);
		params->rfd = remote->rfd;
		params->offset = offset;
		params->size = size;
		efs_incClient(reads);
		results = (struct efs_readReturn *)
					efs_call(remote->conn,EFS_READ,params);

		/*
		 * Get the information from the
		 * results.
		 */
		if (results == NULL)
			return(u.u_error);
		rsize = results->size;
		efs_stats.readBytes.client += rsize;
		auio.uio_iov = &aiov[0];
		mbuftouio(efs_data(results), rsize, &auio, 20);
		error = uiocopy(&auio, uio, rsize);
		/* BUG what if we just got an EFAULT */
		error = results->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 (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_remoteRead(clientConn, clientId, operation, params)
   connection_t *clientConn;
   u_long clientId;
   u_short operation;
   struct efs_readCall *params;
{
	register struct efs_readReturn *results;
	register efs_localFile_t *local;

	efs_incServer(reads);
	results = efs_makeReturnPacket(params,read,params->size);
	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 into which
		 * the data will be read and set up a
		 * uio structure to use for reading the
		 * file into the prpc memory.
		 */
		prpc_memsize(params->size);
		uiofilluser(&uuio, &uiov, params->offset, USRTEXT,
			    params->size);

		/*
		 * Read the data from the file into the
		 * prpc memory.
		 */
		EFS_SETJMP();
		u.u_error = rwip((struct inode *)local->fp->f_data, &uuio,
				 UIO_READ);
		EFS_UNSETJMP();
		results->size = params->size - uuio.uio_resid;

		/*
		 * Copy the data from prpc memory
		 * into the result packet and free
		 * the prpc memory.
		 */
		usertombuf(USRTEXT, efs_data(results), params->size);
		prpc_memsize(0);
	}
	efs_stats.readBytes.server += results->size;
	rpc_freeParams(params);
	return((caddr_t)results);
}
