static char rcsid[] = "$Header: dfs_misc.c,v 820.1 86/12/04 19:44:21 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 subroutines
 *
 *	Bakul Shah
 *
 * bvs 840321 --original version
 * jht 841115,16,26-29 -- complete conversion to use prpc.
 * jht 850324 -- cleanup interface to connection-layer.
 * jht 850401 -- fill out the gathering of statistics.
 */

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


#define UIO_SEG_KD	1	/* Kernel data */

extern	struct inode *	dfsClient_namei();
extern	connection_t *	conn_findByName();

/*
 * Get the root inode of remote node.
 * *cpp points to <hostname>.
 * Return NULL if the host does not exist in the host table.
 * *cpp is updated to point to
 * the next component after <hostname>.
 */
struct inode *
dfs_root(cp, flag, follow)
	register char	*	cp;
{
	register struct	inode *	ip;
	register connection_t *	conn;
	register char	*	hostname = cp;
	
	/*
	 * skip over the hostname and null terminate it.
	 */
	while (*cp && *cp != '/')
		cp++;
	/*
	 * replace any '/'s after the hostname by nulls.
	 */
	while (*cp && *cp == '/')
		*cp++ = '\0';

	/*
	 * Map the next component of the pathname
	 * into a connection_t *.
	 */
	conn = conn_findByName(hostname);
	if (!conn) {
		u.u_error = DFS_ENOHOST;
		return NULL;
	}
#ifdef	DFS_SELF_CONNECTION
	/*
	 * Return with our root
	 * if this is a self connection
	 */
	if (nodecmp(conn->node, myNode)) {
		/*
		 * Rebuild the bridge between the hostname
		 * and the rest of the pathname.
		 */
		*--cp = '/';
		DFS_DEBUG(12,("dfs_root/I-0: self reference: cp='%s'\n", cp));
		/*
		 * We do not
		 *	ilock(rootdir); rootdir->i_count++;
		 * because namei(), in continuing
		 * to scan the pathname, will do so.
		 * We have also overloaded the semantics
		 * of the returned inode pointer -- rootdir,
		 * c.f., namei()..
		 */
		conn_free(conn);
		return rootdir;
	}
#endif	DFS_SELF_CONNECTION
 	/*
	 * We have the connection.
	 * Now make a remote call to open the root.
	 */
	ip = dfsClient_namei(cp, flag, follow, conn, NULL);
	conn_free(conn);
	return ip;
}

#ifndef	VALID_EFS
/*
 * Fill a uio structure and it's iovec
 * structures with values that represent
 * the data in the mbuf chain whose top is
 * supplied.  Return the total number of
 * bytes of data on success and -1 on failure.
 */
int
mbuftouio(base, len, uio, maxiov)
   register caddr_t	base;
   register int		len;
   register struct uio *uio;
   register int		maxiov;
{
	register struct iovec *	iov = uio->uio_iov;
	register struct mbuf  *	m;
	register int		size;

	dfs_incTotal(mbuftouio);
	uio->uio_segflg = UIO_SEG_KD;
	uio->uio_iovcnt = 0;
	uio->uio_resid  = 0;
	/*
	 * First one is a special case since data does not
	 * necessarily begin at the first byte of data in
	 * the mbuf.
	 */
	m = dtom(base);
	size = m->m_len - ((int)base - mtod(m, int));
	if (size <= 0)
		panic("mbuftouio");
	while (len) {
		if (++uio->uio_iovcnt > maxiov)
			return(-1);
		size = MIN(size, len);
		uio->uio_resid += size;
		iov->iov_len = size;
		iov->iov_base = base;
		++iov;
		len -= size;
		if ((m = m->m_next) == 0)
			break;
		size = m->m_len;
		base = mtod(m, caddr_t);
	}
	return(uio->uio_resid);
}

/*
 * Copy data from one uio to another uio.
 * This is done one iov at a time using
 * whichever iov has the smaller amount
 * of data.  Return an error code if any
 * errors are encountered.  Note: one of
 * the uios must represent data in kernel
 * space.
 */
uiocopy(suio, duio, count)
	register struct uio *suio,
		       *duio;
	register int		count;
{
	dfs_incTotal(uiocopy);
	if (suio->uio_segflg != UIO_SEG_KD && duio->uio_segflg != UIO_SEG_KD ||
	    suio->uio_resid < count || duio->uio_resid < count)
		panic("uiocopy");
	while (count) {
		register struct iovec *siov = suio->uio_iov;
		register struct iovec *diov = duio->uio_iov;
		register int size = MIN(siov->iov_len, diov->iov_len);
		register int error;

		size = MIN(size, count);
		if (suio->uio_segflg == UIO_SEG_KD) {
			error = uiomove(siov->iov_base, size, UIO_READ, duio);
			uioflush(size, suio);
		} else {
			error = uiomove(diov->iov_base, size, UIO_WRITE, suio);
			uioflush(size, duio);
		}
		if (error)
			return(error);
		count -= size;
	}
	return(0);
}

/*
 * mbufchar is analogous to uchar and schar
 * in that it returns the next character from
 * u.u_dirp in the appropriate address space.
 * It does, however, perform an mtod and
 * guarantee that mbuf chains are followed.
 */
mbufchar()
{
	register struct mbuf *	m;
	register char		c;

	if (!u.u_dirp) {
		u.u_error = DFS_EFAULT;
		return 0;
	}
	c = *u.u_dirp++;
	m = dtom(u.u_dirp);
	if (u.u_dirp  >= mtod(m, char *) + m->m_len) {
		m = m->m_next;
		u.u_dirp = m ? mtod(m, caddr_t) : NULL;
	}
	return c;
}
#endif	not VALID_EFS

/*
 * Structure is the address of the structure in an mbuf.
 * Field is the address of a field,
 * as if structure were really in a contiguous piece of memory.
 * The routine returns address in the correct mbuf
 * where the field starts,
 * or NULL we run out of the chain.
 */
caddr_t
m_field(structure, field)
	register caddr_t	structure;
	register caddr_t	field;
{
	register struct	mbuf *	m = dtom(structure);
	register long		offset;

	if (!m)
		return NULL;
	/*
	 * Pull up structure to point to beginning of data
	 */
	structure = mtod(m, caddr_t);

	/*
	 * Turn field address into offset from beginning of data
	 */
	offset = field - structure;

	/*
	 * Skip over leading mbufs
	 */
	for (;;) {
		if (m->m_len > offset)
			break;
		offset -= m->m_len;
		m = m->m_next;
		if (!m)
			return NULL;
	}
	return mtod(m, caddr_t) + offset;
}

/*
 * Copy size bytes from source to destination.
 * The destination is in an mbuf chain.
 * The source may NOT span an mbuf boundary,
 * and generally is not an mbuf.
 * The routine returns the number of bytes that can not be copied.
 */
mtombuf(destination, source, size)
	register caddr_t	destination;	/* Ptr to an mbuf chain	*/
	register caddr_t	source;		/* May not span mbufs!	*/
	register int		size;
{
	register struct mbuf *	m = dtom(destination);	/* Normalize */
	register long		len;
#ifdef	DEBUG
	caddr_t			origDestination	= destination;
	caddr_t			origSource	= source;
	int			origSize	= size;
#endif	DEBUG

	dfs_incTotal(mtombuf);
	if (!m || !source || size <= 0)
		return size;
	/*
	 * Amount of data that can fit
	 * in the first destination mbuf.
	 */
	len = mtod(m, long) + m->m_len - (long)destination;
	if (len > size)
		len = size;

	for (;;) {
		bcopy(source, destination, len);
		size -= len;
		if (size <= 0)
			break;
		m = m->m_next;
		if (!m)
			break;
		source += len;
		destination = mtod(m, caddr_t);	/* Data area within mbuf */
		len = MIN(size, m->m_len);
	}
#ifdef	DEBUG
	if (size) {
		printf("mtombuf: dest=0x%X source=0x%X, size=0x%x residue=0x%X\n",
			origDestination, origSource, origSize, size);
		panic("mtombuf: incomplete transfer");
	}
#endif	DEBUG
	return size;
}

/*
 * Copy size bytes from source to destination.
 * The source is in an mbuf chain.
 * The routine returns the number of bytes that can not be copied.
 */
mbuftom(destination, source, size)
	register caddr_t	destination;	/* May not span mbufs!	*/
	register caddr_t	source;		/* Ptr to an mbuf chain	*/
	register int		size;
{
	register struct mbuf *	m = dtom(source);	/* Normalize */
	register long		len;
#ifdef	DEBUG
	caddr_t			origDestination	= destination;
	caddr_t			origSource	= source;
	int			origSize	= size;
#endif	DEBUG

	dfs_incTotal(mbuftom);
	if (!m || !destination || size <= 0)
		return size;
	len = mtod(m, long) + m->m_len - (long)source;
	if (len > size)
		len = size;
	for (;;) {
		bcopy(source, destination, len);
		size -= len;
		if (size <= 0)
			break;
		m = m->m_next;
		if (!m)
			break;
		destination += len;
		source = mtod(m, caddr_t);
		len = MIN(size, m->m_len);
	}
#ifdef	DEBUG
	if (size) {
		printf("mbuftom: dest=0x%X source=0x%X, size=0x%x residue=0x%X\n",
			origDestination, origSource, origSize, size);
		panic("mbuftom: incomplete transfer");
	}
#endif	DEBUG
	return size;
}

/*
 * Look up the host from u.u_dirp just
 * as namei() would.  Return a pointer
 * to the host table entry for the host
 * if it exists, else return NULL.  Also
 * return NULL if the host name is too
 * long.
 */
connection_t *
dfs_uhostLookup()
{
	register connection_t *	conn;
	char		        hostname[DFS_HOSTNAME_MAXLEN];
	register char	*	cp = &hostname[0];

	while ((*cp = uchar()) && *cp != '/')
		if (++cp == &hostname[DFS_HOSTNAME_MAXLEN])
			return(NULL);
	--u.u_dirp;
	*cp = '\0';

	/*
	 * Locate the connection information
	 * and make no change to the connection count.
	 */
	conn = conn_findByName(hostname);
	if (conn)
		conn_free(conn);
	return conn;
}

/*
 * To retain the UNIX paradigm,
 * a local-root shall be effectively a network-root,
 * unless the system administrator says otherwise.
 *
 * For an unspecified node,
 * we map root to the following
 * uid/gid values:
 */
int dfs_suuid = 0;
int dfs_sugid = 0;

/*
 * Lookup the list of nodes, allowed
 * super user access.  If the node
 * is not in the list, return dfs_suuid.
 */
dfs_uid(node)
	register node_t * node;
{
	/* BUG: no database of hosts vs uid's */
	return dfs_suuid;
}

/*
 * Lookup the list of nodes, allowed
 * super user access.  If the node
 * is not in the list, return dfs_sugid.
 */
dfs_gid(node)
	register node_t * node;
{
	/* BUG: no database of hosts vs gid's */
	return dfs_sugid;
}

/*
 * Copy inode image from an mbuf * to a local inode.
 * Copy only the relevant fields.
 */
mbuftoinode(ip, rp)
	register struct inode * ip;	/* Destination	*/
	register struct inode * rp;	/* Source inode	*/
{
	register u_short flag = *(u_short *)m_field(rp, &rp->i_flag);

	dfs_incTotal(mbuftoinode);
	/*
	 * We can't copy ILOCKED, IWANTED flags
	 * as they are local flags.
	 * CHECK if ILWAIT flag has to be non-local.
	 */
	ip->i_flag |= DFS_INODE_FLAGS(flag);

	/*
	 * Copy any other needed transient
	 * DFS inode information.
	 */
#ifdef	DFS_WCOUNT
	ip->i_wcount	= *(u_short *) m_field(rp, &rp->i_wcount);
#endif	DFS_WCOUNT
	ip->i_shlockc	= *(u_short *) m_field(rp, &rp->i_shlockc);
	ip->i_exlockc	= *(u_short *) m_field(rp, &rp->i_exlockc);

	/*
	 * Copy all of the disk-resident
	 * portion of the inode.
	 */
	mbuftom(&ip->i_ic,
		m_field(rp, &rp->i_ic),
		sizeof (rp->i_ic));
}

/*
 * Validate the context
 * within a dfsClient_*() routine
 * prior to getting a call-pkt.
 * 
 * Return	-1 iff all OK,
 *		and set u.u_error appropriately;
 * 
 *		Otherwise NULL.
 */
dfsClient_validContext(ip, cp)
	register struct inode	* ip;
	register char		* cp;		/* String labeling our caller */
{
	if (!dfs_enabled) {			/* DFS is not enabled */
		DFS_DEBUG(16,("dfsClient_%s: dfsServer_validContext/I-0: DFS disabled.\n", cp));
		u.u_error = DFS_ENOTENABLED;
		return NULL;
	}
	dfs_incClient(validContext);
	/*
	 * Check for those dfsClient_*() routines
	 * which do not receive an inode ptr.
	 */
	if (-1 == (int) ip) {
		return NULL;
	}
	if (!ip) {
		dfs_incClientAbort(validContext);
		DFS_DEBUG(8,("dfsClient_%s: dfsClient_validContext/E-1: ip=0\n", cp));
		u.u_error = DFS_EINVAL;
		return NULL;
	}
	if (ip->i_flag & IDEADSERVER) {		/* Server for agent is dead */
		dfs_incClientAbort(validContext);
		u.u_error = DFS_ECRASHED;
		return NULL;
	}
	if (ip == DFS_ILLEGAL_INODE) {
		dfs_incClientAbort(validContext);
		DFS_DEBUG(8,("dfsClient_%s: dfsClient_validContext/E-2: ip=0X%X\n", cp, ip));
		u.u_error = DFS_EINVAL;
		return NULL;
	}
	return -1;	/* All is OK */
}

#ifdef	DFS_PACKETHDR
 extern dfsPktHdr_t	myDfsPktHdr;	/* What we support */
#endif	DFS_PACKETHDR
/*
 * Validate the context
 * within a dfsServer_*() routine
 * prior to getting a result-pkt.
 * 
 * Return	-1 iff all OK,
 *		and set u.u_error appropriately;
 * 
 *		Otherwise NULL.
 */
dfsServer_validContext(operation, callPktp, cp)
	register 		  operation;
	register caddr_t	  callPktp;	/* Addr of call packet or NULL*/
	register char		* cp;		/* String labeling our caller */
{
	dfs_incServer(validContext);
	if (!dfs_enabled) {
		dfs_incServerAbort(validContext);
		/*
		 * This "cannot happen"!
		 */
		DFS_DEBUG(8,("%s: dfsServer_validContext/E-0: DFS disabled!\n", cp));
		u.u_error = DFS_ENOTENABLED;
		return NULL;
	}
#ifdef	DFS_PACKETHDR
	/*
	 * Are we even vaguely compatible?
	 */
	if	      (myDfsPktHdr.dfs_version.majorVersNo
	 != ((dfsPktHdr_t *)callPktp)->dfs_version.majorVersNo) {
		dfs_badVersion(	callPktp,
				NULL,
				cp,
				"dfsServer_validContext/E-1: Incompatible major version #!\n");
		dfs_incServerAbort(validContext);
		return NULL;
	}
	/*
	 * Are we closely compatible?
	 */
	if	      (myDfsPktHdr.dfs_version.minorVersNo
	 != ((dfsPktHdr_t *)callPktp)->dfs_version.minorVersNo) {
		dfs_badVersion(	callPktp,
				NULL,
				cp,
				"dfsServer_validContext/E-2: Incompatible minor version #!\n");
		dfs_incServerAbort(validContext);
		return NULL;
	}
	if (operation >= sizeof(myDfsPktHdr.functions) * NBBY) {
		dfs_badVersion(callPktp, NULL, cp, "dfsServer_validContext/E-3: operation=0x%x beyond range=0x%X\n", operation, sizeof(myDfsPktHdr.functions) * NBBY );
		dfs_incServerAbort(validContext);
		return NULL;
	}
	/*
	 * Can we service the operation?
	 */
	if (!(myDfsPktHdr.functions & (1<<operation))) {
		dfs_badVersion(callPktp, NULL, cp, "dfsServer_validContext/E-4: operation=0x%x NOT supported!\n", operation);
		dfs_incServerAbort(validContext);
		return NULL;
	}
#endif	DFS_PACKETHDR

	return -1;	/* All is OK */
}

/*
 * Conditionally report a DFS version mismatch.
 */
dfs_badVersion(callPktp, resultPktp, s, t, a,b,c,d,e,f,g,h)
	register caddr_t	  callPktp;	/*   Call-packet addr	  */
	register caddr_t	  resultPktp;	/* Result-packet addr	  */
	register char		* s;		/* Caller's caller: label */
	register char		* t;		/* Caller:	    label */
	register caddr_t	* a,b,c,d,e,f,g,h;	/* printf() parms */
{
	if (s && *s) {
		DFS_DEBUG(8,("\n%s: ", s));
	}
#ifdef	DFS_PACKETHDR
	DFS_DEBUG(8,("dfs_badVersion: myMajor/myMinor version=%d/%d myFunctions=0x%X\n",
		myDfsPktHdr.dfs_version.majorVersNo,
		myDfsPktHdr.dfs_version.minorVersNo,
		myDfsPktHdr.functions));
	if (callPktp) {
	    DFS_DEBUG(8,("dfs_badVersion:    maj/min version=%d/%d clientFunctions=0x%X\n",
		((dfsPktHdr_t *)callPktp)->dfs_version.majorVersNo,
		((dfsPktHdr_t *)callPktp)->dfs_version.minorVersNo,
		((dfsPktHdr_t *)callPktp)->functions));
	    DFS_DEBUG(8,("dfs_badVersion/I-1: incompatible pkt from CLIENT:\n"));
	}
	if (resultPktp) {
	    DFS_DEBUG(8,("   maj/min version=%d/%d ServerFunctions=0x%X\n",
		((dfsPktHdr_t *)resultPktp)->dfs_version.majorVersNo,
		((dfsPktHdr_t *)resultPktp)->dfs_version.minorVersNo,
		((dfsPktHdr_t *)resultPktp)->functions));
	    DFS_DEBUG(8,("dfs_badVersion/I-1: incompatible pkt from SERVER:\n"));
	}
#endif	DFS_PACKETHDR
	DFS_DEBUG(8,(t, a,b,c,d,e,f,g,h));
	u.u_error = DFS_EINCOMPAT;	/* DFS version incompatibility */
}
