root/usr/src/uts/common/fs/nfs/nfs3_xdr.c
/*
 * 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 2010 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 */

/*
 * Copyright (c) 2013 by Delphix. 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:
                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 ((ftype3)vap->va_type < NF3REG ||
                    (ftype3)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 ((ftype3)vap->va_type < NF3REG ||
                    (ftype3)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) {

                mp = resokp->data.mp;
                if (mp != NULL) {
                        if (xdrs->x_ops == &xdrmblk_ops) {
                                if (xdrmblk_putmblk(xdrs, mp, resokp->count)) {
                                        resokp->data.mp = NULL;
                                        return (TRUE);
                                } else {
                                        return (FALSE);
                                }
                        } else if (mp->b_cont != NULL) {
                                /*
                                 * We have read results in an mblk chain, but
                                 * the encoding operations don't handle mblks
                                 * (they'll operate on data.data_val rather
                                 * than data.mp).  Because data_val can only
                                 * point at a single data buffer, we need to
                                 * pullup the read results into a single data
                                 * block and reset data_val to point to it.
                                 *
                                 * This happens with RPC GSS where the wrapping
                                 * function does XDR serialization into a
                                 * temporary buffer prior to applying GSS.
                                 * Because we're not in a performance sensitive
                                 * path, the pullupmsg() here shouldn't hurt us
                                 * too badly.
                                 */
                                if (pullupmsg(mp, -1) == 0)
                                        return (FALSE);
                                resokp->data.data_val = (caddr_t)mp->b_rptr;
                        }
                } else {
                        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));
}