usr/src/uts/common/fs/nfs/nfs3_xdr.c
author Siddheshwar Mahesh <Siddheshwar.Mahesh@Sun.COM>
Fri, 10 Apr 2009 22:57:35 -0700
changeset 9348 7155ecb17858
parent 7387 0b3a92e31fd8
child 10954 2b7813ae5034
permissions -rw-r--r--
6760947 NFS/RDMA port should be changed to IANA assigned 20049 6762173 rdma panic on writes from linux client 6790590 readdir fails from Linux client against Solaris server 6790588 linux client fails to decode READ replies from Solaris server 6790586 Solaris server should better handle chunked RPC/RDMA messages 6826476 rpcib leaks memory registrations while handling multiple chunks

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */

#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/dirent.h>
#include <sys/vfs.h>
#include <sys/stream.h>
#include <sys/strsubr.h>
#include <sys/debug.h>
#include <sys/t_lock.h>
#include <sys/cmn_err.h>
#include <sys/dnlc.h>
#include <sys/cred.h>
#include <sys/time.h>
#include <sys/sdt.h>

#include <rpc/types.h>
#include <rpc/xdr.h>

#include <nfs/nfs.h>
#include <nfs/rnode.h>
#include <rpc/rpc_rdma.h>

/*
 * These are the XDR routines used to serialize and deserialize
 * the various structures passed as parameters across the network
 * between NFS clients and servers.
 */

/*
 * XDR null terminated ASCII strings
 * xdr_string3 deals with "C strings" - arrays of bytes that are
 * terminated by a NULL character.  The parameter cpp references a
 * pointer to storage; If the pointer is null, then the necessary
 * storage is allocated.  The last parameter is the max allowed length
 * of the string as allowed by the system.  The NFS Version 3 protocol
 * does not place limits on strings, but the implementation needs to
 * place a reasonable limit to avoid problems.
 */
bool_t
xdr_string3(XDR *xdrs, char **cpp, uint_t maxsize)
{
	char *sp;
	uint_t size;
	uint_t nodesize;
	bool_t mem_alloced = FALSE;

	/*
	 * first deal with the length since xdr strings are counted-strings
	 */
	sp = *cpp;
	switch (xdrs->x_op) {
	case XDR_FREE:
		if (sp == NULL || sp == nfs3nametoolong)
			return (TRUE);	/* already free */
		/* FALLTHROUGH */

	case XDR_ENCODE:
		size = (uint_t)strlen(sp);
		break;

	case XDR_DECODE:
		break;
	}

	if (!xdr_u_int(xdrs, &size))
		return (FALSE);

	/*
	 * now deal with the actual bytes
	 */
	switch (xdrs->x_op) {
	case XDR_DECODE:
		if (size >= maxsize) {
			*cpp = nfs3nametoolong;
			if (!XDR_CONTROL(xdrs, XDR_SKIPBYTES, &size))
				return (FALSE);
			return (TRUE);
		}
		nodesize = size + 1;
		if (nodesize == 0)
			return (TRUE);
		if (sp == NULL) {
			sp = kmem_alloc(nodesize, KM_NOSLEEP);
			*cpp = sp;
			if (sp == NULL)
				return (FALSE);
			mem_alloced = TRUE;
		}
		sp[size] = 0;

		if (xdr_opaque(xdrs, sp, size)) {
			if (strlen(sp) != size) {
				if (mem_alloced)
					kmem_free(sp, nodesize);
				*cpp = NULL;
				return (FALSE);
			}
		} else {
			if (mem_alloced)
				kmem_free(sp, nodesize);
			*cpp = NULL;
			return (FALSE);
		}
		return (TRUE);

	case XDR_ENCODE:
		return (xdr_opaque(xdrs, sp, size));

	case XDR_FREE:
		nodesize = size + 1;
		kmem_free(sp, nodesize);
		*cpp = NULL;
		return (TRUE);
	}

	return (FALSE);
}

/*
 * XDR_INLINE decode a filehandle.
 */
bool_t
xdr_inline_decode_nfs_fh3(uint32_t *ptr, nfs_fh3 *fhp, uint32_t fhsize)
{
	uchar_t *bp = (uchar_t *)ptr;
	uchar_t *cp;
	uint32_t dsize;
	uintptr_t resid;

	/*
	 * Check to see if what the client sent us is bigger or smaller
	 * than what we can ever possibly send out. NFS_FHMAXDATA is
	 * unfortunately badly named as it is no longer the max and is
	 * really the min of what is sent over the wire.
	 */
	if (fhsize > sizeof (fhandle3_t) || fhsize < (sizeof (fsid_t) +
	    sizeof (ushort_t) + NFS_FHMAXDATA +
	    sizeof (ushort_t) + NFS_FHMAXDATA)) {
		return (FALSE);
	}

	/*
	 * All internal parts of a filehandle are in native byte order.
	 *
	 * Decode what should be fh3_fsid, it is aligned.
	 */
	fhp->fh3_fsid.val[0] = *(uint32_t *)bp;
	bp += BYTES_PER_XDR_UNIT;
	fhp->fh3_fsid.val[1] = *(uint32_t *)bp;
	bp += BYTES_PER_XDR_UNIT;

	/*
	 * Decode what should be fh3_len.  fh3_len is two bytes, so we're
	 * unaligned now.
	 */
	cp = (uchar_t *)&fhp->fh3_len;
	*cp++ = *bp++;
	*cp++ = *bp++;
	fhsize -= 2 * BYTES_PER_XDR_UNIT + sizeof (ushort_t);

	/*
	 * For backwards compatability, the fid length may be less than
	 * NFS_FHMAXDATA, but it was always encoded as NFS_FHMAXDATA bytes.
	 */
	dsize = fhp->fh3_len < NFS_FHMAXDATA ? NFS_FHMAXDATA : fhp->fh3_len;

	/*
	 * Make sure the client isn't sending us a bogus length for fh3x_data.
	 */
	if (fhsize < dsize)
		return (FALSE);
	bcopy(bp, fhp->fh3_data, dsize);
	bp += dsize;
	fhsize -= dsize;

	if (fhsize < sizeof (ushort_t))
		return (FALSE);
	cp = (uchar_t *)&fhp->fh3_xlen;
	*cp++ = *bp++;
	*cp++ = *bp++;
	fhsize -= sizeof (ushort_t);

	dsize = fhp->fh3_xlen < NFS_FHMAXDATA ? NFS_FHMAXDATA : fhp->fh3_xlen;

	/*
	 * Make sure the client isn't sending us a bogus length for fh3x_xdata.
	 */
	if (fhsize < dsize)
		return (FALSE);
	bcopy(bp, fhp->fh3_xdata, dsize);
	fhsize -= dsize;
	bp += dsize;

	/*
	 * We realign things on purpose, so skip any padding
	 */
	resid = (uintptr_t)bp % BYTES_PER_XDR_UNIT;
	if (resid != 0) {
		if (fhsize < (BYTES_PER_XDR_UNIT - resid))
			return (FALSE);
		bp += BYTES_PER_XDR_UNIT - resid;
		fhsize -= BYTES_PER_XDR_UNIT - resid;
	}

	/*
	 * Make sure client didn't send extra bytes
	 */
	if (fhsize != 0)
		return (FALSE);
	return (TRUE);
}

static bool_t
xdr_decode_nfs_fh3(XDR *xdrs, nfs_fh3 *objp)
{
	uint32_t fhsize;		/* filehandle size */
	uint32_t bufsize;
	rpc_inline_t *ptr;
	uchar_t *bp;

	ASSERT(xdrs->x_op == XDR_DECODE);

	/*
	 * Retrieve the filehandle length.
	 */
	if (!XDR_GETINT32(xdrs, (int32_t *)&fhsize))
		return (FALSE);

	bzero(objp->fh3_u.data, sizeof (objp->fh3_u.data));
	objp->fh3_length = 0;

	/*
	 * Check to see if what the client sent us is bigger or smaller
	 * than what we can ever possibly send out. NFS_FHMAXDATA is
	 * unfortunately badly named as it is no longer the max and is
	 * really the min of what is sent over the wire.
	 */
	if (fhsize > sizeof (fhandle3_t) || fhsize < (sizeof (fsid_t) +
	    sizeof (ushort_t) + NFS_FHMAXDATA +
	    sizeof (ushort_t) + NFS_FHMAXDATA)) {
		if (!XDR_CONTROL(xdrs, XDR_SKIPBYTES, &fhsize))
			return (FALSE);
		return (TRUE);
	}

	/*
	 * bring in fhsize plus any padding
	 */
	bufsize = RNDUP(fhsize);
	ptr = XDR_INLINE(xdrs, bufsize);
	bp = (uchar_t *)ptr;
	if (ptr == NULL) {
		bp = kmem_alloc(bufsize, KM_SLEEP);
		if (!xdr_opaque(xdrs, (char *)bp, bufsize)) {
			kmem_free(bp, bufsize);
			return (FALSE);
		}
	}

	objp->fh3_length = sizeof (fhandle3_t);

	if (xdr_inline_decode_nfs_fh3((uint32_t *)bp, objp, fhsize) == FALSE) {
		/*
		 * If in the process of decoding we find the file handle
		 * is not correctly formed, we need to continue decoding
		 * and trigger an NFS layer error. Set the nfs_fh3_len to
		 * zero so it gets caught as a bad length.
		 */
		bzero(objp->fh3_u.data, sizeof (objp->fh3_u.data));
		objp->fh3_length = 0;
	}

	if (ptr == NULL)
		kmem_free(bp, bufsize);
	return (TRUE);
}

/*
 * XDR_INLINE encode a filehandle.
 */
bool_t
xdr_inline_encode_nfs_fh3(uint32_t **ptrp, uint32_t *ptr_redzone,
	nfs_fh3 *fhp)
{
	uint32_t *ptr = *ptrp;
	uchar_t *cp;
	uint_t otw_len, fsize, xsize;   /* otw, file, and export sizes */
	uint32_t padword;

	fsize = fhp->fh3_len < NFS_FHMAXDATA ? NFS_FHMAXDATA : fhp->fh3_len;
	xsize = fhp->fh3_xlen < NFS_FHMAXDATA ? NFS_FHMAXDATA : fhp->fh3_xlen;

	/*
	 * First get the initial and variable sized part of the filehandle.
	 */
	otw_len = sizeof (fhp->fh3_fsid) +
	    sizeof (fhp->fh3_len) + fsize +
	    sizeof (fhp->fh3_xlen) + xsize;

	/*
	 * Round out to a full word.
	 */
	otw_len = RNDUP(otw_len);
	padword = (otw_len / BYTES_PER_XDR_UNIT);	/* includes fhlen */

	/*
	 * Make sure we don't exceed our buffer.
	 */
	if ((ptr + (otw_len / BYTES_PER_XDR_UNIT) + 1) > ptr_redzone)
		return (FALSE);

	/*
	 * Zero out the pading.
	 */
	ptr[padword] = 0;

	IXDR_PUT_U_INT32(ptr, otw_len);

	/*
	 * The rest of the filehandle is in native byteorder
	 */
	/* fh3_fsid */
	*ptr++ = (uint32_t)fhp->fh3_fsid.val[0];
	*ptr++ = (uint32_t)fhp->fh3_fsid.val[1];

	/*
	 * Since the next pieces are unaligned, we need to
	 * do bytewise copies.
	 */
	cp = (uchar_t *)ptr;

	/* fh3_len + fh3_data */
	bcopy(&fhp->fh3_len, cp, sizeof (fhp->fh3_len) + fsize);
	cp += sizeof (fhp->fh3_len) + fsize;

	/* fh3_xlen + fh3_xdata */
	bcopy(&fhp->fh3_xlen, cp, sizeof (fhp->fh3_xlen) + xsize);
	cp += sizeof (fhp->fh3_xlen) + xsize;

	/* do necessary rounding/padding */
	cp = (uchar_t *)RNDUP((uintptr_t)cp);
	ptr = (uint32_t *)cp;

	/*
	 * With the above padding, we're word aligned again.
	 */
	ASSERT(((uintptr_t)ptr % BYTES_PER_XDR_UNIT) == 0);

	*ptrp = ptr;

	return (TRUE);
}

static bool_t
xdr_encode_nfs_fh3(XDR *xdrs, nfs_fh3 *objp)
{
	uint_t otw_len, fsize, xsize;   /* otw, file, and export sizes */
	bool_t ret;
	rpc_inline_t *ptr;
	rpc_inline_t *buf = NULL;
	uint32_t *ptr_redzone;

	ASSERT(xdrs->x_op == XDR_ENCODE);

	fsize = objp->fh3_len < NFS_FHMAXDATA ? NFS_FHMAXDATA : objp->fh3_len;
	xsize = objp->fh3_xlen < NFS_FHMAXDATA ? NFS_FHMAXDATA : objp->fh3_xlen;

	/*
	 * First get the over the wire size, it is the 4 bytes
	 * for the length, plus the combined size of the
	 * file handle components.
	 */
	otw_len = BYTES_PER_XDR_UNIT + sizeof (objp->fh3_fsid) +
	    sizeof (objp->fh3_len) + fsize +
	    sizeof (objp->fh3_xlen) + xsize;
	/*
	 * Round out to a full word.
	 */
	otw_len = RNDUP(otw_len);

	/*
	 * Next try to inline the XDR stream, if that fails (rare)
	 * allocate a buffer to encode the file handle and then
	 * copy it using xdr_opaque and free the buffer.
	 */
	ptr = XDR_INLINE(xdrs, otw_len);
	if (ptr == NULL)
		ptr = buf = kmem_alloc(otw_len, KM_SLEEP);

	ptr_redzone = (uint32_t *)(ptr + (otw_len / BYTES_PER_XDR_UNIT));
	ret = xdr_inline_encode_nfs_fh3((uint32_t **)&ptr, ptr_redzone, objp);

	if (buf != NULL) {
		if (ret == TRUE)
			ret = xdr_opaque(xdrs, (char *)buf, otw_len);
		kmem_free(buf, otw_len);
	}
	return (ret);
}

/*
 * XDR a NFSv3 filehandle the naive way.
 */
bool_t
xdr_nfs_fh3(XDR *xdrs, nfs_fh3 *objp)
{
	if (xdrs->x_op == XDR_FREE)
		return (TRUE);

	if (!xdr_u_int(xdrs, &objp->fh3_length))
		return (FALSE);

	if (objp->fh3_length > NFS3_FHSIZE)
		return (FALSE);

	return (xdr_opaque(xdrs, objp->fh3_u.data, objp->fh3_length));
}

/*
 * XDR a NFSv3 filehandle with intelligence on the server.
 * Encoding goes from our in-memory structure to wire format.
 * Decoding goes from wire format to our in-memory structure.
 */
bool_t
xdr_nfs_fh3_server(XDR *xdrs, nfs_fh3 *objp)
{
	switch (xdrs->x_op) {
	case XDR_ENCODE:
		if (objp->fh3_flags & FH_WEBNFS)
			return (xdr_nfs_fh3(xdrs, objp));
		else
			return (xdr_encode_nfs_fh3(xdrs, objp));
	case XDR_DECODE:
		return (xdr_decode_nfs_fh3(xdrs, objp));
	case XDR_FREE:
		if (objp->fh3_u.data != NULL)
			bzero(objp->fh3_u.data, sizeof (objp->fh3_u.data));
		return (TRUE);
	}
	return (FALSE);
}

bool_t
xdr_diropargs3(XDR *xdrs, diropargs3 *objp)
{
	switch (xdrs->x_op) {
	case XDR_FREE:
	case XDR_ENCODE:
		if (!xdr_nfs_fh3(xdrs, objp->dirp))
			return (FALSE);
		break;
	case XDR_DECODE:
		if (!xdr_nfs_fh3_server(xdrs, &objp->dir))
			return (FALSE);
		break;
	}
	return (xdr_string3(xdrs, &objp->name, MAXNAMELEN));
}

static bool_t
xdr_fattr3(XDR *xdrs, fattr3 *na)
{
	int32_t *ptr;

	if (xdrs->x_op == XDR_FREE)
		return (TRUE);

	ptr = XDR_INLINE(xdrs, NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT);
	if (ptr != NULL) {
		if (xdrs->x_op == XDR_DECODE) {
			na->type = IXDR_GET_ENUM(ptr, enum ftype3);
			na->mode = IXDR_GET_U_INT32(ptr);
			na->nlink = IXDR_GET_U_INT32(ptr);
			na->uid = IXDR_GET_U_INT32(ptr);
			na->gid = IXDR_GET_U_INT32(ptr);
			IXDR_GET_U_HYPER(ptr, na->size);
			IXDR_GET_U_HYPER(ptr, na->used);
			na->rdev.specdata1 = IXDR_GET_U_INT32(ptr);
			na->rdev.specdata2 = IXDR_GET_U_INT32(ptr);
			IXDR_GET_U_HYPER(ptr, na->fsid);
			IXDR_GET_U_HYPER(ptr, na->fileid);
			na->atime.seconds = IXDR_GET_U_INT32(ptr);
			na->atime.nseconds = IXDR_GET_U_INT32(ptr);
			na->mtime.seconds = IXDR_GET_U_INT32(ptr);
			na->mtime.nseconds = IXDR_GET_U_INT32(ptr);
			na->ctime.seconds = IXDR_GET_U_INT32(ptr);
			na->ctime.nseconds = IXDR_GET_U_INT32(ptr);
		} else {
			IXDR_PUT_ENUM(ptr, na->type);
			IXDR_PUT_U_INT32(ptr, na->mode);
			IXDR_PUT_U_INT32(ptr, na->nlink);
			IXDR_PUT_U_INT32(ptr, na->uid);
			IXDR_PUT_U_INT32(ptr, na->gid);
			IXDR_PUT_U_HYPER(ptr, na->size);
			IXDR_PUT_U_HYPER(ptr, na->used);
			IXDR_PUT_U_INT32(ptr, na->rdev.specdata1);
			IXDR_PUT_U_INT32(ptr, na->rdev.specdata2);
			IXDR_PUT_U_HYPER(ptr, na->fsid);
			IXDR_PUT_U_HYPER(ptr, na->fileid);
			IXDR_PUT_U_INT32(ptr, na->atime.seconds);
			IXDR_PUT_U_INT32(ptr, na->atime.nseconds);
			IXDR_PUT_U_INT32(ptr, na->mtime.seconds);
			IXDR_PUT_U_INT32(ptr, na->mtime.nseconds);
			IXDR_PUT_U_INT32(ptr, na->ctime.seconds);
			IXDR_PUT_U_INT32(ptr, na->ctime.nseconds);
		}
		return (TRUE);
	}
	if (!(xdr_enum(xdrs, (enum_t *)&na->type) &&
	    xdr_u_int(xdrs, &na->mode) &&
	    xdr_u_int(xdrs, &na->nlink) &&
	    xdr_u_int(xdrs, &na->uid) &&
	    xdr_u_int(xdrs, &na->gid) &&
	    xdr_u_longlong_t(xdrs, &na->size) &&
	    xdr_u_longlong_t(xdrs, &na->used) &&
	    xdr_u_int(xdrs, &na->rdev.specdata1) &&
	    xdr_u_int(xdrs, &na->rdev.specdata2) &&
	    xdr_u_longlong_t(xdrs, &na->fsid) &&
	    xdr_u_longlong_t(xdrs, &na->fileid) &&
	    xdr_u_int(xdrs, &na->atime.seconds) &&
	    xdr_u_int(xdrs, &na->atime.nseconds) &&
	    xdr_u_int(xdrs, &na->mtime.seconds) &&
	    xdr_u_int(xdrs, &na->mtime.nseconds) &&
	    xdr_u_int(xdrs, &na->ctime.seconds) &&
	    xdr_u_int(xdrs, &na->ctime.nseconds)))
			return (FALSE);
	return (TRUE);
}

/*
 * Fast decode of an fattr3 to a vattr
 * Only return FALSE on decode error, all other fattr to vattr translation
 * failures set status.
 *
 * Callers must catch the following errors:
 *	EFBIG - file size will not fit in va_size
 *	EOVERFLOW - time will not fit in va_*time
 */
static bool_t
xdr_fattr3_to_vattr(XDR *xdrs, fattr3_res *objp)
{
	int32_t *ptr;
	size3 used;
	specdata3 rdev;
	uint32_t ntime;
	vattr_t *vap = objp->vap;

	/*
	 * DECODE only
	 */
	ASSERT(xdrs->x_op == XDR_DECODE);

	objp->status = 0;
	ptr = XDR_INLINE(xdrs, NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT);
	if (ptr != NULL) {
		/*
		 * Common case
		 */
		vap->va_type = IXDR_GET_ENUM(ptr, enum vtype);
		if (vap->va_type < NF3REG || vap->va_type > NF3FIFO)
			vap->va_type = VBAD;
		else
			vap->va_type = nf3_to_vt[vap->va_type];
		vap->va_mode = IXDR_GET_U_INT32(ptr);
		vap->va_nlink = IXDR_GET_U_INT32(ptr);
		vap->va_uid = (uid_t)IXDR_GET_U_INT32(ptr);
		if (vap->va_uid == NFS_UID_NOBODY)
			vap->va_uid = UID_NOBODY;
		vap->va_gid = (gid_t)IXDR_GET_U_INT32(ptr);
		if (vap->va_gid == NFS_GID_NOBODY)
			vap->va_gid = GID_NOBODY;
		IXDR_GET_U_HYPER(ptr, vap->va_size);
		/*
		 * If invalid size, stop decode, set status, and
		 * return TRUE, x_handy will be correct, caller must ignore vap.
		 */
		if (!NFS3_SIZE_OK(vap->va_size)) {
			objp->status = EFBIG;
			return (TRUE);
		}
		IXDR_GET_U_HYPER(ptr, used);
		rdev.specdata1 = IXDR_GET_U_INT32(ptr);
		rdev.specdata2 = IXDR_GET_U_INT32(ptr);
		/* fsid is ignored */
		ptr += 2;
		IXDR_GET_U_HYPER(ptr, vap->va_nodeid);

		/*
		 * nfs protocol defines times as unsigned so don't
		 * extend sign, unless sysadmin set nfs_allow_preepoch_time.
		 * The inline macros do the equivilant of NFS_TIME_T_CONVERT
		 */
		if (nfs_allow_preepoch_time) {
			vap->va_atime.tv_sec = IXDR_GET_INT32(ptr);
			vap->va_atime.tv_nsec = IXDR_GET_U_INT32(ptr);
			vap->va_mtime.tv_sec = IXDR_GET_INT32(ptr);
			vap->va_mtime.tv_nsec = IXDR_GET_U_INT32(ptr);
			vap->va_ctime.tv_sec = IXDR_GET_INT32(ptr);
			vap->va_ctime.tv_nsec = IXDR_GET_U_INT32(ptr);
		} else {
			/*
			 * Check if the time would overflow on 32-bit
			 */
			ntime = IXDR_GET_U_INT32(ptr);
			/*CONSTCOND*/
			if (NFS3_TIME_OVERFLOW(ntime)) {
				objp->status = EOVERFLOW;
				return (TRUE);
			}
			vap->va_atime.tv_sec = ntime;
			vap->va_atime.tv_nsec = IXDR_GET_U_INT32(ptr);

			ntime = IXDR_GET_U_INT32(ptr);
			/*CONSTCOND*/
			if (NFS3_TIME_OVERFLOW(ntime)) {
				objp->status = EOVERFLOW;
				return (TRUE);
			}
			vap->va_mtime.tv_sec = ntime;
			vap->va_mtime.tv_nsec = IXDR_GET_U_INT32(ptr);

			ntime = IXDR_GET_U_INT32(ptr);
			/*CONSTCOND*/
			if (NFS3_TIME_OVERFLOW(ntime)) {
				objp->status = EOVERFLOW;
				return (TRUE);
			}
			vap->va_ctime.tv_sec = ntime;
			vap->va_ctime.tv_nsec = IXDR_GET_U_INT32(ptr);
		}

	} else {
		uint64 fsid;

		/*
		 * Slow path
		 */
		if (!(xdr_enum(xdrs, (enum_t *)&vap->va_type) &&
		    xdr_u_int(xdrs, &vap->va_mode) &&
		    xdr_u_int(xdrs, &vap->va_nlink) &&
		    xdr_u_int(xdrs, (uint_t *)&vap->va_uid) &&
		    xdr_u_int(xdrs, (uint_t *)&vap->va_gid) &&
		    xdr_u_longlong_t(xdrs, &vap->va_size) &&
		    xdr_u_longlong_t(xdrs, &used) &&
		    xdr_u_int(xdrs, &rdev.specdata1) &&
		    xdr_u_int(xdrs, &rdev.specdata2) &&
		    xdr_u_longlong_t(xdrs, &fsid) &&	/* ignored */
		    xdr_u_longlong_t(xdrs, &vap->va_nodeid)))
				return (FALSE);

		if (nfs_allow_preepoch_time) {
			if (!xdr_u_int(xdrs, &ntime))
				return (FALSE);
			vap->va_atime.tv_sec = (int32_t)ntime;
			if (!xdr_u_int(xdrs, &ntime))
				return (FALSE);
			vap->va_atime.tv_nsec = ntime;

			if (!xdr_u_int(xdrs, &ntime))
				return (FALSE);
			vap->va_mtime.tv_sec = (int32_t)ntime;
			if (!xdr_u_int(xdrs, &ntime))
				return (FALSE);
			vap->va_mtime.tv_nsec = ntime;

			if (!xdr_u_int(xdrs, &ntime))
				return (FALSE);
			vap->va_ctime.tv_sec = (int32_t)ntime;
			if (!xdr_u_int(xdrs, &ntime))
				return (FALSE);
			vap->va_ctime.tv_nsec = ntime;
		} else {
			/*
			 * Check if the time would overflow on 32-bit
			 * Set status and keep decoding stream.
			 */
			if (!xdr_u_int(xdrs, &ntime))
				return (FALSE);
			/*CONSTCOND*/
			if (NFS3_TIME_OVERFLOW(ntime)) {
				objp->status = EOVERFLOW;
			}
			vap->va_atime.tv_sec = ntime;
			if (!xdr_u_int(xdrs, &ntime))
				return (FALSE);
			vap->va_atime.tv_nsec = ntime;

			if (!xdr_u_int(xdrs, &ntime))
				return (FALSE);
			/*CONSTCOND*/
			if (NFS3_TIME_OVERFLOW(ntime)) {
				objp->status = EOVERFLOW;
			}
			vap->va_mtime.tv_sec = ntime;
			if (!xdr_u_int(xdrs, &ntime))
				return (FALSE);
			vap->va_mtime.tv_nsec = ntime;

			if (!xdr_u_int(xdrs, &ntime))
				return (FALSE);
			/*CONSTCOND*/
			if (NFS3_TIME_OVERFLOW(ntime)) {
				objp->status = EOVERFLOW;
			}
			vap->va_ctime.tv_sec = ntime;
			if (!xdr_u_int(xdrs, &ntime))
				return (FALSE);
			vap->va_ctime.tv_nsec = ntime;
		}

		/*
		 * Fixup as needed
		 */
		if (vap->va_type < NF3REG || vap->va_type > NF3FIFO)
			vap->va_type = VBAD;
		else
			vap->va_type = nf3_to_vt[vap->va_type];
		if (vap->va_uid == NFS_UID_NOBODY)
			vap->va_uid = UID_NOBODY;
		if (vap->va_gid == NFS_GID_NOBODY)
			vap->va_gid = GID_NOBODY;
		/*
		 * If invalid size, set status, and
		 * return TRUE, caller must ignore vap.
		 */
		if (!NFS3_SIZE_OK(vap->va_size)) {
			objp->status = EFBIG;
			return (TRUE);
		}
	}

	/*
	 * Fill in derived fields
	 */
	vap->va_fsid = objp->vp->v_vfsp->vfs_dev;
	vap->va_seq = 0;

	/*
	 * Common case values
	 */
	vap->va_rdev = 0;
	vap->va_blksize = MAXBSIZE;
	vap->va_nblocks = 0;

	switch (vap->va_type) {
	case VREG:
	case VDIR:
	case VLNK:
		vap->va_nblocks = (u_longlong_t)
		    ((used + (size3)DEV_BSIZE - (size3)1) /
		    (size3)DEV_BSIZE);
		break;
	case VBLK:
		vap->va_blksize = DEV_BSIZE;
		/* FALLTHRU */
	case VCHR:
		vap->va_rdev = makedevice(rdev.specdata1, rdev.specdata2);
		break;
	case VSOCK:
	case VFIFO:
	default:
		break;
	}

	return (TRUE);
}

static bool_t
xdr_post_op_vattr(XDR *xdrs, post_op_vattr *objp)
{
	/*
	 * DECODE only
	 */
	ASSERT(xdrs->x_op == XDR_DECODE);

	if (!xdr_bool(xdrs, &objp->attributes))
		return (FALSE);

	if (objp->attributes == FALSE)
		return (TRUE);

	if (objp->attributes != TRUE)
		return (FALSE);

	if (!xdr_fattr3_to_vattr(xdrs, &objp->fres))
		return (FALSE);

	/*
	 * The file size may cause an EFBIG or the time values
	 * may cause EOVERFLOW, if so simply drop the attributes.
	 */
	if (objp->fres.status != NFS3_OK)
		objp->attributes = FALSE;

	return (TRUE);
}

bool_t
xdr_post_op_attr(XDR *xdrs, post_op_attr *objp)
{
	if (!xdr_bool(xdrs, &objp->attributes))
		return (FALSE);

	if (objp->attributes == FALSE)
		return (TRUE);

	if (objp->attributes != TRUE)
		return (FALSE);

	if (!xdr_fattr3(xdrs, &objp->attr))
		return (FALSE);

	/*
	 * Check that we don't get a file we can't handle through
	 *	existing interfaces (especially stat64()).
	 * Decode only check since on encode the data has
	 * been dealt with in the above call to xdr_fattr3().
	 */
	if (xdrs->x_op == XDR_DECODE) {
		/* Set attrs to false if invalid size or time */
		if (!NFS3_SIZE_OK(objp->attr.size)) {
			objp->attributes = FALSE;
			return (TRUE);
		}
#ifndef _LP64
		if (!NFS3_FATTR_TIME_OK(&objp->attr))
			objp->attributes = FALSE;
#endif
	}
	return (TRUE);
}

static bool_t
xdr_wcc_data(XDR *xdrs, wcc_data *objp)
{
	int32_t *ptr;
	wcc_attr *attrp;

	if (xdrs->x_op == XDR_FREE)
		return (TRUE);

	if (xdrs->x_op == XDR_DECODE) {
		/* pre_op_attr */
		if (!xdr_bool(xdrs, &objp->before.attributes))
			return (FALSE);

		switch (objp->before.attributes) {
		case TRUE:
			attrp = &objp->before.attr;
			ptr = XDR_INLINE(xdrs, 6 * BYTES_PER_XDR_UNIT);
			if (ptr != NULL) {
				IXDR_GET_U_HYPER(ptr, attrp->size);
				attrp->mtime.seconds = IXDR_GET_U_INT32(ptr);
				attrp->mtime.nseconds = IXDR_GET_U_INT32(ptr);
				attrp->ctime.seconds = IXDR_GET_U_INT32(ptr);
				attrp->ctime.nseconds = IXDR_GET_U_INT32(ptr);
			} else {
				if (!xdr_u_longlong_t(xdrs, &attrp->size))
					return (FALSE);
				if (!xdr_u_int(xdrs, &attrp->mtime.seconds))
					return (FALSE);
				if (!xdr_u_int(xdrs, &attrp->mtime.nseconds))
					return (FALSE);
				if (!xdr_u_int(xdrs, &attrp->ctime.seconds))
					return (FALSE);
				if (!xdr_u_int(xdrs, &attrp->ctime.nseconds))
					return (FALSE);
			}

#ifndef _LP64
			/*
			 * check time overflow.
			 */
			if (!NFS3_TIME_OK(attrp->mtime.seconds) ||
			    !NFS3_TIME_OK(attrp->ctime.seconds))
				objp->before.attributes = FALSE;
#endif
			break;
		case FALSE:
			break;
		default:
			return (FALSE);
		}
	}

	if (xdrs->x_op == XDR_ENCODE) {
		/* pre_op_attr */
		if (!xdr_bool(xdrs, &objp->before.attributes))
			return (FALSE);

		switch (objp->before.attributes) {
		case TRUE:
			attrp = &objp->before.attr;

			ptr = XDR_INLINE(xdrs, 6 * BYTES_PER_XDR_UNIT);
			if (ptr != NULL) {
				IXDR_PUT_U_HYPER(ptr, attrp->size);
				IXDR_PUT_U_INT32(ptr, attrp->mtime.seconds);
				IXDR_PUT_U_INT32(ptr, attrp->mtime.nseconds);
				IXDR_PUT_U_INT32(ptr, attrp->ctime.seconds);
				IXDR_PUT_U_INT32(ptr, attrp->ctime.nseconds);
			} else {
				if (!xdr_u_longlong_t(xdrs, &attrp->size))
					return (FALSE);
				if (!xdr_u_int(xdrs, &attrp->mtime.seconds))
					return (FALSE);
				if (!xdr_u_int(xdrs, &attrp->mtime.nseconds))
					return (FALSE);
				if (!xdr_u_int(xdrs, &attrp->ctime.seconds))
					return (FALSE);
				if (!xdr_u_int(xdrs, &attrp->ctime.nseconds))
					return (FALSE);
			}
			break;
		case FALSE:
			break;
		default:
			return (FALSE);
		}
	}
	return (xdr_post_op_attr(xdrs, &objp->after));
}

bool_t
xdr_post_op_fh3(XDR *xdrs, post_op_fh3 *objp)
{
	if (!xdr_bool(xdrs, &objp->handle_follows))
		return (FALSE);
	switch (objp->handle_follows) {
	case TRUE:
		switch (xdrs->x_op) {
		case XDR_ENCODE:
			if (!xdr_nfs_fh3_server(xdrs, &objp->handle))
				return (FALSE);
			break;
		case XDR_FREE:
		case XDR_DECODE:
			if (!xdr_nfs_fh3(xdrs, &objp->handle))
				return (FALSE);
			break;
		}
		return (TRUE);
	case FALSE:
		return (TRUE);
	default:
		return (FALSE);
	}
}

static bool_t
xdr_sattr3(XDR *xdrs, sattr3 *objp)
{
	/* set_mode3 */
	if (!xdr_bool(xdrs, &objp->mode.set_it))
		return (FALSE);
	if (objp->mode.set_it)
		if (!xdr_u_int(xdrs, &objp->mode.mode))
			return (FALSE);
	/* set_uid3 */
	if (!xdr_bool(xdrs, &objp->uid.set_it))
		return (FALSE);
	if (objp->uid.set_it)
		if (!xdr_u_int(xdrs, &objp->uid.uid))
			return (FALSE);
	/* set_gid3 */
	if (!xdr_bool(xdrs, &objp->gid.set_it))
		return (FALSE);
	if (objp->gid.set_it)
		if (!xdr_u_int(xdrs, &objp->gid.gid))
			return (FALSE);

	/* set_size3 */
	if (!xdr_bool(xdrs, &objp->size.set_it))
		return (FALSE);
	if (objp->size.set_it)
		if (!xdr_u_longlong_t(xdrs, &objp->size.size))
			return (FALSE);

	/* set_atime */
	if (!xdr_enum(xdrs, (enum_t *)&objp->atime.set_it))
		return (FALSE);
	if (objp->atime.set_it == SET_TO_CLIENT_TIME) {
		if (!xdr_u_int(xdrs, &objp->atime.atime.seconds))
			return (FALSE);
		if (!xdr_u_int(xdrs, &objp->atime.atime.nseconds))
			return (FALSE);
	}

	/* set_mtime */
	if (!xdr_enum(xdrs, (enum_t *)&objp->mtime.set_it))
		return (FALSE);
	if (objp->mtime.set_it == SET_TO_CLIENT_TIME) {
		if (!xdr_u_int(xdrs, &objp->mtime.mtime.seconds))
			return (FALSE);
		if (!xdr_u_int(xdrs, &objp->mtime.mtime.nseconds))
			return (FALSE);
	}

	return (TRUE);
}

bool_t
xdr_GETATTR3res(XDR *xdrs, GETATTR3res *objp)
{
	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	if (objp->status != NFS3_OK)
		return (TRUE);
	/* xdr_GETATTR3resok */
	return (xdr_fattr3(xdrs, &objp->resok.obj_attributes));
}

bool_t
xdr_GETATTR3vres(XDR *xdrs, GETATTR3vres *objp)
{
	/*
	 * DECODE or FREE only
	 */
	if (xdrs->x_op == XDR_FREE)
		return (TRUE);

	if (xdrs->x_op != XDR_DECODE)
		return (FALSE);

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);

	if (objp->status != NFS3_OK)
		return (TRUE);

	return (xdr_fattr3_to_vattr(xdrs, &objp->fres));
}


bool_t
xdr_SETATTR3args(XDR *xdrs, SETATTR3args *objp)
{
	switch (xdrs->x_op) {
	case XDR_FREE:
	case XDR_ENCODE:
		if (!xdr_nfs_fh3(xdrs, &objp->object))
			return (FALSE);
		break;
	case XDR_DECODE:
		if (!xdr_nfs_fh3_server(xdrs, &objp->object))
			return (FALSE);
		break;
	}
	if (!xdr_sattr3(xdrs, &objp->new_attributes))
		return (FALSE);

	/* sattrguard3 */
	if (!xdr_bool(xdrs, &objp->guard.check))
		return (FALSE);
	switch (objp->guard.check) {
	case TRUE:
		if (!xdr_u_int(xdrs, &objp->guard.obj_ctime.seconds))
			return (FALSE);
		return (xdr_u_int(xdrs, &objp->guard.obj_ctime.nseconds));
	case FALSE:
		return (TRUE);
	default:
		return (FALSE);
	}
}

bool_t
xdr_SETATTR3res(XDR *xdrs, SETATTR3res *objp)
{
	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	switch (objp->status) {
	case NFS3_OK:
		return (xdr_wcc_data(xdrs, &objp->resok.obj_wcc));
	default:
		return (xdr_wcc_data(xdrs, &objp->resfail.obj_wcc));
	}
}

bool_t
xdr_LOOKUP3res(XDR *xdrs, LOOKUP3res *objp)
{
	LOOKUP3resok *resokp;

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);

	if (objp->status != NFS3_OK)
		return (xdr_post_op_attr(xdrs, &objp->resfail.dir_attributes));

	/* xdr_LOOKUP3resok */
	resokp = &objp->resok;
	switch (xdrs->x_op) {
	case XDR_ENCODE:
		if (!xdr_nfs_fh3_server(xdrs, &resokp->object))
			return (FALSE);
		break;
	case XDR_FREE:
	case XDR_DECODE:
		if (!xdr_nfs_fh3(xdrs, &resokp->object))
			return (FALSE);
		break;
	}
	if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes))
		return (FALSE);
	return (xdr_post_op_attr(xdrs, &resokp->dir_attributes));
}

bool_t
xdr_LOOKUP3vres(XDR *xdrs, LOOKUP3vres *objp)
{
	/*
	 * DECODE or FREE only
	 */
	if (xdrs->x_op == XDR_FREE)
		return (TRUE);

	if (xdrs->x_op != XDR_DECODE)
		return (FALSE);

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);

	if (objp->status != NFS3_OK)
		return (xdr_post_op_vattr(xdrs, &objp->dir_attributes));

	if (!xdr_nfs_fh3(xdrs, &objp->object))
		return (FALSE);
	if (!xdr_post_op_vattr(xdrs, &objp->obj_attributes))
		return (FALSE);
	return (xdr_post_op_vattr(xdrs, &objp->dir_attributes));
}

bool_t
xdr_ACCESS3args(XDR *xdrs, ACCESS3args *objp)
{
	switch (xdrs->x_op) {
	case XDR_FREE:
	case XDR_ENCODE:
		if (!xdr_nfs_fh3(xdrs, &objp->object))
			return (FALSE);
		break;
	case XDR_DECODE:
		if (!xdr_nfs_fh3_server(xdrs, &objp->object))
			return (FALSE);
		break;
	}
	return (xdr_u_int(xdrs, &objp->access));
}


bool_t
xdr_ACCESS3res(XDR *xdrs, ACCESS3res *objp)
{
	ACCESS3resok *resokp;

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	if (objp->status != NFS3_OK)
		return (xdr_post_op_attr(xdrs, &objp->resfail.obj_attributes));

	/* xdr_ACCESS3resok */
	resokp = &objp->resok;
	if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes))
		return (FALSE);
	return (xdr_u_int(xdrs, &resokp->access));
}

bool_t
xdr_READLINK3args(XDR *xdrs,  READLINK3args *objp)
{
	rdma_chunkinfo_t rci;
	struct xdr_ops *xops = xdrrdma_xops();

	if ((xdrs->x_ops == &xdrrdma_ops || xdrs->x_ops == xops) &&
	    xdrs->x_op == XDR_ENCODE) {
		rci.rci_type = RCI_REPLY_CHUNK;
		rci.rci_len = MAXPATHLEN;
		XDR_CONTROL(xdrs, XDR_RDMA_ADD_CHUNK, &rci);
	}
	if (!xdr_nfs_fh3(xdrs, (nfs_fh3 *)objp))
		return (FALSE);
	return (TRUE);
}

bool_t
xdr_READLINK3res(XDR *xdrs, READLINK3res *objp)
{

	READLINK3resok *resokp;

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	if (objp->status != NFS3_OK)
		return (xdr_post_op_attr(xdrs,
		    &objp->resfail.symlink_attributes));

	/* xdr_READLINK3resok */
	resokp = &objp->resok;
	if (!xdr_post_op_attr(xdrs, &resokp->symlink_attributes))
		return (FALSE);
	return (xdr_string3(xdrs, &resokp->data, MAXPATHLEN));
}

bool_t
xdr_READ3args(XDR *xdrs, READ3args *objp)
{
	rdma_chunkinfo_t rci;
	rdma_wlist_conn_info_t rwci;
	struct xdr_ops *xops = xdrrdma_xops();

	switch (xdrs->x_op) {
	case XDR_FREE:
	case XDR_ENCODE:
		if (!xdr_nfs_fh3(xdrs, &objp->file))
			return (FALSE);
		break;
	case XDR_DECODE:
		if (!xdr_nfs_fh3_server(xdrs, &objp->file))
			return (FALSE);
		break;
	}
	if (!xdr_u_longlong_t(xdrs, &objp->offset))
		return (FALSE);
	if (!xdr_u_int(xdrs, &objp->count))
		return (FALSE);

	DTRACE_PROBE1(xdr__i__read3_buf_len, int, objp->count);

	objp->wlist = NULL;

	/* if xdrrdma_sizeof in progress, then store the size */
	if (xdrs->x_ops == xops && xdrs->x_op == XDR_ENCODE) {
		rci.rci_type = RCI_WRITE_ADDR_CHUNK;
		rci.rci_len = objp->count;
		(void) XDR_CONTROL(xdrs, XDR_RDMA_ADD_CHUNK, &rci);
	}

	if (xdrs->x_ops != &xdrrdma_ops || xdrs->x_op == XDR_FREE)
		return (TRUE);

	if (xdrs->x_op == XDR_ENCODE) {

		if (objp->res_uiop != NULL) {
			rci.rci_type = RCI_WRITE_UIO_CHUNK;
			rci.rci_a.rci_uiop = objp->res_uiop;
			rci.rci_len = objp->count;
			rci.rci_clpp = &objp->wlist;
		} else {
			rci.rci_type = RCI_WRITE_ADDR_CHUNK;
			rci.rci_a.rci_addr = objp->res_data_val_alt;
			rci.rci_len = objp->count;
			rci.rci_clpp = &objp->wlist;
		}

		return (XDR_CONTROL(xdrs, XDR_RDMA_ADD_CHUNK, &rci));
	}

	/* XDR_DECODE case */
	(void) XDR_CONTROL(xdrs, XDR_RDMA_GET_WCINFO, &rwci);
	objp->wlist = rwci.rwci_wlist;
	objp->conn = rwci.rwci_conn;

	return (TRUE);
}

bool_t
xdr_READ3res(XDR *xdrs, READ3res *objp)
{
	READ3resok *resokp;
	bool_t ret;
	mblk_t *mp;

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);

	if (objp->status != NFS3_OK)
		return (xdr_post_op_attr(xdrs, &objp->resfail.file_attributes));

	resokp = &objp->resok;

	if (xdr_post_op_attr(xdrs, &resokp->file_attributes) == FALSE ||
	    xdr_u_int(xdrs, &resokp->count) == FALSE ||
	    xdr_bool(xdrs, &resokp->eof) == FALSE) {
		return (FALSE);
	}

	if (xdrs->x_op == XDR_ENCODE) {
		int i, rndup;

		mp = resokp->data.mp;
		if (mp != NULL && xdrs->x_ops == &xdrmblk_ops) {
			mp->b_wptr += resokp->count;
			rndup = BYTES_PER_XDR_UNIT -
			    (resokp->data.data_len % BYTES_PER_XDR_UNIT);
			if (rndup != BYTES_PER_XDR_UNIT)
				for (i = 0; i < rndup; i++)
					*mp->b_wptr++ = '\0';
			if (xdrmblk_putmblk(xdrs, mp, resokp->count) == TRUE) {
				resokp->data.mp = NULL;
				return (TRUE);
			}
		} else if (mp == NULL) {
			if (xdr_u_int(xdrs, &resokp->count) == FALSE) {
				return (FALSE);
			}
			/*
			 * If read data sent by wlist (RDMA_WRITE), don't do
			 * xdr_bytes() below.   RDMA_WRITE transfers the data.
			 * Note: this is encode-only because the client code
			 * uses xdr_READ3vres/xdr_READ3uiores to decode results.
			 */
			if (resokp->wlist) {
				if (resokp->count != 0) {
					return (xdrrdma_send_read_data(
					    xdrs, resokp->count,
					    resokp->wlist));
				}
				return (TRUE);
			}
		}
		/*
		 * Fall thru for the xdr_bytes()
		 *
		 * note: the mblk will be freed in
		 * rfs3_read_free.
		 */
	}

	/* no RDMA_WRITE transfer -- send data inline */

	ret = xdr_bytes(xdrs, (char **)&resokp->data.data_val,
	    &resokp->data.data_len, nfs3tsize());

	return (ret);
}

bool_t
xdr_READ3vres(XDR *xdrs, READ3vres *objp)
{
	count3 ocount;
	/*
	 * DECODE or FREE only
	 */
	if (xdrs->x_op == XDR_FREE)
		return (TRUE);

	if (xdrs->x_op != XDR_DECODE)
		return (FALSE);

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);

	if (!xdr_post_op_vattr(xdrs, &objp->pov))
		return (FALSE);

	if (objp->status != NFS3_OK)
		return (TRUE);

	if (!xdr_u_int(xdrs, &objp->count))
		return (FALSE);

	if (!xdr_bool(xdrs, &objp->eof))
		return (FALSE);

	/*
	 * If read data received via RDMA_WRITE, don't do xdr_bytes().
	 * RDMA_WRITE already moved the data so decode length of RDMA_WRITE.
	 */
	if (xdrs->x_ops == &xdrrdma_ops) {
		struct clist *cl;

		XDR_CONTROL(xdrs, XDR_RDMA_GET_WLIST, &cl);

		if (cl) {
			if (!xdr_u_int(xdrs, &ocount)) {
				return (FALSE);
			}
			if (ocount != objp->count) {
				DTRACE_PROBE2(xdr__e__read3vres_fail,
				    int, ocount, int, objp->count);
				objp->wlist = NULL;
				return (FALSE);
			}

			objp->wlist_len = clist_len(cl);
			objp->data.data_len = ocount;

			if (objp->wlist_len !=
			    roundup(objp->data.data_len, BYTES_PER_XDR_UNIT)) {
				DTRACE_PROBE2(
				    xdr__e__read3vres_fail,
				    int, ocount,
				    int, objp->data.data_len);
				objp->wlist = NULL;
				return (FALSE);
			}
			return (TRUE);
		}
	}

	return (xdr_bytes(xdrs, (char **)&objp->data.data_val,
	    &objp->data.data_len, nfs3tsize()));
}

bool_t
xdr_READ3uiores(XDR *xdrs, READ3uiores *objp)
{
	count3 ocount;
	bool_t attributes;
	mblk_t *mp;
	size_t n;
	int error;
	int size = (int)objp->size;
	struct uio *uiop = objp->uiop;
	int32_t fattr3_len = NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT;
	int32_t *ptr;

	/*
	 * DECODE or FREE only
	 */
	if (xdrs->x_op == XDR_FREE)
		return (TRUE);

	if (xdrs->x_op != XDR_DECODE)
		return (FALSE);

	if (!XDR_GETINT32(xdrs, (int32_t *)&objp->status))
		return (FALSE);

	if (!XDR_GETINT32(xdrs, (int32_t *)&attributes))
		return (FALSE);

	/*
	 * For directio we just skip over attributes if present
	 */
	switch (attributes) {
	case TRUE:
		if (!XDR_CONTROL(xdrs, XDR_SKIPBYTES, &fattr3_len))
			return (FALSE);
		break;
	case FALSE:
		break;
	default:
		return (FALSE);
	}

	if (objp->status != NFS3_OK)
		return (TRUE);

	if (!XDR_GETINT32(xdrs, (int32_t *)&objp->count))
		return (FALSE);

	if (!XDR_GETINT32(xdrs, (int32_t *)&objp->eof))
		return (FALSE);

	if (xdrs->x_ops == &xdrmblk_ops) {
		if (!xdrmblk_getmblk(xdrs, &mp, &objp->size))
			return (FALSE);

		if (objp->size == 0)
			return (TRUE);

		if (objp->size > size)
			return (FALSE);

		size = (int)objp->size;
		do {
			n = MIN(size, mp->b_wptr - mp->b_rptr);
			if ((n = MIN(uiop->uio_resid, n)) != 0) {

				error = uiomove((char *)mp->b_rptr, n, UIO_READ,
				    uiop);
				if (error)
					return (FALSE);
				mp->b_rptr += n;
				size -= n;
			}

			while (mp && (mp->b_rptr >= mp->b_wptr))
				mp = mp->b_cont;
		} while (mp && size > 0 && uiop->uio_resid > 0);

		return (TRUE);
	}

	if (xdrs->x_ops == &xdrrdma_ops) {
		struct clist *cl;

		XDR_CONTROL(xdrs, XDR_RDMA_GET_WLIST, &cl);

		objp->wlist = cl;

		if (objp->wlist) {
			if (!xdr_u_int(xdrs, &ocount)) {
				objp->wlist = NULL;
				return (FALSE);
			}

			if (ocount != objp->count) {
				DTRACE_PROBE2(xdr__e__read3uiores_fail,
				    int, ocount, int, objp->count);
				objp->wlist = NULL;
				return (FALSE);
			}

			objp->wlist_len = clist_len(cl);

			uiop->uio_resid -= objp->count;
			uiop->uio_iov->iov_len -= objp->count;
			uiop->uio_iov->iov_base += objp->count;
			uiop->uio_loffset += objp->count;

			/*
			 * XXX: Assume 1 iov, needs to be changed.
			 */
			objp->size = objp->count;

			return (TRUE);
		}
	}

	/*
	 * This isn't an xdrmblk stream nor RDMA.
	 * Handle the likely case that it can be
	 * inlined (ex. xdrmem).
	 */
	if (!XDR_GETINT32(xdrs, (int32_t *)&objp->size))
		return (FALSE);

	if (objp->size == 0)
		return (TRUE);

	if (objp->size > size)
		return (FALSE);

	size = (int)objp->size;
	if ((ptr = XDR_INLINE(xdrs, size)) != NULL)
		return (uiomove(ptr, size, UIO_READ, uiop) ? FALSE : TRUE);

	/*
	 * Handle some other (unlikely) stream type that will need a copy.
	 */
	if ((ptr = kmem_alloc(size, KM_NOSLEEP)) == NULL)
		return (FALSE);

	if (!XDR_GETBYTES(xdrs, (caddr_t)ptr, size)) {
		kmem_free(ptr, size);
		return (FALSE);
	}
	error = uiomove(ptr, size, UIO_READ, uiop);
	kmem_free(ptr, size);

	return (error ? FALSE : TRUE);
}

bool_t
xdr_WRITE3args(XDR *xdrs, WRITE3args *objp)
{
	switch (xdrs->x_op) {
	case XDR_FREE:
	case XDR_ENCODE:
		if (!xdr_nfs_fh3(xdrs, &objp->file))
			return (FALSE);
		break;
	case XDR_DECODE:
		if (!xdr_nfs_fh3_server(xdrs, &objp->file))
			return (FALSE);
		break;
	}
	if (!xdr_u_longlong_t(xdrs, &objp->offset))
		return (FALSE);
	if (!xdr_u_int(xdrs, &objp->count))
		return (FALSE);
	if (!xdr_enum(xdrs, (enum_t *)&objp->stable))
		return (FALSE);

	if (xdrs->x_op == XDR_DECODE) {
		if (xdrs->x_ops == &xdrmblk_ops) {
			if (xdrmblk_getmblk(xdrs, &objp->mblk,
			    &objp->data.data_len) == TRUE) {
				objp->data.data_val = NULL;
				return (TRUE);
			}
		}
		objp->mblk = NULL;

		if (xdrs->x_ops == &xdrrdmablk_ops) {
			if (xdrrdma_getrdmablk(xdrs, &objp->rlist,
			    &objp->data.data_len,
			    &objp->conn, nfs3tsize()) == TRUE) {
				objp->data.data_val = NULL;
				if (xdrrdma_read_from_client(
				    objp->rlist,
				    &objp->conn,
				    objp->count) == FALSE) {
					return (FALSE);
				}
				return (TRUE);
			}
		}
		objp->rlist = NULL;

		/* Else fall thru for the xdr_bytes(). */
	}

	if (xdrs->x_op == XDR_FREE) {
		if (objp->rlist != NULL) {
			(void) xdrrdma_free_clist(objp->conn, objp->rlist);
			objp->rlist = NULL;
			objp->data.data_val = NULL;
			return (TRUE);
		}
	}

	DTRACE_PROBE1(xdr__i__write3_buf_len,
	    int, objp->data.data_len);

	return (xdr_bytes(xdrs, (char **)&objp->data.data_val,
	    &objp->data.data_len, nfs3tsize()));
}

bool_t
xdr_WRITE3res(XDR *xdrs, WRITE3res *objp)
{
	WRITE3resok *resokp;

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	if (objp->status != NFS3_OK) /* xdr_WRITE3resfail */
		return (xdr_wcc_data(xdrs, &objp->resfail.file_wcc));

	/* xdr_WRITE3resok */
	resokp = &objp->resok;
	if (!xdr_wcc_data(xdrs, &resokp->file_wcc))
		return (FALSE);
	if (!xdr_u_int(xdrs, &resokp->count))
		return (FALSE);
	if (!xdr_enum(xdrs, (enum_t *)&resokp->committed))
		return (FALSE);
	/*
	 * writeverf3 is really an opaque 8 byte
	 * quantity, but we will treat it as a
	 * hyper for efficiency, the cost of
	 * a byteswap here saves bcopys elsewhere
	 */
	return (xdr_u_longlong_t(xdrs, &resokp->verf));
}

bool_t
xdr_CREATE3args(XDR *xdrs, CREATE3args *objp)
{
	createhow3 *howp;

	if (!xdr_diropargs3(xdrs, &objp->where))
		return (FALSE);

	/* xdr_createhow3 */
	howp = &objp->how;

	if (!xdr_enum(xdrs, (enum_t *)&howp->mode))
		return (FALSE);
	switch (howp->mode) {
	case UNCHECKED:
	case GUARDED:
		return (xdr_sattr3(xdrs, &howp->createhow3_u.obj_attributes));
	case EXCLUSIVE:
		/*
		 * createverf3 is really an opaque 8 byte
		 * quantity, but we will treat it as a
		 * hyper for efficiency, the cost of
		 * a byteswap here saves bcopys elsewhere
		 */
		return (xdr_u_longlong_t(xdrs, &howp->createhow3_u.verf));
	default:
		return (FALSE);
	}
}

bool_t
xdr_CREATE3res(XDR *xdrs, CREATE3res *objp)
{
	CREATE3resok *resokp;

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	switch (objp->status) {
	case NFS3_OK:
		/* xdr_CREATE3resok */
		resokp = &objp->resok;

		if (!xdr_post_op_fh3(xdrs, &resokp->obj))
			return (FALSE);
		if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes))
			return (FALSE);
		return (xdr_wcc_data(xdrs, &resokp->dir_wcc));
	default:
		/* xdr_CREATE3resfail */
		return (xdr_wcc_data(xdrs, &objp->resfail.dir_wcc));
	}
}

bool_t
xdr_MKDIR3args(XDR *xdrs, MKDIR3args *objp)
{
	if (!xdr_diropargs3(xdrs, &objp->where))
		return (FALSE);
	return (xdr_sattr3(xdrs, &objp->attributes));
}

bool_t
xdr_MKDIR3res(XDR *xdrs, MKDIR3res *objp)
{
	MKDIR3resok *resokp;

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	switch (objp->status) {
	case NFS3_OK:
		/* xdr_MKDIR3resok */
		resokp = &objp->resok;

		if (!xdr_post_op_fh3(xdrs, &resokp->obj))
			return (FALSE);
		if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes))
			return (FALSE);
		return (xdr_wcc_data(xdrs, &resokp->dir_wcc));
	default:
		return (xdr_wcc_data(xdrs, &objp->resfail.dir_wcc));
	}
}

bool_t
xdr_SYMLINK3args(XDR *xdrs, SYMLINK3args *objp)
{
	if (!xdr_diropargs3(xdrs, &objp->where))
		return (FALSE);
	if (!xdr_sattr3(xdrs, &objp->symlink.symlink_attributes))
		return (FALSE);
	return (xdr_string3(xdrs, &objp->symlink.symlink_data, MAXPATHLEN));
}

bool_t
xdr_SYMLINK3res(XDR *xdrs, SYMLINK3res *objp)
{
	SYMLINK3resok *resokp;

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	switch (objp->status) {
	case NFS3_OK:
		resokp = &objp->resok;
		/* xdr_SYMLINK3resok */
		if (!xdr_post_op_fh3(xdrs, &resokp->obj))
			return (FALSE);
		if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes))
			return (FALSE);
		return (xdr_wcc_data(xdrs, &resokp->dir_wcc));
	default:
		return (xdr_wcc_data(xdrs, &objp->resfail.dir_wcc));
	}
}

bool_t
xdr_MKNOD3args(XDR *xdrs, MKNOD3args *objp)
{
	mknoddata3 *whatp;
	devicedata3 *nod_objp;

	if (!xdr_diropargs3(xdrs, &objp->where))
		return (FALSE);

	whatp = &objp->what;
	if (!xdr_enum(xdrs, (enum_t *)&whatp->type))
		return (FALSE);
	switch (whatp->type) {
	case NF3CHR:
	case NF3BLK:
		/* xdr_devicedata3 */
		nod_objp = &whatp->mknoddata3_u.device;
		if (!xdr_sattr3(xdrs, &nod_objp->dev_attributes))
			return (FALSE);
		if (!xdr_u_int(xdrs, &nod_objp->spec.specdata1))
			return (FALSE);
		return (xdr_u_int(xdrs, &nod_objp->spec.specdata2));
	case NF3SOCK:
	case NF3FIFO:
		return (xdr_sattr3(xdrs, &whatp->mknoddata3_u.pipe_attributes));
	default:
		break;
	}
	return (TRUE);
}

bool_t
xdr_MKNOD3res(XDR *xdrs, MKNOD3res *objp)
{
	MKNOD3resok *resokp;

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	switch (objp->status) {
	case NFS3_OK:
		/* xdr_MKNOD3resok */
		resokp = &objp->resok;
		if (!xdr_post_op_fh3(xdrs, &resokp->obj))
			return (FALSE);
		if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes))
			return (FALSE);
		return (xdr_wcc_data(xdrs, &resokp->dir_wcc));
	default:
		return (xdr_wcc_data(xdrs, &objp->resfail.dir_wcc));
	}
}

bool_t
xdr_REMOVE3res(XDR *xdrs, REMOVE3res *objp)
{
	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	switch (objp->status) {
	case NFS3_OK:
		return (xdr_wcc_data(xdrs, &objp->resok.dir_wcc));
	default:
		return (xdr_wcc_data(xdrs, &objp->resfail.dir_wcc));
	}
}

bool_t
xdr_RMDIR3res(XDR *xdrs, RMDIR3res *objp)
{
	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	switch (objp->status) {
	case NFS3_OK:
		return (xdr_wcc_data(xdrs, &objp->resok.dir_wcc));
	default:
		return (xdr_wcc_data(xdrs, &objp->resfail.dir_wcc));
	}
}

bool_t
xdr_RENAME3args(XDR *xdrs, RENAME3args *objp)
{
	if (!xdr_diropargs3(xdrs, &objp->from))
		return (FALSE);
	return (xdr_diropargs3(xdrs, &objp->to));
}

bool_t
xdr_RENAME3res(XDR *xdrs, RENAME3res *objp)
{
	RENAME3resok *resokp;
	RENAME3resfail *resfailp;

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	switch (objp->status) {
	case NFS3_OK:
		/* xdr_RENAME3resok */
		resokp = &objp->resok;

		if (!xdr_wcc_data(xdrs, &resokp->fromdir_wcc))
			return (FALSE);
		return (xdr_wcc_data(xdrs, &resokp->todir_wcc));
	default:
		/* xdr_RENAME3resfail */
		resfailp = &objp->resfail;
		if (!xdr_wcc_data(xdrs, &resfailp->fromdir_wcc))
			return (FALSE);
		return (xdr_wcc_data(xdrs, &resfailp->todir_wcc));
	}
}

bool_t
xdr_LINK3args(XDR *xdrs, LINK3args *objp)
{
	switch (xdrs->x_op) {
	case XDR_FREE:
	case XDR_ENCODE:
		if (!xdr_nfs_fh3(xdrs, &objp->file))
			return (FALSE);
		break;
	case XDR_DECODE:
		if (!xdr_nfs_fh3_server(xdrs, &objp->file))
			return (FALSE);
		break;
	}
	return (xdr_diropargs3(xdrs, &objp->link));
}

bool_t
xdr_LINK3res(XDR *xdrs, LINK3res *objp)
{
	LINK3resok *resokp;
	LINK3resfail *resfailp;

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	switch (objp->status) {
	case NFS3_OK:
		/* xdr_LINK3resok */
		resokp = &objp->resok;
		if (!xdr_post_op_attr(xdrs, &resokp->file_attributes))
			return (FALSE);
		return (xdr_wcc_data(xdrs, &resokp->linkdir_wcc));
	default:
		/* xdr_LINK3resfail */
		resfailp = &objp->resfail;
		if (!xdr_post_op_attr(xdrs, &resfailp->file_attributes))
			return (FALSE);
		return (xdr_wcc_data(xdrs, &resfailp->linkdir_wcc));
	}
}

bool_t
xdr_READDIR3args(XDR *xdrs, READDIR3args *objp)
{
	rdma_chunkinfo_t rci;
	struct xdr_ops *xops = xdrrdma_xops();

	if (xdrs->x_op == XDR_FREE)
		return (TRUE);

	switch (xdrs->x_op) {
	case XDR_FREE:
	case XDR_ENCODE:
		if (!xdr_nfs_fh3(xdrs, &objp->dir))
			return (FALSE);
		break;
	case XDR_DECODE:
		if (!xdr_nfs_fh3_server(xdrs, &objp->dir))
			return (FALSE);
		break;
	}
	if ((xdrs->x_ops == &xdrrdma_ops || xdrs->x_ops == xops) &&
	    xdrs->x_op == XDR_ENCODE) {
		rci.rci_type = RCI_REPLY_CHUNK;
		rci.rci_len = objp->count;
		XDR_CONTROL(xdrs, XDR_RDMA_ADD_CHUNK, &rci);
	}

	if (!xdr_u_longlong_t(xdrs, &objp->cookie))
		return (FALSE);
	/*
	 * cookieverf is really an opaque 8 byte
	 * quantity, but we will treat it as a
	 * hyper for efficiency, the cost of
	 * a byteswap here saves bcopys elsewhere
	 */
	if (!xdr_u_longlong_t(xdrs, &objp->cookieverf))
		return (FALSE);
	return (xdr_u_int(xdrs, &objp->count));
}

#ifdef	nextdp
#undef	nextdp
#endif
#define	nextdp(dp)	((struct dirent64 *)((char *)(dp) + (dp)->d_reclen))
#ifdef	roundup
#undef	roundup
#endif
#define	roundup(x, y)	((((x) + ((y) - 1)) / (y)) * (y))

/*
 * ENCODE ONLY
 */
static bool_t
xdr_putdirlist(XDR *xdrs, READDIR3resok *objp)
{
	struct dirent64 *dp;
	char *name;
	int size;
	int bufsize;
	uint_t namlen;
	bool_t true = TRUE;
	bool_t false = FALSE;
	int entrysz;
	int tofit;
	fileid3 fileid;
	cookie3 cookie;

	if (xdrs->x_op != XDR_ENCODE)
		return (FALSE);

	/*
	 * bufsize is used to keep track of the size of the response.
	 * It is primed with:
	 *	1 for the status +
	 *	1 for the dir_attributes.attributes boolean +
	 *	2 for the cookie verifier
	 * all times BYTES_PER_XDR_UNIT to convert from XDR units
	 * to bytes.  If there are directory attributes to be
	 * returned, then:
	 *	NFS3_SIZEOF_FATTR3 for the dir_attributes.attr fattr3
	 * time BYTES_PER_XDR_UNIT is added to account for them.
	 */
	bufsize = (1 + 1 + 2) * BYTES_PER_XDR_UNIT;
	if (objp->dir_attributes.attributes)
		bufsize += NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT;
	for (size = objp->size, dp = (struct dirent64 *)objp->reply.entries;
	    size > 0;
	    size -= dp->d_reclen, dp = nextdp(dp)) {
		if (dp->d_reclen == 0)
			return (FALSE);
		if (dp->d_ino == 0)
			continue;
		name = dp->d_name;
		namlen = (uint_t)strlen(dp->d_name);
		/*
		 * An entry is composed of:
		 *	1 for the true/false list indicator +
		 *	2 for the fileid +
		 *	1 for the length of the name +
		 *	2 for the cookie +
		 * all times BYTES_PER_XDR_UNIT to convert from
		 * XDR units to bytes, plus the length of the name
		 * rounded up to the nearest BYTES_PER_XDR_UNIT.
		 */
		entrysz = (1 + 2 + 1 + 2) * BYTES_PER_XDR_UNIT +
		    roundup(namlen, BYTES_PER_XDR_UNIT);
		/*
		 * We need to check to see if the number of bytes left
		 * to go into the buffer will actually fit into the
		 * buffer.  This is calculated as the size of this
		 * entry plus:
		 *	1 for the true/false list indicator +
		 *	1 for the eof indicator
		 * times BYTES_PER_XDR_UNIT to convert from from
		 * XDR units to bytes.
		 */
		tofit = entrysz + (1 + 1) * BYTES_PER_XDR_UNIT;
		if (bufsize + tofit > objp->count) {
			objp->reply.eof = FALSE;
			break;
		}
		fileid = (fileid3)(dp->d_ino);
		cookie = (cookie3)(dp->d_off);
		if (!xdr_bool(xdrs, &true) ||
		    !xdr_u_longlong_t(xdrs, &fileid) ||
		    !xdr_bytes(xdrs, &name, &namlen, ~0) ||
		    !xdr_u_longlong_t(xdrs, &cookie)) {
			return (FALSE);
		}
		bufsize += entrysz;
	}
	if (!xdr_bool(xdrs, &false))
		return (FALSE);
	if (!xdr_bool(xdrs, &objp->reply.eof))
		return (FALSE);
	return (TRUE);
}

bool_t
xdr_READDIR3res(XDR *xdrs, READDIR3res *objp)
{
	READDIR3resok *resokp;

	/*
	 * ENCODE or FREE only
	 */
	if (xdrs->x_op == XDR_DECODE)
		return (FALSE);

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	if (objp->status != NFS3_OK)
		return (xdr_post_op_attr(xdrs, &objp->resfail.dir_attributes));

	/* xdr_READDIR3resok */
	resokp = &objp->resok;
	if (!xdr_post_op_attr(xdrs, &resokp->dir_attributes))
		return (FALSE);
	if (xdrs->x_op != XDR_ENCODE)
		return (TRUE);
	/*
	 * cookieverf is really an opaque 8 byte
	 * quantity, but we will treat it as a
	 * hyper for efficiency, the cost of
	 * a byteswap here saves bcopys elsewhere
	 */
	if (!xdr_u_longlong_t(xdrs, &resokp->cookieverf))
		return (FALSE);
	return (xdr_putdirlist(xdrs, resokp));
}

bool_t
xdr_READDIR3vres(XDR *xdrs, READDIR3vres *objp)
{
	dirent64_t *dp;
	uint_t entries_size;
	int outcount = 0;

	/*
	 * DECODE or FREE only
	 */
	if (xdrs->x_op == XDR_FREE)
		return (TRUE);

	if (xdrs->x_op != XDR_DECODE)
		return (FALSE);

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);

	if (!xdr_post_op_vattr(xdrs, &objp->dir_attributes))
		return (FALSE);

	if (objp->status != NFS3_OK)
		return (TRUE);

	/*
	 * cookieverf is really an opaque 8 byte
	 * quantity, but we will treat it as a
	 * hyper for efficiency, the cost of
	 * a byteswap here saves bcopys elsewhere
	 */
	if (!xdr_u_longlong_t(xdrs, &objp->cookieverf))
		return (FALSE);

	entries_size = objp->entries_size;
	dp = objp->entries;

	for (;;) {
		uint_t this_reclen;
		bool_t valid;
		uint_t namlen;
		ino64_t fileid;

		if (!XDR_GETINT32(xdrs, (int32_t *)&valid))
			return (FALSE);
		if (!valid) {
			/*
			 * We have run out of entries, decode eof.
			 */
			if (!XDR_GETINT32(xdrs, (int32_t *)&objp->eof))
				return (FALSE);

			break;
		}

		/*
		 * fileid3 fileid
		 */
		if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)&fileid))
			return (FALSE);

		/*
		 * filename3 name
		 */
		if (!XDR_GETINT32(xdrs, (int32_t *)&namlen))
			return (FALSE);
		this_reclen = DIRENT64_RECLEN(namlen);

		/*
		 * If this will overflow buffer, stop decoding
		 */
		if ((outcount + this_reclen) > entries_size) {
			objp->eof = FALSE;
			break;
		}
		dp->d_reclen = this_reclen;
		dp->d_ino = fileid;

		if (!xdr_opaque(xdrs, dp->d_name, namlen))
			return (FALSE);
		bzero(&dp->d_name[namlen],
		    DIRENT64_NAMELEN(this_reclen) - namlen);

		/*
		 * cookie3 cookie
		 */
		if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)&dp->d_off))
			return (FALSE);
		objp->loff = dp->d_off;

		outcount += this_reclen;
		dp = (dirent64_t *)((intptr_t)dp + this_reclen);
	}

	objp->size = outcount;
	return (TRUE);
}

bool_t
xdr_READDIRPLUS3args(XDR *xdrs, READDIRPLUS3args *objp)
{
	rdma_chunkinfo_t rci;
	struct xdr_ops *xops = xdrrdma_xops();

	if (xdrs->x_op == XDR_FREE)
		return (TRUE);

	switch (xdrs->x_op) {
	case XDR_FREE:
	case XDR_ENCODE:
		if (!xdr_nfs_fh3(xdrs, &objp->dir))
			return (FALSE);
		break;
	case XDR_DECODE:
		if (!xdr_nfs_fh3_server(xdrs, &objp->dir))
			return (FALSE);
		break;
	}
	if ((xdrs->x_ops == &xdrrdma_ops || xdrs->x_ops == xops) &&
	    xdrs->x_op == XDR_ENCODE) {
		rci.rci_type = RCI_REPLY_CHUNK;
		rci.rci_len = objp->maxcount;
		XDR_CONTROL(xdrs, XDR_RDMA_ADD_CHUNK, &rci);
	}

	if (!xdr_u_longlong_t(xdrs, &objp->cookie))
		return (FALSE);
	/*
	 * cookieverf is really an opaque 8 byte
	 * quantity, but we will treat it as a
	 * hyper for efficiency, the cost of
	 * a byteswap here saves bcopys elsewhere
	 */
	if (!xdr_u_longlong_t(xdrs, &objp->cookieverf))
		return (FALSE);
	if (!xdr_u_int(xdrs, &objp->dircount))
		return (FALSE);
	return (xdr_u_int(xdrs, &objp->maxcount));
}

/*
 * ENCODE ONLY
 */
static bool_t
xdr_putdirpluslist(XDR *xdrs, READDIRPLUS3resok *objp)
{
	struct dirent64 *dp;
	char *name;
	int nents;
	bool_t true = TRUE;
	bool_t false = FALSE;
	fileid3 fileid;
	cookie3 cookie;
	entryplus3_info *infop;

	if (xdrs->x_op != XDR_ENCODE)
		return (FALSE);

	dp = (struct dirent64 *)objp->reply.entries;
	nents = objp->size;
	infop = objp->infop;

	while (nents > 0) {
		if (dp->d_reclen == 0)
			return (FALSE);
		if (dp->d_ino != 0) {
			name = dp->d_name;
			fileid = (fileid3)(dp->d_ino);
			cookie = (cookie3)(dp->d_off);
			if (!xdr_bool(xdrs, &true) ||
			    !xdr_u_longlong_t(xdrs, &fileid) ||
			    !xdr_bytes(xdrs, &name, &infop->namelen, ~0) ||
			    !xdr_u_longlong_t(xdrs, &cookie) ||
			    !xdr_post_op_attr(xdrs, &infop->attr) ||
			    !xdr_post_op_fh3(xdrs, &infop->fh)) {
				return (FALSE);
			}
		}
		dp = nextdp(dp);
		infop++;
		nents--;
	}

	if (!xdr_bool(xdrs, &false))
		return (FALSE);
	if (!xdr_bool(xdrs, &objp->reply.eof))
		return (FALSE);
	return (TRUE);
}

bool_t
xdr_READDIRPLUS3res(XDR *xdrs, READDIRPLUS3res *objp)
{
	READDIRPLUS3resok *resokp;

	/*
	 * ENCODE or FREE only
	 */
	if (xdrs->x_op == XDR_DECODE)
		return (FALSE);

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	switch (objp->status) {
	case NFS3_OK:
		/* xdr_READDIRPLUS3resok */
		resokp = &objp->resok;
		if (!xdr_post_op_attr(xdrs, &resokp->dir_attributes))
			return (FALSE);
		/*
		 * cookieverf is really an opaque 8 byte
		 * quantity, but we will treat it as a
		 * hyper for efficiency, the cost of
		 * a byteswap here saves bcopys elsewhere
		 */
		if (!xdr_u_longlong_t(xdrs, &resokp->cookieverf))
			return (FALSE);
		if (xdrs->x_op == XDR_ENCODE) {
			if (!xdr_putdirpluslist(xdrs, resokp))
				return (FALSE);
		}
		break;
	default:
		return (xdr_post_op_attr(xdrs, &objp->resfail.dir_attributes));
	}
	return (TRUE);
}

/*
 * Decode readdirplus directly into a dirent64_t and do the DNLC caching.
 */
bool_t
xdr_READDIRPLUS3vres(XDR *xdrs, READDIRPLUS3vres *objp)
{
	dirent64_t *dp;
	vnode_t *dvp;
	uint_t entries_size;
	int outcount = 0;
	vnode_t *nvp;
	rnode_t *rp;
	post_op_vattr pov;
	vattr_t va;

	/*
	 * DECODE or FREE only
	 */
	if (xdrs->x_op == XDR_FREE)
		return (TRUE);

	if (xdrs->x_op != XDR_DECODE)
		return (FALSE);

	if (!XDR_GETINT32(xdrs, (int32_t *)&objp->status))
		return (FALSE);

	if (!xdr_post_op_vattr(xdrs, &objp->dir_attributes))
		return (FALSE);

	if (objp->status != NFS3_OK)
		return (TRUE);

	/*
	 * cookieverf is really an opaque 8 byte
	 * quantity, but we will treat it as a
	 * hyper for efficiency, the cost of
	 * a byteswap here saves bcopys elsewhere
	 */
	if (!xdr_u_longlong_t(xdrs, &objp->cookieverf))
		return (FALSE);

	dvp = objp->dir_attributes.fres.vp;
	rp = VTOR(dvp);

	pov.fres.vap = &va;
	pov.fres.vp = dvp;

	entries_size = objp->entries_size;
	dp = objp->entries;

	for (;;) {
		uint_t this_reclen;
		bool_t valid;
		uint_t namlen;
		nfs_fh3 fh;
		int va_valid;
		int fh_valid;
		ino64_t fileid;

		if (!XDR_GETINT32(xdrs, (int32_t *)&valid))
			return (FALSE);
		if (!valid) {
			/*
			 * We have run out of entries, decode eof.
			 */
			if (!XDR_GETINT32(xdrs, (int32_t *)&objp->eof))
				return (FALSE);

			break;
		}

		/*
		 * fileid3 fileid
		 */
		if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)&fileid))
			return (FALSE);

		/*
		 * filename3 name
		 */
		if (!XDR_GETINT32(xdrs, (int32_t *)&namlen))
			return (FALSE);
		this_reclen = DIRENT64_RECLEN(namlen);

		/*
		 * If this will overflow buffer, stop decoding
		 */
		if ((outcount + this_reclen) > entries_size) {
			objp->eof = FALSE;
			break;
		}
		dp->d_reclen = this_reclen;
		dp->d_ino = fileid;

		if (!xdr_opaque(xdrs, dp->d_name, namlen))
			return (FALSE);
		bzero(&dp->d_name[namlen],
		    DIRENT64_NAMELEN(this_reclen) - namlen);

		/*
		 * cookie3 cookie
		 */
		if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)&dp->d_off))
			return (FALSE);
		objp->loff = dp->d_off;

		/*
		 * post_op_attr name_attributes
		 */
		if (!xdr_post_op_vattr(xdrs, &pov))
			return (FALSE);

		if (pov.attributes == TRUE &&
		    pov.fres.status == NFS3_OK)
			va_valid = TRUE;
		else
			va_valid = FALSE;

		/*
		 * post_op_fh3 name_handle
		 */
		if (!XDR_GETINT32(xdrs, (int32_t *)&fh_valid))
			return (FALSE);

		/*
		 * By definition of the standard fh_valid can be 0 (FALSE) or
		 * 1 (TRUE), but we have to account for it being anything else
		 * in case some other system didn't follow the standard.  Note
		 * that this is why the else checks if the fh_valid variable
		 * is != FALSE.
		 */
		if (fh_valid == TRUE) {
			if (!xdr_nfs_fh3(xdrs, &fh))
				return (FALSE);
		} else {
			if (fh_valid != FALSE)
				return (FALSE);
		}

		/*
		 * If the name is "." or there are no attributes,
		 * don't polute the DNLC with "." entries or files
		 * we cannot determine the type for.
		 */
		if (!(namlen == 1 && dp->d_name[0] == '.') &&
		    va_valid && fh_valid) {

			/*
			 * Do the DNLC caching
			 */
			nvp = makenfs3node_va(&fh, &va, dvp->v_vfsp,
			    objp->time, objp->credentials,
			    rp->r_path, dp->d_name);
			dnlc_update(dvp, dp->d_name, nvp);
			VN_RELE(nvp);
		}

		outcount += this_reclen;
		dp = (dirent64_t *)((intptr_t)dp + this_reclen);
	}

	objp->size = outcount;
	return (TRUE);
}

bool_t
xdr_FSSTAT3res(XDR *xdrs, FSSTAT3res *objp)
{
	FSSTAT3resok *resokp;

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	if (objp->status != NFS3_OK)
		return (xdr_post_op_attr(xdrs, &objp->resfail.obj_attributes));

	/* xdr_FSSTAT3resok */
	resokp = &objp->resok;
	if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes))
		return (FALSE);
	if (!xdr_u_longlong_t(xdrs, &resokp->tbytes))
		return (FALSE);
	if (!xdr_u_longlong_t(xdrs, &resokp->fbytes))
		return (FALSE);
	if (!xdr_u_longlong_t(xdrs, &resokp->abytes))
		return (FALSE);
	if (!xdr_u_longlong_t(xdrs, &resokp->tfiles))
		return (FALSE);
	if (!xdr_u_longlong_t(xdrs, &resokp->ffiles))
		return (FALSE);
	if (!xdr_u_longlong_t(xdrs, &resokp->afiles))
		return (FALSE);
	return (xdr_u_int(xdrs, &resokp->invarsec));
}

bool_t
xdr_FSINFO3res(XDR *xdrs, FSINFO3res *objp)
{
	FSINFO3resok *resokp;

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	if (objp->status != NFS3_OK) /* xdr_FSSTAT3resfail */
		return (xdr_post_op_attr(xdrs, &objp->resfail.obj_attributes));

	/* xdr_FSINFO3resok */
	resokp = &objp->resok;
	if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes))
		return (FALSE);
	if (!xdr_u_int(xdrs, &resokp->rtmax))
		return (FALSE);
	if (!xdr_u_int(xdrs, &resokp->rtpref))
		return (FALSE);
	if (!xdr_u_int(xdrs, &resokp->rtmult))
		return (FALSE);
	if (!xdr_u_int(xdrs, &resokp->wtmax))
		return (FALSE);
	if (!xdr_u_int(xdrs, &resokp->wtpref))
		return (FALSE);
	if (!xdr_u_int(xdrs, &resokp->wtmult))
		return (FALSE);
	if (!xdr_u_int(xdrs, &resokp->dtpref))
		return (FALSE);
	if (!xdr_u_longlong_t(xdrs, &resokp->maxfilesize))
		return (FALSE);
	if (!xdr_u_int(xdrs, &resokp->time_delta.seconds))
		return (FALSE);
	if (!xdr_u_int(xdrs, &resokp->time_delta.nseconds))
		return (FALSE);
	return (xdr_u_int(xdrs, &resokp->properties));
}

bool_t
xdr_PATHCONF3res(XDR *xdrs, PATHCONF3res *objp)
{
	PATHCONF3resok *resokp;

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	if (objp->status != NFS3_OK)
		return (xdr_post_op_attr(xdrs, &objp->resfail.obj_attributes));

	/* xdr_PATHCONF3resok */
	resokp = &objp->resok;
	if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes))
		return (FALSE);
	if (!xdr_u_int(xdrs, &resokp->info.link_max))
		return (FALSE);
	if (!xdr_u_int(xdrs, &resokp->info.name_max))
		return (FALSE);
	if (!xdr_bool(xdrs, &resokp->info.no_trunc))
		return (FALSE);
	if (!xdr_bool(xdrs, &resokp->info.chown_restricted))
		return (FALSE);
	if (!xdr_bool(xdrs, &resokp->info.case_insensitive))
		return (FALSE);
	return (xdr_bool(xdrs, &resokp->info.case_preserving));
}

bool_t
xdr_COMMIT3args(XDR *xdrs, COMMIT3args *objp)
{
	if (xdrs->x_op == XDR_FREE)
		return (TRUE);

	switch (xdrs->x_op) {
	case XDR_FREE:
	case XDR_ENCODE:
		if (!xdr_nfs_fh3(xdrs, &objp->file))
			return (FALSE);
		break;
	case XDR_DECODE:
		if (!xdr_nfs_fh3_server(xdrs, &objp->file))
			return (FALSE);
		break;
	}
	if (!xdr_u_longlong_t(xdrs, &objp->offset))
		return (FALSE);
	return (xdr_u_int(xdrs, &objp->count));
}

bool_t
xdr_COMMIT3res(XDR *xdrs, COMMIT3res *objp)
{
	COMMIT3resok *resokp;

	if (!xdr_enum(xdrs, (enum_t *)&objp->status))
		return (FALSE);
	if (objp->status != NFS3_OK)
		return (xdr_wcc_data(xdrs, &objp->resfail.file_wcc));

	/* xdr_COMMIT3resok */
	resokp = &objp->resok;
	if (!xdr_wcc_data(xdrs, &resokp->file_wcc))
		return (FALSE);
	/*
	 * writeverf3 is really an opaque 8 byte
	 * quantity, but we will treat it as a
	 * hyper for efficiency, the cost of
	 * a byteswap here saves bcopys elsewhere
	 */
	return (xdr_u_longlong_t(xdrs, &resokp->verf));
}