view usr/src/uts/common/fs/nfs/nfs3_xdr.c @ 10954:2b7813ae5034

6894964 nfsbugs tests fail in PIT with: assertion failed: sarg->vap->va_mask & AT_TYPE
author Rich Brown <Rich.Brown@Sun.COM>
date Wed, 04 Nov 2009 21:29:57 -0600
parents 7155ecb17858
children 10d35fc3d7fd
line wrap: on
line source

/*
 * 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);

	/* On success, all attributes will be decoded */
	vap->va_mask = AT_ALL;

	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));
}