root/usr/src/uts/common/fs/nfs/nfs_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 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/sdt.h>

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

#include <nfs/nfs.h>

#include <vm/hat.h>
#include <vm/as.h>
#include <vm/seg.h>
#include <vm/seg_map.h>
#include <vm/seg_kmem.h>

static bool_t xdr_fastshorten(XDR *, uint_t);

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

/*
 * File access handle
 * The fhandle struct is treated a opaque data on the wire
 */
bool_t
xdr_fhandle(XDR *xdrs, fhandle_t *fh)
{
        int32_t *ptr;
        int32_t *fhp;

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

        ptr = XDR_INLINE(xdrs, RNDUP(sizeof (fhandle_t)));
        if (ptr != NULL) {
                fhp = (int32_t *)fh;
                if (xdrs->x_op == XDR_DECODE) {
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp = *ptr;
                } else {
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr = *fhp;
                }
                return (TRUE);
        }

        return (xdr_opaque(xdrs, (caddr_t)fh, NFS_FHSIZE));
}

bool_t
xdr_fastfhandle(XDR *xdrs, fhandle_t **fh)
{
        int32_t *ptr;

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

        ptr = XDR_INLINE(xdrs, RNDUP(sizeof (fhandle_t)));
        if (ptr != NULL) {
                *fh = (fhandle_t *)ptr;
                return (TRUE);
        }

        return (FALSE);
}

/*
 * Arguments to remote write and writecache
 */
bool_t
xdr_writeargs(XDR *xdrs, struct nfswriteargs *wa)
{
        int32_t *ptr;
        int32_t *fhp;

        switch (xdrs->x_op) {
        case XDR_DECODE:
                wa->wa_args = &wa->wa_args_buf;
                ptr = XDR_INLINE(xdrs, RNDUP(sizeof (fhandle_t)) +
                    3 * BYTES_PER_XDR_UNIT);
                if (ptr != NULL) {
                        fhp = (int32_t *)&wa->wa_fhandle;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp = *ptr++;
                        wa->wa_begoff = IXDR_GET_U_INT32(ptr);
                        wa->wa_offset = IXDR_GET_U_INT32(ptr);
                        wa->wa_totcount = IXDR_GET_U_INT32(ptr);
                        wa->wa_mblk = NULL;
                        wa->wa_data = NULL;
                        wa->wa_rlist = NULL;
                        wa->wa_conn = NULL;
                        if (xdrs->x_ops == &xdrmblk_ops) {
                                return (xdrmblk_getmblk(xdrs, &wa->wa_mblk,
                                    &wa->wa_count));
                        } else {
                                if (xdrs->x_ops == &xdrrdmablk_ops) {
                                        if (xdrrdma_getrdmablk(xdrs,
                                            &wa->wa_rlist,
                                            &wa->wa_count,
                                            &wa->wa_conn,
                                            NFS_MAXDATA) == TRUE)
                                        return (xdrrdma_read_from_client(
                                            wa->wa_rlist,
                                            &wa->wa_conn,
                                            wa->wa_count));

                                        wa->wa_rlist = NULL;
                                        wa->wa_conn = NULL;
                                }
                        }

                        /*
                         * It is just as efficient to xdr_bytes
                         * an array of unknown length as to inline copy it.
                         */
                        return (xdr_bytes(xdrs, &wa->wa_data,
                            &wa->wa_count, NFS_MAXDATA));
                }
                if (xdr_fhandle(xdrs, &wa->wa_fhandle) &&
                    xdr_u_int(xdrs, &wa->wa_begoff) &&
                    xdr_u_int(xdrs, &wa->wa_offset) &&
                    xdr_u_int(xdrs, &wa->wa_totcount)) {
                        /* deal with the variety of data transfer types */

                        wa->wa_mblk = NULL;
                        wa->wa_data = NULL;
                        wa->wa_rlist = NULL;
                        wa->wa_conn = NULL;

                        if (xdrs->x_ops == &xdrmblk_ops) {
                                if (xdrmblk_getmblk(xdrs, &wa->wa_mblk,
                                    &wa->wa_count) == TRUE)
                                        return (TRUE);
                        } else {
                                if (xdrs->x_ops == &xdrrdmablk_ops) {
                                        if (xdrrdma_getrdmablk(xdrs,
                                            &wa->wa_rlist,
                                            &wa->wa_count,
                                            &wa->wa_conn,
                                            NFS_MAXDATA) == TRUE)
                                        return (xdrrdma_read_from_client(
                                            wa->wa_rlist,
                                            &wa->wa_conn,
                                            wa->wa_count));

                                        wa->wa_rlist = NULL;
                                        wa->wa_conn = NULL;
                                }
                        }
                        return (xdr_bytes(xdrs, &wa->wa_data,
                            &wa->wa_count, NFS_MAXDATA));
                }
                return (FALSE);
        case XDR_ENCODE:
                ptr = XDR_INLINE(xdrs, RNDUP(sizeof (fhandle_t)) +
                    3 * BYTES_PER_XDR_UNIT);
                if (ptr != NULL) {
                        fhp = (int32_t *)&wa->wa_fhandle;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp;
                        IXDR_PUT_U_INT32(ptr, wa->wa_begoff);
                        IXDR_PUT_U_INT32(ptr, wa->wa_offset);
                        IXDR_PUT_U_INT32(ptr, wa->wa_totcount);
                } else {
                        if (!(xdr_fhandle(xdrs, &wa->wa_fhandle) &&
                            xdr_u_int(xdrs, &wa->wa_begoff) &&
                            xdr_u_int(xdrs, &wa->wa_offset) &&
                            xdr_u_int(xdrs, &wa->wa_totcount)))
                                return (FALSE);
                }

                return (xdr_bytes(xdrs, &wa->wa_data, &wa->wa_count,
                    NFS_MAXDATA));
        case XDR_FREE:
                if (wa->wa_rlist) {
                        (void) xdrrdma_free_clist(wa->wa_conn, wa->wa_rlist);
                        wa->wa_rlist = NULL;
                }

                if (wa->wa_data != NULL) {
                        kmem_free(wa->wa_data, wa->wa_count);
                        wa->wa_data = NULL;
                }
                return (TRUE);
        }
        return (FALSE);
}


/*
 * File attributes
 */
bool_t
xdr_fattr(XDR *xdrs, struct nfsfattr *na)
{
        int32_t *ptr;

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

        ptr = XDR_INLINE(xdrs, 17 * BYTES_PER_XDR_UNIT);
        if (ptr != NULL) {
                if (xdrs->x_op == XDR_DECODE) {
                        na->na_type = IXDR_GET_ENUM(ptr, enum nfsftype);
                        na->na_mode = IXDR_GET_U_INT32(ptr);
                        na->na_nlink = IXDR_GET_U_INT32(ptr);
                        na->na_uid = IXDR_GET_U_INT32(ptr);
                        na->na_gid = IXDR_GET_U_INT32(ptr);
                        na->na_size = IXDR_GET_U_INT32(ptr);
                        na->na_blocksize = IXDR_GET_U_INT32(ptr);
                        na->na_rdev = IXDR_GET_U_INT32(ptr);
                        na->na_blocks = IXDR_GET_U_INT32(ptr);
                        na->na_fsid = IXDR_GET_U_INT32(ptr);
                        na->na_nodeid = IXDR_GET_U_INT32(ptr);
                        na->na_atime.tv_sec = IXDR_GET_U_INT32(ptr);
                        na->na_atime.tv_usec = IXDR_GET_U_INT32(ptr);
                        na->na_mtime.tv_sec = IXDR_GET_U_INT32(ptr);
                        na->na_mtime.tv_usec = IXDR_GET_U_INT32(ptr);
                        na->na_ctime.tv_sec = IXDR_GET_U_INT32(ptr);
                        na->na_ctime.tv_usec = IXDR_GET_U_INT32(ptr);
                } else {
                        IXDR_PUT_ENUM(ptr, na->na_type);
                        IXDR_PUT_U_INT32(ptr, na->na_mode);
                        IXDR_PUT_U_INT32(ptr, na->na_nlink);
                        IXDR_PUT_U_INT32(ptr, na->na_uid);
                        IXDR_PUT_U_INT32(ptr, na->na_gid);
                        IXDR_PUT_U_INT32(ptr, na->na_size);
                        IXDR_PUT_U_INT32(ptr, na->na_blocksize);
                        IXDR_PUT_U_INT32(ptr, na->na_rdev);
                        IXDR_PUT_U_INT32(ptr, na->na_blocks);
                        IXDR_PUT_U_INT32(ptr, na->na_fsid);
                        IXDR_PUT_U_INT32(ptr, na->na_nodeid);
                        IXDR_PUT_U_INT32(ptr, na->na_atime.tv_sec);
                        IXDR_PUT_U_INT32(ptr, na->na_atime.tv_usec);
                        IXDR_PUT_U_INT32(ptr, na->na_mtime.tv_sec);
                        IXDR_PUT_U_INT32(ptr, na->na_mtime.tv_usec);
                        IXDR_PUT_U_INT32(ptr, na->na_ctime.tv_sec);
                        IXDR_PUT_U_INT32(ptr, na->na_ctime.tv_usec);
                }
                return (TRUE);
        }

        if (xdr_enum(xdrs, (enum_t *)&na->na_type) &&
            xdr_u_int(xdrs, &na->na_mode) &&
            xdr_u_int(xdrs, &na->na_nlink) &&
            xdr_u_int(xdrs, &na->na_uid) &&
            xdr_u_int(xdrs, &na->na_gid) &&
            xdr_u_int(xdrs, &na->na_size) &&
            xdr_u_int(xdrs, &na->na_blocksize) &&
            xdr_u_int(xdrs, &na->na_rdev) &&
            xdr_u_int(xdrs, &na->na_blocks) &&
            xdr_u_int(xdrs, &na->na_fsid) &&
            xdr_u_int(xdrs, &na->na_nodeid) &&
            xdr_nfs2_timeval(xdrs, &na->na_atime) &&
            xdr_nfs2_timeval(xdrs, &na->na_mtime) &&
            xdr_nfs2_timeval(xdrs, &na->na_ctime)) {
                return (TRUE);
        }
        return (FALSE);
}

#ifdef _LITTLE_ENDIAN
bool_t
xdr_fastfattr(XDR *xdrs, struct nfsfattr *na)
{
        if (xdrs->x_op == XDR_FREE)
                return (TRUE);
        if (xdrs->x_op == XDR_DECODE)
                return (FALSE);

        na->na_type = htonl(na->na_type);
        na->na_mode = htonl(na->na_mode);
        na->na_nlink = htonl(na->na_nlink);
        na->na_uid = htonl(na->na_uid);
        na->na_gid = htonl(na->na_gid);
        na->na_size = htonl(na->na_size);
        na->na_blocksize = htonl(na->na_blocksize);
        na->na_rdev = htonl(na->na_rdev);
        na->na_blocks = htonl(na->na_blocks);
        na->na_fsid = htonl(na->na_fsid);
        na->na_nodeid = htonl(na->na_nodeid);
        na->na_atime.tv_sec = htonl(na->na_atime.tv_sec);
        na->na_atime.tv_usec = htonl(na->na_atime.tv_usec);
        na->na_mtime.tv_sec = htonl(na->na_mtime.tv_sec);
        na->na_mtime.tv_usec = htonl(na->na_mtime.tv_usec);
        na->na_ctime.tv_sec = htonl(na->na_ctime.tv_sec);
        na->na_ctime.tv_usec = htonl(na->na_ctime.tv_usec);
        return (TRUE);
}
#endif

bool_t
xdr_readlink(XDR *xdrs, fhandle_t *fh)
{
        rdma_chunkinfo_t rci;
        struct xdr_ops *xops = xdrrdma_xops();

        if (xdr_fhandle(xdrs, fh)) {
                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);
                }

                return (TRUE);
        }
        return (FALSE);
}

/*
 * Arguments to remote read
 */
bool_t
xdr_readargs(XDR *xdrs, struct nfsreadargs *ra)
{
        int32_t *ptr;
        int32_t *fhp;
        rdma_chunkinfo_t rci;
        rdma_wlist_conn_info_t rwci;
        struct xdr_ops *xops = xdrrdma_xops();

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

        ptr = XDR_INLINE(xdrs,
            RNDUP(sizeof (fhandle_t)) + 3 * BYTES_PER_XDR_UNIT);
        if (ptr != NULL) {
                if (xdrs->x_op == XDR_DECODE) {
                        fhp = (int32_t *)&ra->ra_fhandle;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp = *ptr++;
                        ra->ra_offset = IXDR_GET_INT32(ptr);
                        ra->ra_count = IXDR_GET_INT32(ptr);
                        ra->ra_totcount = IXDR_GET_INT32(ptr);
                } else {
                        fhp = (int32_t *)&ra->ra_fhandle;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp;
                        IXDR_PUT_INT32(ptr, ra->ra_offset);
                        IXDR_PUT_INT32(ptr, ra->ra_count);
                        IXDR_PUT_INT32(ptr, ra->ra_totcount);
                }
        } else {
                if (!xdr_fhandle(xdrs, &ra->ra_fhandle) ||
                    !xdr_u_int(xdrs, &ra->ra_offset) ||
                    !xdr_u_int(xdrs, &ra->ra_count) ||
                    !xdr_u_int(xdrs, &ra->ra_totcount)) {
                        return (FALSE);
                }
        }

        if (ra->ra_count > NFS_MAXDATA)
                return (FALSE);

        ra->ra_wlist = NULL;
        ra->ra_conn = NULL;

        /* If this is xdrrdma_sizeof, record the expect response size */
        if (xdrs->x_ops == xops && xdrs->x_op == XDR_ENCODE) {
                rci.rci_type = RCI_WRITE_ADDR_CHUNK;
                rci.rci_len = ra->ra_count;
                (void) XDR_CONTROL(xdrs, XDR_RDMA_ADD_CHUNK, &rci);
        }
        /* Nothing special to do, return */
        if (xdrs->x_ops != &xdrrdma_ops || xdrs->x_op == XDR_FREE)
                return (TRUE);

        if (xdrs->x_op == XDR_ENCODE) {
                /* Place the target data location into the RDMA header */
                rci.rci_type = RCI_WRITE_ADDR_CHUNK;
                rci.rci_a.rci_addr = ra->ra_data;
                rci.rci_len = ra->ra_count;
                rci.rci_clpp = &ra->ra_wlist;

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

        /* XDR_DECODE case */
        (void) XDR_CONTROL(xdrs, XDR_RDMA_GET_WCINFO, &rwci);
        ra->ra_wlist = rwci.rwci_wlist;
        ra->ra_conn = rwci.rwci_conn;

        return (TRUE);
}


/*
 * Status OK portion of remote read reply
 */
bool_t
xdr_rrok(XDR *xdrs, struct nfsrrok *rrok)
{
        bool_t ret;
        mblk_t *mp;
        struct xdr_ops *xops = xdrrdma_xops();

        if (xdr_fattr(xdrs, &rrok->rrok_attr) == FALSE)
                return (FALSE);

        /* deal with RDMA separately */
        if (xdrs->x_ops == &xdrrdma_ops || xdrs->x_ops == xops) {
                if (xdrs->x_op == XDR_ENCODE &&
                    rrok->rrok_mp != NULL) {
                        ret = xdr_bytes(xdrs, (char **)&rrok->rrok_data,
                            &rrok->rrok_count, NFS_MAXDATA);
                        return (ret);
                }

                if (xdrs->x_op == XDR_ENCODE) {
                        if (xdr_u_int(xdrs, &rrok->rrok_count) == FALSE) {
                                return (FALSE);
                        }
                        /*
                         * If read data sent by wlist (RDMA_WRITE), don't do
                         * xdr_bytes() below.   RDMA_WRITE transfers the data.
                         */
                        if (rrok->rrok_wlist) {
                                if (rrok->rrok_count != 0) {
                                        return (xdrrdma_send_read_data(
                                            xdrs, rrok->rrok_count,
                                            rrok->rrok_wlist));
                                }
                                return (TRUE);
                        }
                        if (rrok->rrok_count == 0) {
                                return (TRUE);
                        }
                } else {
                        struct clist *cl;
                        uint32_t count;

                        XDR_CONTROL(xdrs, XDR_RDMA_GET_WLIST, &cl);

                        if (cl) {
                                if (!xdr_u_int(xdrs, &count))
                                        return (FALSE);
                                if (count == 0) {
                                        rrok->rrok_wlist_len = 0;
                                        rrok->rrok_count = 0;
                                } else {
                                        rrok->rrok_wlist_len = clist_len(cl);
                                        if (rrok->rrok_wlist_len !=
                                            roundup(count,
                                            BYTES_PER_XDR_UNIT)) {
                                                rrok->rrok_wlist_len = 0;
                                                rrok->rrok_count = 0;
                                                return (FALSE);
                                        }
                                        rrok->rrok_count = count;
                                }
                                return (TRUE);
                        }
                }
                ret = xdr_bytes(xdrs, (char **)&rrok->rrok_data,
                    &rrok->rrok_count, NFS_MAXDATA);

                return (ret);
        }

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

                mp = rrok->rrok_mp;
                if (mp != NULL && xdrs->x_ops == &xdrmblk_ops) {
                        mp->b_wptr += rrok->rrok_count;
                        rndup = BYTES_PER_XDR_UNIT -
                            (rrok->rrok_count % 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,
                            rrok->rrok_count) == TRUE) {
                                rrok->rrok_mp = NULL;
                                return (TRUE);
                        }
                }

                /*
                 * Fall thru for the xdr_bytes()
                 *
                 * Note: the mblk mp will be freed in rfs_rdfree
                 */
        }

        ret = xdr_bytes(xdrs, (char **)&rrok->rrok_data,
            &rrok->rrok_count, NFS_MAXDATA);

        return (ret);
}

static struct xdr_discrim rdres_discrim[2] = {
        { NFS_OK, xdr_rrok },
        { __dontcare__, NULL_xdrproc_t }
};

/*
 * Reply from remote read
 */
bool_t
xdr_rdresult(XDR *xdrs, struct nfsrdresult *rr)
{
        return (xdr_union(xdrs, (enum_t *)&(rr->rr_status),
            (caddr_t)&(rr->rr_ok), rdres_discrim, xdr_void));
}

/*
 * File attributes which can be set
 */
bool_t
xdr_sattr(XDR *xdrs, struct nfssattr *sa)
{
        if (xdr_u_int(xdrs, &sa->sa_mode) &&
            xdr_u_int(xdrs, &sa->sa_uid) &&
            xdr_u_int(xdrs, &sa->sa_gid) &&
            xdr_u_int(xdrs, &sa->sa_size) &&
            xdr_nfs2_timeval(xdrs, &sa->sa_atime) &&
            xdr_nfs2_timeval(xdrs, &sa->sa_mtime)) {
                return (TRUE);
        }
        return (FALSE);
}

static struct xdr_discrim attrstat_discrim[2] = {
        { (int)NFS_OK, xdr_fattr },
        { __dontcare__, NULL_xdrproc_t }
};

/*
 * Reply status with file attributes
 */
bool_t
xdr_attrstat(XDR *xdrs, struct nfsattrstat *ns)
{
        return (xdr_union(xdrs, (enum_t *)&(ns->ns_status),
            (caddr_t)&(ns->ns_attr), attrstat_discrim, xdr_void));
}

/*
 * Fast reply status with file attributes
 */
bool_t
xdr_fastattrstat(XDR *xdrs, struct nfsattrstat *ns)
{
#if defined(_LITTLE_ENDIAN)
        /*
         * we deal with the discriminator;  it's an enum
         */
        if (!xdr_fastenum(xdrs, (enum_t *)&ns->ns_status))
                return (FALSE);

        if (ns->ns_status == NFS_OK)
                return (xdr_fastfattr(xdrs, &ns->ns_attr));
#elif defined(_BIG_ENDIAN)
        if (ns->ns_status == NFS_OK)
                return (TRUE);
#endif
        return (xdr_fastshorten(xdrs, sizeof (*ns)));
}

/*
 * NFS_OK part of read sym link reply union
 */
bool_t
xdr_srok(XDR *xdrs, struct nfssrok *srok)
{
        /*
         * It is just as efficient to xdr_bytes
         * an array of unknown length as to inline copy it.
         */
        return (xdr_bytes(xdrs, &srok->srok_data, &srok->srok_count,
            NFS_MAXPATHLEN));
}

static struct xdr_discrim rdlnres_discrim[2] = {
        { (int)NFS_OK, xdr_srok },
        { __dontcare__, NULL_xdrproc_t }
};

/*
 * Result of reading symbolic link
 */
bool_t
xdr_rdlnres(XDR *xdrs, struct nfsrdlnres *rl)
{
        return (xdr_union(xdrs, (enum_t *)&(rl->rl_status),
            (caddr_t)&(rl->rl_srok), rdlnres_discrim, xdr_void));
}

/*
 * Arguments to readdir
 */
bool_t
xdr_rddirargs(XDR *xdrs, struct nfsrddirargs *rda)
{
        int32_t *ptr;
        int32_t *fhp;
        rdma_chunkinfo_t rci;
        struct xdr_ops *xops = xdrrdma_xops();

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

        ptr = XDR_INLINE(xdrs,
            RNDUP(sizeof (fhandle_t)) + 2 * BYTES_PER_XDR_UNIT);

        if ((xdrs->x_ops == &xdrrdma_ops || xdrs->x_ops == xops) &&
            xdrs->x_op == XDR_ENCODE) {
                rci.rci_type = RCI_REPLY_CHUNK;
                rci.rci_len = rda->rda_count;
                XDR_CONTROL(xdrs, XDR_RDMA_ADD_CHUNK, &rci);
        }

        if (ptr != NULL) {
                if (xdrs->x_op == XDR_DECODE) {
                        fhp = (int32_t *)&rda->rda_fh;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp = *ptr++;
                        rda->rda_offset = IXDR_GET_U_INT32(ptr);
                        rda->rda_count = IXDR_GET_U_INT32(ptr);
                } else {
                        fhp = (int32_t *)&rda->rda_fh;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp;
                        IXDR_PUT_U_INT32(ptr, rda->rda_offset);
                        IXDR_PUT_U_INT32(ptr, rda->rda_count);
                }
                return (TRUE);
        }

        if (xdr_fhandle(xdrs, &rda->rda_fh) &&
            xdr_u_int(xdrs, &rda->rda_offset) &&
            xdr_u_int(xdrs, &rda->rda_count)) {
                return (TRUE);
        }
        return (FALSE);
}


/*
 * Directory read reply:
 * union (enum status) {
 *      NFS_OK: entlist;
 *              boolean eof;
 *      default:
 * }
 *
 * Directory entries
 *      struct  direct {
 *              off_t   d_off;                  * offset of next entry *
 *              u_int   d_fileno;               * inode number of entry *
 *              u_short d_reclen;               * length of this record *
 *              u_short d_namlen;               * length of string in d_name *
 *              char    d_name[MAXNAMLEN + 1];  * name no longer than this *
 *      };
 * are on the wire as:
 * union entlist (boolean valid) {
 *      TRUE:   struct otw_dirent;
 *              u_int nxtoffset;
 *              union entlist;
 *      FALSE:
 * }
 * where otw_dirent is:
 *      struct dirent {
 *              u_int   de_fid;
 *              string  de_name<NFS_MAXNAMELEN>;
 *      }
 */

#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
 */
bool_t
xdr_putrddirres(XDR *xdrs, struct nfsrddirres *rd)
{
        struct dirent64 *dp;
        char *name;
        int size;
        uint_t namlen;
        bool_t true = TRUE;
        bool_t false = FALSE;
        int entrysz;
        int tofit;
        int bufsize;
        uint32_t ino, off;

        if (xdrs->x_op != XDR_ENCODE)
                return (FALSE);
        if (!xdr_enum(xdrs, (enum_t *)&rd->rd_status))
                return (FALSE);
        if (rd->rd_status != NFS_OK)
                return (TRUE);

        bufsize = 1 * BYTES_PER_XDR_UNIT;
        for (size = rd->rd_size, dp = rd->rd_entries;
            size > 0;
            size -= dp->d_reclen, dp = nextdp(dp)) {
                if (dp->d_reclen == 0 /* || DIRSIZ(dp) > dp->d_reclen */)
                        return (FALSE);
                if (dp->d_ino == 0)
                        continue;
                ino = (uint32_t)dp->d_ino; /* for LP64 we clip the bits */
                if (dp->d_ino != (ino64_t)ino)  /* and they better be zeros */
                        return (FALSE);
                off = (uint32_t)dp->d_off;
                name = dp->d_name;
                namlen = (uint_t)strlen(name);
                entrysz = (1 + 1 + 1 + 1) * BYTES_PER_XDR_UNIT +
                    roundup(namlen, BYTES_PER_XDR_UNIT);
                tofit = entrysz + 2 * BYTES_PER_XDR_UNIT;
                if (bufsize + tofit > rd->rd_bufsize) {
                        rd->rd_eof = FALSE;
                        break;
                }
                if (!xdr_bool(xdrs, &true) ||
                    !xdr_u_int(xdrs, &ino) ||
                    !xdr_bytes(xdrs, &name, &namlen, NFS_MAXNAMLEN) ||
                    !xdr_u_int(xdrs, &off)) {
                        return (FALSE);
                }
                bufsize += entrysz;
        }
        if (!xdr_bool(xdrs, &false))
                return (FALSE);
        if (!xdr_bool(xdrs, &rd->rd_eof))
                return (FALSE);
        return (TRUE);
}

/*
 * DECODE ONLY
 */
bool_t
xdr_getrddirres(XDR *xdrs, struct nfsrddirres *rd)
{
        struct dirent64 *dp;
        uint_t namlen;
        int size;
        bool_t valid;
        uint32_t offset;
        uint_t fileid, this_reclen;

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

        if (!xdr_enum(xdrs, (enum_t *)&rd->rd_status))
                return (FALSE);
        if (rd->rd_status != NFS_OK)
                return (TRUE);

        size = rd->rd_size;
        dp = rd->rd_entries;
        offset = rd->rd_offset;
        for (;;) {
                if (!xdr_bool(xdrs, &valid))
                        return (FALSE);
                if (!valid)
                        break;
                if (!xdr_u_int(xdrs, &fileid) ||
                    !xdr_u_int(xdrs, &namlen))
                        return (FALSE);
                this_reclen = DIRENT64_RECLEN(namlen);
                if (this_reclen > size) {
                        rd->rd_eof = FALSE;
                        goto bufovflw;
                }
                if (!xdr_opaque(xdrs, dp->d_name, namlen)||
                    !xdr_u_int(xdrs, &offset)) {
                        return (FALSE);
                }
                bzero(&dp->d_name[namlen],
                    DIRENT64_NAMELEN(this_reclen) - namlen);
                dp->d_ino = (ino64_t)fileid;
                dp->d_reclen = this_reclen;
                dp->d_off = (off64_t)offset;
                size -= dp->d_reclen;
                dp = nextdp(dp);
        }
        if (!xdr_bool(xdrs, &rd->rd_eof))
                return (FALSE);
bufovflw:
        rd->rd_size = (uint32_t)((char *)dp - (char *)(rd->rd_entries));
        rd->rd_offset = offset;
        return (TRUE);
}

/*
 * Arguments for directory operations
 */
bool_t
xdr_diropargs(XDR *xdrs, struct nfsdiropargs *da)
{
        int32_t *ptr;
        int32_t *fhp;
        uint32_t size;
        uint32_t nodesize;
        int i;
        int rndup;
        char *cptr;

        if (xdrs->x_op == XDR_DECODE) {
                da->da_fhandle = &da->da_fhandle_buf;
                ptr = XDR_INLINE(xdrs, RNDUP(sizeof (fhandle_t)) +
                    1 * BYTES_PER_XDR_UNIT);
                if (ptr != NULL) {
                        fhp = (int32_t *)da->da_fhandle;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp = *ptr++;
                        size = IXDR_GET_U_INT32(ptr);
                        if (size > NFS_MAXNAMLEN)
                                return (FALSE);
                        nodesize = size + 1;
                        if (nodesize == 0)
                                return (TRUE);
                        if (da->da_name == NULL) {
                                da->da_name = kmem_alloc(nodesize, KM_NOSLEEP);
                                if (da->da_name == NULL)
                                        return (FALSE);
                                da->da_flags |= DA_FREENAME;
                        }
                        ptr = XDR_INLINE(xdrs, RNDUP(size));
                        if (ptr == NULL) {
                                if (!xdr_opaque(xdrs, da->da_name, size)) {
                                        if (da->da_flags & DA_FREENAME) {
                                                kmem_free(da->da_name,
                                                    nodesize);
                                                da->da_name = NULL;
                                        }
                                        return (FALSE);
                                }
                                da->da_name[size] = '\0';
                                if (strlen(da->da_name) != size) {
                                        if (da->da_flags & DA_FREENAME) {
                                                kmem_free(da->da_name,
                                                    nodesize);
                                                da->da_name = NULL;
                                        }
                                        return (FALSE);
                                }
                                return (TRUE);
                        }
                        bcopy(ptr, da->da_name, size);
                        da->da_name[size] = '\0';
                        if (strlen(da->da_name) != size) {
                                if (da->da_flags & DA_FREENAME) {
                                        kmem_free(da->da_name, nodesize);
                                        da->da_name = NULL;
                                }
                                return (FALSE);
                        }
                        return (TRUE);
                }
                if (da->da_name == NULL)
                        da->da_flags |= DA_FREENAME;
        }

        if (xdrs->x_op == XDR_ENCODE) {
                size = (uint32_t)strlen(da->da_name);
                if (size > NFS_MAXNAMLEN)
                        return (FALSE);
                ptr = XDR_INLINE(xdrs, (int)(RNDUP(sizeof (fhandle_t)) +
                    1 * BYTES_PER_XDR_UNIT + RNDUP(size)));
                if (ptr != NULL) {
                        fhp = (int32_t *)da->da_fhandle;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp;
                        IXDR_PUT_U_INT32(ptr, (uint32_t)size);
                        bcopy(da->da_name, ptr, size);
                        rndup = BYTES_PER_XDR_UNIT -
                            (size % BYTES_PER_XDR_UNIT);
                        if (rndup != BYTES_PER_XDR_UNIT) {
                                cptr = (char *)ptr + size;
                                for (i = 0; i < rndup; i++)
                                        *cptr++ = '\0';
                        }
                        return (TRUE);
                }
        }

        if (xdrs->x_op == XDR_FREE) {
                if (da->da_name == NULL)
                        return (TRUE);
                size = (uint32_t)strlen(da->da_name);
                if (size > NFS_MAXNAMLEN)
                        return (FALSE);
                if (da->da_flags & DA_FREENAME)
                        kmem_free(da->da_name, size + 1);
                da->da_name = NULL;
                return (TRUE);
        }

        if (xdr_fhandle(xdrs, da->da_fhandle) &&
            xdr_string(xdrs, &da->da_name, NFS_MAXNAMLEN)) {
                return (TRUE);
        }
        return (FALSE);
}

/*
 * NFS_OK part of directory operation result
 */
bool_t
xdr_drok(XDR *xdrs, struct nfsdrok *drok)
{
        int32_t *ptr;
        int32_t *fhp;
        struct nfsfattr *na;

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

        ptr = XDR_INLINE(xdrs,
            RNDUP(sizeof (fhandle_t)) + 17 * BYTES_PER_XDR_UNIT);
        if (ptr != NULL) {
                if (xdrs->x_op == XDR_DECODE) {
                        fhp = (int32_t *)&drok->drok_fhandle;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp++ = *ptr++;
                        *fhp = *ptr++;
                        na = &drok->drok_attr;
                        na->na_type = IXDR_GET_ENUM(ptr, enum nfsftype);
                        na->na_mode = IXDR_GET_U_INT32(ptr);
                        na->na_nlink = IXDR_GET_U_INT32(ptr);
                        na->na_uid = IXDR_GET_U_INT32(ptr);
                        na->na_gid = IXDR_GET_U_INT32(ptr);
                        na->na_size = IXDR_GET_U_INT32(ptr);
                        na->na_blocksize = IXDR_GET_U_INT32(ptr);
                        na->na_rdev = IXDR_GET_U_INT32(ptr);
                        na->na_blocks = IXDR_GET_U_INT32(ptr);
                        na->na_fsid = IXDR_GET_U_INT32(ptr);
                        na->na_nodeid = IXDR_GET_U_INT32(ptr);
                        na->na_atime.tv_sec = IXDR_GET_U_INT32(ptr);
                        na->na_atime.tv_usec = IXDR_GET_U_INT32(ptr);
                        na->na_mtime.tv_sec = IXDR_GET_U_INT32(ptr);
                        na->na_mtime.tv_usec = IXDR_GET_U_INT32(ptr);
                        na->na_ctime.tv_sec = IXDR_GET_U_INT32(ptr);
                        na->na_ctime.tv_usec = IXDR_GET_U_INT32(ptr);
                } else {
                        fhp = (int32_t *)&drok->drok_fhandle;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp++;
                        *ptr++ = *fhp;
                        na = &drok->drok_attr;
                        IXDR_PUT_ENUM(ptr, na->na_type);
                        IXDR_PUT_U_INT32(ptr, na->na_mode);
                        IXDR_PUT_U_INT32(ptr, na->na_nlink);
                        IXDR_PUT_U_INT32(ptr, na->na_uid);
                        IXDR_PUT_U_INT32(ptr, na->na_gid);
                        IXDR_PUT_U_INT32(ptr, na->na_size);
                        IXDR_PUT_U_INT32(ptr, na->na_blocksize);
                        IXDR_PUT_U_INT32(ptr, na->na_rdev);
                        IXDR_PUT_U_INT32(ptr, na->na_blocks);
                        IXDR_PUT_U_INT32(ptr, na->na_fsid);
                        IXDR_PUT_U_INT32(ptr, na->na_nodeid);
                        IXDR_PUT_U_INT32(ptr, na->na_atime.tv_sec);
                        IXDR_PUT_U_INT32(ptr, na->na_atime.tv_usec);
                        IXDR_PUT_U_INT32(ptr, na->na_mtime.tv_sec);
                        IXDR_PUT_U_INT32(ptr, na->na_mtime.tv_usec);
                        IXDR_PUT_U_INT32(ptr, na->na_ctime.tv_sec);
                        IXDR_PUT_U_INT32(ptr, na->na_ctime.tv_usec);
                }
                return (TRUE);
        }

        if (xdr_fhandle(xdrs, &drok->drok_fhandle) &&
            xdr_fattr(xdrs, &drok->drok_attr)) {
                return (TRUE);
        }
        return (FALSE);
}

#ifdef _LITTLE_ENDIAN
bool_t
xdr_fastdrok(XDR *xdrs, struct nfsdrok *drok)
{
        struct nfsfattr *na;

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

        na = &drok->drok_attr;
        na->na_type = (enum nfsftype)htonl(na->na_type);
        na->na_mode = (uint32_t)htonl(na->na_mode);
        na->na_nlink = (uint32_t)htonl(na->na_nlink);
        na->na_uid = (uint32_t)htonl(na->na_uid);
        na->na_gid = (uint32_t)htonl(na->na_gid);
        na->na_size = (uint32_t)htonl(na->na_size);
        na->na_blocksize = (uint32_t)htonl(na->na_blocksize);
        na->na_rdev = (uint32_t)htonl(na->na_rdev);
        na->na_blocks = (uint32_t)htonl(na->na_blocks);
        na->na_fsid = (uint32_t)htonl(na->na_fsid);
        na->na_nodeid = (uint32_t)htonl(na->na_nodeid);
        na->na_atime.tv_sec = htonl(na->na_atime.tv_sec);
        na->na_atime.tv_usec = htonl(na->na_atime.tv_usec);
        na->na_mtime.tv_sec = htonl(na->na_mtime.tv_sec);
        na->na_mtime.tv_usec = htonl(na->na_mtime.tv_usec);
        na->na_ctime.tv_sec = htonl(na->na_ctime.tv_sec);
        na->na_ctime.tv_usec = htonl(na->na_ctime.tv_usec);
        return (TRUE);
}
#endif

static struct xdr_discrim diropres_discrim[2] = {
        { NFS_OK, xdr_drok },
        { __dontcare__, NULL_xdrproc_t }
};

/*
 * Results from directory operation
 */
bool_t
xdr_diropres(XDR *xdrs, struct nfsdiropres *dr)
{
        return (xdr_union(xdrs, (enum_t *)&(dr->dr_status),
            (caddr_t)&(dr->dr_drok), diropres_discrim, xdr_void));
}

/*
 * Results from directory operation
 */
bool_t
xdr_fastdiropres(XDR *xdrs, struct nfsdiropres *dr)
{
#if defined(_LITTLE_ENDIAN)
        /*
         * we deal with the discriminator;  it's an enum
         */
        if (!xdr_fastenum(xdrs, (enum_t *)&dr->dr_status))
                return (FALSE);

        if (dr->dr_status == NFS_OK)
                return (xdr_fastdrok(xdrs, &dr->dr_drok));
#elif defined(_BIG_ENDIAN)
        if (dr->dr_status == NFS_OK)
                return (TRUE);
#endif
        return (xdr_fastshorten(xdrs, sizeof (*dr)));
}

/*
 * Time Structure, unsigned
 */
bool_t
xdr_nfs2_timeval(XDR *xdrs, struct nfs2_timeval *tv)
{
        if (xdr_u_int(xdrs, &tv->tv_sec) &&
            xdr_u_int(xdrs, &tv->tv_usec))
                return (TRUE);
        return (FALSE);
}

/*
 * arguments to setattr
 */
bool_t
xdr_saargs(XDR *xdrs, struct nfssaargs *argp)
{
        int32_t *ptr;
        int32_t *arg;
        struct nfssattr *sa;

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

        ptr = XDR_INLINE(xdrs,
            RNDUP(sizeof (fhandle_t)) + 8 * BYTES_PER_XDR_UNIT);
        if (ptr != NULL) {
                if (xdrs->x_op == XDR_DECODE) {
                        arg = (int32_t *)&argp->saa_fh;
                        *arg++ = *ptr++;
                        *arg++ = *ptr++;
                        *arg++ = *ptr++;
                        *arg++ = *ptr++;
                        *arg++ = *ptr++;
                        *arg++ = *ptr++;
                        *arg++ = *ptr++;
                        *arg = *ptr++;
                        sa = &argp->saa_sa;
                        sa->sa_mode = IXDR_GET_U_INT32(ptr);
                        sa->sa_uid = IXDR_GET_U_INT32(ptr);
                        sa->sa_gid = IXDR_GET_U_INT32(ptr);
                        sa->sa_size = IXDR_GET_U_INT32(ptr);
                        sa->sa_atime.tv_sec = IXDR_GET_U_INT32(ptr);
                        sa->sa_atime.tv_usec = IXDR_GET_U_INT32(ptr);
                        sa->sa_mtime.tv_sec = IXDR_GET_U_INT32(ptr);
                        sa->sa_mtime.tv_usec = IXDR_GET_U_INT32(ptr);
                } else {
                        arg = (int32_t *)&argp->saa_fh;
                        *ptr++ = *arg++;
                        *ptr++ = *arg++;
                        *ptr++ = *arg++;
                        *ptr++ = *arg++;
                        *ptr++ = *arg++;
                        *ptr++ = *arg++;
                        *ptr++ = *arg++;
                        *ptr++ = *arg;
                        sa = &argp->saa_sa;
                        IXDR_PUT_U_INT32(ptr, sa->sa_mode);
                        IXDR_PUT_U_INT32(ptr, sa->sa_uid);
                        IXDR_PUT_U_INT32(ptr, sa->sa_gid);
                        IXDR_PUT_U_INT32(ptr, sa->sa_size);
                        IXDR_PUT_U_INT32(ptr, sa->sa_atime.tv_sec);
                        IXDR_PUT_U_INT32(ptr, sa->sa_atime.tv_usec);
                        IXDR_PUT_U_INT32(ptr, sa->sa_mtime.tv_sec);
                        IXDR_PUT_U_INT32(ptr, sa->sa_mtime.tv_usec);
                }
                return (TRUE);
        }

        if (xdr_fhandle(xdrs, &argp->saa_fh) &&
            xdr_sattr(xdrs, &argp->saa_sa)) {
                return (TRUE);
        }
        return (FALSE);
}


/*
 * arguments to create and mkdir
 */
bool_t
xdr_creatargs(XDR *xdrs, struct nfscreatargs *argp)
{
        argp->ca_sa = &argp->ca_sa_buf;

        if (xdrs->x_op == XDR_DECODE)
                argp->ca_sa = &argp->ca_sa_buf;
        if (xdr_diropargs(xdrs, &argp->ca_da) &&
            xdr_sattr(xdrs, argp->ca_sa)) {
                return (TRUE);
        }
        return (FALSE);
}

/*
 * arguments to link
 */
bool_t
xdr_linkargs(XDR *xdrs, struct nfslinkargs *argp)
{
        if (xdrs->x_op == XDR_DECODE)
                argp->la_from = &argp->la_from_buf;
        if (xdr_fhandle(xdrs, argp->la_from) &&
            xdr_diropargs(xdrs, &argp->la_to)) {
                return (TRUE);
        }
        return (FALSE);
}

/*
 * arguments to rename
 */
bool_t
xdr_rnmargs(XDR *xdrs, struct nfsrnmargs *argp)
{
        if (xdr_diropargs(xdrs, &argp->rna_from) &&
            xdr_diropargs(xdrs, &argp->rna_to))
                return (TRUE);
        return (FALSE);
}


/*
 * arguments to symlink
 */
bool_t
xdr_slargs(XDR *xdrs, struct nfsslargs *argp)
{
        if (xdrs->x_op == XDR_FREE) {
                if (!xdr_diropargs(xdrs, &argp->sla_from))
                        return (FALSE);
                if ((argp->sla_tnm_flags & SLA_FREETNM) &&
                    !xdr_string(xdrs, &argp->sla_tnm, (uint_t)NFS_MAXPATHLEN))
                        return (FALSE);
                return (TRUE);
        }

        if (xdrs->x_op == XDR_DECODE) {
                argp->sla_sa = &argp->sla_sa_buf;
                if (argp->sla_tnm == NULL)
                        argp->sla_tnm_flags |= SLA_FREETNM;
        }

        if (xdr_diropargs(xdrs, &argp->sla_from) &&
            xdr_string(xdrs, &argp->sla_tnm, (uint_t)NFS_MAXPATHLEN) &&
            xdr_sattr(xdrs, argp->sla_sa)) {
                return (TRUE);
        }
        return (FALSE);
}


/*
 * NFS_OK part of statfs operation
 */
bool_t
xdr_fsok(XDR *xdrs, struct nfsstatfsok *fsok)
{
        int32_t *ptr;

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

        ptr = XDR_INLINE(xdrs, 5 * BYTES_PER_XDR_UNIT);
        if (ptr != NULL) {
                if (xdrs->x_op == XDR_DECODE) {
                        fsok->fsok_tsize = IXDR_GET_INT32(ptr);
                        fsok->fsok_bsize = IXDR_GET_INT32(ptr);
                        fsok->fsok_blocks = IXDR_GET_INT32(ptr);
                        fsok->fsok_bfree = IXDR_GET_INT32(ptr);
                        fsok->fsok_bavail = IXDR_GET_INT32(ptr);
                } else {
                        IXDR_PUT_INT32(ptr, fsok->fsok_tsize);
                        IXDR_PUT_INT32(ptr, fsok->fsok_bsize);
                        IXDR_PUT_INT32(ptr, fsok->fsok_blocks);
                        IXDR_PUT_INT32(ptr, fsok->fsok_bfree);
                        IXDR_PUT_INT32(ptr, fsok->fsok_bavail);
                }
                return (TRUE);
        }

        if (xdr_u_int(xdrs, &fsok->fsok_tsize) &&
            xdr_u_int(xdrs, &fsok->fsok_bsize) &&
            xdr_u_int(xdrs, &fsok->fsok_blocks) &&
            xdr_u_int(xdrs, &fsok->fsok_bfree) &&
            xdr_u_int(xdrs, &fsok->fsok_bavail)) {
                return (TRUE);
        }
        return (FALSE);
}

#ifdef _LITTLE_ENDIAN
bool_t
xdr_fastfsok(XDR *xdrs, struct nfsstatfsok *fsok)
{

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

        fsok->fsok_tsize = htonl(fsok->fsok_tsize);
        fsok->fsok_bsize = htonl(fsok->fsok_bsize);
        fsok->fsok_blocks = htonl(fsok->fsok_blocks);
        fsok->fsok_bfree = htonl(fsok->fsok_bfree);
        fsok->fsok_bavail = htonl(fsok->fsok_bavail);
        return (TRUE);
}
#endif

static struct xdr_discrim statfs_discrim[2] = {
        { NFS_OK, xdr_fsok },
        { __dontcare__, NULL_xdrproc_t }
};

/*
 * Results of statfs operation
 */
bool_t
xdr_statfs(XDR *xdrs, struct nfsstatfs *fs)
{
        return (xdr_union(xdrs, (enum_t *)&(fs->fs_status),
            (caddr_t)&(fs->fs_fsok), statfs_discrim, xdr_void));
}

/*
 * Results of statfs operation
 */
bool_t
xdr_faststatfs(XDR *xdrs, struct nfsstatfs *fs)
{
#if defined(_LITTLE_ENDIAN)
        /*
         * we deal with the discriminator;  it's an enum
         */
        if (!xdr_fastenum(xdrs, (enum_t *)&fs->fs_status))
                return (FALSE);

        if (fs->fs_status == NFS_OK)
                return (xdr_fastfsok(xdrs, &fs->fs_fsok));
#elif defined(_BIG_ENDIAN)
        if (fs->fs_status == NFS_OK)
                return (TRUE);
#endif
        return (xdr_fastshorten(xdrs, sizeof (*fs)));
}

#ifdef _LITTLE_ENDIAN
/*
 * XDR enumerations
 */
#ifndef lint
static enum sizecheck { SIZEVAL } sizecheckvar; /* used to find the size of */
                                                /* an enum */
#endif
bool_t
xdr_fastenum(XDR *xdrs, enum_t *ep)
{
        if (xdrs->x_op == XDR_FREE)
                return (TRUE);
        if (xdrs->x_op == XDR_DECODE)
                return (FALSE);

#ifndef lint
        /*
         * enums are treated as ints
         */
        if (sizeof (sizecheckvar) == sizeof (int32_t)) {
                *ep = (enum_t)htonl((int32_t)(*ep));
        } else if (sizeof (sizecheckvar) == sizeof (short)) {
                *ep = (enum_t)htons((short)(*ep));
        } else {
                return (FALSE);
        }
        return (TRUE);
#else
        (void) (xdr_short(xdrs, (short *)ep));
        return (xdr_int(xdrs, (int *)ep));
#endif
}
#endif

static bool_t
xdr_fastshorten(XDR *xdrs, uint_t ressize)
{
        uint_t curpos;

        curpos = XDR_GETPOS(xdrs);
        ressize -= BYTES_PER_XDR_UNIT;
        curpos -= ressize;
        return (XDR_SETPOS(xdrs, curpos));
}