root/sys/fs/nfsserver/nfs_nfsdserv.c
/*-
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * Copyright (c) 1989, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Rick Macklem at The University of Guelph.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <sys/cdefs.h>
#include "opt_inet.h"
#include "opt_inet6.h"
/*
 * nfs version 2, 3 and 4 server calls to vnode ops
 * - these routines generally have 3 phases
 *   1 - break down and validate rpc request in mbuf list
 *   2 - do the vnode ops for the request, usually by calling a nfsvno_XXX()
 *       function in nfsd_port.c
 *   3 - build the rpc reply in an mbuf list
 * For nfsv4, these functions are called for each Op within the Compound RPC.
 */

#include <fs/nfs/nfsport.h>
#include <sys/extattr.h>
#include <sys/filio.h>

/* Global vars */
extern u_int32_t newnfs_false, newnfs_true;
extern __enum_uint8(vtype) nv34tov_type[8];
extern struct timeval nfsboottime;
extern int nfsrv_enable_crossmntpt;
extern int nfsrv_statehashsize;
extern int nfsrv_layouthashsize;
extern time_t nfsdev_time;
extern volatile int nfsrv_devidcnt;
extern int nfsd_debuglevel;
extern u_long sb_max_adj;
extern int nfsrv_pnfsatime;
extern int nfsrv_maxpnfsmirror;
extern uint32_t nfs_srvmaxio;
extern int nfsrv_issuedelegs;

static int      nfs_async = 0;
SYSCTL_DECL(_vfs_nfsd);
SYSCTL_INT(_vfs_nfsd, OID_AUTO, async, CTLFLAG_RW, &nfs_async, 0,
    "Tell client that writes were synced even though they were not");
extern int      nfsrv_doflexfile;
SYSCTL_INT(_vfs_nfsd, OID_AUTO, default_flexfile, CTLFLAG_RW,
    &nfsrv_doflexfile, 0, "Make Flex File Layout the default for pNFS");
static int      nfsrv_linux42server = 1;
SYSCTL_INT(_vfs_nfsd, OID_AUTO, linux42server, CTLFLAG_RW,
    &nfsrv_linux42server, 0,
    "Enable Linux style NFSv4.2 server (non-RFC compliant)");
static bool     nfsrv_openaccess = true;
SYSCTL_BOOL(_vfs_nfsd, OID_AUTO, v4openaccess, CTLFLAG_RW,
    &nfsrv_openaccess, 0,
    "Enable Linux style NFSv4 Open access check");
static char nfsrv_scope[NFSV4_OPAQUELIMIT];
SYSCTL_STRING(_vfs_nfsd, OID_AUTO, scope, CTLFLAG_RWTUN,
    &nfsrv_scope, NFSV4_OPAQUELIMIT, "Server scope");
static char nfsrv_owner_major[NFSV4_OPAQUELIMIT];
SYSCTL_STRING(_vfs_nfsd, OID_AUTO, owner_major, CTLFLAG_RWTUN,
    &nfsrv_owner_major, NFSV4_OPAQUELIMIT, "Server owner major");
static uint64_t nfsrv_owner_minor;
SYSCTL_U64(_vfs_nfsd, OID_AUTO, owner_minor, CTLFLAG_RWTUN,
    &nfsrv_owner_minor, 0, "Server owner minor");
/*
 * Only enable this if all your exported file systems
 * (or pNFS DSs for the pNFS case) support VOP_ALLOCATE.
 */
static bool     nfsrv_doallocate = false;
SYSCTL_BOOL(_vfs_nfsd, OID_AUTO, enable_v42allocate, CTLFLAG_RW,
    &nfsrv_doallocate, 0,
    "Enable NFSv4.2 Allocate operation");
static uint64_t nfsrv_maxcopyrange = SSIZE_MAX;
SYSCTL_U64(_vfs_nfsd, OID_AUTO, maxcopyrange, CTLFLAG_RW,
    &nfsrv_maxcopyrange, 0, "Max size of a Copy so RPC times reasonable");

/*
 * This list defines the GSS mechanisms supported.
 * (Don't ask me how you get these strings from the RFC stuff like
 *  iso(1), org(3)... but someone did it, so I don't need to know.)
 */
static struct nfsgss_mechlist nfsgss_mechlist[] = {
        { 9, "\052\206\110\206\367\022\001\002\002", 11 },
        { 0, "", 0 },
};

/* local functions */
static void nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
    int *diraft_retp, nfsattrbit_t *attrbitp,
    NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
    int pathlen);
static void nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
    int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp, NFSACL_T *daclp,
    NFSPROC_T *p, struct nfsexstuff *exp);

/*
 * nfs access service (not a part of NFS V2)
 */
int
nfsrvd_access(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        u_int32_t *tl;
        int getret, error = 0;
        struct nfsvattr nva;
        u_int32_t testmode, nfsmode, supported = 0;
        accmode_t deletebit;
        struct thread *p = curthread;

        if (nd->nd_repstat) {
                nfsrv_postopattr(nd, 1, &nva);
                goto out;
        }
        NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
        nfsmode = fxdr_unsigned(u_int32_t, *tl);
        if ((nd->nd_flag & ND_NFSV4) &&
            (nfsmode & ~(NFSACCESS_READ | NFSACCESS_LOOKUP |
             NFSACCESS_MODIFY | NFSACCESS_EXTEND | NFSACCESS_DELETE |
             NFSACCESS_EXECUTE | NFSACCESS_XAREAD | NFSACCESS_XAWRITE |
             NFSACCESS_XALIST))) {
                nd->nd_repstat = NFSERR_INVAL;
                vput(vp);
                goto out;
        }
        if (nfsmode & NFSACCESS_READ) {
                supported |= NFSACCESS_READ;
                if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
                    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
                        nfsmode &= ~NFSACCESS_READ;
        }
        if (nfsmode & NFSACCESS_MODIFY) {
                supported |= NFSACCESS_MODIFY;
                if (nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp, p,
                    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
                        nfsmode &= ~NFSACCESS_MODIFY;
        }
        if (nfsmode & NFSACCESS_EXTEND) {
                supported |= NFSACCESS_EXTEND;
                if (nfsvno_accchk(vp, VWRITE | VAPPEND, nd->nd_cred, exp, p,
                    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
                        nfsmode &= ~NFSACCESS_EXTEND;
        }
        if (nfsmode & NFSACCESS_XAREAD) {
                supported |= NFSACCESS_XAREAD;
                if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
                    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
                        nfsmode &= ~NFSACCESS_XAREAD;
        }
        if (nfsmode & NFSACCESS_XAWRITE) {
                supported |= NFSACCESS_XAWRITE;
                if (nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp, p,
                    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
                        nfsmode &= ~NFSACCESS_XAWRITE;
        }
        if (nfsmode & NFSACCESS_XALIST) {
                supported |= NFSACCESS_XALIST;
                if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
                    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
                        nfsmode &= ~NFSACCESS_XALIST;
        }
        if (nfsmode & NFSACCESS_DELETE) {
                supported |= NFSACCESS_DELETE;
                if (vp->v_type == VDIR)
                        deletebit = VDELETE_CHILD;
                else
                        deletebit = VDELETE;
                if (nfsvno_accchk(vp, deletebit, nd->nd_cred, exp, p,
                    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
                        nfsmode &= ~NFSACCESS_DELETE;
        }
        if (vp->v_type == VDIR)
                testmode = NFSACCESS_LOOKUP;
        else
                testmode = NFSACCESS_EXECUTE;
        if (nfsmode & testmode) {
                supported |= (nfsmode & testmode);
                if (nfsvno_accchk(vp, VEXEC, nd->nd_cred, exp, p,
                    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
                        nfsmode &= ~testmode;
        }
        nfsmode &= supported;
        if (nd->nd_flag & ND_NFSV3) {
                getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
                nfsrv_postopattr(nd, getret, &nva);
        }
        vput(vp);
        if (nd->nd_flag & ND_NFSV4) {
                NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(supported);
        } else
                NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
        *tl = txdr_unsigned(nfsmode);

out:
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs getattr service
 */
int
nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        struct nfsvattr nva;
        fhandle_t fh;
        int at_root = 0, error = 0, ret, supports_nfsv4acls;
        struct nfsreferral *refp;
        nfsattrbit_t attrbits, tmpbits;
        struct mount *mp;
        struct vnode *tvp = NULL;
        struct vattr va;
        uint64_t mounted_on_fileno = 0;
        accmode_t accmode;
        struct thread *p = curthread;
        size_t atsiz;
        long pathval;
        bool has_caseinsensitive, has_hiddensystem, has_namedattr, xattrsupp;
        uint32_t clone_blksize;

        if (nd->nd_repstat)
                goto out;
        if (nd->nd_flag & ND_NFSV4) {
                error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
                if (error) {
                        vput(vp);
                        goto out;
                }

                /*
                 * Check for a referral.
                 */
                refp = nfsv4root_getreferral(vp, NULL, 0);
                if (refp != NULL) {
                        (void) nfsrv_putreferralattr(nd, &attrbits, refp, 1,
                            &nd->nd_repstat);
                        vput(vp);
                        goto out;
                }
                if (nd->nd_repstat == 0) {
                        accmode = 0;
                        NFSSET_ATTRBIT(&tmpbits, &attrbits);

                        /*
                         * GETATTR with write-only attr time_access_set and time_modify_set
                         * should return NFS4ERR_INVAL.
                         */
                        if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_TIMEACCESSSET) ||
                                        NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_TIMEMODIFYSET)){
                                error = NFSERR_INVAL;
                                vput(vp);
                                goto out;
                        }
                        if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_ACL)) {
                                NFSCLRBIT_ATTRBIT(&tmpbits, NFSATTRBIT_ACL);
                                accmode |= VREAD_ACL;
                        }
                        if (NFSNONZERO_ATTRBIT(&tmpbits))
                                accmode |= VREAD_ATTRIBUTES;
                        if (accmode != 0)
                                nd->nd_repstat = nfsvno_accchk(vp, accmode,
                                    nd->nd_cred, exp, p, NFSACCCHK_NOOVERRIDE,
                                    NFSACCCHK_VPISLOCKED, NULL);
                }
        }
        if (!nd->nd_repstat)
                nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, &attrbits);
        if (!nd->nd_repstat) {
                if (nd->nd_flag & ND_NFSV4) {
                        if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_FILEHANDLE))
                                nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
                        if (!nd->nd_repstat)
                                nd->nd_repstat = nfsrv_checkgetattr(nd, vp,
                                    &nva, &attrbits, p);
                        if (nd->nd_repstat == 0) {
                                supports_nfsv4acls = nfs_supportsacls(vp);
                                xattrsupp = false;
                                if (NFSISSET_ATTRBIT(&attrbits,
                                    NFSATTRBIT_XATTRSUPPORT)) {
                                        ret = VOP_GETEXTATTR(vp,
                                            EXTATTR_NAMESPACE_USER,
                                            "xxx", NULL, &atsiz, nd->nd_cred,
                                            p);
                                        xattrsupp = ret != EOPNOTSUPP;
                                }
                                if (VOP_PATHCONF(vp, _PC_HAS_HIDDENSYSTEM,
                                    &pathval) != 0)
                                        pathval = 0;
                                has_hiddensystem = pathval > 0;
                                pathval = 0;
                                if (NFSISSET_ATTRBIT(&attrbits,
                                    NFSATTRBIT_NAMEDATTR) &&
                                    VOP_PATHCONF(vp, _PC_HAS_NAMEDATTR,
                                    &pathval) != 0)
                                        pathval = 0;
                                has_namedattr = pathval > 0;
                                pathval = 0;
                                if (VOP_PATHCONF(vp, _PC_CLONE_BLKSIZE,
                                    &pathval) != 0)
                                        pathval = 0;
                                clone_blksize = pathval;
                                if (VOP_PATHCONF(vp, _PC_CASE_INSENSITIVE,
                                    &pathval) != 0)
                                        pathval = 0;
                                has_caseinsensitive = pathval > 0;
                                mp = vp->v_mount;
                                if (nfsrv_enable_crossmntpt != 0 &&
                                    vp->v_type == VDIR &&
                                    (vp->v_vflag & VV_ROOT) != 0 &&
                                    vp != rootvnode) {
                                        tvp = mp->mnt_vnodecovered;
                                        vref(tvp);
                                        at_root = 1;
                                } else
                                        at_root = 0;
                                vfs_ref(mp);
                                NFSVOPUNLOCK(vp);
                                if (at_root != 0) {
                                        if ((nd->nd_repstat =
                                             NFSVOPLOCK(tvp, LK_SHARED)) == 0) {
                                                nd->nd_repstat = VOP_GETATTR(
                                                    tvp, &va, nd->nd_cred);
                                                vput(tvp);
                                        } else
                                                vrele(tvp);
                                        if (nd->nd_repstat == 0)
                                                mounted_on_fileno = (uint64_t)
                                                    va.va_fileid;
                                        else
                                                at_root = 0;
                                }
                                if (nd->nd_repstat == 0)
                                        nd->nd_repstat = vfs_busy(mp, 0);
                                vfs_rel(mp);
                                if (nd->nd_repstat == 0) {
                                        (void)nfsvno_fillattr(nd, mp, vp, &nva,
                                            &fh, 0, &attrbits, nd->nd_cred, p,
                                            isdgram, 1, supports_nfsv4acls,
                                            at_root, mounted_on_fileno,
                                            xattrsupp, has_hiddensystem,
                                            has_namedattr, clone_blksize,
                                            has_caseinsensitive);
                                        vfs_unbusy(mp);
                                }
                                vrele(vp);
                        } else
                                vput(vp);
                } else {
                        nfsrv_fillattr(nd, &nva);
                        vput(vp);
                }
        } else {
                vput(vp);
        }

out:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs setattr service
 */
int
nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        struct nfsvattr nva, nva2;
        u_int32_t *tl;
        int preat_ret = 1, postat_ret = 1, gcheck = 0, error = 0;
        int gotproxystateid;
        struct timespec guard = { 0, 0 };
        nfsattrbit_t atimeonly, attrbits, retbits;
        nfsv4stateid_t stateid;
        NFSACL_T *aclp = NULL, *daclp = NULL;
        struct thread *p = curthread;

        NFSZERO_ATTRBIT(&retbits);
        if (nd->nd_repstat) {
                nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
                goto out;
        }
#ifdef NFS4_ACL_EXTATTR_NAME
        aclp = acl_alloc(M_WAITOK);
        aclp->acl_cnt = 0;
        daclp = acl_alloc(M_WAITOK);
        daclp->acl_cnt = 0;
#endif
        gotproxystateid = 0;
        NFSVNO_ATTRINIT(&nva);
        if (nd->nd_flag & ND_NFSV4) {
                NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
                stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
                stateid.other[0] = *tl++;
                stateid.other[1] = *tl++;
                stateid.other[2] = *tl;
                if (stateid.other[0] == 0x55555555 &&
                    stateid.other[1] == 0x55555555 &&
                    stateid.other[2] == 0x55555555 &&
                    stateid.seqid == 0xffffffff)
                        gotproxystateid = 1;
        }
        error = nfsrv_sattr(nd, vp, &nva, &attrbits, aclp, daclp, p);
        if (error)
                goto nfsmout;

        /* For NFSv4, only va_uid and va_flags is used from nva2. */
        NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
        NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_ARCHIVE);
        NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_HIDDEN);
        NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SYSTEM);
        preat_ret = nfsvno_getattr(vp, &nva2, nd, p, 1, &retbits);
        if (!nd->nd_repstat)
                nd->nd_repstat = preat_ret;

        NFSZERO_ATTRBIT(&retbits);
        if (nd->nd_flag & ND_NFSV3) {
                NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
                gcheck = fxdr_unsigned(int, *tl);
                if (gcheck) {
                        NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
                        fxdr_nfsv3time(tl, &guard);
                }
                if (!nd->nd_repstat && gcheck &&
                    (nva2.na_ctime.tv_sec != guard.tv_sec ||
                     nva2.na_ctime.tv_nsec != guard.tv_nsec))
                        nd->nd_repstat = NFSERR_NOT_SYNC;
                if (nd->nd_repstat) {
                        vput(vp);
#ifdef NFS4_ACL_EXTATTR_NAME
                        acl_free(aclp);
                        acl_free(daclp);
#endif
                        nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
                        goto out;
                }
        } else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
                nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);

        /*
         * Now that we have all the fields, lets do it.
         * If the size is being changed write access is required, otherwise
         * just check for a read only file system.
         */
        if (!nd->nd_repstat) {
                if (NFSVNO_NOTSETSIZE(&nva)) {
                        /*
                         * For an NFSv4.2 Setattr of atime only that fails with
                         * EROFS, pretend the operation succeeded.  This makes
                         * the semantics of copying files from a ZFS snapshot
                         * the same over NFSv4.2 as it is locally.
                         * Without this "hack", the copy will fail
                         * with EROFS unless the NFSv4.2 mount has the
                         * "noatime" mount option.
                         */
                        NFSZERO_ATTRBIT(&atimeonly);
                        NFSSETBIT_ATTRBIT(&atimeonly, NFSATTRBIT_TIMEACCESSSET);
                        if (NFSVNO_EXRDONLY(exp) ||
                            (vp->v_mount->mnt_flag & MNT_RDONLY)) {
                                if ((nd->nd_flag & ND_NFSV42) != 0 &&
                                    NFSEQUAL_ATTRBIT(&attrbits, &atimeonly)) {
                                        NFSCLRBIT_ATTRBIT(&attrbits,
                                            NFSATTRBIT_TIMEACCESSSET);
                                        NFSSETBIT_ATTRBIT(&retbits,
                                            NFSATTRBIT_TIMEACCESSSET);
                                } else
                                        nd->nd_repstat = EROFS;
                        }
                } else {
                        if (vp->v_type != VREG)
                                nd->nd_repstat = EINVAL;
                        else if (nva2.na_uid != nd->nd_cred->cr_uid ||
                            NFSVNO_EXSTRICTACCESS(exp))
                                nd->nd_repstat = nfsvno_accchk(vp,
                                    VWRITE, nd->nd_cred, exp, p,
                                    NFSACCCHK_NOOVERRIDE,
                                    NFSACCCHK_VPISLOCKED, NULL);
                }
        }
        /*
         * Proxy operations from the MDS are allowed via the all 0s special
         * stateid.
         */
        if (nd->nd_repstat == 0 && (nd->nd_flag & ND_NFSV4) != 0 &&
            gotproxystateid == 0)
                nd->nd_repstat = nfsrv_checksetattr(vp, nd, &stateid,
                    &nva, &attrbits, exp, p);

        if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
            u_long oldflags;

            oldflags = nva2.na_flags;
            /*
             * For V4, try setting the attributes in sets, so that the
             * reply bitmap will be correct for an error case.
             */
            if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER) ||
                NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP)) {
                NFSVNO_ATTRINIT(&nva2);
                NFSVNO_SETATTRVAL(&nva2, uid, nva.na_uid);
                NFSVNO_SETATTRVAL(&nva2, gid, nva.na_gid);
                nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
                    exp);
                if (!nd->nd_repstat) {
                    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER))
                        NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
                    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP))
                        NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNERGROUP);
                }
            }
            if (!nd->nd_repstat &&
                NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SIZE)) {
                NFSVNO_ATTRINIT(&nva2);
                NFSVNO_SETATTRVAL(&nva2, size, nva.na_size);
                nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
                    exp);
                if (!nd->nd_repstat)
                    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SIZE);
            }
            if (!nd->nd_repstat &&
                (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET) ||
                 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))) {
                NFSVNO_ATTRINIT(&nva2);
                NFSVNO_SETATTRVAL(&nva2, atime, nva.na_atime);
                NFSVNO_SETATTRVAL(&nva2, mtime, nva.na_mtime);
                if (nva.na_vaflags & VA_UTIMES_NULL) {
                        nva2.na_vaflags |= VA_UTIMES_NULL;
                        NFSVNO_SETACTIVE(&nva2, vaflags);
                }
                nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
                    exp);
                if (!nd->nd_repstat) {
                    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET))
                        NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEACCESSSET);
                    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))
                        NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEMODIFYSET);
                }
            }
            if (!nd->nd_repstat &&
                NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMECREATE)) {
                NFSVNO_ATTRINIT(&nva2);
                NFSVNO_SETATTRVAL(&nva2, btime, nva.na_btime);
                nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
                    exp);
                if (!nd->nd_repstat)
                    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMECREATE);
            }
            if (!nd->nd_repstat &&
                (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE) ||
                 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODESETMASKED))) {
                NFSVNO_ATTRINIT(&nva2);
                NFSVNO_SETATTRVAL(&nva2, mode, nva.na_mode);
                nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
                    exp);
                if (!nd->nd_repstat) {
                    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE))
                        NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODE);
                    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODESETMASKED))
                        NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODESETMASKED);
                }
            }
            if (!nd->nd_repstat &&
                (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ARCHIVE) ||
                 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN) ||
                 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM))) {
                if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ARCHIVE)) {
                    if ((nva.na_flags & UF_ARCHIVE) != 0)
                        oldflags |= UF_ARCHIVE;
                    else
                        oldflags &= ~UF_ARCHIVE;
                }
                if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN)) {
                    if ((nva.na_flags & UF_HIDDEN) != 0)
                        oldflags |= UF_HIDDEN;
                    else
                        oldflags &= ~UF_HIDDEN;
                }
                if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM)) {
                    if ((nva.na_flags & UF_SYSTEM) != 0)
                        oldflags |= UF_SYSTEM;
                    else
                        oldflags &= ~UF_SYSTEM;
                }
                NFSVNO_ATTRINIT(&nva2);
                NFSVNO_SETATTRVAL(&nva2, flags, oldflags);
                nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
                    exp);
                if (!nd->nd_repstat) {
                    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ARCHIVE))
                        NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_ARCHIVE);
                    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN))
                        NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_HIDDEN);
                    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM))
                        NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SYSTEM);
                }
            }

#ifdef NFS4_ACL_EXTATTR_NAME
            if (!nd->nd_repstat && aclp->acl_cnt > 0 &&
                NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ACL)) {
                nd->nd_repstat = nfsrv_setacl(vp, aclp, ACL_TYPE_NFS4,
                    nd->nd_cred, p);
                if (!nd->nd_repstat)
                    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_ACL);
            }
            if (!nd->nd_repstat && aclp->acl_cnt > 0 &&
                NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_POSIXACCESSACL)) {
                nd->nd_repstat = nfsrv_setacl(vp, aclp, ACL_TYPE_ACCESS,
                    nd->nd_cred, p);
                if (!nd->nd_repstat)
                    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_POSIXACCESSACL);
            }
            if (!nd->nd_repstat &&
                NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_POSIXDEFAULTACL)) {
                if (daclp == NULL)
                        nd->nd_repstat = NFSERR_INVAL;
                if (nd->nd_repstat == 0)
                        nd->nd_repstat = nfsrv_setacl(vp, daclp,
                            ACL_TYPE_DEFAULT, nd->nd_cred, p);
                if (nd->nd_repstat == 0)
                        NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_POSIXDEFAULTACL);
            }
#endif
        } else if (!nd->nd_repstat) {
                nd->nd_repstat = nfsvno_setattr(vp, &nva, nd->nd_cred, p,
                    exp);
        }
        if (nd->nd_flag & (ND_NFSV2 | ND_NFSV3)) {
                postat_ret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
                if (!nd->nd_repstat)
                        nd->nd_repstat = postat_ret;
        }
        vput(vp);
#ifdef NFS4_ACL_EXTATTR_NAME
        acl_free(aclp);
        acl_free(daclp);
#endif
        if (nd->nd_flag & ND_NFSV3)
                nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
        else if (nd->nd_flag & ND_NFSV4)
                (void) nfsrv_putattrbit(nd, &retbits);
        else if (!nd->nd_repstat)
                nfsrv_fillattr(nd, &nva);

out:
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        vput(vp);
#ifdef NFS4_ACL_EXTATTR_NAME
        acl_free(aclp);
        acl_free(daclp);
#endif
        if (nd->nd_flag & ND_NFSV4) {
                /*
                 * For all nd_repstat, the V4 reply includes a bitmap,
                 * even NFSERR_BADXDR, which is what this will end up
                 * returning.
                 */
                (void) nfsrv_putattrbit(nd, &retbits);
        }
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs lookup rpc
 * (Also performs lookup parent for v4)
 */
int
nfsrvd_lookup(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
{
        struct nameidata named;
        vnode_t vp, dirp = NULL;
        int error = 0, dattr_ret = 1;
        struct nfsvattr nva, dattr;
        char *bufp;
        u_long *hashp;
        struct thread *p = curthread;
        struct componentname *cnp;
        short irflag;

        if (nd->nd_repstat) {
                nfsrv_postopattr(nd, dattr_ret, &dattr);
                goto out;
        }

        /*
         * For some reason, if dp is a symlink, the error
         * returned is supposed to be NFSERR_SYMLINK and not NFSERR_NOTDIR.
         */
        if (dp->v_type == VLNK && (nd->nd_flag & ND_NFSV4)) {
                nd->nd_repstat = NFSERR_SYMLINK;
                vrele(dp);
                goto out;
        }

        cnp = &named.ni_cnd;
        irflag = vn_irflag_read(dp);
        if ((irflag & VIRF_NAMEDDIR) != 0)
                NFSNAMEICNDSET(cnp, nd->nd_cred, LOOKUP, LOCKLEAF | OPENNAMED);
        else
                NFSNAMEICNDSET(cnp, nd->nd_cred, LOOKUP, LOCKLEAF);
        nfsvno_setpathbuf(&named, &bufp, &hashp);
        error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
        if (error) {
                vrele(dp);
                nfsvno_relpathbuf(&named);
                goto out;
        }
        if (!nd->nd_repstat) {
                /* Don't set OPENNAMED for Lookupp (".."). */
                if (cnp->cn_namelen == 2 && *cnp->cn_pnbuf == '.' &&
                    *(cnp->cn_pnbuf + 1) == '.')
                        cnp->cn_flags &= ~OPENNAMED;
                nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, &dirp);
        } else {
                vrele(dp);
                nfsvno_relpathbuf(&named);
        }
        if (nd->nd_repstat) {
                if (dirp) {
                        if (nd->nd_flag & ND_NFSV3)
                                dattr_ret = nfsvno_getattr(dirp, &dattr, nd, p,
                                    0, NULL);
                        vrele(dirp);
                }
                if (nd->nd_flag & ND_NFSV3)
                        nfsrv_postopattr(nd, dattr_ret, &dattr);
                goto out;
        }
        nfsvno_relpathbuf(&named);
        vp = named.ni_vp;
        if ((nd->nd_flag & ND_NFSV4) != 0 && !NFSVNO_EXPORTED(exp) &&
            vp->v_type != VDIR && vp->v_type != VLNK)
                /*
                 * Only allow lookup of VDIR and VLNK for traversal of
                 * non-exported volumes during NFSv4 mounting.
                 */
                nd->nd_repstat = ENOENT;
        if (nd->nd_repstat == 0) {
                nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
                /*
                 * EOPNOTSUPP indicates the file system cannot be exported,
                 * so just pretend the entry does not exist.
                 */
                if (nd->nd_repstat == EOPNOTSUPP)
                        nd->nd_repstat = ENOENT;
        }
        if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
                nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
        if (vpp != NULL && nd->nd_repstat == 0)
                *vpp = vp;
        else
                vput(vp);
        if (dirp) {
                if (nd->nd_flag & ND_NFSV3)
                        dattr_ret = nfsvno_getattr(dirp, &dattr, nd, p, 0,
                            NULL);
                vrele(dirp);
        }
        if (nd->nd_repstat) {
                if (nd->nd_flag & ND_NFSV3)
                        nfsrv_postopattr(nd, dattr_ret, &dattr);
                goto out;
        }
        if (nd->nd_flag & ND_NFSV2) {
                (void)nfsm_fhtom(NULL, nd, (u_int8_t *)fhp, 0, 0);
                nfsrv_fillattr(nd, &nva);
        } else if (nd->nd_flag & ND_NFSV3) {
                (void)nfsm_fhtom(NULL, nd, (u_int8_t *)fhp, 0, 0);
                nfsrv_postopattr(nd, 0, &nva);
                nfsrv_postopattr(nd, dattr_ret, &dattr);
        }

out:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs readlink service
 */
int
nfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        u_int32_t *tl;
        struct mbuf *mp = NULL, *mpend = NULL;
        int getret = 1, len;
        struct nfsvattr nva;
        struct thread *p = curthread;
        uint16_t off;

        if (nd->nd_repstat) {
                nfsrv_postopattr(nd, getret, &nva);
                goto out;
        }
        if (vp->v_type != VLNK) {
                if (nd->nd_flag & ND_NFSV2)
                        nd->nd_repstat = ENXIO;
                else
                        nd->nd_repstat = EINVAL;
        }
        if (nd->nd_repstat == 0) {
                if ((nd->nd_flag & ND_EXTPG) != 0)
                        nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred,
                            nd->nd_maxextsiz, p, &mp, &mpend, &len);
                else
                        nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred,
                            0, p, &mp, &mpend, &len);
        }
        if (nd->nd_flag & ND_NFSV3)
                getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
        vput(vp);
        if (nd->nd_flag & ND_NFSV3)
                nfsrv_postopattr(nd, getret, &nva);
        if (nd->nd_repstat)
                goto out;
        NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
        *tl = txdr_unsigned(len);
        if (mp != NULL) {
                nd->nd_mb->m_next = mp;
                nd->nd_mb = mpend;
                if ((mpend->m_flags & M_EXTPG) != 0) {
                        nd->nd_bextpg = mpend->m_epg_npgs - 1;
                        nd->nd_bpos = (char *)(void *)
                            PHYS_TO_DMAP(mpend->m_epg_pa[nd->nd_bextpg]);
                        off = (nd->nd_bextpg == 0) ? mpend->m_epg_1st_off : 0;
                        nd->nd_bpos += off + mpend->m_epg_last_len;
                        nd->nd_bextpgsiz = PAGE_SIZE - mpend->m_epg_last_len -
                            off;
                } else
                        nd->nd_bpos = mtod(mpend, char *) + mpend->m_len;
        }

out:
        NFSEXITCODE2(0, nd);
        return (0);
}

/*
 * nfs read service
 */
int
nfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        u_int32_t *tl;
        int error = 0, cnt, getret = 1, gotproxystateid, reqlen, eof = 0;
        struct mbuf *m2, *m3;
        struct nfsvattr nva;
        off_t off = 0x0;
        struct nfsstate st, *stp = &st;
        struct nfslock lo, *lop = &lo;
        nfsv4stateid_t stateid;
        nfsquad_t clientid;
        struct thread *p = curthread;
        uint16_t poff;

        if (nd->nd_repstat) {
                nfsrv_postopattr(nd, getret, &nva);
                goto out;
        }
        if (nd->nd_flag & ND_NFSV2) {
                NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
                off = (off_t)fxdr_unsigned(u_int32_t, *tl++);
                reqlen = fxdr_unsigned(int, *tl);
        } else if (nd->nd_flag & ND_NFSV3) {
                NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
                off = fxdr_hyper(tl);
                tl += 2;
                reqlen = fxdr_unsigned(int, *tl);
        } else {
                NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3*NFSX_UNSIGNED);
                reqlen = fxdr_unsigned(int, *(tl + 6));
        }
        if (reqlen > NFS_SRVMAXDATA(nd)) {
                reqlen = NFS_SRVMAXDATA(nd);
        } else if (reqlen < 0) {
                error = EBADRPC;
                goto nfsmout;
        }
        gotproxystateid = 0;
        if (nd->nd_flag & ND_NFSV4) {
                stp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
                lop->lo_flags = NFSLCK_READ;
                stp->ls_ownerlen = 0;
                stp->ls_op = NULL;
                stp->ls_uid = nd->nd_cred->cr_uid;
                stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
                clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
                clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
                if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                        if ((nd->nd_flag & ND_NFSV41) != 0)
                                clientid.qval = nd->nd_clientid.qval;
                        else if (nd->nd_clientid.qval != clientid.qval)
                                printf("EEK1 multiple clids\n");
                } else {
                        if ((nd->nd_flag & ND_NFSV41) != 0)
                                printf("EEK! no clientid from session\n");
                        nd->nd_flag |= ND_IMPLIEDCLID;
                        nd->nd_clientid.qval = clientid.qval;
                }
                stp->ls_stateid.other[2] = *tl++;
                /*
                 * Don't allow the client to use a special stateid for a DS op.
                 */
                if ((nd->nd_flag & ND_DSSERVER) != 0 &&
                    ((stp->ls_stateid.other[0] == 0x0 &&
                    stp->ls_stateid.other[1] == 0x0 &&
                    stp->ls_stateid.other[2] == 0x0) ||
                    (stp->ls_stateid.other[0] == 0xffffffff &&
                    stp->ls_stateid.other[1] == 0xffffffff &&
                    stp->ls_stateid.other[2] == 0xffffffff) ||
                    stp->ls_stateid.seqid != 0))
                        nd->nd_repstat = NFSERR_BADSTATEID;
                /* However, allow the proxy stateid. */
                if (stp->ls_stateid.seqid == 0xffffffff &&
                    stp->ls_stateid.other[0] == 0x55555555 &&
                    stp->ls_stateid.other[1] == 0x55555555 &&
                    stp->ls_stateid.other[2] == 0x55555555)
                        gotproxystateid = 1;
                off = fxdr_hyper(tl);
                lop->lo_first = off;
                tl += 2;
                lop->lo_end = off + reqlen;
                /*
                 * Paranoia, just in case it wraps around.
                 */
                if (lop->lo_end < off)
                        lop->lo_end = NFS64BITSSET;
        }
        if (vp->v_type != VREG) {
                if (nd->nd_flag & ND_NFSV3)
                        nd->nd_repstat = EINVAL;
                else
                        nd->nd_repstat = (vp->v_type == VDIR) ? EISDIR :
                            EINVAL;
        }
        getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
        if (!nd->nd_repstat)
                nd->nd_repstat = getret;
        if (!nd->nd_repstat &&
            (nva.na_uid != nd->nd_cred->cr_uid ||
             NFSVNO_EXSTRICTACCESS(exp))) {
                nd->nd_repstat = nfsvno_accchk(vp, VREAD,
                    nd->nd_cred, exp, p,
                    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
                if (nd->nd_repstat)
                        nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
                            nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
                            NFSACCCHK_VPISLOCKED, NULL);
        }
        /*
         * DS reads are marked by ND_DSSERVER or use the proxy special
         * stateid.
         */
        if (nd->nd_repstat == 0 && (nd->nd_flag & (ND_NFSV4 | ND_DSSERVER)) ==
            ND_NFSV4 && gotproxystateid == 0)
                nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
                    &stateid, exp, nd, p);
        if (nd->nd_repstat) {
                vput(vp);
                if (nd->nd_flag & ND_NFSV3)
                        nfsrv_postopattr(nd, getret, &nva);
                goto out;
        }
        if (off >= nva.na_size) {
                cnt = 0;
                eof = 1;
        } else if (reqlen == 0)
                cnt = 0;
        else if ((off + reqlen) >= nva.na_size) {
                cnt = nva.na_size - off;
                eof = 1;
        } else
                cnt = reqlen;
        m3 = NULL;
        if (cnt > 0) {
                /*
                 * If cnt > MCLBYTES and the reply will not be saved, use
                 * ext_pgs mbufs for TLS.
                 * For NFSv4.0, we do not know for sure if the reply will
                 * be saved, so do not use ext_pgs mbufs for NFSv4.0.
                 * Always use ext_pgs mbufs if ND_EXTPG is set.
                 */
                if ((nd->nd_flag & ND_EXTPG) != 0 || (cnt > MCLBYTES &&
                    (nd->nd_flag & (ND_TLS | ND_SAVEREPLY)) == ND_TLS &&
                    (nd->nd_flag & (ND_NFSV4 | ND_NFSV41)) != ND_NFSV4))
                        nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred,
                            nd->nd_maxextsiz, p, &m3, &m2);
                else
                        nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred,
                            0, p, &m3, &m2);
                if (!(nd->nd_flag & ND_NFSV4)) {
                        getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
                        if (!nd->nd_repstat)
                                nd->nd_repstat = getret;
                }
                if (nd->nd_repstat) {
                        vput(vp);
                        if (m3)
                                m_freem(m3);
                        if (nd->nd_flag & ND_NFSV3)
                                nfsrv_postopattr(nd, getret, &nva);
                        goto out;
                }
        }
        vput(vp);
        if (nd->nd_flag & ND_NFSV2) {
                nfsrv_fillattr(nd, &nva);
                NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
        } else {
                if (nd->nd_flag & ND_NFSV3) {
                        nfsrv_postopattr(nd, getret, &nva);
                        NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
                        *tl++ = txdr_unsigned(cnt);
                } else
                        NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
                if (eof)
                        *tl++ = newnfs_true;
                else
                        *tl++ = newnfs_false;
        }
        *tl = txdr_unsigned(cnt);
        if (m3) {
                nd->nd_mb->m_next = m3;
                nd->nd_mb = m2;
                if ((m2->m_flags & M_EXTPG) != 0) {
                        nd->nd_flag |= ND_EXTPG;
                        nd->nd_bextpg = m2->m_epg_npgs - 1;
                        nd->nd_bpos = (char *)(void *)
                            PHYS_TO_DMAP(m2->m_epg_pa[nd->nd_bextpg]);
                        poff = (nd->nd_bextpg == 0) ? m2->m_epg_1st_off : 0;
                        nd->nd_bpos += poff + m2->m_epg_last_len;
                        nd->nd_bextpgsiz = PAGE_SIZE - m2->m_epg_last_len -
                            poff;
                } else
                        nd->nd_bpos = mtod(m2, char *) + m2->m_len;
        }

out:
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs write service
 */
int
nfsrvd_write(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        u_int32_t *tl;
        struct nfsvattr nva, forat;
        int aftat_ret = 1, retlen, len, error = 0, forat_ret = 1;
        int gotproxystateid, stable = NFSWRITE_FILESYNC;
        off_t off;
        struct nfsstate st, *stp = &st;
        struct nfslock lo, *lop = &lo;
        nfsv4stateid_t stateid;
        nfsquad_t clientid;
        nfsattrbit_t attrbits;
        struct thread *p = curthread;

        if (nd->nd_repstat) {
                nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
                goto out;
        }
        gotproxystateid = 0;
        if (nd->nd_flag & ND_NFSV2) {
                NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
                off = (off_t)fxdr_unsigned(u_int32_t, *++tl);
                tl += 2;
                retlen = len = fxdr_unsigned(int32_t, *tl);
        } else if (nd->nd_flag & ND_NFSV3) {
                NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
                off = fxdr_hyper(tl);
                tl += 3;
                stable = fxdr_unsigned(int, *tl++);
                retlen = len = fxdr_unsigned(int32_t, *tl);
        } else {
                NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 4 * NFSX_UNSIGNED);
                stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
                lop->lo_flags = NFSLCK_WRITE;
                stp->ls_ownerlen = 0;
                stp->ls_op = NULL;
                stp->ls_uid = nd->nd_cred->cr_uid;
                stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
                clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
                clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
                if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                        if ((nd->nd_flag & ND_NFSV41) != 0)
                                clientid.qval = nd->nd_clientid.qval;
                        else if (nd->nd_clientid.qval != clientid.qval)
                                printf("EEK2 multiple clids\n");
                } else {
                        if ((nd->nd_flag & ND_NFSV41) != 0)
                                printf("EEK! no clientid from session\n");
                        nd->nd_flag |= ND_IMPLIEDCLID;
                        nd->nd_clientid.qval = clientid.qval;
                }
                stp->ls_stateid.other[2] = *tl++;
                /*
                 * Don't allow the client to use a special stateid for a DS op.
                 */
                if ((nd->nd_flag & ND_DSSERVER) != 0 &&
                    ((stp->ls_stateid.other[0] == 0x0 &&
                    stp->ls_stateid.other[1] == 0x0 &&
                    stp->ls_stateid.other[2] == 0x0) ||
                    (stp->ls_stateid.other[0] == 0xffffffff &&
                    stp->ls_stateid.other[1] == 0xffffffff &&
                    stp->ls_stateid.other[2] == 0xffffffff) ||
                    stp->ls_stateid.seqid != 0))
                        nd->nd_repstat = NFSERR_BADSTATEID;
                /* However, allow the proxy stateid. */
                if (stp->ls_stateid.seqid == 0xffffffff &&
                    stp->ls_stateid.other[0] == 0x55555555 &&
                    stp->ls_stateid.other[1] == 0x55555555 &&
                    stp->ls_stateid.other[2] == 0x55555555)
                        gotproxystateid = 1;
                off = fxdr_hyper(tl);
                lop->lo_first = off;
                tl += 2;
                stable = fxdr_unsigned(int, *tl++);
                retlen = len = fxdr_unsigned(int32_t, *tl);
                lop->lo_end = off + len;
                /*
                 * Paranoia, just in case it wraps around, which shouldn't
                 * ever happen anyhow.
                 */
                if (lop->lo_end < lop->lo_first)
                        lop->lo_end = NFS64BITSSET;
        }

        if (retlen > nfs_srvmaxio || retlen < 0)
                nd->nd_repstat = EIO;
        if (vp->v_type != VREG && !nd->nd_repstat) {
                if (nd->nd_flag & ND_NFSV3)
                        nd->nd_repstat = EINVAL;
                else
                        nd->nd_repstat = (vp->v_type == VDIR) ? EISDIR :
                            EINVAL;
        }
        NFSZERO_ATTRBIT(&attrbits);
        NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
        forat_ret = nfsvno_getattr(vp, &forat, nd, p, 1, &attrbits);
        if (!nd->nd_repstat)
                nd->nd_repstat = forat_ret;
        if (!nd->nd_repstat &&
            (forat.na_uid != nd->nd_cred->cr_uid ||
             NFSVNO_EXSTRICTACCESS(exp)))
                nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
                    nd->nd_cred, exp, p,
                    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
        /*
         * DS reads are marked by ND_DSSERVER or use the proxy special
         * stateid.
         */
        if (nd->nd_repstat == 0 && (nd->nd_flag & (ND_NFSV4 | ND_DSSERVER)) ==
            ND_NFSV4 && gotproxystateid == 0)
                nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
                    &stateid, exp, nd, p);
        if (nd->nd_repstat) {
                vput(vp);
                if (nd->nd_flag & ND_NFSV3)
                        nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
                goto out;
        }

        /*
         * For NFS Version 2, it is not obvious what a write of zero length
         * should do, but I might as well be consistent with Version 3,
         * which is to return ok so long as there are no permission problems.
         */
        if (retlen > 0) {
                nd->nd_repstat = nfsvno_write(vp, off, retlen, &stable,
                    nd->nd_md, nd->nd_dpos, nd->nd_cred, p);
                error = nfsm_advance(nd, NFSM_RNDUP(retlen), -1);
                if (error)
                        goto nfsmout;
        }
        if (nd->nd_flag & ND_NFSV4)
                aftat_ret = 0;
        else
                aftat_ret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
        vput(vp);
        if (!nd->nd_repstat)
                nd->nd_repstat = aftat_ret;
        if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
                if (nd->nd_flag & ND_NFSV3)
                        nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
                if (nd->nd_repstat)
                        goto out;
                NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(retlen);
                /*
                 * If nfs_async is set, then pretend the write was FILESYNC.
                 * Warning: Doing this violates RFC1813 and runs a risk
                 * of data written by a client being lost when the server
                 * crashes/reboots.
                 */
                if (stable == NFSWRITE_UNSTABLE && nfs_async == 0)
                        *tl++ = txdr_unsigned(stable);
                else
                        *tl++ = txdr_unsigned(NFSWRITE_FILESYNC);
                /*
                 * Actually, there is no need to txdr these fields,
                 * but it may make the values more human readable,
                 * for debugging purposes.
                 */
                *tl++ = txdr_unsigned(nfsboottime.tv_sec);
                *tl = txdr_unsigned(nfsboottime.tv_usec);
        } else if (!nd->nd_repstat)
                nfsrv_fillattr(nd, &nva);

out:
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs create service (creates regular files for V2 and V3. Spec. files for V2.)
 * now does a truncate to 0 length via. setattr if it already exists
 * The core creation routine has been extracted out into nfsrv_creatsub(),
 * so it can also be used by nfsrv_open() for V4.
 */
int
nfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t dp, struct nfsexstuff *exp)
{
        struct nfsvattr nva, dirfor, diraft;
        struct nfsv2_sattr *sp;
        struct nameidata named;
        u_int32_t *tl;
        int error = 0, tsize, dirfor_ret = 1, diraft_ret = 1;
        int how = NFSCREATE_UNCHECKED, exclusive_flag = 0;
        NFSDEV_T rdev = 0;
        vnode_t vp = NULL, dirp = NULL;
        fhandle_t fh;
        char *bufp;
        u_long *hashp;
        __enum_uint8(vtype) vtyp;
        int32_t cverf[2], tverf[2] = { 0, 0 };
        struct thread *p = curthread;

        if (nd->nd_repstat) {
                nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
                goto out;
        }
        NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
            LOCKPARENT | LOCKLEAF | NOCACHE);
        nfsvno_setpathbuf(&named, &bufp, &hashp);
        error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
        if (error)
                goto nfsmout;
        if (!nd->nd_repstat) {
                NFSVNO_ATTRINIT(&nva);
                if (nd->nd_flag & ND_NFSV2) {
                        NFSM_DISSECT(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
                        vtyp = IFTOVT(fxdr_unsigned(u_int32_t, sp->sa_mode));
                        if (vtyp == VNON)
                                vtyp = VREG;
                        NFSVNO_SETATTRVAL(&nva, type, vtyp);
                        NFSVNO_SETATTRVAL(&nva, mode,
                            nfstov_mode(sp->sa_mode));
                        switch (nva.na_type) {
                        case VREG:
                                tsize = fxdr_unsigned(int32_t, sp->sa_size);
                                if (tsize != -1)
                                        NFSVNO_SETATTRVAL(&nva, size,
                                            (u_quad_t)tsize);
                                break;
                        case VCHR:
                        case VBLK:
                        case VFIFO:
                                rdev = fxdr_unsigned(NFSDEV_T, sp->sa_size);
                                break;
                        default:
                                break;
                        }
                } else {
                        NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
                        how = fxdr_unsigned(int, *tl);
                        switch (how) {
                        case NFSCREATE_GUARDED:
                        case NFSCREATE_UNCHECKED:
                                error = nfsrv_sattr(nd, NULL, &nva, NULL, NULL,
                                    NULL, p);
                                if (error)
                                        goto nfsmout;
                                break;
                        case NFSCREATE_EXCLUSIVE:
                                NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
                                cverf[0] = *tl++;
                                cverf[1] = *tl;
                                exclusive_flag = 1;
                                break;
                        }
                        NFSVNO_SETATTRVAL(&nva, type, VREG);
                }
        }
        if (nd->nd_repstat) {
                nfsvno_relpathbuf(&named);
                if (nd->nd_flag & ND_NFSV3) {
                        dirfor_ret = nfsvno_getattr(dp, &dirfor, nd, p, 1,
                            NULL);
                        nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
                            &diraft);
                }
                vput(dp);
                goto out;
        }

        nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, &dirp);
        if (dirp) {
                if (nd->nd_flag & ND_NFSV2) {
                        vrele(dirp);
                        dirp = NULL;
                } else {
                        dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
                            NULL);
                }
        }
        if (nd->nd_repstat) {
                if (nd->nd_flag & ND_NFSV3)
                        nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
                            &diraft);
                if (dirp)
                        vrele(dirp);
                goto out;
        }

        if (!(nd->nd_flag & ND_NFSV2)) {
                switch (how) {
                case NFSCREATE_GUARDED:
                        if (named.ni_vp)
                                nd->nd_repstat = EEXIST;
                        break;
                case NFSCREATE_UNCHECKED:
                        break;
                case NFSCREATE_EXCLUSIVE:
                        if (named.ni_vp == NULL)
                                NFSVNO_SETATTRVAL(&nva, mode, 0);
                        break;
                }
        }

        /*
         * Iff doesn't exist, create it
         * otherwise just truncate to 0 length
         *   should I set the mode too ?
         */
        nd->nd_repstat = nfsvno_createsub(nd, &named, &vp, &nva,
            &exclusive_flag, cverf, rdev, exp);

        if (!nd->nd_repstat) {
                nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
                if (!nd->nd_repstat)
                        nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1,
                            NULL);
                vput(vp);
                if (!nd->nd_repstat) {
                        tverf[0] = nva.na_atime.tv_sec;
                        tverf[1] = nva.na_atime.tv_nsec;
                }
        }
        if (nd->nd_flag & ND_NFSV2) {
                if (!nd->nd_repstat) {
                        (void)nfsm_fhtom(NULL, nd, (u_int8_t *)&fh, 0, 0);
                        nfsrv_fillattr(nd, &nva);
                }
        } else {
                if (exclusive_flag && !nd->nd_repstat && (cverf[0] != tverf[0]
                    || cverf[1] != tverf[1]))
                        nd->nd_repstat = EEXIST;
                diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
                vrele(dirp);
                if (!nd->nd_repstat) {
                        (void)nfsm_fhtom(NULL, nd, (u_int8_t *)&fh, 0, 1);
                        nfsrv_postopattr(nd, 0, &nva);
                }
                nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
        }

out:
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        vput(dp);
        nfsvno_relpathbuf(&named);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs v3 mknod service (and v4 create)
 */
int
nfsrvd_mknod(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
{
        struct nfsvattr nva, dirfor, diraft;
        u_int32_t *tl;
        struct nameidata named;
        int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
        u_int32_t major, minor;
        __enum_uint8(vtype) vtyp = VNON;
        nfstype nfs4type = NFNON;
        vnode_t vp, dirp = NULL;
        nfsattrbit_t attrbits;
        char *bufp = NULL, *pathcp = NULL;
        u_long *hashp, cnflags, setflags;
        NFSACL_T *aclp = NULL, *daclp = NULL;
        struct thread *p = curthread;

        NFSVNO_ATTRINIT(&nva);
        cnflags = LOCKPARENT;
        if (nd->nd_repstat) {
                nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
                goto out;
        }
#ifdef NFS4_ACL_EXTATTR_NAME
        aclp = acl_alloc(M_WAITOK);
        aclp->acl_cnt = 0;
        daclp = acl_alloc(M_WAITOK);
        daclp->acl_cnt = 0;
#endif

        /*
         * For V4, the creation stuff is here, Yuck!
         */
        if (nd->nd_flag & ND_NFSV4) {
                NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
                vtyp = nfsv34tov_type(*tl);
                nfs4type = fxdr_unsigned(nfstype, *tl);
                if ((vn_irflag_read(dp) & VIRF_NAMEDDIR) != 0) {
                        /*
                         * Don't allow creation of non-regular file objects
                         * in a named attribute directory.
                         */
                        nd->nd_repstat = NFSERR_INVAL;
                        vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
                        acl_free(aclp);
#endif
                        goto out;
                }
                switch (nfs4type) {
                case NFLNK:
                        error = nfsvno_getsymlink(nd, &nva, p, &pathcp,
                            &pathlen);
                        if (error)
                                goto nfsmout;
                        break;
                case NFCHR:
                case NFBLK:
                        NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
                        major = fxdr_unsigned(u_int32_t, *tl++);
                        minor = fxdr_unsigned(u_int32_t, *tl);
                        nva.na_rdev = NFSMAKEDEV(major, minor);
                        break;
                case NFSOCK:
                case NFFIFO:
                        break;
                case NFDIR:
                        cnflags = LOCKPARENT;
                        break;
                default:
                        nd->nd_repstat = NFSERR_BADTYPE;
                        vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
                        acl_free(aclp);
                        acl_free(daclp);
#endif
                        goto out;
                }
        }
        NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, cnflags | NOCACHE);
        nfsvno_setpathbuf(&named, &bufp, &hashp);
        error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
        if (error)
                goto nfsmout;
        if (!nd->nd_repstat) {
                if (nd->nd_flag & ND_NFSV3) {
                        NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
                        vtyp = nfsv34tov_type(*tl);
                }
                error = nfsrv_sattr(nd, NULL, &nva, &attrbits, aclp, daclp, p);
                if (error)
                        goto nfsmout;
                nva.na_type = vtyp;
                if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV3) &&
                    (vtyp == VCHR || vtyp == VBLK)) {
                        NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
                        major = fxdr_unsigned(u_int32_t, *tl++);
                        minor = fxdr_unsigned(u_int32_t, *tl);
                        nva.na_rdev = NFSMAKEDEV(major, minor);
                }
        }

        dirfor_ret = nfsvno_getattr(dp, &dirfor, nd, p, 0, NULL);
        if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
                if (!dirfor_ret && NFSVNO_ISSETGID(&nva) &&
                    dirfor.na_gid == nva.na_gid)
                        NFSVNO_UNSET(&nva, gid);
                nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
        }
        if (nd->nd_repstat) {
                vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
                acl_free(aclp);
                acl_free(daclp);
#endif
                nfsvno_relpathbuf(&named);
                if (pathcp)
                        free(pathcp, M_TEMP);
                if (nd->nd_flag & ND_NFSV3)
                        nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
                            &diraft);
                goto out;
        }

        /*
         * Yuck! For V4, mkdir and link are here and some V4 clients don't fill
         * in va_mode, so we'll have to set a default here.
         */
        if (NFSVNO_NOTSETMODE(&nva)) {
                if (vtyp == VLNK)
                        nva.na_mode = 0755;
                else
                        nva.na_mode = 0400;
        }

        if (vtyp == VDIR)
                named.ni_cnd.cn_flags |= WILLBEDIR;
        nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, &dirp);
        if (nd->nd_repstat) {
                if (dirp) {
                        if (nd->nd_flag & ND_NFSV3)
                                dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd,
                                    p, 0, NULL);
                        vrele(dirp);
                }
#ifdef NFS4_ACL_EXTATTR_NAME
                acl_free(aclp);
                acl_free(daclp);
#endif
                if (nd->nd_flag & ND_NFSV3)
                        nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
                            &diraft);
                goto out;
        }
        if (dirp)
                dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL);

        if ((nd->nd_flag & ND_NFSV4) && (vtyp == VDIR || vtyp == VLNK)) {
                if (vtyp == VDIR) {
                        nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp,
                            &dirfor, &diraft, &diraft_ret, &attrbits, aclp,
                            daclp, p, exp);
#ifdef NFS4_ACL_EXTATTR_NAME
                        acl_free(aclp);
                        acl_free(daclp);
#endif
                        goto out;
                } else if (vtyp == VLNK) {
                        nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
                            &dirfor, &diraft, &diraft_ret, &attrbits,
                            aclp, p, exp, pathcp, pathlen);
#ifdef NFS4_ACL_EXTATTR_NAME
                        acl_free(aclp);
                        acl_free(daclp);
#endif
                        free(pathcp, M_TEMP);
                        goto out;
                }
        }

        /* For NFSv4, set na_flags via nfsrv_fixattr(). */
        setflags = nva.na_flags;
        nva.na_flags = VNOVAL;
        nd->nd_repstat = nfsvno_mknod(&named, &nva, nd->nd_cred, p);
        if (!nd->nd_repstat) {
                vp = named.ni_vp;
                nva.na_flags = setflags;
                nfsrv_fixattr(nd, vp, &nva, aclp, daclp, p, &attrbits, false);
                nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
                if ((nd->nd_flag & ND_NFSV3) && !nd->nd_repstat)
                        nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1,
                            NULL);
                if (vpp != NULL && nd->nd_repstat == 0) {
                        NFSVOPUNLOCK(vp);
                        *vpp = vp;
                } else
                        vput(vp);
        }

        diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
        vrele(dirp);
        if (!nd->nd_repstat) {
                if (nd->nd_flag & ND_NFSV3) {
                        (void)nfsm_fhtom(NULL, nd, (u_int8_t *)fhp, 0, 1);
                        nfsrv_postopattr(nd, 0, &nva);
                } else {
                        NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
                        *tl++ = newnfs_false;
                        txdr_hyper(dirfor.na_filerev, tl);
                        tl += 2;
                        txdr_hyper(diraft.na_filerev, tl);
                        (void) nfsrv_putattrbit(nd, &attrbits);
                }
        }
        if (nd->nd_flag & ND_NFSV3)
                nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
#ifdef NFS4_ACL_EXTATTR_NAME
        acl_free(aclp);
        acl_free(daclp);
#endif

out:
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
        acl_free(aclp);
        acl_free(daclp);
#endif
        if (bufp)
                nfsvno_relpathbuf(&named);
        if (pathcp)
                free(pathcp, M_TEMP);

        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs remove service
 */
int
nfsrvd_remove(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t dp, struct nfsexstuff *exp)
{
        struct nameidata named;
        u_int32_t *tl;
        int error = 0, dirfor_ret = 1, diraft_ret = 1;
        vnode_t dirp = NULL;
        struct nfsvattr dirfor, diraft;
        char *bufp;
        u_long *hashp;
        struct thread *p = curthread;

        if (nd->nd_repstat) {
                nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
                goto out;
        }
        NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, DELETE,
            LOCKPARENT | LOCKLEAF);
        nfsvno_setpathbuf(&named, &bufp, &hashp);
        error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
        if (error) {
                vput(dp);
                nfsvno_relpathbuf(&named);
                goto out;
        }
        if (!nd->nd_repstat) {
                nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, &dirp);
        } else {
                vput(dp);
                nfsvno_relpathbuf(&named);
        }
        if (dirp) {
                if (!(nd->nd_flag & ND_NFSV2)) {
                        dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
                            NULL);
                } else {
                        vrele(dirp);
                        dirp = NULL;
                }
        }
        if (!nd->nd_repstat) {
                if (nd->nd_flag & ND_NFSV4) {
                        if (named.ni_vp->v_type == VDIR)
                                nd->nd_repstat = nfsvno_rmdirsub(&named, 1,
                                    nd->nd_cred, p, exp);
                        else
                                nd->nd_repstat = nfsvno_removesub(&named, true,
                                    nd, p, exp);
                } else if (nd->nd_procnum == NFSPROC_RMDIR) {
                        nd->nd_repstat = nfsvno_rmdirsub(&named, 0,
                            nd->nd_cred, p, exp);
                } else {
                        nd->nd_repstat = nfsvno_removesub(&named, false, nd, p,
                            exp);
                }
        }
        if (!(nd->nd_flag & ND_NFSV2)) {
                if (dirp) {
                        diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0,
                            NULL);
                        vrele(dirp);
                }
                if (nd->nd_flag & ND_NFSV3) {
                        nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
                            &diraft);
                } else if (!nd->nd_repstat) {
                        NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
                        *tl++ = newnfs_false;
                        txdr_hyper(dirfor.na_filerev, tl);
                        tl += 2;
                        txdr_hyper(diraft.na_filerev, tl);
                }
        }

out:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs rename service
 */
int
nfsrvd_rename(struct nfsrv_descript *nd, int isdgram,
    vnode_t dp, vnode_t todp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
{
        u_int32_t *tl;
        int error = 0, fdirfor_ret = 1, fdiraft_ret = 1;
        int tdirfor_ret = 1, tdiraft_ret = 1;
        struct nameidata fromnd, tond;
        vnode_t fdirp = NULL, tdirp = NULL, tdp = NULL;
        struct nfsvattr fdirfor, fdiraft, tdirfor, tdiraft;
        struct nfsexstuff tnes;
        struct nfsrvfh tfh;
        char *bufp, *tbufp = NULL;
        u_long *hashp;
        fhandle_t fh;
        struct thread *p = curthread;

        if (nd->nd_repstat) {
                nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
                nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
                goto out;
        }
        if (!(nd->nd_flag & ND_NFSV2))
                fdirfor_ret = nfsvno_getattr(dp, &fdirfor, nd, p, 1, NULL);
        tond.ni_cnd.cn_nameiop = 0;
        tond.ni_startdir = NULL;
        NFSNAMEICNDSET(&fromnd.ni_cnd, nd->nd_cred, DELETE, WANTPARENT);
        nfsvno_setpathbuf(&fromnd, &bufp, &hashp);
        error = nfsrv_parsename(nd, bufp, hashp, &fromnd.ni_pathlen);
        if (error) {
                vput(dp);
                if (todp)
                        vrele(todp);
                nfsvno_relpathbuf(&fromnd);
                goto out;
        }
        /*
         * Unlock dp in this code section, so it is unlocked before
         * tdp gets locked. This avoids a potential LOR if tdp is the
         * parent directory of dp.
         */
        if (nd->nd_flag & ND_NFSV4) {
                tdp = todp;
                tnes = *toexp;
                if (dp != tdp) {
                        NFSVOPUNLOCK(dp);
                        /* Might lock tdp. */
                        tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd, p, 0,
                            NULL);
                } else {
                        tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd, p, 1,
                            NULL);
                        NFSVOPUNLOCK(dp);
                }
        } else {
                tfh.nfsrvfh_len = 0;
                error = nfsrv_mtofh(nd, &tfh);
                if (error == 0)
                        error = nfsvno_getfh(dp, &fh, p);
                if (error) {
                        vput(dp);
                        /* todp is always NULL except NFSv4 */
                        nfsvno_relpathbuf(&fromnd);
                        goto out;
                }

                /* If this is the same file handle, just VREF() the vnode. */
                if (!NFSBCMP(tfh.nfsrvfh_data, &fh, NFSX_MYFH)) {
                        vref(dp);
                        tdp = dp;
                        tnes = *exp;
                        tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd, p, 1,
                            NULL);
                        NFSVOPUNLOCK(dp);
                } else {
                        NFSVOPUNLOCK(dp);
                        nd->nd_cred->cr_uid = nd->nd_saveduid;
                        nfsd_fhtovp(nd, &tfh, LK_EXCLUSIVE, &tdp, &tnes, NULL,
                            0, -1);     /* Locks tdp. */
                        if (tdp) {
                                tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd,
                                    p, 1, NULL);
                                NFSVOPUNLOCK(tdp);
                        }
                }
        }
        NFSNAMEICNDSET(&tond.ni_cnd, nd->nd_cred, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE);
        nfsvno_setpathbuf(&tond, &tbufp, &hashp);
        if (!nd->nd_repstat) {
                error = nfsrv_parsename(nd, tbufp, hashp, &tond.ni_pathlen);
                if (error) {
                        if (tdp)
                                vrele(tdp);
                        vrele(dp);
                        nfsvno_relpathbuf(&fromnd);
                        nfsvno_relpathbuf(&tond);
                        goto out;
                }
        }
        if (nd->nd_repstat) {
                if (nd->nd_flag & ND_NFSV3) {
                        nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
                            &fdiraft);
                        nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
                            &tdiraft);
                }
                if (tdp)
                        vrele(tdp);
                vrele(dp);
                nfsvno_relpathbuf(&fromnd);
                nfsvno_relpathbuf(&tond);
                goto out;
        }

        /*
         * Done parsing, now down to business.
         */
        nd->nd_repstat = nfsvno_namei(nd, &fromnd, dp, 0, exp, &fdirp);
        if (nd->nd_repstat) {
                if (nd->nd_flag & ND_NFSV3) {
                        nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
                            &fdiraft);
                        nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
                            &tdiraft);
                }
                if (fdirp)
                        vrele(fdirp);
                if (tdp)
                        vrele(tdp);
                nfsvno_relpathbuf(&tond);
                goto out;
        }
        if (fromnd.ni_vp->v_type == VDIR)
                tond.ni_cnd.cn_flags |= WILLBEDIR;
        nd->nd_repstat = nfsvno_namei(nd, &tond, tdp, 0, &tnes, &tdirp);
        nd->nd_repstat = nfsvno_rename(&fromnd, &tond, nd, p);
        if (fdirp)
                fdiraft_ret = nfsvno_getattr(fdirp, &fdiraft, nd, p, 0, NULL);
        if (tdirp)
                tdiraft_ret = nfsvno_getattr(tdirp, &tdiraft, nd, p, 0, NULL);
        if (fdirp)
                vrele(fdirp);
        if (tdirp)
                vrele(tdirp);
        if (nd->nd_flag & ND_NFSV3) {
                nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
                nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
        } else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
                NFSM_BUILD(tl, u_int32_t *, 10 * NFSX_UNSIGNED);
                *tl++ = newnfs_false;
                txdr_hyper(fdirfor.na_filerev, tl);
                tl += 2;
                txdr_hyper(fdiraft.na_filerev, tl);
                tl += 2;
                *tl++ = newnfs_false;
                txdr_hyper(tdirfor.na_filerev, tl);
                tl += 2;
                txdr_hyper(tdiraft.na_filerev, tl);
        }

out:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs link service
 */
int
nfsrvd_link(struct nfsrv_descript *nd, int isdgram,
    vnode_t vp, vnode_t tovp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
{
        struct nameidata named;
        u_int32_t *tl;
        int error = 0, dirfor_ret = 1, diraft_ret = 1, getret = 1;
        vnode_t dirp = NULL, dp = NULL;
        struct nfsvattr dirfor, diraft, at;
        struct nfsexstuff tnes;
        struct nfsrvfh dfh;
        char *bufp;
        u_long *hashp;
        struct thread *p = curthread;
        nfsquad_t clientid;

        if (nd->nd_repstat) {
                nfsrv_postopattr(nd, getret, &at);
                nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
                goto out;
        }
        if ((vn_irflag_read(vp) & (VIRF_NAMEDDIR | VIRF_NAMEDATTR)) != 0 ||
            (tovp != NULL &&
             (vn_irflag_read(tovp) & (VIRF_NAMEDDIR | VIRF_NAMEDATTR)) != 0)) {
                nd->nd_repstat = NFSERR_INVAL;
                if (tovp != NULL)
                        vrele(tovp);
        }
        NFSVOPUNLOCK(vp);
        if (!nd->nd_repstat && vp->v_type == VDIR) {
                if (nd->nd_flag & ND_NFSV4)
                        nd->nd_repstat = NFSERR_ISDIR;
                else
                        nd->nd_repstat = NFSERR_INVAL;
                if (tovp)
                        vrele(tovp);
        }
        if (!nd->nd_repstat) {
                if (nd->nd_flag & ND_NFSV4) {
                        dp = tovp;
                        tnes = *toexp;
                } else {
                        error = nfsrv_mtofh(nd, &dfh);
                        if (error) {
                                vrele(vp);
                                /* tovp is always NULL unless NFSv4 */
                                goto out;
                        }
                        nfsd_fhtovp(nd, &dfh, LK_EXCLUSIVE, &dp, &tnes, NULL,
                            0, -1);
                        if (dp)
                                NFSVOPUNLOCK(dp);
                }
        }
        NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, LOCKPARENT | NOCACHE);
        if (!nd->nd_repstat) {
                nfsvno_setpathbuf(&named, &bufp, &hashp);
                error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
                if (error) {
                        vrele(vp);
                        if (dp)
                                vrele(dp);
                        nfsvno_relpathbuf(&named);
                        goto out;
                }
                if (!nd->nd_repstat) {
                        nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, &tnes,
                            &dirp);
                } else {
                        if (dp)
                                vrele(dp);
                        nfsvno_relpathbuf(&named);
                }
        }
        if (dirp) {
                if (nd->nd_flag & ND_NFSV2) {
                        vrele(dirp);
                        dirp = NULL;
                } else {
                        dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
                            NULL);
                }
        }
        if (!nd->nd_repstat) {
                clientid.qval = 0;
                if ((nd->nd_flag & (ND_IMPLIEDCLID | ND_NFSV41)) ==
                    (ND_IMPLIEDCLID | ND_NFSV41))
                        clientid.qval = nd->nd_clientid.qval;
                nd->nd_repstat = nfsvno_link(&named, vp, clientid, nd->nd_cred,
                    p, exp);
        }
        if (nd->nd_flag & ND_NFSV3)
                getret = nfsvno_getattr(vp, &at, nd, p, 0, NULL);
        if (dirp) {
                diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
                vrele(dirp);
        }
        vrele(vp);
        if (nd->nd_flag & ND_NFSV3) {
                nfsrv_postopattr(nd, getret, &at);
                nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
        } else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
                NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
                *tl++ = newnfs_false;
                txdr_hyper(dirfor.na_filerev, tl);
                tl += 2;
                txdr_hyper(diraft.na_filerev, tl);
        }

out:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs symbolic link service
 */
int
nfsrvd_symlink(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
{
        struct nfsvattr nva, dirfor, diraft;
        struct nameidata named;
        int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
        vnode_t dirp = NULL;
        char *bufp, *pathcp = NULL;
        u_long *hashp;
        struct thread *p = curthread;

        if (nd->nd_repstat) {
                nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
                goto out;
        }
        if (vpp)
                *vpp = NULL;
        NFSVNO_ATTRINIT(&nva);
        NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
            LOCKPARENT | NOCACHE);
        nfsvno_setpathbuf(&named, &bufp, &hashp);
        error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
        if (!error && !nd->nd_repstat)
                error = nfsvno_getsymlink(nd, &nva, p, &pathcp, &pathlen);
        if (error) {
                vrele(dp);
                nfsvno_relpathbuf(&named);
                goto out;
        }
        if (!nd->nd_repstat) {
                nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, &dirp);
        } else {
                vrele(dp);
                nfsvno_relpathbuf(&named);
        }
        if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
                vrele(dirp);
                dirp = NULL;
        }

        /*
         * And call nfsrvd_symlinksub() to do the common code. It will
         * return EBADRPC upon a parsing error, 0 otherwise.
         */
        if (!nd->nd_repstat) {
                if (dirp != NULL)
                        dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
                            NULL);
                nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
                    &dirfor, &diraft, &diraft_ret, NULL, NULL, p, exp,
                    pathcp, pathlen);
        } else if (dirp != NULL) {
                dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL);
                vrele(dirp);
        }
        if (pathcp)
                free(pathcp, M_TEMP);

        if (nd->nd_flag & ND_NFSV3) {
                if (!nd->nd_repstat) {
                        (void)nfsm_fhtom(NULL, nd, (u_int8_t *)fhp, 0, 1);
                        nfsrv_postopattr(nd, 0, &nva);
                }
                nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
        }

out:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * Common code for creating a symbolic link.
 */
static void
nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
    int *diraft_retp, nfsattrbit_t *attrbitp,
    NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
    int pathlen)
{
        u_int32_t *tl;
        u_long setflags;

        setflags = nvap->na_flags;
        nvap->na_flags = (u_long)VNOVAL;
        nd->nd_repstat = nfsvno_symlink(ndp, nvap, pathcp, pathlen,
            !(nd->nd_flag & ND_NFSV2), nd->nd_saveduid, nd->nd_cred, p, exp);
        if (!nd->nd_repstat && !(nd->nd_flag & ND_NFSV2)) {
                nvap->na_flags = setflags;
                nfsrv_fixattr(nd, ndp->ni_vp, nvap, aclp, NULL, p, attrbitp,
                    false);
                if (nd->nd_flag & ND_NFSV3) {
                        nd->nd_repstat = nfsvno_getfh(ndp->ni_vp, fhp, p);
                        if (!nd->nd_repstat)
                                nd->nd_repstat = nfsvno_getattr(ndp->ni_vp,
                                    nvap, nd, p, 1, NULL);
                }
                if (vpp != NULL && nd->nd_repstat == 0) {
                        NFSVOPUNLOCK(ndp->ni_vp);
                        *vpp = ndp->ni_vp;
                } else
                        vput(ndp->ni_vp);
        }
        if (dirp) {
                *diraft_retp = nfsvno_getattr(dirp, diraftp, nd, p, 0, NULL);
                vrele(dirp);
        }
        if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
                NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
                *tl++ = newnfs_false;
                txdr_hyper(dirforp->na_filerev, tl);
                tl += 2;
                txdr_hyper(diraftp->na_filerev, tl);
                (void) nfsrv_putattrbit(nd, attrbitp);
        }

        NFSEXITCODE2(0, nd);
}

/*
 * nfs mkdir service
 */
int
nfsrvd_mkdir(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
{
        struct nfsvattr nva, dirfor, diraft;
        struct nameidata named;
        u_int32_t *tl;
        int error = 0, dirfor_ret = 1, diraft_ret = 1;
        vnode_t dirp = NULL;
        char *bufp;
        u_long *hashp;
        struct thread *p = curthread;

        if (nd->nd_repstat) {
                nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
                goto out;
        }
        NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, LOCKPARENT | NOCACHE);
        nfsvno_setpathbuf(&named, &bufp, &hashp);
        error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
        if (error)
                goto nfsmout;
        if (!nd->nd_repstat) {
                NFSVNO_ATTRINIT(&nva);
                if (nd->nd_flag & ND_NFSV3) {
                        error = nfsrv_sattr(nd, NULL, &nva, NULL, NULL, NULL,
                            p);
                        if (error)
                                goto nfsmout;
                } else {
                        NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
                        nva.na_mode = nfstov_mode(*tl++);
                }
        }
        if (!nd->nd_repstat) {
                nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, &dirp);
        } else {
                vrele(dp);
                nfsvno_relpathbuf(&named);
        }
        if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
                vrele(dirp);
                dirp = NULL;
        }
        if (nd->nd_repstat) {
                if (dirp != NULL) {
                        dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
                            NULL);
                        vrele(dirp);
                }
                if (nd->nd_flag & ND_NFSV3)
                        nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
                            &diraft);
                goto out;
        }
        if (dirp != NULL)
                dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL);

        /*
         * Call nfsrvd_mkdirsub() for the code common to V4 as well.
         */
        nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp, &dirfor, &diraft,
            &diraft_ret, NULL, NULL, NULL, p, exp);

        if (nd->nd_flag & ND_NFSV3) {
                if (!nd->nd_repstat) {
                        (void)nfsm_fhtom(NULL, nd, (u_int8_t *)fhp, 0, 1);
                        nfsrv_postopattr(nd, 0, &nva);
                }
                nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
        } else if (!nd->nd_repstat) {
                (void)nfsm_fhtom(NULL, nd, (u_int8_t *)fhp, 0, 0);
                nfsrv_fillattr(nd, &nva);
        }

out:
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        vrele(dp);
        nfsvno_relpathbuf(&named);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * Code common to mkdir for V2,3 and 4.
 */
static void
nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
    int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp, NFSACL_T *daclp,
    NFSPROC_T *p, struct nfsexstuff *exp)
{
        vnode_t vp;
        u_int32_t *tl;
        u_long setflags;

        setflags = nvap->na_flags;
        nvap->na_flags = (u_long)VNOVAL;
        NFSVNO_SETATTRVAL(nvap, type, VDIR);
        nd->nd_repstat = nfsvno_mkdir(ndp, nvap, nd->nd_saveduid,
            nd->nd_cred, p, exp);
        if (!nd->nd_repstat) {
                vp = ndp->ni_vp;
                nvap->na_flags = setflags;
                nfsrv_fixattr(nd, vp, nvap, aclp, daclp, p, attrbitp, false);
                nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
                if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
                        nd->nd_repstat = nfsvno_getattr(vp, nvap, nd, p, 1,
                            NULL);
                if (vpp && !nd->nd_repstat) {
                        NFSVOPUNLOCK(vp);
                        *vpp = vp;
                } else {
                        vput(vp);
                }
        }
        if (dirp) {
                *diraft_retp = nfsvno_getattr(dirp, diraftp, nd, p, 0, NULL);
                vrele(dirp);
        }
        if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
                NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
                *tl++ = newnfs_false;
                txdr_hyper(dirforp->na_filerev, tl);
                tl += 2;
                txdr_hyper(diraftp->na_filerev, tl);
                (void) nfsrv_putattrbit(nd, attrbitp);
        }

        NFSEXITCODE2(0, nd);
}

/*
 * nfs commit service
 */
int
nfsrvd_commit(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        struct nfsvattr bfor, aft;
        u_int32_t *tl;
        int error = 0, for_ret = 1, aft_ret = 1, cnt;
        u_int64_t off;
        struct thread *p = curthread;

       if (nd->nd_repstat) {
                nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
                goto out;
        }

        /* Return NFSERR_ISDIR in NFSv4 when commit on a directory. */
        if (vp->v_type != VREG) {
                if (nd->nd_flag & ND_NFSV3)
                        error = NFSERR_NOTSUPP;
                else
                        error = (vp->v_type == VDIR) ? NFSERR_ISDIR : NFSERR_INVAL;
                goto nfsmout;
        }
        NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);

        /*
         * XXX At this time VOP_FSYNC() does not accept offset and byte
         * count parameters, so these arguments are useless (someday maybe).
         */
        off = fxdr_hyper(tl);
        tl += 2;
        cnt = fxdr_unsigned(int, *tl);
        if (nd->nd_flag & ND_NFSV3)
                for_ret = nfsvno_getattr(vp, &bfor, nd, p, 1, NULL);
        nd->nd_repstat = nfsvno_fsync(vp, off, cnt, nd->nd_cred, p);
        if (nd->nd_flag & ND_NFSV3) {
                aft_ret = nfsvno_getattr(vp, &aft, nd, p, 1, NULL);
                nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
        }
        vput(vp);
        if (!nd->nd_repstat) {
                NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
                *tl++ = txdr_unsigned(nfsboottime.tv_sec);
                *tl = txdr_unsigned(nfsboottime.tv_usec);
        }

out:
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs statfs service
 */
int
nfsrvd_statfs(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        struct statfs *sf;
        u_int32_t *tl;
        int getret = 1;
        struct nfsvattr at;
        u_quad_t tval;
        struct thread *p = curthread;

        sf = NULL;
        if (nd->nd_repstat) {
                nfsrv_postopattr(nd, getret, &at);
                goto out;
        }
        sf = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
        nd->nd_repstat = nfsvno_statfs(vp, sf);
        getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL);
        vput(vp);
        if (nd->nd_flag & ND_NFSV3)
                nfsrv_postopattr(nd, getret, &at);
        if (nd->nd_repstat)
                goto out;
        if (nd->nd_flag & ND_NFSV2) {
                NFSM_BUILD(tl, u_int32_t *, NFSX_V2STATFS);
                *tl++ = txdr_unsigned(NFS_V2MAXDATA);
                *tl++ = txdr_unsigned(sf->f_bsize);
                *tl++ = txdr_unsigned(sf->f_blocks);
                *tl++ = txdr_unsigned(sf->f_bfree);
                *tl = txdr_unsigned(sf->f_bavail);
        } else {
                NFSM_BUILD(tl, u_int32_t *, NFSX_V3STATFS);
                tval = (u_quad_t)sf->f_blocks;
                tval *= (u_quad_t)sf->f_bsize;
                txdr_hyper(tval, tl); tl += 2;
                tval = (u_quad_t)sf->f_bfree;
                tval *= (u_quad_t)sf->f_bsize;
                txdr_hyper(tval, tl); tl += 2;
                tval = (u_quad_t)sf->f_bavail;
                tval *= (u_quad_t)sf->f_bsize;
                txdr_hyper(tval, tl); tl += 2;
                tval = (u_quad_t)sf->f_files;
                txdr_hyper(tval, tl); tl += 2;
                tval = (u_quad_t)sf->f_ffree;
                txdr_hyper(tval, tl); tl += 2;
                tval = (u_quad_t)sf->f_ffree;
                txdr_hyper(tval, tl); tl += 2;
                *tl = 0;
        }

out:
        free(sf, M_STATFS);
        NFSEXITCODE2(0, nd);
        return (0);
}

/*
 * nfs fsinfo service
 */
int
nfsrvd_fsinfo(struct nfsrv_descript *nd, int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        u_int32_t *tl;
        struct nfsfsinfo fs;
        int getret = 1;
        struct nfsvattr at;
        struct thread *p = curthread;

        if (nd->nd_repstat) {
                nfsrv_postopattr(nd, getret, &at);
                goto out;
        }
        getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL);
        nfsvno_getfs(&fs, isdgram);
        vput(vp);
        nfsrv_postopattr(nd, getret, &at);
        NFSM_BUILD(tl, u_int32_t *, NFSX_V3FSINFO);
        *tl++ = txdr_unsigned(fs.fs_rtmax);
        *tl++ = txdr_unsigned(fs.fs_rtpref);
        *tl++ = txdr_unsigned(fs.fs_rtmult);
        *tl++ = txdr_unsigned(fs.fs_wtmax);
        *tl++ = txdr_unsigned(fs.fs_wtpref);
        *tl++ = txdr_unsigned(fs.fs_wtmult);
        *tl++ = txdr_unsigned(fs.fs_dtpref);
        txdr_hyper(fs.fs_maxfilesize, tl);
        tl += 2;
        txdr_nfsv3time(&fs.fs_timedelta, tl);
        tl += 2;
        *tl = txdr_unsigned(fs.fs_properties);

out:
        NFSEXITCODE2(0, nd);
        return (0);
}

/*
 * nfs pathconf service
 */
int
nfsrvd_pathconf(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        struct nfsv3_pathconf *pc;
        int getret = 1;
        long linkmax, namemax, chownres, notrunc;
        struct nfsvattr at;
        struct thread *p = curthread;

        if (nd->nd_repstat) {
                nfsrv_postopattr(nd, getret, &at);
                goto out;
        }
        nd->nd_repstat = nfsvno_pathconf(vp, _PC_LINK_MAX, &linkmax,
            nd->nd_cred, p);
        if (!nd->nd_repstat)
                nd->nd_repstat = nfsvno_pathconf(vp, _PC_NAME_MAX, &namemax,
                    nd->nd_cred, p);
        if (!nd->nd_repstat)
                nd->nd_repstat=nfsvno_pathconf(vp, _PC_CHOWN_RESTRICTED,
                    &chownres, nd->nd_cred, p);
        if (!nd->nd_repstat)
                nd->nd_repstat = nfsvno_pathconf(vp, _PC_NO_TRUNC, &notrunc,
                    nd->nd_cred, p);
        getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL);
        vput(vp);
        nfsrv_postopattr(nd, getret, &at);
        if (!nd->nd_repstat) {
                NFSM_BUILD(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
                pc->pc_linkmax = txdr_unsigned(linkmax);
                pc->pc_namemax = txdr_unsigned(namemax);
                pc->pc_notrunc = txdr_unsigned(notrunc);
                pc->pc_chownrestricted = txdr_unsigned(chownres);

                /*
                 * These should probably be supported by VOP_PATHCONF(), but
                 * until msdosfs is exportable (why would you want to?), the
                 * Unix defaults should be ok.
                 */
                pc->pc_caseinsensitive = newnfs_false;
                pc->pc_casepreserving = newnfs_true;
        }

out:
        NFSEXITCODE2(0, nd);
        return (0);
}

/*
 * nfsv4 lock service
 */
int
nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        u_int32_t *tl;
        int i;
        struct nfsstate *stp = NULL;
        struct nfslock *lop;
        struct nfslockconflict cf;
        int error = 0;
        u_short flags = NFSLCK_LOCK, lflags;
        u_int64_t offset, len;
        nfsv4stateid_t stateid;
        nfsquad_t clientid;
        struct thread *p = curthread;

        NFSM_DISSECT(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
        i = fxdr_unsigned(int, *tl++);
        switch (i) {
        case NFSV4LOCKT_READW:
                flags |= NFSLCK_BLOCKING;
        case NFSV4LOCKT_READ:
                lflags = NFSLCK_READ;
                break;
        case NFSV4LOCKT_WRITEW:
                flags |= NFSLCK_BLOCKING;
        case NFSV4LOCKT_WRITE:
                lflags = NFSLCK_WRITE;
                break;
        default:
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        }
        if (*tl++ == newnfs_true)
                flags |= NFSLCK_RECLAIM;
        offset = fxdr_hyper(tl);
        tl += 2;
        len = fxdr_hyper(tl);
        tl += 2;
        if (*tl == newnfs_true)
                flags |= NFSLCK_OPENTOLOCK;
        if (flags & NFSLCK_OPENTOLOCK) {
                NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID);
                i = fxdr_unsigned(int, *(tl+4+(NFSX_STATEID / NFSX_UNSIGNED)));
                if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
                        nd->nd_repstat = NFSERR_BADXDR;
                        goto nfsmout;
                }
                stp = malloc(sizeof (struct nfsstate) + i,
                        M_NFSDSTATE, M_WAITOK);
                stp->ls_ownerlen = i;
                stp->ls_op = nd->nd_rp;
                stp->ls_seq = fxdr_unsigned(int, *tl++);
                stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
                NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
                        NFSX_STATEIDOTHER);
                tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);

                /*
                 * For the special stateid of other all 0s and seqid == 1, set
                 * the stateid to the current stateid, if it is set.
                 */
                if ((nd->nd_flag & ND_NFSV41) != 0 &&
                    stp->ls_stateid.seqid == 1 &&
                    stp->ls_stateid.other[0] == 0 &&
                    stp->ls_stateid.other[1] == 0 &&
                    stp->ls_stateid.other[2] == 0) {
                        if ((nd->nd_flag & ND_CURSTATEID) != 0) {
                                stp->ls_stateid = nd->nd_curstateid;
                                stp->ls_stateid.seqid = 0;
                        } else {
                                nd->nd_repstat = NFSERR_BADSTATEID;
                                goto nfsmout;
                        }
                }

                stp->ls_opentolockseq = fxdr_unsigned(int, *tl++);
                clientid.lval[0] = *tl++;
                clientid.lval[1] = *tl++;
                if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                        if ((nd->nd_flag & ND_NFSV41) != 0)
                                clientid.qval = nd->nd_clientid.qval;
                        else if (nd->nd_clientid.qval != clientid.qval)
                                printf("EEK3 multiple clids\n");
                } else {
                        if ((nd->nd_flag & ND_NFSV41) != 0)
                                printf("EEK! no clientid from session\n");
                        nd->nd_flag |= ND_IMPLIEDCLID;
                        nd->nd_clientid.qval = clientid.qval;
                }
                error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
                if (error)
                        goto nfsmout;
        } else {
                NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
                stp = malloc(sizeof (struct nfsstate),
                        M_NFSDSTATE, M_WAITOK);
                stp->ls_ownerlen = 0;
                stp->ls_op = nd->nd_rp;
                stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
                NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
                        NFSX_STATEIDOTHER);
                tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);

                /*
                 * For the special stateid of other all 0s and seqid == 1, set
                 * the stateid to the current stateid, if it is set.
                 */
                if ((nd->nd_flag & ND_NFSV41) != 0 &&
                    stp->ls_stateid.seqid == 1 &&
                    stp->ls_stateid.other[0] == 0 &&
                    stp->ls_stateid.other[1] == 0 &&
                    stp->ls_stateid.other[2] == 0) {
                        if ((nd->nd_flag & ND_CURSTATEID) != 0) {
                                stp->ls_stateid = nd->nd_curstateid;
                                stp->ls_stateid.seqid = 0;
                        } else {
                                nd->nd_repstat = NFSERR_BADSTATEID;
                                goto nfsmout;
                        }
                }

                stp->ls_seq = fxdr_unsigned(int, *tl);
                clientid.lval[0] = stp->ls_stateid.other[0];
                clientid.lval[1] = stp->ls_stateid.other[1];
                if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                        if ((nd->nd_flag & ND_NFSV41) != 0)
                                clientid.qval = nd->nd_clientid.qval;
                        else if (nd->nd_clientid.qval != clientid.qval)
                                printf("EEK4 multiple clids\n");
                } else {
                        if ((nd->nd_flag & ND_NFSV41) != 0)
                                printf("EEK! no clientid from session\n");
                        nd->nd_flag |= ND_IMPLIEDCLID;
                        nd->nd_clientid.qval = clientid.qval;
                }
        }
        lop = malloc(sizeof (struct nfslock),
                M_NFSDLOCK, M_WAITOK);
        lop->lo_first = offset;
        if (len == NFS64BITSSET) {
                lop->lo_end = NFS64BITSSET;
        } else {
                lop->lo_end = offset + len;
                if (lop->lo_end <= lop->lo_first)
                        nd->nd_repstat = NFSERR_INVAL;
        }
        lop->lo_flags = lflags;
        stp->ls_flags = flags;
        stp->ls_uid = nd->nd_cred->cr_uid;

        /*
         * Do basic access checking.
         */
        if (!nd->nd_repstat && vp->v_type != VREG) {
            if (vp->v_type == VDIR)
                nd->nd_repstat = NFSERR_ISDIR;
            else
                nd->nd_repstat = NFSERR_INVAL;
        }
        if (!nd->nd_repstat) {
            if (lflags & NFSLCK_WRITE) {
                nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
                    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
                    NFSACCCHK_VPISLOCKED, NULL);
            } else {
                nd->nd_repstat = nfsvno_accchk(vp, VREAD,
                    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
                    NFSACCCHK_VPISLOCKED, NULL);
                if (nd->nd_repstat)
                    nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
                        nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
                        NFSACCCHK_VPISLOCKED, NULL);
            }
        }

        /*
         * We call nfsrv_lockctrl() even if nd_repstat set, so that the
         * seqid# gets updated. nfsrv_lockctrl() will return the value
         * of nd_repstat, if it gets that far.
         */
        nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid, 
                &stateid, exp, nd, p);
        if (lop)
                free(lop, M_NFSDLOCK);
        if (stp)
                free(stp, M_NFSDSTATE);
        if (!nd->nd_repstat) {
                /* For NFSv4.1, set the Current StateID. */
                if ((nd->nd_flag & ND_NFSV41) != 0) {
                        nd->nd_curstateid = stateid;
                        nd->nd_flag |= ND_CURSTATEID;
                }
                NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
                *tl++ = txdr_unsigned(stateid.seqid);
                NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
        } else if (nd->nd_repstat == NFSERR_DENIED) {
                NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
                txdr_hyper(cf.cl_first, tl);
                tl += 2;
                if (cf.cl_end == NFS64BITSSET)
                        len = NFS64BITSSET;
                else
                        len = cf.cl_end - cf.cl_first;
                txdr_hyper(len, tl);
                tl += 2;
                if (cf.cl_flags == NFSLCK_WRITE)
                        *tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
                else
                        *tl++ = txdr_unsigned(NFSV4LOCKT_READ);
                *tl++ = stateid.other[0];
                *tl = stateid.other[1];
                (void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
        }
        vput(vp);
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        vput(vp);
        if (stp)
                free(stp, M_NFSDSTATE);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 lock test service
 */
int
nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        u_int32_t *tl;
        int i;
        struct nfsstate *stp = NULL;
        struct nfslock lo, *lop = &lo;
        struct nfslockconflict cf;
        int error = 0;
        nfsv4stateid_t stateid;
        nfsquad_t clientid;
        u_int64_t len;
        struct thread *p = curthread;

        NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
        i = fxdr_unsigned(int, *(tl + 7));
        if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        }
        stp = malloc(sizeof (struct nfsstate) + i,
            M_NFSDSTATE, M_WAITOK);
        stp->ls_ownerlen = i;
        stp->ls_op = NULL;
        stp->ls_flags = NFSLCK_TEST;
        stp->ls_uid = nd->nd_cred->cr_uid;
        i = fxdr_unsigned(int, *tl++);
        switch (i) {
        case NFSV4LOCKT_READW:
                stp->ls_flags |= NFSLCK_BLOCKING;
        case NFSV4LOCKT_READ:
                lo.lo_flags = NFSLCK_READ;
                break;
        case NFSV4LOCKT_WRITEW:
                stp->ls_flags |= NFSLCK_BLOCKING;
        case NFSV4LOCKT_WRITE:
                lo.lo_flags = NFSLCK_WRITE;
                break;
        default:
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        }
        lo.lo_first = fxdr_hyper(tl);
        tl += 2;
        len = fxdr_hyper(tl);
        if (len == NFS64BITSSET) {
                lo.lo_end = NFS64BITSSET;
        } else {
                lo.lo_end = lo.lo_first + len;
                if (lo.lo_end <= lo.lo_first)
                        nd->nd_repstat = NFSERR_INVAL;
        }
        tl += 2;
        clientid.lval[0] = *tl++;
        clientid.lval[1] = *tl;
        if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        clientid.qval = nd->nd_clientid.qval;
                else if (nd->nd_clientid.qval != clientid.qval)
                        printf("EEK5 multiple clids\n");
        } else {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        printf("EEK! no clientid from session\n");
                nd->nd_flag |= ND_IMPLIEDCLID;
                nd->nd_clientid.qval = clientid.qval;
        }
        error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
        if (error)
                goto nfsmout;
        if (!nd->nd_repstat && vp->v_type != VREG) {
            if (vp->v_type == VDIR)
                nd->nd_repstat = NFSERR_ISDIR;
            else
                nd->nd_repstat = NFSERR_INVAL;
        }
        if (!nd->nd_repstat)
          nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
            &stateid, exp, nd, p);
        if (nd->nd_repstat) {
            if (nd->nd_repstat == NFSERR_DENIED) {
                NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
                txdr_hyper(cf.cl_first, tl);
                tl += 2;
                if (cf.cl_end == NFS64BITSSET)
                        len = NFS64BITSSET;
                else
                        len = cf.cl_end - cf.cl_first;
                txdr_hyper(len, tl);
                tl += 2;
                if (cf.cl_flags == NFSLCK_WRITE)
                        *tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
                else
                        *tl++ = txdr_unsigned(NFSV4LOCKT_READ);
                *tl++ = stp->ls_stateid.other[0];
                *tl = stp->ls_stateid.other[1];
                (void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
            }
        }
        vput(vp);
        if (stp)
                free(stp, M_NFSDSTATE);
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        vput(vp);
        if (stp)
                free(stp, M_NFSDSTATE);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 unlock service
 */
int
nfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        u_int32_t *tl;
        int i;
        struct nfsstate *stp;
        struct nfslock *lop;
        int error = 0;
        nfsv4stateid_t stateid;
        nfsquad_t clientid;
        u_int64_t len;
        struct thread *p = curthread;

        NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED + NFSX_STATEID);
        stp = malloc(sizeof (struct nfsstate),
            M_NFSDSTATE, M_WAITOK);
        lop = malloc(sizeof (struct nfslock),
            M_NFSDLOCK, M_WAITOK);
        stp->ls_flags = NFSLCK_UNLOCK;
        lop->lo_flags = NFSLCK_UNLOCK;
        stp->ls_op = nd->nd_rp;
        i = fxdr_unsigned(int, *tl++);
        switch (i) {
        case NFSV4LOCKT_READW:
                stp->ls_flags |= NFSLCK_BLOCKING;
        case NFSV4LOCKT_READ:
                break;
        case NFSV4LOCKT_WRITEW:
                stp->ls_flags |= NFSLCK_BLOCKING;
        case NFSV4LOCKT_WRITE:
                break;
        default:
                nd->nd_repstat = NFSERR_BADXDR;
                free(stp, M_NFSDSTATE);
                free(lop, M_NFSDLOCK);
                goto nfsmout;
        }
        stp->ls_ownerlen = 0;
        stp->ls_uid = nd->nd_cred->cr_uid;
        stp->ls_seq = fxdr_unsigned(int, *tl++);
        stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
        NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
            NFSX_STATEIDOTHER);
        tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);

        /*
         * For the special stateid of other all 0s and seqid == 1, set the
         * stateid to the current stateid, if it is set.
         */
        if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 1 &&
            stp->ls_stateid.other[0] == 0 && stp->ls_stateid.other[1] == 0 &&
            stp->ls_stateid.other[2] == 0) {
                if ((nd->nd_flag & ND_CURSTATEID) != 0) {
                        stp->ls_stateid = nd->nd_curstateid;
                        stp->ls_stateid.seqid = 0;
                } else {
                        nd->nd_repstat = NFSERR_BADSTATEID;
                        free(stp, M_NFSDSTATE);
                        free(lop, M_NFSDLOCK);
                        goto nfsmout;
                }
        }

        lop->lo_first = fxdr_hyper(tl);
        tl += 2;
        len = fxdr_hyper(tl);
        if (len == NFS64BITSSET) {
                lop->lo_end = NFS64BITSSET;
        } else {
                lop->lo_end = lop->lo_first + len;
                if (lop->lo_end <= lop->lo_first)
                        nd->nd_repstat = NFSERR_INVAL;
        }
        clientid.lval[0] = stp->ls_stateid.other[0];
        clientid.lval[1] = stp->ls_stateid.other[1];
        if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        clientid.qval = nd->nd_clientid.qval;
                else if (nd->nd_clientid.qval != clientid.qval)
                        printf("EEK6 multiple clids\n");
        } else {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        printf("EEK! no clientid from session\n");
                nd->nd_flag |= ND_IMPLIEDCLID;
                nd->nd_clientid.qval = clientid.qval;
        }
        if (!nd->nd_repstat && vp->v_type != VREG) {
            if (vp->v_type == VDIR)
                nd->nd_repstat = NFSERR_ISDIR;
            else
                nd->nd_repstat = NFSERR_INVAL;
        }
        /*
         * Call nfsrv_lockctrl() even if nd_repstat is set, so that the
         * seqid# gets incremented. nfsrv_lockctrl() will return the
         * value of nd_repstat, if it gets that far.
         */
        nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
            &stateid, exp, nd, p);
        if (stp)
                free(stp, M_NFSDSTATE);
        if (lop)
                free(lop, M_NFSDLOCK);
        if (!nd->nd_repstat) {
                NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
                *tl++ = txdr_unsigned(stateid.seqid);
                NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
        }
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 open service
 */
int
nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t dp, vnode_t *vpp, __unused fhandle_t *fhp, struct nfsexstuff *exp)
{
        u_int32_t *tl;
        int i, retext;
        struct nfsstate *stp = NULL;
        int error = 0, create, claim, override;
        int exclusive_flag = NFSV4_EXCLUSIVE_NONE;
        u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask;
        int how = NFSCREATE_UNCHECKED;
        int32_t cverf[2], tverf[2] = { 0, 0 };
        vnode_t vp = NULL, dirp = NULL;
        struct nfsvattr nva, dirfor, diraft, nva2;
        struct nameidata named;
        nfsv4stateid_t stateid, delegstateid;
        nfsattrbit_t attrbits;
        nfsquad_t clientid;
        char *bufp = NULL;
        u_long *hashp;
        NFSACL_T *aclp = NULL, *daclp = NULL;
        struct thread *p = curthread;
        bool done_namei;
        __enum_uint8_decl(wdelegace) { USENONE, USEMODE, USENFSV4ACL }
            delegace;

#ifdef NFS4_ACL_EXTATTR_NAME
        aclp = acl_alloc(M_WAITOK);
        aclp->acl_cnt = 0;
        daclp = acl_alloc(M_WAITOK);
        daclp->acl_cnt = 0;
#endif
        NFSZERO_ATTRBIT(&attrbits);
        done_namei = false;
        delegace = USEMODE;
        named.ni_cnd.cn_nameiop = 0;
        NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
        i = fxdr_unsigned(int, *(tl + 5));
        if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        }
        stp = malloc(sizeof (struct nfsstate) + i,
            M_NFSDSTATE, M_WAITOK);
        stp->ls_ownerlen = i;
        stp->ls_op = nd->nd_rp;
        stp->ls_flags = NFSLCK_OPEN;
        stp->ls_uid = nd->nd_cred->cr_uid;
        stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
        i = fxdr_unsigned(int, *tl++);
        retext = 0;
        if ((i & (NFSV4OPEN_WANTDELEGMASK | NFSV4OPEN_WANTSIGNALDELEG |
            NFSV4OPEN_WANTPUSHDELEG)) != 0 && (nd->nd_flag & ND_NFSV41) != 0) {
                retext = 1;
                /* For now, ignore these. */
                i &= ~(NFSV4OPEN_WANTPUSHDELEG | NFSV4OPEN_WANTSIGNALDELEG);
                switch (i & NFSV4OPEN_WANTDELEGMASK) {
                case NFSV4OPEN_WANTANYDELEG:
                        stp->ls_flags |= (NFSLCK_WANTRDELEG |
                            NFSLCK_WANTWDELEG);
                        i &= ~NFSV4OPEN_WANTDELEGMASK;
                        break;
                case NFSV4OPEN_WANTREADDELEG:
                        stp->ls_flags |= NFSLCK_WANTRDELEG;
                        i &= ~NFSV4OPEN_WANTDELEGMASK;
                        break;
                case NFSV4OPEN_WANTWRITEDELEG:
                        stp->ls_flags |= NFSLCK_WANTWDELEG;
                        i &= ~NFSV4OPEN_WANTDELEGMASK;
                        break;
                case NFSV4OPEN_WANTNODELEG:
                        stp->ls_flags |= NFSLCK_WANTNODELEG;
                        i &= ~NFSV4OPEN_WANTDELEGMASK;
                        break;
                case NFSV4OPEN_WANTCANCEL:
                        printf("NFSv4: ignore Open WantCancel\n");
                        i &= ~NFSV4OPEN_WANTDELEGMASK;
                        break;
                default:
                        /* nd_repstat will be set to NFSERR_INVAL below. */
                        break;
                }
        }
        switch (i) {
        case NFSV4OPEN_ACCESSREAD:
                stp->ls_flags |= NFSLCK_READACCESS;
                break;
        case NFSV4OPEN_ACCESSWRITE:
                stp->ls_flags |= NFSLCK_WRITEACCESS;
                break;
        case NFSV4OPEN_ACCESSBOTH:
                stp->ls_flags |= (NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
                break;
        default:
                nd->nd_repstat = NFSERR_INVAL;
        }
        i = fxdr_unsigned(int, *tl++);
        switch (i) {
        case NFSV4OPEN_DENYNONE:
                break;
        case NFSV4OPEN_DENYREAD:
                stp->ls_flags |= NFSLCK_READDENY;
                break;
        case NFSV4OPEN_DENYWRITE:
                stp->ls_flags |= NFSLCK_WRITEDENY;
                break;
        case NFSV4OPEN_DENYBOTH:
                stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
                break;
        default:
                nd->nd_repstat = NFSERR_INVAL;
        }
        clientid.lval[0] = *tl++;
        clientid.lval[1] = *tl;
        if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        clientid.qval = nd->nd_clientid.qval;
                else if (nd->nd_clientid.qval != clientid.qval)
                        printf("EEK7 multiple clids\n");
        } else {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        printf("EEK! no clientid from session\n");
                nd->nd_flag |= ND_IMPLIEDCLID;
                nd->nd_clientid.qval = clientid.qval;
        }
        error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
        if (error)
                goto nfsmout;
        NFSVNO_ATTRINIT(&nva);
        NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
        create = fxdr_unsigned(int, *tl);
        if (!nd->nd_repstat)
                nd->nd_repstat = nfsvno_getattr(dp, &dirfor, nd, p, 0, NULL);
        if (create == NFSV4OPEN_CREATE) {
                nva.na_type = VREG;
                nva.na_mode = 0;
                NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
                how = fxdr_unsigned(int, *tl);
                switch (how) {
                case NFSCREATE_UNCHECKED:
                case NFSCREATE_GUARDED:
                        error = nfsv4_sattr(nd, NULL, &nva, &attrbits, aclp,
                            daclp, p);
                        if (error)
                                goto nfsmout;
                        /*
                         * If the na_gid being set is the same as that of
                         * the directory it is going in, clear it, since
                         * that is what will be set by default. This allows
                         * a user that isn't in that group to do the create.
                         */
                        if (!nd->nd_repstat && NFSVNO_ISSETGID(&nva) &&
                            nva.na_gid == dirfor.na_gid)
                                NFSVNO_UNSET(&nva, gid);
                        if (!nd->nd_repstat)
                                nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
                        break;
                case NFSCREATE_EXCLUSIVE:
                        NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
                        cverf[0] = *tl++;
                        cverf[1] = *tl;
                        if ((vn_irflag_read(dp) & VIRF_NAMEDDIR) != 0)
                                nd->nd_repstat = NFSERR_INVAL;
                        break;
                case NFSCREATE_EXCLUSIVE41:
                        NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
                        cverf[0] = *tl++;
                        cverf[1] = *tl;
                        error = nfsv4_sattr(nd, NULL, &nva, &attrbits, aclp,
                            daclp, p);
                        if (error != 0)
                                goto nfsmout;
                        if ((vn_irflag_read(dp) & VIRF_NAMEDDIR) != 0 ||
                            NFSISSET_ATTRBIT(&attrbits,
                            NFSATTRBIT_TIMEACCESSSET))
                                nd->nd_repstat = NFSERR_INVAL;
                        /*
                         * If the na_gid being set is the same as that of
                         * the directory it is going in, clear it, since
                         * that is what will be set by default. This allows
                         * a user that isn't in that group to do the create.
                         */
                        if (nd->nd_repstat == 0 && NFSVNO_ISSETGID(&nva) &&
                            nva.na_gid == dirfor.na_gid)
                                NFSVNO_UNSET(&nva, gid);
                        if (nd->nd_repstat == 0)
                                nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
                        break;
                default:
                        nd->nd_repstat = NFSERR_BADXDR;
                        goto nfsmout;
                }
        } else if (create != NFSV4OPEN_NOCREATE) {
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        }

        /*
         * Now, handle the claim, which usually includes looking up a
         * name in the directory referenced by dp. The exception is
         * NFSV4OPEN_CLAIMPREVIOUS.
         */
        NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
        claim = fxdr_unsigned(int, *tl);
        if (claim == NFSV4OPEN_CLAIMDELEGATECUR || claim ==
            NFSV4OPEN_CLAIMDELEGATECURFH) {
                NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
                stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
                NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
                stp->ls_flags |= NFSLCK_DELEGCUR;
        } else if (claim == NFSV4OPEN_CLAIMDELEGATEPREV || claim ==
            NFSV4OPEN_CLAIMDELEGATEPREVFH) {
                stp->ls_flags |= NFSLCK_DELEGPREV;
        }
        if (claim == NFSV4OPEN_CLAIMNULL || claim == NFSV4OPEN_CLAIMDELEGATECUR
            || claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
                if (!nd->nd_repstat && create == NFSV4OPEN_CREATE &&
                    claim != NFSV4OPEN_CLAIMNULL)
                        nd->nd_repstat = NFSERR_INVAL;
                if (nd->nd_repstat) {
                        nd->nd_repstat = nfsrv_opencheck(clientid,
                            &stateid, stp, NULL, nd, p, nd->nd_repstat);
                        goto nfsmout;
                }
                if (create == NFSV4OPEN_CREATE)
                    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
                        LOCKPARENT | LOCKLEAF | NOCACHE);
                else
                    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
                        LOCKLEAF);
                nfsvno_setpathbuf(&named, &bufp, &hashp);
                error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
                if (error) {
                        vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
                        acl_free(aclp);
                        acl_free(daclp);
#endif
                        free(stp, M_NFSDSTATE);
                        nfsvno_relpathbuf(&named);
                        NFSEXITCODE2(error, nd);
                        return (error);
                }
                if (!nd->nd_repstat) {
                        nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp,
                            &dirp);
                } else {
                        vrele(dp);
                        nfsvno_relpathbuf(&named);
                }
                if (create == NFSV4OPEN_CREATE) {
                    switch (how) {
                    case NFSCREATE_UNCHECKED:
                        if (nd->nd_repstat == 0 && named.ni_vp != NULL) {
                                /*
                                 * Clear the setable attribute bits, except
                                 * for Size, if it is being truncated.
                                 */
                                NFSZERO_ATTRBIT(&attrbits);
                                if (NFSVNO_ISSETSIZE(&nva))
                                        NFSSETBIT_ATTRBIT(&attrbits,
                                            NFSATTRBIT_SIZE);
                        }
                        break;
                    case NFSCREATE_GUARDED:
                        if (nd->nd_repstat == 0 && named.ni_vp != NULL) {
                                nd->nd_repstat = EEXIST;
                                done_namei = true;
                        }
                        break;
                    case NFSCREATE_EXCLUSIVE:
                        if (nd->nd_repstat == 0 && named.ni_vp == NULL)
                                nva.na_mode = 0;
                        exclusive_flag = NFSV4_EXCLUSIVE;
                        /* FALLTHROUGH */
                    case NFSCREATE_EXCLUSIVE41:
                        if (nd->nd_repstat == 0 && named.ni_vp != NULL) {
                                nd->nd_repstat = nfsvno_getattr(named.ni_vp,
                                    &nva2, nd, p, 1, NULL);
                                if (nd->nd_repstat == 0) {
                                        tverf[0] = nva2.na_atime.tv_sec;
                                        tverf[1] = nva2.na_atime.tv_nsec;
                                        if (cverf[0] != tverf[0] ||
                                             cverf[1] != tverf[1])
                                                nd->nd_repstat = EEXIST;
                                }
                                if (nd->nd_repstat != 0)
                                        done_namei = true;
                        }
                        if (how == NFSCREATE_EXCLUSIVE41)
                                exclusive_flag = NFSV4_EXCLUSIVE_41;
                        break;
                    }
                }
                nfsvno_open(nd, &named, clientid, &stateid, stp,
                    &exclusive_flag, &nva, cverf, create, aclp, daclp,
                    &attrbits, nd->nd_cred, done_namei, exp, &vp);
        } else if (claim == NFSV4OPEN_CLAIMPREVIOUS || claim ==
            NFSV4OPEN_CLAIMFH || claim == NFSV4OPEN_CLAIMDELEGATECURFH ||
            claim == NFSV4OPEN_CLAIMDELEGATEPREVFH) {
                if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
                        NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
                        i = fxdr_unsigned(int, *tl);
                        switch (i) {
                        case NFSV4OPEN_DELEGATEREAD:
                                stp->ls_flags |= NFSLCK_DELEGREAD;
                                break;
                        case NFSV4OPEN_DELEGATEWRITE:
                                stp->ls_flags |= NFSLCK_DELEGWRITE;
                        case NFSV4OPEN_DELEGATENONE:
                                break;
                        default:
                                nd->nd_repstat = NFSERR_BADXDR;
                                goto nfsmout;
                        }
                        stp->ls_flags |= NFSLCK_RECLAIM;
                } else {
                        if (nd->nd_repstat == 0 && create == NFSV4OPEN_CREATE)
                                nd->nd_repstat = NFSERR_INVAL;
                }
                vp = dp;
                NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
                if (!VN_IS_DOOMED(vp))
                        nd->nd_repstat = nfsrv_opencheck(clientid, &stateid,
                            stp, vp, nd, p, nd->nd_repstat);
                else
                        nd->nd_repstat = NFSERR_PERM;
        } else {
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        }

        /*
         * Do basic access checking.
         */
        if (!nd->nd_repstat && vp->v_type != VREG) {
                /*
                 * The IETF working group decided that this is the correct
                 * error return for all non-regular files.
                 */
                nd->nd_repstat = (vp->v_type == VDIR) ? NFSERR_ISDIR : NFSERR_SYMLINK;
        }

        /*
         * If the Open is being done for a file that already exists, apply
         * normal permission checking including for the file owner, if
         * vfs.nfsd.v4openaccess is set.
         * Previously, the owner was always allowed to open the file to
         * be consistent with the NFS tradition of always allowing the
         * owner of the file to write to the file regardless of permissions.
         * It now appears that the Linux client expects the owner
         * permissions to be checked for opens that are not creating the
         * file.  I believe the correct approach is to use the Access
         * operation's results to be consistent with NFSv3, but that is
         * not what the current Linux client appears to be doing.
         * Since both the Linux and OpenSolaris NFSv4 servers do this check,
         * I have enabled it by default.  Since Linux does not apply this
         * check for claim_delegate_cur, this code does the same.
         * If this semantic change causes a problem, it can be disabled by
         * setting the sysctl vfs.nfsd.v4openaccess to 0 to re-enable the
         * previous semantics.
         */
        if (nfsrv_openaccess && create == NFSV4OPEN_NOCREATE &&
            (stp->ls_flags & NFSLCK_DELEGCUR) == 0)
                override = NFSACCCHK_NOOVERRIDE;
        else
                override = NFSACCCHK_ALLOWOWNER;
        if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_WRITEACCESS))
            nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred,
                exp, p, override, NFSACCCHK_VPISLOCKED, NULL);
        if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_READACCESS)) {
            nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred,
                exp, p, override, NFSACCCHK_VPISLOCKED, NULL);
            if (nd->nd_repstat)
                nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
                    nd->nd_cred, exp, p, override,
                    NFSACCCHK_VPISLOCKED, NULL);
        }

        if (!nd->nd_repstat)
                nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);

        if (nd->nd_repstat == 0 && aclp != NULL && nfsrv_issuedelegs != 0 &&
            (dp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0) {
                if (aclp->acl_cnt == 0 && create == NFSV4OPEN_NOCREATE) {
                        int retacl;

                        /* We do not yet have an ACL, so try and get one. */
                        retacl = VOP_GETACL(vp, ACL_TYPE_NFS4, aclp,
                            nd->nd_cred, p);
                        if (retacl != 0 && retacl != ENOATTR &&
                            retacl != EOPNOTSUPP && retacl != EINVAL)
                                delegace = USENONE;
                        else if (retacl == 0 && aclp->acl_cnt > 0)
                                delegace = USENFSV4ACL;
                } else if (aclp->acl_cnt > 0 && create == NFSV4OPEN_CREATE) {
                        delegace = USENFSV4ACL;
                }
        }

        /*
         * Do the open locking/delegation stuff.
         */
        if (!nd->nd_repstat)
            nd->nd_repstat = nfsrv_openctrl(nd, vp, &stp, clientid, &stateid,
                &delegstateid, &rflags, exp, p, nva.na_filerev);

        /*
         * vp must be unlocked before the call to nfsvno_getattr(dirp,...)
         * below, to avoid a deadlock with the lookup in nfsvno_namei() above.
         * (ie: Leave the NFSVOPUNLOCK() about here.)
         */
        if (vp)
                NFSVOPUNLOCK(vp);
        if (stp)
                free(stp, M_NFSDSTATE);
        if (!nd->nd_repstat && dirp)
                nd->nd_repstat = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
        if (!nd->nd_repstat) {
                /* For NFSv4.1, set the Current StateID. */
                if ((nd->nd_flag & ND_NFSV41) != 0) {
                        nd->nd_curstateid = stateid;
                        nd->nd_flag |= ND_CURSTATEID;
                }
                NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(stateid.seqid);
                NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
                tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
                if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
                        *tl++ = newnfs_true;
                        *tl++ = 0;
                        *tl++ = 0;
                        *tl++ = 0;
                        *tl++ = 0;
                } else {
                        *tl++ = newnfs_false;   /* Since dirp is not locked */
                        txdr_hyper(dirfor.na_filerev, tl);
                        tl += 2;
                        txdr_hyper(diraft.na_filerev, tl);
                        tl += 2;
                }
                *tl = txdr_unsigned(rflags & NFSV4OPEN_RFLAGS);
                (void) nfsrv_putattrbit(nd, &attrbits);
                NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
                if (rflags & NFSV4OPEN_READDELEGATE)
                        *tl = txdr_unsigned(NFSV4OPEN_DELEGATEREAD);
                else if (rflags & NFSV4OPEN_WRITEDELEGATE)
                        *tl = txdr_unsigned(NFSV4OPEN_DELEGATEWRITE);
                else if (retext != 0) {
                        *tl = txdr_unsigned(NFSV4OPEN_DELEGATENONEEXT);
                        if ((rflags & NFSV4OPEN_WDNOTWANTED) != 0) {
                                NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
                                *tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
                        } else if ((rflags & NFSV4OPEN_WDSUPPFTYPE) != 0) {
                                NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
                                *tl = txdr_unsigned(NFSV4OPEN_NOTSUPPFTYPE);
                        } else if ((rflags & NFSV4OPEN_WDCONTENTION) != 0) {
                                NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
                                *tl++ = txdr_unsigned(NFSV4OPEN_CONTENTION);
                                *tl = newnfs_false;
                        } else if ((rflags & NFSV4OPEN_WDRESOURCE) != 0) {
                                NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
                                *tl++ = txdr_unsigned(NFSV4OPEN_RESOURCE);
                                *tl = newnfs_false;
                        } else if ((rflags &
                            NFSV4OPEN_WDNOTSUPPDOWNGRADE) != 0) {
                                NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                                *tl = txdr_unsigned(NFSV4OPEN_NOTSUPPDOWNGRADE);
                        } else if ((rflags & NFSV4OPEN_WDNOTSUPPUPGRADE) != 0) {
                                NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                                *tl = txdr_unsigned(NFSV4OPEN_NOTSUPPUPGRADE);
                        } else {
                                NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
                                *tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
                        }
                } else
                        *tl = txdr_unsigned(NFSV4OPEN_DELEGATENONE);
                if (rflags & (NFSV4OPEN_READDELEGATE|NFSV4OPEN_WRITEDELEGATE)) {
                        NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID+NFSX_UNSIGNED);
                        *tl++ = txdr_unsigned(delegstateid.seqid);
                        NFSBCOPY((caddr_t)delegstateid.other, (caddr_t)tl,
                            NFSX_STATEIDOTHER);
                        tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
                        if (rflags & NFSV4OPEN_RECALL)
                                *tl = newnfs_true;
                        else
                                *tl = newnfs_false;
                        if (rflags & NFSV4OPEN_WRITEDELEGATE) {
                                NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
                                *tl++ = txdr_unsigned(NFSV4OPEN_LIMITSIZE);
                                txdr_hyper(nva.na_size, tl);
                        }

                        /* Set up the write delegation ACE. */
                        NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
                        if (delegace == USENFSV4ACL) {
                                int j;

                                for (j = 0; j < aclp->acl_cnt; j++) {
                                        if (aclp->acl_entry[j].ae_tag ==
                                            ACL_USER_OBJ ||
                                            aclp->acl_entry[j].ae_entry_type !=
                                            ACL_ENTRY_TYPE_ALLOW)
                                                break;
                                }
                                if (j < aclp->acl_cnt &&
                                    aclp->acl_entry[j].ae_tag ==
                                    ACL_USER_OBJ &&
                                    aclp->acl_entry[j].ae_entry_type ==
                                    ACL_ENTRY_TYPE_ALLOW) {
                                        /* Use this ACE. */
                                        *tl++ = txdr_unsigned(
                                            NFSV4ACE_ALLOWEDTYPE);
                                        *tl++ = txdr_unsigned(0x0);
                                        *tl = txdr_unsigned(
                                            nfs_aceperm(
                                            aclp->acl_entry[j].ae_perm));
                                        (void)nfsm_strtom(nd, "OWNER@", 6);
                                } else
                                        delegace = USENONE;
                        }
                        if (delegace == USENONE) {
                                /* Don't allow anything. */
                                *tl++ = 0x0;
                                *tl++ = 0x0;
                                *tl = 0x0;
                                NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                                *tl = 0;
                        } else if (delegace == USEMODE) {
                                /* Build from mode. */
                                *tl++ = txdr_unsigned(NFSV4ACE_ALLOWEDTYPE);
                                *tl++ = txdr_unsigned(0x0);
                                acemask = NFSV4ACE_ALLFILESMASK;
                                if (nva.na_mode & S_IRUSR)
                                        acemask |= NFSV4ACE_READMASK;
                                if (nva.na_mode & S_IWUSR)
                                        acemask |= NFSV4ACE_WRITEMASK;
                                if (nva.na_mode & S_IXUSR)
                                        acemask |= NFSV4ACE_EXECUTEMASK;
                                *tl = txdr_unsigned(acemask);
                                (void)nfsm_strtom(nd, "OWNER@", 6);
                        }
                }
                *vpp = vp;
        } else if (vp) {
                vrele(vp);
        }
        if (dirp)
                vrele(dirp);
#ifdef NFS4_ACL_EXTATTR_NAME
        acl_free(aclp);
        acl_free(daclp);
#endif
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        vrele(dp);
#ifdef NFS4_ACL_EXTATTR_NAME
        acl_free(aclp);
        acl_free(daclp);
#endif
        if (stp)
                free(stp, M_NFSDSTATE);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 close service
 */
int
nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        u_int32_t *tl;
        struct nfsstate st, *stp = &st;
        int error = 0, writeacc;
        nfsv4stateid_t stateid;
        nfsquad_t clientid;
        struct nfsvattr na;
        struct thread *p = curthread;

        NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
        stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
        stp->ls_ownerlen = 0;
        stp->ls_op = nd->nd_rp;
        stp->ls_uid = nd->nd_cred->cr_uid;
        stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
        NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
            NFSX_STATEIDOTHER);

        /*
         * For the special stateid of other all 0s and seqid == 1, set the
         * stateid to the current stateid, if it is set.
         */
        if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 1 &&
            stp->ls_stateid.other[0] == 0 && stp->ls_stateid.other[1] == 0 &&
            stp->ls_stateid.other[2] == 0) {
                if ((nd->nd_flag & ND_CURSTATEID) != 0)
                        stp->ls_stateid = nd->nd_curstateid;
                else {
                        nd->nd_repstat = NFSERR_BADSTATEID;
                        goto nfsmout;
                }
        }

        stp->ls_flags = NFSLCK_CLOSE;
        clientid.lval[0] = stp->ls_stateid.other[0];
        clientid.lval[1] = stp->ls_stateid.other[1];
        if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        clientid.qval = nd->nd_clientid.qval;
                else if (nd->nd_clientid.qval != clientid.qval)
                        printf("EEK8 multiple clids\n");
        } else {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        printf("EEK! no clientid from session\n");
                nd->nd_flag |= ND_IMPLIEDCLID;
                nd->nd_clientid.qval = clientid.qval;
        }
        nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p,
            &writeacc);
        /* For pNFS, update the attributes. */
        if (writeacc != 0 || nfsrv_pnfsatime != 0)
                nfsrv_updatemdsattr(vp, &na, p);
        vput(vp);
        if (!nd->nd_repstat) {
                /*
                 * If the stateid that has been closed is the current stateid,
                 * unset it.
                 */
                if ((nd->nd_flag & ND_CURSTATEID) != 0 &&
                    stateid.other[0] == nd->nd_curstateid.other[0] &&
                    stateid.other[1] == nd->nd_curstateid.other[1] &&
                    stateid.other[2] == nd->nd_curstateid.other[2])
                        nd->nd_flag &= ~ND_CURSTATEID;
                NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
                *tl++ = txdr_unsigned(stateid.seqid);
                NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
        }
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 delegpurge service
 */
int
nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram,
    __unused vnode_t vp, __unused struct nfsexstuff *exp)
{
        u_int32_t *tl;
        int error = 0;
        nfsquad_t clientid;
        struct thread *p = curthread;

        if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
                goto nfsmout;
        NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
        clientid.lval[0] = *tl++;
        clientid.lval[1] = *tl;
        if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        clientid.qval = nd->nd_clientid.qval;
                else if (nd->nd_clientid.qval != clientid.qval)
                        printf("EEK9 multiple clids\n");
        } else {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        printf("EEK! no clientid from session\n");
                nd->nd_flag |= ND_IMPLIEDCLID;
                nd->nd_clientid.qval = clientid.qval;
        }
        nd->nd_repstat = nfsrv_delegupdate(nd, clientid, NULL, NULL,
            NFSV4OP_DELEGPURGE, nd->nd_cred, p, NULL);
nfsmout:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 delegreturn service
 */
int
nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        u_int32_t *tl;
        int error = 0, writeacc;
        nfsv4stateid_t stateid;
        nfsquad_t clientid;
        struct nfsvattr na;
        struct thread *p = curthread;

        NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
        stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
        NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, NFSX_STATEIDOTHER);
        clientid.lval[0] = stateid.other[0];
        clientid.lval[1] = stateid.other[1];
        if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        clientid.qval = nd->nd_clientid.qval;
                else if (nd->nd_clientid.qval != clientid.qval)
                        printf("EEK10 multiple clids\n");
        } else {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        printf("EEK! no clientid from session\n");
                nd->nd_flag |= ND_IMPLIEDCLID;
                nd->nd_clientid.qval = clientid.qval;
        }
        nd->nd_repstat = nfsrv_delegupdate(nd, clientid, &stateid, vp,
            NFSV4OP_DELEGRETURN, nd->nd_cred, p, &writeacc);
        /* For pNFS, update the attributes. */
        if (writeacc != 0 || nfsrv_pnfsatime != 0)
                nfsrv_updatemdsattr(vp, &na, p);
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 get file handle service
 */
int
nfsrvd_getfh(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        fhandle_t fh;
        struct thread *p = curthread;
        int siz;
        short irflag;

        nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
        irflag = vn_irflag_read(vp);
        vput(vp);
        if (nd->nd_repstat == 0) {
                siz = 0;
                if ((irflag & VIRF_NAMEDDIR) != 0)
                        siz = NFSX_FHMAX + NFSX_V4NAMEDDIRFH;
                else if ((irflag & VIRF_NAMEDATTR) != 0)
                        siz = NFSX_FHMAX + NFSX_V4NAMEDATTRFH;
                (void)nfsm_fhtom(NULL, nd, (u_int8_t *)&fh, siz, 0);
        }
        NFSEXITCODE2(0, nd);
        return (0);
}

/*
 * nfsv4 open confirm service
 */
int
nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        u_int32_t *tl;
        struct nfsstate st, *stp = &st;
        int error = 0;
        nfsv4stateid_t stateid;
        nfsquad_t clientid;
        struct thread *p = curthread;

        if ((nd->nd_flag & ND_NFSV41) != 0) {
                nd->nd_repstat = NFSERR_NOTSUPP;
                goto nfsmout;
        }
        NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
        stp->ls_ownerlen = 0;
        stp->ls_op = nd->nd_rp;
        stp->ls_uid = nd->nd_cred->cr_uid;
        stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
        NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
            NFSX_STATEIDOTHER);
        tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
        stp->ls_seq = fxdr_unsigned(u_int32_t, *tl);
        stp->ls_flags = NFSLCK_CONFIRM;
        clientid.lval[0] = stp->ls_stateid.other[0];
        clientid.lval[1] = stp->ls_stateid.other[1];
        if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        clientid.qval = nd->nd_clientid.qval;
                else if (nd->nd_clientid.qval != clientid.qval)
                        printf("EEK11 multiple clids\n");
        } else {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        printf("EEK! no clientid from session\n");
                nd->nd_flag |= ND_IMPLIEDCLID;
                nd->nd_clientid.qval = clientid.qval;
        }
        nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p,
            NULL);
        if (!nd->nd_repstat) {
                NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
                *tl++ = txdr_unsigned(stateid.seqid);
                NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
        }
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 open downgrade service
 */
int
nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        u_int32_t *tl;
        int i;
        struct nfsstate st, *stp = &st;
        int error = 0;
        nfsv4stateid_t stateid;
        nfsquad_t clientid;
        struct thread *p = curthread;

        /* opendowngrade can only work on a file object.*/
        if (vp->v_type != VREG) {
                error = NFSERR_INVAL;
                goto nfsmout;
        }
        NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
        stp->ls_ownerlen = 0;
        stp->ls_op = nd->nd_rp;
        stp->ls_uid = nd->nd_cred->cr_uid;
        stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
        NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
            NFSX_STATEIDOTHER);
        tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);

        /*
         * For the special stateid of other all 0s and seqid == 1, set the
         * stateid to the current stateid, if it is set.
         */
        if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 1 &&
            stp->ls_stateid.other[0] == 0 && stp->ls_stateid.other[1] == 0 &&
            stp->ls_stateid.other[2] == 0) {
                if ((nd->nd_flag & ND_CURSTATEID) != 0)
                        stp->ls_stateid = nd->nd_curstateid;
                else {
                        nd->nd_repstat = NFSERR_BADSTATEID;
                        goto nfsmout;
                }
        }

        stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
        i = fxdr_unsigned(int, *tl++);
        if ((nd->nd_flag & ND_NFSV41) != 0)
                i &= ~NFSV4OPEN_WANTDELEGMASK;
        switch (i) {
        case NFSV4OPEN_ACCESSREAD:
                stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_DOWNGRADE);
                break;
        case NFSV4OPEN_ACCESSWRITE:
                stp->ls_flags = (NFSLCK_WRITEACCESS | NFSLCK_DOWNGRADE);
                break;
        case NFSV4OPEN_ACCESSBOTH:
                stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_WRITEACCESS |
                    NFSLCK_DOWNGRADE);
                break;
        default:
                nd->nd_repstat = NFSERR_INVAL;
        }
        i = fxdr_unsigned(int, *tl);
        switch (i) {
        case NFSV4OPEN_DENYNONE:
                break;
        case NFSV4OPEN_DENYREAD:
                stp->ls_flags |= NFSLCK_READDENY;
                break;
        case NFSV4OPEN_DENYWRITE:
                stp->ls_flags |= NFSLCK_WRITEDENY;
                break;
        case NFSV4OPEN_DENYBOTH:
                stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
                break;
        default:
                nd->nd_repstat = NFSERR_INVAL;
        }

        clientid.lval[0] = stp->ls_stateid.other[0];
        clientid.lval[1] = stp->ls_stateid.other[1];
        if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        clientid.qval = nd->nd_clientid.qval;
                else if (nd->nd_clientid.qval != clientid.qval)
                        printf("EEK12 multiple clids\n");
        } else {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        printf("EEK! no clientid from session\n");
                nd->nd_flag |= ND_IMPLIEDCLID;
                nd->nd_clientid.qval = clientid.qval;
        }
        if (!nd->nd_repstat)
                nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid,
                    nd, p, NULL);
        if (!nd->nd_repstat) {
                /* For NFSv4.1, set the Current StateID. */
                if ((nd->nd_flag & ND_NFSV41) != 0) {
                        nd->nd_curstateid = stateid;
                        nd->nd_flag |= ND_CURSTATEID;
                }
                NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
                *tl++ = txdr_unsigned(stateid.seqid);
                NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
        }
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 renew lease service
 */
int
nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram,
    __unused vnode_t vp, __unused struct nfsexstuff *exp)
{
        u_int32_t *tl;
        int error = 0;
        nfsquad_t clientid;
        struct thread *p = curthread;

        if ((nd->nd_flag & ND_NFSV41) != 0) {
                nd->nd_repstat = NFSERR_NOTSUPP;
                goto nfsmout;
        }
        if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
                goto nfsmout;
        NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
        clientid.lval[0] = *tl++;
        clientid.lval[1] = *tl;
        if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        clientid.qval = nd->nd_clientid.qval;
                else if (nd->nd_clientid.qval != clientid.qval)
                        printf("EEK13 multiple clids\n");
        } else {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        printf("EEK! no clientid from session\n");
                nd->nd_flag |= ND_IMPLIEDCLID;
                nd->nd_clientid.qval = clientid.qval;
        }
        nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW),
            NULL, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p);
nfsmout:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 security info service
 */
int
nfsrvd_secinfo(struct nfsrv_descript *nd, int isdgram,
    vnode_t dp, struct nfsexstuff *exp)
{
        u_int32_t *tl;
        int len;
        struct nameidata named;
        vnode_t dirp = NULL, vp;
        struct nfsrvfh fh;
        struct nfsexstuff retnes;
        u_int32_t *sizp;
        int error = 0, i;
        uint64_t savflag;
        char *bufp;
        u_long *hashp;
        struct thread *p = curthread;

        /*
         * All this just to get the export flags for the name.
         */
        NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
            LOCKLEAF);
        nfsvno_setpathbuf(&named, &bufp, &hashp);
        error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
        if (error) {
                vput(dp);
                nfsvno_relpathbuf(&named);
                goto out;
        }
        if (!nd->nd_repstat) {
                nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, &dirp);
        } else {
                vput(dp);
                nfsvno_relpathbuf(&named);
        }
        if (dirp)
                vrele(dirp);
        if (nd->nd_repstat)
                goto out;
        nfsvno_relpathbuf(&named);
        fh.nfsrvfh_len = NFSX_MYFH;
        vp = named.ni_vp;
        nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
        vput(vp);
        savflag = nd->nd_flag;
        if (!nd->nd_repstat) {
                /*
                 * Pretend the next op is Secinfo, so that no wrongsec
                 * test will be done.
                 */
                nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0,
                    NFSV4OP_SECINFO);
                if (vp)
                        vput(vp);
        }
        nd->nd_flag = savflag;
        if (nd->nd_repstat)
                goto out;

        /*
         * Finally have the export flags for name, so we can create
         * the security info.
         */
        len = 0;
        NFSM_BUILD(sizp, u_int32_t *, NFSX_UNSIGNED);

        /* If nes_numsecflavor == 0, all are allowed. */
        if (retnes.nes_numsecflavor == 0) {
                NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(RPCAUTH_UNIX);
                *tl = txdr_unsigned(RPCAUTH_GSS);
                nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
                    nfsgss_mechlist[KERBV_MECH].len);
                NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(GSS_KERBV_QOP);
                *tl++ = txdr_unsigned(RPCAUTHGSS_SVCNONE);
                *tl = txdr_unsigned(RPCAUTH_GSS);
                nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
                    nfsgss_mechlist[KERBV_MECH].len);
                NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(GSS_KERBV_QOP);
                *tl++ = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
                *tl = txdr_unsigned(RPCAUTH_GSS);
                nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
                    nfsgss_mechlist[KERBV_MECH].len);
                NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(GSS_KERBV_QOP);
                *tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
                len = 4;
        }
        for (i = 0; i < retnes.nes_numsecflavor; i++) {
                if (retnes.nes_secflavors[i] == AUTH_SYS) {
                        NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
                        *tl = txdr_unsigned(RPCAUTH_UNIX);
                        len++;
                } else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) {
                        NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
                        *tl++ = txdr_unsigned(RPCAUTH_GSS);
                        (void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
                            nfsgss_mechlist[KERBV_MECH].len);
                        NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
                        *tl++ = txdr_unsigned(GSS_KERBV_QOP);
                        *tl = txdr_unsigned(RPCAUTHGSS_SVCNONE);
                        len++;
                } else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) {
                        NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
                        *tl++ = txdr_unsigned(RPCAUTH_GSS);
                        (void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
                            nfsgss_mechlist[KERBV_MECH].len);
                        NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
                        *tl++ = txdr_unsigned(GSS_KERBV_QOP);
                        *tl = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
                        len++;
                } else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) {
                        NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
                        *tl++ = txdr_unsigned(RPCAUTH_GSS);
                        (void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
                            nfsgss_mechlist[KERBV_MECH].len);
                        NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
                        *tl++ = txdr_unsigned(GSS_KERBV_QOP);
                        *tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
                        len++;
                }
        }
        *sizp = txdr_unsigned(len);

out:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 security info no name service
 */
int
nfsrvd_secinfononame(struct nfsrv_descript *nd, int isdgram,
    vnode_t dp, struct nfsexstuff *exp)
{
        uint32_t *tl, *sizp;
        struct nameidata named;
        vnode_t dirp = NULL, vp;
        struct nfsrvfh fh;
        struct nfsexstuff retnes;
        int error = 0, fhstyle, i, len;
        uint64_t savflag;
        char *bufp;
        u_long *hashp;
        struct thread *p = curthread;

        NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
        fhstyle = fxdr_unsigned(int, *tl);
        switch (fhstyle) {
        case NFSSECINFONONAME_PARENT:
                if (dp->v_type != VDIR) {
                        vput(dp);
                        nd->nd_repstat = NFSERR_NOTDIR;
                        goto nfsmout;
                }
                NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
                    LOCKLEAF);
                nfsvno_setpathbuf(&named, &bufp, &hashp);
                error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
                if (error != 0) {
                        vput(dp);
                        nfsvno_relpathbuf(&named);
                        goto nfsmout;
                }
                if (nd->nd_repstat == 0)
                        nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, &dirp);
                else
                        vput(dp);
                if (dirp != NULL)
                        vrele(dirp);
                nfsvno_relpathbuf(&named);
                vp = named.ni_vp;
                break;
        case NFSSECINFONONAME_CURFH:
                vp = dp;
                break;
        default:
                nd->nd_repstat = NFSERR_INVAL;
                vput(dp);
        }
        if (nd->nd_repstat != 0)
                goto nfsmout;
        fh.nfsrvfh_len = NFSX_MYFH;
        nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
        vput(vp);
        savflag = nd->nd_flag;
        if (nd->nd_repstat == 0) {
                /*
                 * Pretend the next op is Secinfo, so that no wrongsec
                 * test will be done.
                 */
                nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0,
                    NFSV4OP_SECINFO);
                if (vp != NULL)
                        vput(vp);
        }
        nd->nd_flag = savflag;
        if (nd->nd_repstat != 0)
                goto nfsmout;

        /*
         * Finally have the export flags for fh/parent, so we can create
         * the security info.
         */
        len = 0;
        NFSM_BUILD(sizp, uint32_t *, NFSX_UNSIGNED);

        /* If nes_numsecflavor == 0, all are allowed. */
        if (retnes.nes_numsecflavor == 0) {
                NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(RPCAUTH_UNIX);
                *tl = txdr_unsigned(RPCAUTH_GSS);
                nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
                    nfsgss_mechlist[KERBV_MECH].len);
                NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(GSS_KERBV_QOP);
                *tl++ = txdr_unsigned(RPCAUTHGSS_SVCNONE);
                *tl = txdr_unsigned(RPCAUTH_GSS);
                nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
                    nfsgss_mechlist[KERBV_MECH].len);
                NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(GSS_KERBV_QOP);
                *tl++ = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
                *tl = txdr_unsigned(RPCAUTH_GSS);
                nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
                    nfsgss_mechlist[KERBV_MECH].len);
                NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(GSS_KERBV_QOP);
                *tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
                len = 4;
        }
        for (i = 0; i < retnes.nes_numsecflavor; i++) {
                if (retnes.nes_secflavors[i] == AUTH_SYS) {
                        NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                        *tl = txdr_unsigned(RPCAUTH_UNIX);
                        len++;
                } else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) {
                        NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                        *tl = txdr_unsigned(RPCAUTH_GSS);
                        nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
                            nfsgss_mechlist[KERBV_MECH].len);
                        NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
                        *tl++ = txdr_unsigned(GSS_KERBV_QOP);
                        *tl = txdr_unsigned(RPCAUTHGSS_SVCNONE);
                        len++;
                } else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) {
                        NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                        *tl = txdr_unsigned(RPCAUTH_GSS);
                        nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
                            nfsgss_mechlist[KERBV_MECH].len);
                        NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
                        *tl++ = txdr_unsigned(GSS_KERBV_QOP);
                        *tl = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
                        len++;
                } else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) {
                        NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                        *tl = txdr_unsigned(RPCAUTH_GSS);
                        nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
                            nfsgss_mechlist[KERBV_MECH].len);
                        NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
                        *tl++ = txdr_unsigned(GSS_KERBV_QOP);
                        *tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
                        len++;
                }
        }
        *sizp = txdr_unsigned(len);

nfsmout:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 set client id service
 */
int
nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram,
    __unused vnode_t vp, __unused struct nfsexstuff *exp)
{
        u_int32_t *tl;
        int i;
        int error = 0, idlen;
        struct nfsclient *clp = NULL;
#ifdef INET
        struct sockaddr_in *rin;
#endif
#ifdef INET6
        struct sockaddr_in6 *rin6;
#endif
#if defined(INET) || defined(INET6)
        u_char *ucp, *ucp2;
#endif
        u_char *verf, *addrbuf;
        nfsquad_t clientid, confirm;
        struct thread *p = curthread;

        if ((nd->nd_flag & ND_NFSV41) != 0) {
                nd->nd_repstat = NFSERR_NOTSUPP;
                goto nfsmout;
        }
        if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
                goto out;
        NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
        verf = (u_char *)tl;
        tl += (NFSX_VERF / NFSX_UNSIGNED);
        i = fxdr_unsigned(int, *tl);
        if (i > NFSV4_OPAQUELIMIT || i <= 0) {
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        }
        idlen = i;
        if (nd->nd_flag & ND_GSS)
                i += nd->nd_princlen;
        clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
            M_ZERO);
        clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
            nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
        NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
        /* Allocated large enough for an AF_INET or AF_INET6 socket. */
        clp->lc_req.nr_nam = malloc(sizeof(struct sockaddr_in6), M_SONAME,
            M_WAITOK | M_ZERO);
        clp->lc_req.nr_cred = NULL;
        NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
        clp->lc_idlen = idlen;
        error = nfsrv_mtostr(nd, clp->lc_id, idlen);
        if (error)
                goto nfsmout;
        if (nd->nd_flag & ND_GSS) {
                clp->lc_flags = LCL_GSS;
                if (nd->nd_flag & ND_GSSINTEGRITY)
                        clp->lc_flags |= LCL_GSSINTEGRITY;
                else if (nd->nd_flag & ND_GSSPRIVACY)
                        clp->lc_flags |= LCL_GSSPRIVACY;
        } else {
                clp->lc_flags = 0;
        }
        if ((nd->nd_flag & ND_GSS) && nd->nd_princlen > 0) {
                clp->lc_flags |= LCL_NAME;
                clp->lc_namelen = nd->nd_princlen;
                clp->lc_name = &clp->lc_id[idlen];
                NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
        } else {
                clp->lc_uid = nd->nd_cred->cr_uid;
                clp->lc_gid = nd->nd_cred->cr_gid;
        }

        /* If the client is using TLS, do so for the callback connection. */
        if (nd->nd_flag & ND_TLS)
                clp->lc_flags |= LCL_TLSCB;

        NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
        clp->lc_program = fxdr_unsigned(u_int32_t, *tl);
        error = nfsrv_getclientipaddr(nd, clp);
        if (error)
                goto nfsmout;
        NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
        clp->lc_callback = fxdr_unsigned(u_int32_t, *tl);

        /*
         * nfsrv_setclient() does the actual work of adding it to the
         * client list. If there is no error, the structure has been
         * linked into the client list and clp should no longer be used
         * here. When an error is returned, it has not been linked in,
         * so it should be free'd.
         */
        nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
        if (nd->nd_repstat == NFSERR_CLIDINUSE) {
                /*
                 * 8 is the maximum length of the port# string.
                 */
                addrbuf = malloc(INET6_ADDRSTRLEN + 8, M_TEMP, M_WAITOK);
                switch (clp->lc_req.nr_nam->sa_family) {
#ifdef INET
                case AF_INET:
                        if (clp->lc_flags & LCL_TCPCALLBACK)
                                (void) nfsm_strtom(nd, "tcp", 3);
                        else 
                                (void) nfsm_strtom(nd, "udp", 3);
                        rin = (struct sockaddr_in *)clp->lc_req.nr_nam;
                        ucp = (u_char *)&rin->sin_addr.s_addr;
                        ucp2 = (u_char *)&rin->sin_port;
                        sprintf(addrbuf, "%d.%d.%d.%d.%d.%d", ucp[0] & 0xff,
                            ucp[1] & 0xff, ucp[2] & 0xff, ucp[3] & 0xff,
                            ucp2[0] & 0xff, ucp2[1] & 0xff);
                        break;
#endif
#ifdef INET6
                case AF_INET6:
                        if (clp->lc_flags & LCL_TCPCALLBACK)
                                (void) nfsm_strtom(nd, "tcp6", 4);
                        else 
                                (void) nfsm_strtom(nd, "udp6", 4);
                        rin6 = (struct sockaddr_in6 *)clp->lc_req.nr_nam;
                        ucp = inet_ntop(AF_INET6, &rin6->sin6_addr, addrbuf,
                            INET6_ADDRSTRLEN);
                        if (ucp != NULL)
                                i = strlen(ucp);
                        else
                                i = 0;
                        ucp2 = (u_char *)&rin6->sin6_port;
                        sprintf(&addrbuf[i], ".%d.%d", ucp2[0] & 0xff,
                            ucp2[1] & 0xff);
                        break;
#endif
                }
                (void) nfsm_strtom(nd, addrbuf, strlen(addrbuf));
                free(addrbuf, M_TEMP);
        }
        if (clp) {
                free(clp->lc_req.nr_nam, M_SONAME);
                NFSFREEMUTEX(&clp->lc_req.nr_mtx);
                free(clp->lc_stateid, M_NFSDCLIENT);
                free(clp, M_NFSDCLIENT);
        }
        if (!nd->nd_repstat) {
                NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_HYPER);
                *tl++ = clientid.lval[0];
                *tl++ = clientid.lval[1];
                *tl++ = confirm.lval[0];
                *tl = confirm.lval[1];
        }

out:
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        if (clp) {
                free(clp->lc_req.nr_nam, M_SONAME);
                NFSFREEMUTEX(&clp->lc_req.nr_mtx);
                free(clp->lc_stateid, M_NFSDCLIENT);
                free(clp, M_NFSDCLIENT);
        }
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 set client id confirm service
 */
int
nfsrvd_setclientidcfrm(struct nfsrv_descript *nd,
    __unused int isdgram, __unused vnode_t vp,
    __unused struct nfsexstuff *exp)
{
        u_int32_t *tl;
        int error = 0;
        nfsquad_t clientid, confirm;
        struct thread *p = curthread;

        if ((nd->nd_flag & ND_NFSV41) != 0) {
                nd->nd_repstat = NFSERR_NOTSUPP;
                goto nfsmout;
        }
        if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
                goto nfsmout;
        NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER);
        clientid.lval[0] = *tl++;
        clientid.lval[1] = *tl++;
        confirm.lval[0] = *tl++;
        confirm.lval[1] = *tl;

        /*
         * nfsrv_getclient() searches the client list for a match and
         * returns the appropriate NFSERR status.
         */
        nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_CONFIRM|CLOPS_RENEW),
            NULL, NULL, confirm, 0, nd, p);
nfsmout:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 verify service
 */
int
nfsrvd_verify(struct nfsrv_descript *nd, int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        int error = 0, ret, fhsize = NFSX_MYFH;
        struct nfsvattr nva;
        struct statfs *sf;
        struct nfsfsinfo fs;
        fhandle_t fh;
        struct thread *p = curthread;

        sf = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
        nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
        if (!nd->nd_repstat)
                nd->nd_repstat = nfsvno_statfs(vp, sf);
        if (!nd->nd_repstat)
                nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
        if (!nd->nd_repstat) {
                nfsvno_getfs(&fs, isdgram);
                error = nfsv4_loadattr(nd, vp, &nva, NULL, &fh, fhsize, NULL,
                    sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, NULL, NULL, NULL,
                    p, nd->nd_cred);
                if (!error) {
                        if (nd->nd_procnum == NFSV4OP_NVERIFY) {
                                if (ret == 0)
                                        nd->nd_repstat = NFSERR_SAME;
                                else if (ret != NFSERR_NOTSAME)
                                        nd->nd_repstat = ret;
                        } else if (ret)
                                nd->nd_repstat = ret;
                }
        }
        vput(vp);
        free(sf, M_STATFS);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs openattr rpc
 */
int
nfsrvd_openattr(struct nfsrv_descript *nd, __unused int isdgram,
    struct vnode *dp, struct vnode **vpp, __unused fhandle_t *fhp,
    __unused struct nfsexstuff *exp)
{
        uint32_t *tl;
        struct componentname cn;
        int error = 0;

        NFSNAMEICNDSET(&cn, nd->nd_cred, LOOKUP, OPENNAMED | ISLASTCN |
            NOFOLLOW | LOCKLEAF);
        cn.cn_nameptr = ".";
        cn.cn_namelen = 1;
        cn.cn_lkflags = LK_SHARED;
        NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
        if (*tl == newnfs_true)
                cn.cn_flags |= CREATENAMED;

        nd->nd_repstat = vn_lock(dp, LK_SHARED);
        if (nd->nd_repstat != 0)
                goto nfsmout;

        if ((dp->v_mount->mnt_flag & MNT_NAMEDATTR) == 0)
                nd->nd_repstat = NFSERR_NOTSUPP;
        if (nd->nd_repstat == 0 && (vn_irflag_read(dp) & (VIRF_NAMEDDIR |
            VIRF_NAMEDATTR)) != 0)
                nd->nd_repstat = NFSERR_WRONGTYPE;
        if (nd->nd_repstat == 0) {
                nd->nd_repstat = VOP_LOOKUP(dp, vpp, &cn);
                if (nd->nd_repstat == ENOATTR)
                        nd->nd_repstat = NFSERR_NOENT;
        }
        if (nd->nd_repstat == 0)
                NFSVOPUNLOCK(*vpp);

        vput(dp);
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        vrele(dp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 release lock owner service
 */
int
nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram,
    __unused vnode_t vp, __unused struct nfsexstuff *exp)
{
        u_int32_t *tl;
        struct nfsstate *stp = NULL;
        int error = 0, len;
        nfsquad_t clientid;
        struct thread *p = curthread;

        if ((nd->nd_flag & ND_NFSV41) != 0) {
                nd->nd_repstat = NFSERR_NOTSUPP;
                goto nfsmout;
        }
        if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
                goto nfsmout;
        NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
        len = fxdr_unsigned(int, *(tl + 2));
        if (len <= 0 || len > NFSV4_OPAQUELIMIT) {
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        }
        stp = malloc(sizeof (struct nfsstate) + len,
            M_NFSDSTATE, M_WAITOK);
        stp->ls_ownerlen = len;
        stp->ls_op = NULL;
        stp->ls_flags = NFSLCK_RELEASE;
        stp->ls_uid = nd->nd_cred->cr_uid;
        clientid.lval[0] = *tl++;
        clientid.lval[1] = *tl;
        if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        clientid.qval = nd->nd_clientid.qval;
                else if (nd->nd_clientid.qval != clientid.qval)
                        printf("EEK14 multiple clids\n");
        } else {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        printf("EEK! no clientid from session\n");
                nd->nd_flag |= ND_IMPLIEDCLID;
                nd->nd_clientid.qval = clientid.qval;
        }
        error = nfsrv_mtostr(nd, stp->ls_owner, len);
        if (error)
                goto nfsmout;
        nd->nd_repstat = nfsrv_releaselckown(stp, clientid, p);
        free(stp, M_NFSDSTATE);

        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        if (stp)
                free(stp, M_NFSDSTATE);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 exchange_id service
 */
int
nfsrvd_exchangeid(struct nfsrv_descript *nd, __unused int isdgram,
    __unused vnode_t vp, __unused struct nfsexstuff *exp)
{
        uint32_t *tl;
        int error = 0, i, idlen;
        struct nfsclient *clp = NULL;
        nfsquad_t clientid, confirm;
        uint8_t *verf;
        uint32_t sp4type, v41flags;
        struct timespec verstime;
        nfsopbit_t mustops, allowops;
#ifdef INET
        struct sockaddr_in *sin, *rin;
#endif
#ifdef INET6
        struct sockaddr_in6 *sin6, *rin6;
#endif
        struct thread *p = curthread;
        char *s;

        if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
                goto nfsmout;
        NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
        verf = (uint8_t *)tl;
        tl += (NFSX_VERF / NFSX_UNSIGNED);
        i = fxdr_unsigned(int, *tl);
        if (i > NFSV4_OPAQUELIMIT || i <= 0) {
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        }
        idlen = i;
        if (nd->nd_flag & ND_GSS)
                i += nd->nd_princlen;
        clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
            M_ZERO);
        clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
            nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
        NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
        /* Allocated large enough for an AF_INET or AF_INET6 socket. */
        clp->lc_req.nr_nam = malloc(sizeof(struct sockaddr_in6), M_SONAME,
            M_WAITOK | M_ZERO);
        switch (nd->nd_nam->sa_family) {
#ifdef INET
        case AF_INET:
                rin = (struct sockaddr_in *)clp->lc_req.nr_nam;
                sin = (struct sockaddr_in *)nd->nd_nam;
                rin->sin_family = AF_INET;
                rin->sin_len = sizeof(struct sockaddr_in);
                rin->sin_port = 0;
                rin->sin_addr.s_addr = sin->sin_addr.s_addr;
                break;
#endif
#ifdef INET6
        case AF_INET6:
                rin6 = (struct sockaddr_in6 *)clp->lc_req.nr_nam;
                sin6 = (struct sockaddr_in6 *)nd->nd_nam;
                rin6->sin6_family = AF_INET6;
                rin6->sin6_len = sizeof(struct sockaddr_in6);
                rin6->sin6_port = 0;
                rin6->sin6_addr = sin6->sin6_addr;
                break;
#endif
        }
        clp->lc_req.nr_cred = NULL;
        NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
        clp->lc_idlen = idlen;
        error = nfsrv_mtostr(nd, clp->lc_id, idlen);
        if (error != 0)
                goto nfsmout;
        if ((nd->nd_flag & ND_GSS) != 0) {
                clp->lc_flags = LCL_GSS | LCL_NFSV41;
                if ((nd->nd_flag & ND_GSSINTEGRITY) != 0)
                        clp->lc_flags |= LCL_GSSINTEGRITY;
                else if ((nd->nd_flag & ND_GSSPRIVACY) != 0)
                        clp->lc_flags |= LCL_GSSPRIVACY;
        } else
                clp->lc_flags = LCL_NFSV41;
        if ((nd->nd_flag & ND_NFSV42) != 0)
                clp->lc_flags |= LCL_NFSV42;
        if ((nd->nd_flag & ND_GSS) != 0 && nd->nd_princlen > 0) {
                clp->lc_flags |= LCL_NAME;
                clp->lc_namelen = nd->nd_princlen;
                clp->lc_name = &clp->lc_id[idlen];
                NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
        } else {
                clp->lc_uid = nd->nd_cred->cr_uid;
                clp->lc_gid = nd->nd_cred->cr_gid;
        }
        NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
        v41flags = fxdr_unsigned(uint32_t, *tl++);
        if ((v41flags & ~(NFSV4EXCH_SUPPMOVEDREFER | NFSV4EXCH_SUPPMOVEDMIGR |
            NFSV4EXCH_BINDPRINCSTATEID | NFSV4EXCH_MASKPNFS |
            NFSV4EXCH_UPDCONFIRMEDRECA)) != 0) {
                nd->nd_repstat = NFSERR_INVAL;
                goto nfsmout;
        }
        if ((v41flags & NFSV4EXCH_UPDCONFIRMEDRECA) != 0)
                confirm.lval[1] = 1;
        else
                confirm.lval[1] = 0;
        if (nfsrv_devidcnt == 0)
                v41flags = NFSV4EXCH_USENONPNFS | NFSV4EXCH_USEPNFSDS;
        else
                v41flags = NFSV4EXCH_USEPNFSMDS;
        sp4type = fxdr_unsigned(uint32_t, *tl);
        if (sp4type == NFSV4EXCH_SP4MACHCRED) {
                if ((nd->nd_flag & (ND_GSSINTEGRITY | ND_GSSPRIVACY)) == 0 ||
                    nd->nd_princlen == 0)
                        nd->nd_repstat = (NFSERR_AUTHERR | AUTH_TOOWEAK);
                if (nd->nd_repstat == 0)
                        nd->nd_repstat = nfsrv_getopbits(nd, &mustops, NULL);
                if (nd->nd_repstat == 0)
                        nd->nd_repstat = nfsrv_getopbits(nd, &allowops, NULL);
                if (nd->nd_repstat != 0)
                        goto nfsmout;
                NFSOPBIT_CLRNOTMUST(&mustops);
                NFSSET_OPBIT(&clp->lc_mustops, &mustops);
                NFSOPBIT_CLRNOTALLOWED(&allowops);
                NFSSET_OPBIT(&clp->lc_allowops, &allowops);
                clp->lc_flags |= LCL_MACHCRED;
        } else if (sp4type != NFSV4EXCH_SP4NONE) {
                nd->nd_repstat = NFSERR_NOTSUPP;
                goto nfsmout;
        }

        /*
         * nfsrv_setclient() does the actual work of adding it to the
         * client list. If there is no error, the structure has been
         * linked into the client list and clp should no longer be used
         * here. When an error is returned, it has not been linked in,
         * so it should be free'd.
         */
        nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
        if (clp != NULL) {
                free(clp->lc_req.nr_nam, M_SONAME);
                NFSFREEMUTEX(&clp->lc_req.nr_mtx);
                free(clp->lc_stateid, M_NFSDCLIENT);
                free(clp, M_NFSDCLIENT);
        }
        if (nd->nd_repstat == 0) {
                if (confirm.lval[1] != 0)
                        v41flags |= NFSV4EXCH_CONFIRMEDR;
                NFSM_BUILD(tl, uint32_t *, NFSX_HYPER + 3 * NFSX_UNSIGNED);
                *tl++ = clientid.lval[0];                       /* ClientID */
                *tl++ = clientid.lval[1];
                *tl++ = txdr_unsigned(confirm.lval[0]);         /* SequenceID */
                *tl++ = txdr_unsigned(v41flags);                /* Exch flags */
                *tl = txdr_unsigned(sp4type);                   /* No SSV */
                if (sp4type == NFSV4EXCH_SP4MACHCRED) {
                        nfsrv_putopbit(nd, &mustops);
                        nfsrv_putopbit(nd, &allowops);
                }
                NFSM_BUILD(tl, uint32_t *, NFSX_HYPER);
                txdr_hyper(nfsrv_owner_minor, tl);      /* Owner Minor */
                if (nfsrv_owner_major[0] != 0)
                        s = nfsrv_owner_major;
                else
                        s = nd->nd_cred->cr_prison->pr_hostuuid;
                nfsm_strtom(nd, s, strlen(s));          /* Owner Major */
                if (nfsrv_scope[0] != 0)
                        s = nfsrv_scope;
                else
                        s = nd->nd_cred->cr_prison->pr_hostuuid;
                nfsm_strtom(nd, s, strlen(s)    );              /* Scope */
                NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                *tl = txdr_unsigned(1);
                (void)nfsm_strtom(nd, "freebsd.org", strlen("freebsd.org"));
                (void)nfsm_strtom(nd, version, strlen(version));
                NFSM_BUILD(tl, uint32_t *, NFSX_V4TIME);
                verstime.tv_sec = 1293840000;           /* Jan 1, 2011 */
                verstime.tv_nsec = 0;
                txdr_nfsv4time(&verstime, tl);
        }
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        if (clp != NULL) {
                free(clp->lc_req.nr_nam, M_SONAME);
                NFSFREEMUTEX(&clp->lc_req.nr_mtx);
                free(clp->lc_stateid, M_NFSDCLIENT);
                free(clp, M_NFSDCLIENT);
        }
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 create session service
 */
int
nfsrvd_createsession(struct nfsrv_descript *nd, __unused int isdgram,
    __unused vnode_t vp, __unused struct nfsexstuff *exp)
{
        uint32_t *tl;
        int error = 0;
        nfsquad_t clientid, confirm;
        struct nfsdsession *sep = NULL;
        uint32_t rdmacnt;
        struct thread *p = curthread;
        static bool do_printf = true;

        if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
                goto nfsmout;
        sep = (struct nfsdsession *)malloc(sizeof(struct nfsdsession),
            M_NFSDSESSION, M_WAITOK | M_ZERO);
        sep->sess_refcnt = 1;
        mtx_init(&sep->sess_cbsess.nfsess_mtx, "nfscbsession", NULL, MTX_DEF);
        NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + 2 * NFSX_UNSIGNED);
        clientid.lval[0] = *tl++;
        clientid.lval[1] = *tl++;
        confirm.lval[0] = fxdr_unsigned(uint32_t, *tl++);
        sep->sess_crflags = fxdr_unsigned(uint32_t, *tl);
        /* Persistent sessions and RDMA are not supported. */
        sep->sess_crflags &= NFSV4CRSESS_CONNBACKCHAN;

        /* Fore channel attributes. */
        NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
        tl++;                                   /* Header pad always 0. */
        sep->sess_maxreq = fxdr_unsigned(uint32_t, *tl++);
        if (sep->sess_maxreq > sb_max_adj - NFS_MAXXDR) {
                sep->sess_maxreq = sb_max_adj - NFS_MAXXDR;
                if (do_printf)
                        printf("Consider increasing kern.ipc.maxsockbuf\n");
                do_printf = false;
        }
        sep->sess_maxresp = fxdr_unsigned(uint32_t, *tl++);
        if (sep->sess_maxresp > sb_max_adj - NFS_MAXXDR) {
                sep->sess_maxresp = sb_max_adj - NFS_MAXXDR;
                if (do_printf)
                        printf("Consider increasing kern.ipc.maxsockbuf\n");
                do_printf = false;
        }
        sep->sess_maxrespcached = fxdr_unsigned(uint32_t, *tl++);
        sep->sess_maxops = fxdr_unsigned(uint32_t, *tl++);
        sep->sess_maxslots = fxdr_unsigned(uint32_t, *tl++);
        if (sep->sess_maxslots > NFSV4_SLOTS)
                sep->sess_maxslots = NFSV4_SLOTS;
        rdmacnt = fxdr_unsigned(uint32_t, *tl);
        if (rdmacnt > 1) {
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        } else if (rdmacnt == 1)
                NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);

        /* Back channel attributes. */
        NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
        tl++;                                   /* Header pad always 0. */
        sep->sess_cbmaxreq = fxdr_unsigned(uint32_t, *tl++);
        sep->sess_cbmaxresp = fxdr_unsigned(uint32_t, *tl++);
        sep->sess_cbmaxrespcached = fxdr_unsigned(uint32_t, *tl++);
        sep->sess_cbmaxops = fxdr_unsigned(uint32_t, *tl++);
        sep->sess_cbsess.nfsess_foreslots = fxdr_unsigned(uint32_t, *tl++);
        rdmacnt = fxdr_unsigned(uint32_t, *tl);
        if (rdmacnt > 1) {
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        } else if (rdmacnt == 1)
                NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);

        NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
        sep->sess_cbprogram = fxdr_unsigned(uint32_t, *tl);

        /*
         * nfsrv_getclient() searches the client list for a match and
         * returns the appropriate NFSERR status.
         */
        nd->nd_repstat = nfsrv_getclient(clientid, CLOPS_CONFIRM | CLOPS_RENEW,
            NULL, sep, confirm, sep->sess_cbprogram, nd, p);
        if (nd->nd_repstat == 0) {
                NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
                NFSBCOPY(sep->sess_sessionid, tl, NFSX_V4SESSIONID);
                NFSM_BUILD(tl, uint32_t *, 18 * NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(confirm.lval[0]); /* sequenceid */
                *tl++ = txdr_unsigned(sep->sess_crflags);

                /* Fore channel attributes. */
                *tl++ = 0;
                *tl++ = txdr_unsigned(sep->sess_maxreq);
                *tl++ = txdr_unsigned(sep->sess_maxresp);
                *tl++ = txdr_unsigned(sep->sess_maxrespcached);
                *tl++ = txdr_unsigned(sep->sess_maxops);
                *tl++ = txdr_unsigned(sep->sess_maxslots);
                *tl++ = txdr_unsigned(1);
                *tl++ = txdr_unsigned(0);                       /* No RDMA. */

                /* Back channel attributes. */
                *tl++ = 0;
                *tl++ = txdr_unsigned(sep->sess_cbmaxreq);
                *tl++ = txdr_unsigned(sep->sess_cbmaxresp);
                *tl++ = txdr_unsigned(sep->sess_cbmaxrespcached);
                *tl++ = txdr_unsigned(sep->sess_cbmaxops);
                *tl++ = txdr_unsigned(sep->sess_cbsess.nfsess_foreslots);
                *tl++ = txdr_unsigned(1);
                *tl = txdr_unsigned(0);                 /* No RDMA. */
        }
nfsmout:
        if (nd->nd_repstat != 0 && sep != NULL)
                free(sep, M_NFSDSESSION);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 sequence service
 */
int
nfsrvd_sequence(struct nfsrv_descript *nd, __unused int isdgram,
    __unused vnode_t vp, __unused struct nfsexstuff *exp)
{
        uint32_t *tl;
        uint32_t highest_slotid, sequenceid, sflags, target_highest_slotid;
        int cache_this, error = 0;
        struct thread *p = curthread;

        if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
                goto nfsmout;
        NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID);
        NFSBCOPY(tl, nd->nd_sessionid, NFSX_V4SESSIONID);
        NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
        sequenceid = fxdr_unsigned(uint32_t, *tl++);
        nd->nd_slotid = fxdr_unsigned(uint32_t, *tl++);
        highest_slotid = fxdr_unsigned(uint32_t, *tl++);
        if (*tl == newnfs_true)
                cache_this = 1;
        else
                cache_this = 0;
        nd->nd_repstat = nfsrv_checksequence(nd, sequenceid, &highest_slotid,
            &target_highest_slotid, cache_this, &sflags, p);
        if (nd->nd_repstat != NFSERR_BADSLOT)
                nd->nd_flag |= ND_HASSEQUENCE;
        if (nd->nd_repstat == 0) {
                NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
                NFSBCOPY(nd->nd_sessionid, tl, NFSX_V4SESSIONID);
                NFSM_BUILD(tl, uint32_t *, 5 * NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(sequenceid);
                *tl++ = txdr_unsigned(nd->nd_slotid);
                *tl++ = txdr_unsigned(highest_slotid);
                *tl++ = txdr_unsigned(target_highest_slotid);
                *tl = txdr_unsigned(sflags);
        }
nfsmout:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 reclaim complete service
 */
int
nfsrvd_reclaimcomplete(struct nfsrv_descript *nd, __unused int isdgram,
    __unused vnode_t vp, __unused struct nfsexstuff *exp)
{
        uint32_t *tl;
        int error = 0, onefs;

        NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
        /*
         * I believe that a ReclaimComplete with rca_one_fs == TRUE is only
         * to be used after a file system has been transferred to a different
         * file server.  However, RFC5661 is somewhat vague w.r.t. this and
         * the ESXi 6.7 client does both a ReclaimComplete with rca_one_fs
         * == TRUE and one with ReclaimComplete with rca_one_fs == FALSE.
         * Therefore, just ignore the rca_one_fs == TRUE operation and return
         * NFS_OK without doing anything.
         */
        onefs = 0;
        if (*tl == newnfs_true)
                onefs = 1;
        nd->nd_repstat = nfsrv_checkreclaimcomplete(nd, onefs);
nfsmout:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 destroy clientid service
 */
int
nfsrvd_destroyclientid(struct nfsrv_descript *nd, __unused int isdgram,
    __unused vnode_t vp, __unused struct nfsexstuff *exp)
{
        uint32_t *tl;
        nfsquad_t clientid;
        int error = 0;
        struct thread *p = curthread;

        if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
                goto nfsmout;
        NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
        clientid.lval[0] = *tl++;
        clientid.lval[1] = *tl;
        nd->nd_repstat = nfsrv_destroyclient(nd, clientid, p);
nfsmout:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 bind connection to session service
 */
int
nfsrvd_bindconnsess(struct nfsrv_descript *nd, __unused int isdgram,
    __unused vnode_t vp, __unused struct nfsexstuff *exp)
{
        uint32_t *tl;
        uint8_t sessid[NFSX_V4SESSIONID];
        int error = 0, foreaft;

        if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
                goto nfsmout;
        NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID + 2 * NFSX_UNSIGNED);
        NFSBCOPY(tl, sessid, NFSX_V4SESSIONID);
        tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
        foreaft = fxdr_unsigned(int, *tl++);
        if (*tl == newnfs_true) {
                /* RDMA is not supported. */
                nd->nd_repstat = NFSERR_NOTSUPP;
                goto nfsmout;
        }

        nd->nd_repstat = nfsrv_bindconnsess(nd, sessid, &foreaft);
        if (nd->nd_repstat == 0) {
                NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 2 *
                    NFSX_UNSIGNED);
                NFSBCOPY(sessid, tl, NFSX_V4SESSIONID);
                tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(foreaft);
                *tl = newnfs_false;
        }
nfsmout:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 destroy session service
 */
int
nfsrvd_destroysession(struct nfsrv_descript *nd, __unused int isdgram,
    __unused vnode_t vp, __unused struct nfsexstuff *exp)
{
        uint8_t *cp, sessid[NFSX_V4SESSIONID];
        int error = 0;

        if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
                goto nfsmout;
        NFSM_DISSECT(cp, uint8_t *, NFSX_V4SESSIONID);
        NFSBCOPY(cp, sessid, NFSX_V4SESSIONID);
        nd->nd_repstat = nfsrv_destroysession(nd, sessid);
nfsmout:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 free stateid service
 */
int
nfsrvd_freestateid(struct nfsrv_descript *nd, __unused int isdgram,
    __unused vnode_t vp, __unused struct nfsexstuff *exp)
{
        uint32_t *tl;
        nfsv4stateid_t stateid;
        int error = 0;
        struct thread *p = curthread;

        NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
        stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
        NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);

        /*
         * For the special stateid of other all 0s and seqid == 1, set the
         * stateid to the current stateid, if it is set.
         */
        if (stateid.seqid == 1 && stateid.other[0] == 0 &&
            stateid.other[1] == 0 && stateid.other[2] == 0) {
                if ((nd->nd_flag & ND_CURSTATEID) != 0) {
                        stateid = nd->nd_curstateid;
                        stateid.seqid = 0;
                } else {
                        nd->nd_repstat = NFSERR_BADSTATEID;
                        goto nfsmout;
                }
        }

        nd->nd_repstat = nfsrv_freestateid(nd, &stateid, p);

        /* If the current stateid has been free'd, unset it. */
        if (nd->nd_repstat == 0 && (nd->nd_flag & ND_CURSTATEID) != 0 &&
            stateid.other[0] == nd->nd_curstateid.other[0] &&
            stateid.other[1] == nd->nd_curstateid.other[1] &&
            stateid.other[2] == nd->nd_curstateid.other[2])
                nd->nd_flag &= ~ND_CURSTATEID;
nfsmout:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 layoutget service
 */
int
nfsrvd_layoutget(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        uint32_t *tl;
        nfsv4stateid_t stateid;
        int error = 0, layoutlen, layouttype, iomode, maxcnt, retonclose;
        uint64_t offset, len, minlen;
        char *layp;
        struct thread *p = curthread;

        NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED + 3 * NFSX_HYPER +
            NFSX_STATEID);
        tl++;           /* Signal layout available. Ignore for now. */
        layouttype = fxdr_unsigned(int, *tl++);
        iomode = fxdr_unsigned(int, *tl++);
        offset = fxdr_hyper(tl); tl += 2;
        len = fxdr_hyper(tl); tl += 2;
        minlen = fxdr_hyper(tl); tl += 2;
        stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
        NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
        tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
        maxcnt = fxdr_unsigned(int, *tl);
        NFSD_DEBUG(4, "layoutget ltyp=%d iom=%d off=%ju len=%ju mlen=%ju\n",
            layouttype, iomode, (uintmax_t)offset, (uintmax_t)len,
            (uintmax_t)minlen);
        if (len < minlen ||
            (minlen != UINT64_MAX && offset + minlen < offset) ||
            (len != UINT64_MAX && offset + len < offset)) {
                nd->nd_repstat = NFSERR_INVAL;
                goto nfsmout;
        }

        /*
         * For the special stateid of other all 0s and seqid == 1, set the
         * stateid to the current stateid, if it is set.
         */
        if (stateid.seqid == 1 && stateid.other[0] == 0 &&
            stateid.other[1] == 0 && stateid.other[2] == 0) {
                if ((nd->nd_flag & ND_CURSTATEID) != 0) {
                        stateid = nd->nd_curstateid;
                        stateid.seqid = 0;
                } else {
                        nd->nd_repstat = NFSERR_BADSTATEID;
                        goto nfsmout;
                }
        }

        layp = NULL;
        if (layouttype == NFSLAYOUT_NFSV4_1_FILES && nfsrv_maxpnfsmirror == 1)
                layp = malloc(NFSX_V4FILELAYOUT, M_TEMP, M_WAITOK);
        else if (layouttype == NFSLAYOUT_FLEXFILE)
                layp = malloc(NFSX_V4FLEXLAYOUT(nfsrv_maxpnfsmirror), M_TEMP,
                    M_WAITOK);
        else
                nd->nd_repstat = NFSERR_UNKNLAYOUTTYPE;
        if (layp != NULL)
                nd->nd_repstat = nfsrv_layoutget(nd, vp, exp, layouttype,
                    &iomode, &offset, &len, minlen, &stateid, maxcnt,
                    &retonclose, &layoutlen, layp, nd->nd_cred, p);
        NFSD_DEBUG(4, "nfsrv_layoutget stat=%u layoutlen=%d\n", nd->nd_repstat,
            layoutlen);
        if (nd->nd_repstat == 0) {
                /* For NFSv4.1, set the Current StateID. */
                if ((nd->nd_flag & ND_NFSV41) != 0) {
                        nd->nd_curstateid = stateid;
                        nd->nd_flag |= ND_CURSTATEID;
                }
                NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED + NFSX_STATEID +
                    2 * NFSX_HYPER);
                *tl++ = txdr_unsigned(retonclose);
                *tl++ = txdr_unsigned(stateid.seqid);
                NFSBCOPY(stateid.other, tl, NFSX_STATEIDOTHER);
                tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(1);       /* Only returns one layout. */
                txdr_hyper(offset, tl); tl += 2;
                txdr_hyper(len, tl); tl += 2;
                *tl++ = txdr_unsigned(iomode);
                *tl = txdr_unsigned(layouttype);
                nfsm_strtom(nd, layp, layoutlen);
        } else if (nd->nd_repstat == NFSERR_LAYOUTTRYLATER) {
                NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                *tl = newnfs_false;
        }
        free(layp, M_TEMP);
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 layoutcommit service
 */
int
nfsrvd_layoutcommit(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        uint32_t *tl;
        nfsv4stateid_t stateid;
        int error = 0, hasnewoff, hasnewmtime, layouttype, maxcnt, reclaim;
        int hasnewsize;
        uint64_t offset, len, newoff = 0, newsize;
        struct timespec newmtime;
        char *layp;
        struct thread *p = curthread;

        layp = NULL;
        NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED + 2 * NFSX_HYPER +
            NFSX_STATEID);
        offset = fxdr_hyper(tl); tl += 2;
        len = fxdr_hyper(tl); tl += 2;
        reclaim = fxdr_unsigned(int, *tl++);
        stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
        NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
        tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
        /*
         * For the special stateid of other all 0s and seqid == 1, set the
         * stateid to the current stateid, if it is set.
         */
        if (stateid.seqid == 1 && stateid.other[0] == 0 &&
            stateid.other[1] == 0 && stateid.other[2] == 0) {
                if ((nd->nd_flag & ND_CURSTATEID) != 0) {
                        stateid = nd->nd_curstateid;
                        stateid.seqid = 0;
                } else {
                        nd->nd_repstat = NFSERR_BADSTATEID;
                        goto nfsmout;
                }
        }

        hasnewoff = fxdr_unsigned(int, *tl);
        if (hasnewoff != 0) {
                NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + NFSX_UNSIGNED);
                newoff = fxdr_hyper(tl); tl += 2;
        } else
                NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
        hasnewmtime = fxdr_unsigned(int, *tl);
        if (hasnewmtime != 0) {
                NFSM_DISSECT(tl, uint32_t *, NFSX_V4TIME + 2 * NFSX_UNSIGNED);
                fxdr_nfsv4time(tl, &newmtime);
                tl += (NFSX_V4TIME / NFSX_UNSIGNED);
        } else
                NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
        layouttype = fxdr_unsigned(int, *tl++);
        maxcnt = fxdr_unsigned(int, *tl);
        /* There is no limit in the RFC, so use 1000 as a sanity limit. */
        if (maxcnt < 0 || maxcnt > 1000) {
                error = NFSERR_BADXDR;
                goto nfsmout;
        }
        if (maxcnt > 0) {
                layp = malloc(maxcnt + 1, M_TEMP, M_WAITOK);
                error = nfsrv_mtostr(nd, layp, maxcnt);
                if (error != 0)
                        goto nfsmout;
        }
        nd->nd_repstat = nfsrv_layoutcommit(nd, vp, layouttype, hasnewoff,
            newoff, offset, len, hasnewmtime, &newmtime, reclaim, &stateid,
            maxcnt, layp, &hasnewsize, &newsize, nd->nd_cred, p);
        NFSD_DEBUG(4, "nfsrv_layoutcommit stat=%u\n", nd->nd_repstat);
        if (nd->nd_repstat == 0) {
                if (hasnewsize != 0) {
                        NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED + NFSX_HYPER);
                        *tl++ = newnfs_true;
                        txdr_hyper(newsize, tl);
                } else {
                        NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                        *tl = newnfs_false;
                }
        }
nfsmout:
        free(layp, M_TEMP);
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 layoutreturn service
 */
int
nfsrvd_layoutreturn(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        uint32_t *tl, *layp;
        nfsv4stateid_t stateid;
        int error = 0, fnd, kind, layouttype, iomode, maxcnt, reclaim;
        uint64_t offset, len;
        struct thread *p = curthread;

        layp = NULL;
        NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
        reclaim = *tl++;
        layouttype = fxdr_unsigned(int, *tl++);
        iomode = fxdr_unsigned(int, *tl++);
        kind = fxdr_unsigned(int, *tl);
        NFSD_DEBUG(4, "layoutreturn recl=%d ltyp=%d iom=%d kind=%d\n", reclaim,
            layouttype, iomode, kind);
        if (kind == NFSV4LAYOUTRET_FILE) {
                NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_STATEID +
                    NFSX_UNSIGNED);
                offset = fxdr_hyper(tl); tl += 2;
                len = fxdr_hyper(tl); tl += 2;
                stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
                NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
                tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);

                /*
                 * For the special stateid of other all 0s and seqid == 1, set
                 * the stateid to the current stateid, if it is set.
                 */
                if (stateid.seqid == 1 && stateid.other[0] == 0 &&
                    stateid.other[1] == 0 && stateid.other[2] == 0) {
                        if ((nd->nd_flag & ND_CURSTATEID) != 0) {
                                stateid = nd->nd_curstateid;
                                stateid.seqid = 0;
                        } else {
                                nd->nd_repstat = NFSERR_BADSTATEID;
                                goto nfsmout;
                        }
                }

                maxcnt = fxdr_unsigned(int, *tl);
                /*
                 * There is no fixed upper bound defined in the RFCs,
                 * but 128Kbytes should be more than sufficient.
                 */
                if (maxcnt < 0 || maxcnt > 131072)
                        maxcnt = 0;
                if (maxcnt > 0) {
                        layp = malloc(maxcnt + 1, M_TEMP, M_WAITOK);
                        error = nfsrv_mtostr(nd, (char *)layp, maxcnt);
                        if (error != 0)
                                goto nfsmout;
                }
        } else {
                if (reclaim == newnfs_true) {
                        nd->nd_repstat = NFSERR_INVAL;
                        goto nfsmout;
                }
                offset = len = 0;
                maxcnt = 0;
        }
        nd->nd_repstat = nfsrv_layoutreturn(nd, vp, layouttype, iomode,
            offset, len, reclaim, kind, &stateid, maxcnt, layp, &fnd,
            nd->nd_cred, p);
        NFSD_DEBUG(4, "nfsrv_layoutreturn stat=%u fnd=%d\n", nd->nd_repstat,
            fnd);
        if (nd->nd_repstat == 0) {
                NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                if (fnd != 0) {
                        *tl = newnfs_true;
                        NFSM_BUILD(tl, uint32_t *, NFSX_STATEID);
                        *tl++ = txdr_unsigned(stateid.seqid);
                        NFSBCOPY(stateid.other, tl, NFSX_STATEIDOTHER);
                } else
                        *tl = newnfs_false;
        }
nfsmout:
        free(layp, M_TEMP);
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 layout error service
 */
int
nfsrvd_layouterror(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        uint32_t *tl;
        nfsv4stateid_t stateid;
        int cnt, error = 0, i, stat;
        int opnum __unused;
        char devid[NFSX_V4DEVICEID];
        uint64_t offset, len;

        NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_STATEID +
            NFSX_UNSIGNED);
        offset = fxdr_hyper(tl); tl += 2;
        len = fxdr_hyper(tl); tl += 2;
        stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
        NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
        tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
        cnt = fxdr_unsigned(int, *tl);
        NFSD_DEBUG(4, "layouterror off=%ju len=%ju cnt=%d\n", (uintmax_t)offset,
            (uintmax_t)len, cnt);
        /*
         * For the special stateid of other all 0s and seqid == 1, set
         * the stateid to the current stateid, if it is set.
         */
        if (stateid.seqid == 1 && stateid.other[0] == 0 &&
            stateid.other[1] == 0 && stateid.other[2] == 0) {
                if ((nd->nd_flag & ND_CURSTATEID) != 0) {
                        stateid = nd->nd_curstateid;
                        stateid.seqid = 0;
                } else {
                        nd->nd_repstat = NFSERR_BADSTATEID;
                        goto nfsmout;
                }
        }

        /*
         * Ignore offset, len and stateid for now.
         */
        for (i = 0; i < cnt; i++) {
                NFSM_DISSECT(tl, uint32_t *, NFSX_V4DEVICEID + 2 *
                    NFSX_UNSIGNED);
                NFSBCOPY(tl, devid, NFSX_V4DEVICEID);
                tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
                stat = fxdr_unsigned(int, *tl++);
                opnum = fxdr_unsigned(int, *tl);
                NFSD_DEBUG(4, "nfsrvd_layouterr op=%d stat=%d\n", opnum, stat);
                /*
                 * Except for NFSERR_ACCES, NFSERR_STALE and NFSERR_NOSPC
                 * errors, disable the mirror.
                 */
                if (stat != NFSERR_ACCES && stat != NFSERR_STALE &&
                    stat != NFSERR_NOSPC)
                        nfsrv_delds(devid, curthread);

                /* For NFSERR_NOSPC, mark all deviceids and layouts. */
                if (stat == NFSERR_NOSPC)
                        nfsrv_marknospc(devid, true);
        }
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 layout stats service
 */
int
nfsrvd_layoutstats(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        uint32_t *tl;
        nfsv4stateid_t stateid;
        int cnt, error = 0;
        int layouttype __unused;
        char devid[NFSX_V4DEVICEID] __unused;
        uint64_t offset __unused, len __unused, readcount __unused;
        uint64_t readbytes __unused, writecount __unused, writebytes __unused;

        NFSM_DISSECT(tl, uint32_t *, 6 * NFSX_HYPER + NFSX_STATEID +
            NFSX_V4DEVICEID + 2 * NFSX_UNSIGNED);
        offset = fxdr_hyper(tl); tl += 2;
        len = fxdr_hyper(tl); tl += 2;
        stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
        NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
        tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
        readcount = fxdr_hyper(tl); tl += 2;
        readbytes = fxdr_hyper(tl); tl += 2;
        writecount = fxdr_hyper(tl); tl += 2;
        writebytes = fxdr_hyper(tl); tl += 2;
        NFSBCOPY(tl, devid, NFSX_V4DEVICEID);
        tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
        layouttype = fxdr_unsigned(int, *tl++);
        cnt = fxdr_unsigned(int, *tl);
        error = nfsm_advance(nd, NFSM_RNDUP(cnt), -1);
        if (error != 0)
                goto nfsmout;
        NFSD_DEBUG(4, "layoutstats cnt=%d\n", cnt);
        /*
         * For the special stateid of other all 0s and seqid == 1, set
         * the stateid to the current stateid, if it is set.
         */
        if (stateid.seqid == 1 && stateid.other[0] == 0 &&
            stateid.other[1] == 0 && stateid.other[2] == 0) {
                if ((nd->nd_flag & ND_CURSTATEID) != 0) {
                        stateid = nd->nd_curstateid;
                        stateid.seqid = 0;
                } else {
                        nd->nd_repstat = NFSERR_BADSTATEID;
                        goto nfsmout;
                }
        }

        /*
         * No use for the stats for now.
         */
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 io_advise service
 */
int
nfsrvd_ioadvise(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        uint32_t *tl;
        nfsv4stateid_t stateid;
        nfsattrbit_t hints;
        int error = 0, ret;
        off_t offset, len;

        NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + 2 * NFSX_HYPER);
        stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
        NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
        tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
        offset = fxdr_hyper(tl); tl += 2;
        len = fxdr_hyper(tl);
        error = nfsrv_getattrbits(nd, &hints, NULL, NULL);
        if (error != 0)
                goto nfsmout;
        /*
         * For the special stateid of other all 0s and seqid == 1, set
         * the stateid to the current stateid, if it is set.
         */
        if (stateid.seqid == 1 && stateid.other[0] == 0 &&
            stateid.other[1] == 0 && stateid.other[2] == 0) {
                if ((nd->nd_flag & ND_CURSTATEID) != 0) {
                        stateid = nd->nd_curstateid;
                        stateid.seqid = 0;
                } else {
                        nd->nd_repstat = NFSERR_BADSTATEID;
                        goto nfsmout;
                }
        }

        if (offset < 0) {
                nd->nd_repstat = NFSERR_INVAL;
                goto nfsmout;
        }
        if (len < 0)
                len = 0;
        if (vp->v_type != VREG) {
                if (vp->v_type == VDIR)
                        nd->nd_repstat = NFSERR_ISDIR;
                else
                        nd->nd_repstat = NFSERR_WRONGTYPE;
                goto nfsmout;
        }

        /*
         * For now, we can only handle WILLNEED and DONTNEED and don't use
         * the stateid.
         */
        if ((NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED) &&
            !NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_DONTNEED)) ||
            (NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_DONTNEED) &&
            !NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED))) {
                NFSVOPUNLOCK(vp);
                if (NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED)) {
                        ret = VOP_ADVISE(vp, offset, len, POSIX_FADV_WILLNEED);
                        NFSZERO_ATTRBIT(&hints);
                        if (ret == 0)
                                NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED);
                        else
                                NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_NORMAL);
                } else {
                        ret = VOP_ADVISE(vp, offset, len, POSIX_FADV_DONTNEED);
                        NFSZERO_ATTRBIT(&hints);
                        if (ret == 0)
                                NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_DONTNEED);
                        else
                                NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_NORMAL);
                }
                vrele(vp);
        } else {
                NFSZERO_ATTRBIT(&hints);
                NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_NORMAL);
                vput(vp);
        }
        nfsrv_putattrbit(nd, &hints);
        NFSEXITCODE2(error, nd);
        return (error);
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 getdeviceinfo service
 */
int
nfsrvd_getdevinfo(struct nfsrv_descript *nd, __unused int isdgram,
    __unused vnode_t vp, __unused struct nfsexstuff *exp)
{
        uint32_t *tl, maxcnt, notify[NFSV4_NOTIFYBITMAP];
        int cnt, devaddrlen, error = 0, i, layouttype;
        char devid[NFSX_V4DEVICEID], *devaddr;
        time_t dev_time;

        NFSM_DISSECT(tl, uint32_t *, 3 * NFSX_UNSIGNED + NFSX_V4DEVICEID);
        NFSBCOPY(tl, devid, NFSX_V4DEVICEID);
        tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
        layouttype = fxdr_unsigned(int, *tl++);
        maxcnt = fxdr_unsigned(uint32_t, *tl++);
        cnt = fxdr_unsigned(int, *tl);
        NFSD_DEBUG(4, "getdevinfo ltyp=%d maxcnt=%u bitcnt=%d\n", layouttype,
            maxcnt, cnt);
        if (cnt > NFSV4_NOTIFYBITMAP || cnt < 0) {
                nd->nd_repstat = NFSERR_INVAL;
                goto nfsmout;
        }
        if (cnt > 0) {
                NFSM_DISSECT(tl, uint32_t *, cnt * NFSX_UNSIGNED);
                for (i = 0; i < cnt; i++)
                        notify[i] = fxdr_unsigned(uint32_t, *tl++);
        }
        for (i = cnt; i < NFSV4_NOTIFYBITMAP; i++)
                notify[i] = 0;

        /*
         * Check that the device id is not stale.  Device ids are recreated
         * each time the nfsd threads are restarted.
         */
        NFSBCOPY(devid, &dev_time, sizeof(dev_time));
        if (dev_time != nfsdev_time) {
                nd->nd_repstat = NFSERR_NOENT;
                goto nfsmout;
        }

        /* Look for the device id. */
        nd->nd_repstat = nfsrv_getdevinfo(devid, layouttype, &maxcnt,
            notify, &devaddrlen, &devaddr);
        NFSD_DEBUG(4, "nfsrv_getdevinfo stat=%u\n", nd->nd_repstat);
        if (nd->nd_repstat == 0) {
                NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                *tl = txdr_unsigned(layouttype);
                nfsm_strtom(nd, devaddr, devaddrlen);
                cnt = 0;
                for (i = 0; i < NFSV4_NOTIFYBITMAP; i++) {
                        if (notify[i] != 0)
                                cnt = i + 1;
                }
                NFSM_BUILD(tl, uint32_t *, (cnt + 1) * NFSX_UNSIGNED);
                *tl++ = txdr_unsigned(cnt);
                for (i = 0; i < cnt; i++)
                        *tl++ = txdr_unsigned(notify[i]);
        } else if (nd->nd_repstat == NFSERR_TOOSMALL) {
                NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                *tl = txdr_unsigned(maxcnt);
        }
nfsmout:
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfsv4 test stateid service
 */
int
nfsrvd_teststateid(struct nfsrv_descript *nd, __unused int isdgram,
    __unused vnode_t vp, __unused struct nfsexstuff *exp)
{
        uint32_t *tl;
        nfsv4stateid_t *stateidp = NULL, *tstateidp;
        int cnt, error = 0, i, ret;
        struct thread *p = curthread;

        NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
        cnt = fxdr_unsigned(int, *tl);
        if (cnt <= 0 || cnt > 1024) {
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        }
        stateidp = mallocarray(cnt, sizeof(nfsv4stateid_t), M_TEMP, M_WAITOK);
        tstateidp = stateidp;
        for (i = 0; i < cnt; i++) {
                NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
                tstateidp->seqid = fxdr_unsigned(uint32_t, *tl++);
                NFSBCOPY(tl, tstateidp->other, NFSX_STATEIDOTHER);
                tstateidp++;
        }
        NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
        *tl = txdr_unsigned(cnt);
        tstateidp = stateidp;
        for (i = 0; i < cnt; i++) {
                ret = nfsrv_teststateid(nd, tstateidp, p);
                NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                *tl = txdr_unsigned(ret);
                tstateidp++;
        }
nfsmout:
        free(stateidp, M_TEMP);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs allocate service
 */
int
nfsrvd_allocate(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        uint32_t *tl;
        struct nfsvattr forat;
        int error = 0, forat_ret = 1, gotproxystateid;
        off_t off, len;
        struct nfsstate st, *stp = &st;
        struct nfslock lo, *lop = &lo;
        nfsv4stateid_t stateid;
        nfsquad_t clientid;
        nfsattrbit_t attrbits;

        if (!nfsrv_doallocate) {
                /*
                 * If any exported file system, such as a ZFS one, cannot
                 * do VOP_ALLOCATE(), this operation cannot be supported
                 * for NFSv4.2.  This cannot be done 'per filesystem', but
                 * must be for the entire nfsd NFSv4.2 service.
                 */
                nd->nd_repstat = NFSERR_NOTSUPP;
                goto nfsmout;
        }
        gotproxystateid = 0;
        NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + 2 * NFSX_HYPER);
        stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
        lop->lo_flags = NFSLCK_WRITE;
        stp->ls_ownerlen = 0;
        stp->ls_op = NULL;
        stp->ls_uid = nd->nd_cred->cr_uid;
        stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
        clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
        clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
        if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        clientid.qval = nd->nd_clientid.qval;
                else if (nd->nd_clientid.qval != clientid.qval)
                        printf("EEK2 multiple clids\n");
        } else {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        printf("EEK! no clientid from session\n");
                nd->nd_flag |= ND_IMPLIEDCLID;
                nd->nd_clientid.qval = clientid.qval;
        }
        stp->ls_stateid.other[2] = *tl++;
        /*
         * Don't allow this to be done for a DS.
         */
        if ((nd->nd_flag & ND_DSSERVER) != 0)
                nd->nd_repstat = NFSERR_NOTSUPP;
        /* However, allow the proxy stateid. */
        if (stp->ls_stateid.seqid == 0xffffffff &&
            stp->ls_stateid.other[0] == 0x55555555 &&
            stp->ls_stateid.other[1] == 0x55555555 &&
            stp->ls_stateid.other[2] == 0x55555555)
                gotproxystateid = 1;
        off = fxdr_hyper(tl); tl += 2;
        lop->lo_first = off;
        len = fxdr_hyper(tl);
        lop->lo_end = lop->lo_first + len;
        /*
         * Sanity check the offset and length.
         * off and len are off_t (signed int64_t) whereas
         * lo_first and lo_end are uint64_t and, as such,
         * if off >= 0 && len > 0, lo_end cannot overflow
         * unless off_t is changed to something other than
         * int64_t.  Check lo_end < lo_first in case that
         * is someday the case.
         */
        if (nd->nd_repstat == 0 && (len <= 0 || off < 0 || lop->lo_end >
            OFF_MAX || lop->lo_end < lop->lo_first))
                nd->nd_repstat = NFSERR_INVAL;

        if (nd->nd_repstat == 0 && vp->v_type != VREG)
                nd->nd_repstat = NFSERR_WRONGTYPE;
        NFSZERO_ATTRBIT(&attrbits);
        NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
        forat_ret = nfsvno_getattr(vp, &forat, nd, curthread, 1, &attrbits);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = forat_ret;
        if (nd->nd_repstat == 0 && (forat.na_uid != nd->nd_cred->cr_uid ||
             NFSVNO_EXSTRICTACCESS(exp)))
                nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp,
                    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
                    NULL);
        if (nd->nd_repstat == 0 && gotproxystateid == 0)
                nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
                    &stateid, exp, nd, curthread);

        NFSD_DEBUG(4, "nfsrvd_allocate: off=%jd len=%jd stat=%d\n",
            (intmax_t)off, (intmax_t)len, nd->nd_repstat);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = nfsvno_allocate(vp, off, len, nd->nd_cred,
                    curthread);
        NFSD_DEBUG(4, "nfsrvd_allocate: aft nfsvno_allocate=%d\n",
            nd->nd_repstat);
        vput(vp);
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs deallocate service
 */
int
nfsrvd_deallocate(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        uint32_t *tl;
        struct nfsvattr forat;
        int error = 0, forat_ret = 1, gotproxystateid;
        off_t off, len;
        struct nfsstate st, *stp = &st;
        struct nfslock lo, *lop = &lo;
        nfsv4stateid_t stateid;
        nfsquad_t clientid;
        nfsattrbit_t attrbits;

        gotproxystateid = 0;
        NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + 2 * NFSX_HYPER);
        stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
        lop->lo_flags = NFSLCK_WRITE;
        stp->ls_ownerlen = 0;
        stp->ls_op = NULL;
        stp->ls_uid = nd->nd_cred->cr_uid;
        stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
        clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
        clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
        if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        clientid.qval = nd->nd_clientid.qval;
                else if (nd->nd_clientid.qval != clientid.qval)
                        printf("EEK2 multiple clids\n");
        } else {
                if ((nd->nd_flag & ND_NFSV41) != 0)
                        printf("EEK! no clientid from session\n");
                nd->nd_flag |= ND_IMPLIEDCLID;
                nd->nd_clientid.qval = clientid.qval;
        }
        stp->ls_stateid.other[2] = *tl++;
        /*
         * Don't allow this to be done for a DS.
         */
        if ((nd->nd_flag & ND_DSSERVER) != 0)
                nd->nd_repstat = NFSERR_NOTSUPP;
        /* However, allow the proxy stateid. */
        if (stp->ls_stateid.seqid == 0xffffffff &&
            stp->ls_stateid.other[0] == 0x55555555 &&
            stp->ls_stateid.other[1] == 0x55555555 &&
            stp->ls_stateid.other[2] == 0x55555555)
                gotproxystateid = 1;
        off = fxdr_hyper(tl); tl += 2;
        lop->lo_first = off;
        len = fxdr_hyper(tl);
        if (len < 0)
                len = OFF_MAX;
        NFSD_DEBUG(4, "dealloc: off=%jd len=%jd\n", (intmax_t)off,
            (intmax_t)len);
        lop->lo_end = lop->lo_first + len;
        /*
         * Sanity check the offset and length.
         * off and len are off_t (signed int64_t) whereas
         * lo_first and lo_end are uint64_t and, as such,
         * if off >= 0 && len > 0, lo_end cannot overflow
         * unless off_t is changed to something other than
         * int64_t.  Check lo_end < lo_first in case that
         * is someday the case.
         * The error to return is not specified by RFC 7862 so I
         * made this compatible with the Linux knfsd.
         */
        if (nd->nd_repstat == 0) {
                if (off < 0 || lop->lo_end > NFSRV_MAXFILESIZE)
                        nd->nd_repstat = NFSERR_FBIG;
                else if (len == 0 || lop->lo_end < lop->lo_first)
                        nd->nd_repstat = NFSERR_INVAL;
        }

        if (nd->nd_repstat == 0 && vp->v_type != VREG)
                nd->nd_repstat = NFSERR_WRONGTYPE;
        NFSZERO_ATTRBIT(&attrbits);
        NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
        forat_ret = nfsvno_getattr(vp, &forat, nd, curthread, 1, &attrbits);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = forat_ret;
        if (nd->nd_repstat == 0 && (forat.na_uid != nd->nd_cred->cr_uid ||
             NFSVNO_EXSTRICTACCESS(exp)))
                nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp,
                    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
                    NULL);
        if (nd->nd_repstat == 0 && gotproxystateid == 0)
                nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
                    &stateid, exp, nd, curthread);

        if (nd->nd_repstat == 0)
                nd->nd_repstat = nfsvno_deallocate(vp, off, len, nd->nd_cred,
                    curthread);
        vput(vp);
        NFSD_DEBUG(4, "eo deallocate=%d\n", nd->nd_repstat);
        NFSEXITCODE2(0, nd);
        return (0);
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs copy service
 */
int
nfsrvd_copy_file_range(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, vnode_t tovp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
{
        uint32_t *tl;
        struct nfsvattr at;
        int cnt, error = 0, ret;
        off_t inoff, outoff;
        uint64_t len;
        size_t xfer;
        struct nfsstate inst, outst, *instp = &inst, *outstp = &outst;
        struct nfslock inlo, outlo, *inlop = &inlo, *outlop = &outlo;
        nfsquad_t clientid;
        nfsv4stateid_t stateid;
        nfsattrbit_t attrbits;
        void *rl_rcookie, *rl_wcookie;

        rl_rcookie = rl_wcookie = NULL;
        if (nfsrv_maxcopyrange == 0 || nfsrv_devidcnt > 0) {
                /*
                 * For a pNFS server, reply NFSERR_NOTSUPP so that the client
                 * will do the copy via I/O on the DS(s).
                 * If vfs.nfsd.maxcopyrange set to 0, disable Copy.
                 */
                nd->nd_repstat = NFSERR_NOTSUPP;
                goto nfsmout;
        }
        if (vp == tovp) {
                /* Copying a byte range within the same file is not allowed. */
                nd->nd_repstat = NFSERR_INVAL;
                goto nfsmout;
        }
        NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_STATEID + 3 * NFSX_HYPER +
            3 * NFSX_UNSIGNED);
        instp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
        inlop->lo_flags = NFSLCK_READ;
        instp->ls_ownerlen = 0;
        instp->ls_op = NULL;
        instp->ls_uid = nd->nd_cred->cr_uid;
        instp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
        clientid.lval[0] = instp->ls_stateid.other[0] = *tl++;
        clientid.lval[1] = instp->ls_stateid.other[1] = *tl++;
        if ((nd->nd_flag & ND_IMPLIEDCLID) != 0)
                clientid.qval = nd->nd_clientid.qval;
        instp->ls_stateid.other[2] = *tl++;
        outstp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
        outlop->lo_flags = NFSLCK_WRITE;
        outstp->ls_ownerlen = 0;
        outstp->ls_op = NULL;
        outstp->ls_uid = nd->nd_cred->cr_uid;
        outstp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
        outstp->ls_stateid.other[0] = *tl++;
        outstp->ls_stateid.other[1] = *tl++;
        outstp->ls_stateid.other[2] = *tl++;
        inoff = fxdr_hyper(tl); tl += 2;
        inlop->lo_first = inoff;
        outoff = fxdr_hyper(tl); tl += 2;
        outlop->lo_first = outoff;
        len = fxdr_hyper(tl); tl += 2;
        if (len == 0) {
                /* len == 0 means to EOF. */
                inlop->lo_end = OFF_MAX;
                outlop->lo_end = OFF_MAX;
        } else {
                inlop->lo_end = inlop->lo_first + len;
                outlop->lo_end = outlop->lo_first + len;
        }

        /*
         * At this time only consecutive, synchronous copy is supported,
         * so ca_consecutive and ca_synchronous can be ignored.
         */
        tl += 2;

        cnt = fxdr_unsigned(int, *tl);
        if ((nd->nd_flag & ND_DSSERVER) != 0 || cnt != 0)
                nd->nd_repstat = NFSERR_NOTSUPP;
        if (nd->nd_repstat == 0 && (inoff > OFF_MAX || outoff > OFF_MAX ||
            inlop->lo_end > OFF_MAX || outlop->lo_end > OFF_MAX ||
            inlop->lo_end < inlop->lo_first || outlop->lo_end <
            outlop->lo_first))
                nd->nd_repstat = NFSERR_INVAL;

        if (nd->nd_repstat == 0 && vp->v_type != VREG)
                nd->nd_repstat = NFSERR_WRONGTYPE;

        /* Check permissions for the input file. */
        NFSZERO_ATTRBIT(&attrbits);
        NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
        ret = nfsvno_getattr(vp, &at, nd, curthread, 1, &attrbits);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = ret;
        if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
             NFSVNO_EXSTRICTACCESS(exp)))
                nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred, exp,
                    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
                    NULL);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = nfsrv_lockctrl(vp, &instp, &inlop, NULL,
                    clientid, &stateid, exp, nd, curthread);
        NFSVOPUNLOCK(vp);
        if (nd->nd_repstat != 0)
                goto out;

        error = NFSVOPLOCK(tovp, LK_SHARED);
        if (error != 0)
                goto out;
        if (tovp->v_type != VREG)
                nd->nd_repstat = NFSERR_WRONGTYPE;

        /* For the output file, we only need the Owner attribute. */
        ret = nfsvno_getattr(tovp, &at, nd, curthread, 1, &attrbits);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = ret;
        if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
             NFSVNO_EXSTRICTACCESS(exp)))
                nd->nd_repstat = nfsvno_accchk(tovp, VWRITE, nd->nd_cred, toexp,
                    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
                    NULL);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = nfsrv_lockctrl(tovp, &outstp, &outlop, NULL,
                    clientid, &stateid, toexp, nd, curthread);
        NFSVOPUNLOCK(tovp);

        /* Range lock the byte ranges for both invp and outvp. */
        if (nd->nd_repstat == 0) {
                for (;;) {
                        if (len == 0) {
                                rl_wcookie = vn_rangelock_wlock(tovp, outoff,
                                    OFF_MAX);
                                rl_rcookie = vn_rangelock_tryrlock(vp, inoff,
                                    OFF_MAX);
                        } else {
                                rl_wcookie = vn_rangelock_wlock(tovp, outoff,
                                    outoff + len);
                                rl_rcookie = vn_rangelock_tryrlock(vp, inoff,
                                    inoff + len);
                        }
                        if (rl_rcookie != NULL)
                                break;
                        vn_rangelock_unlock(tovp, rl_wcookie);
                        if (len == 0)
                                rl_rcookie = vn_rangelock_rlock(vp, inoff,
                                    OFF_MAX);
                        else
                                rl_rcookie = vn_rangelock_rlock(vp, inoff,
                                    inoff + len);
                        vn_rangelock_unlock(vp, rl_rcookie);
                }

                error = NFSVOPLOCK(vp, LK_SHARED);
                if (error == 0) {
                        ret = nfsvno_getattr(vp, &at, nd, curthread, 1, NULL);
                        if (ret == 0) {
                                /*
                                 * Since invp is range locked, na_size should
                                 * not change.
                                 */
                                if (len == 0 && at.na_size > inoff) {
                                        /*
                                         * If len == 0, set it based on invp's 
                                         * size. If offset is past EOF, just
                                         * leave len == 0.
                                         */
                                        len = at.na_size - inoff;
                                } else if (nfsrv_linux42server == 0 &&
                                    inoff + len > at.na_size) {
                                        /*
                                         * RFC-7862 says that NFSERR_INVAL must
                                         * be returned when inoff + len exceeds
                                         * the file size, however the NFSv4.2
                                         * Linux client likes to do this, so
                                         * only check if nfsrv_linux42server
                                         * is not set.
                                         */
                                        nd->nd_repstat = NFSERR_INVAL;
                                }
                        }
                        NFSVOPUNLOCK(vp);
                        if (ret != 0 && nd->nd_repstat == 0)
                                nd->nd_repstat = ret;
                } else if (nd->nd_repstat == 0)
                        nd->nd_repstat = error;
        }

        /*
         * Do the actual copy to an upper limit of vfs.nfsd.maxcopyrange.
         * This size limit can be set to limit the time a copy RPC will
         * take.
         */
        if (len > nfsrv_maxcopyrange)
                xfer = nfsrv_maxcopyrange;
        else
                xfer = len;
        if (nd->nd_repstat == 0) {
                nd->nd_repstat = vn_copy_file_range(vp, &inoff, tovp, &outoff,
                    &xfer, COPY_FILE_RANGE_TIMEO1SEC, nd->nd_cred, nd->nd_cred,
                    NULL);
                if (nd->nd_repstat == 0)
                        len = xfer;
        }

        /* Unlock the ranges. */
        if (rl_rcookie != NULL)
                vn_rangelock_unlock(vp, rl_rcookie);
        if (rl_wcookie != NULL)
                vn_rangelock_unlock(tovp, rl_wcookie);

        if (nd->nd_repstat == 0) {
                NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED + NFSX_HYPER +
                    NFSX_VERF);
                *tl++ = txdr_unsigned(0);       /* No callback ids. */
                txdr_hyper(len, tl); tl += 2;
                *tl++ = txdr_unsigned(NFSWRITE_UNSTABLE);
                *tl++ = txdr_unsigned(nfsboottime.tv_sec);
                *tl++ = txdr_unsigned(nfsboottime.tv_usec);
                *tl++ = newnfs_true;
                *tl = newnfs_true;
        }
out:
        vrele(vp);
        vrele(tovp);
        NFSEXITCODE2(error, nd);
        return (error);
nfsmout:
        vput(vp);
        vrele(tovp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs clone service
 */
int
nfsrvd_clone(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, vnode_t tovp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
{
        uint32_t *tl;
        struct nfsvattr at;
        int error = 0, ret;
        off_t inoff, outoff;
        uint64_t len;
        size_t xfer;
        struct nfsstate inst, outst, *instp = &inst, *outstp = &outst;
        struct nfslock inlo, outlo, *inlop = &inlo, *outlop = &outlo;
        nfsquad_t clientid;
        nfsv4stateid_t stateid;
        nfsattrbit_t attrbits;
        void *rl_rcookie, *rl_wcookie;
        long pathval;

        rl_rcookie = rl_wcookie = NULL;
        pathval = 0;
        if (nfsrv_maxcopyrange == 0 || nfsrv_devidcnt > 0 ||
            VOP_PATHCONF(vp, _PC_CLONE_BLKSIZE, &pathval) != 0 ||
            pathval == 0) {
                /*
                 * For a pNFS server, reply NFSERR_NOTSUPP so that the client
                 * will not do the clone and will do I/O on the DS(s).
                 * If vfs.nfsd.maxcopyrange set to 0, disable Clone.
                 */
                nd->nd_repstat = NFSERR_NOTSUPP;
                goto nfsmout;
        }
        NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_STATEID + 3 * NFSX_HYPER);
        instp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
        inlop->lo_flags = NFSLCK_READ;
        instp->ls_ownerlen = 0;
        instp->ls_op = NULL;
        instp->ls_uid = nd->nd_cred->cr_uid;
        instp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
        clientid.lval[0] = instp->ls_stateid.other[0] = *tl++;
        clientid.lval[1] = instp->ls_stateid.other[1] = *tl++;
        if ((nd->nd_flag & ND_IMPLIEDCLID) != 0)
                clientid.qval = nd->nd_clientid.qval;
        instp->ls_stateid.other[2] = *tl++;
        outstp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
        outlop->lo_flags = NFSLCK_WRITE;
        outstp->ls_ownerlen = 0;
        outstp->ls_op = NULL;
        outstp->ls_uid = nd->nd_cred->cr_uid;
        outstp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
        outstp->ls_stateid.other[0] = *tl++;
        outstp->ls_stateid.other[1] = *tl++;
        outstp->ls_stateid.other[2] = *tl++;
        inoff = fxdr_hyper(tl); tl += 2;
        inlop->lo_first = inoff;
        outoff = fxdr_hyper(tl); tl += 2;
        outlop->lo_first = outoff;
        len = fxdr_hyper(tl);
        if (len == 0) {
                /* len == 0 means to EOF. */
                inlop->lo_end = OFF_MAX;
                outlop->lo_end = OFF_MAX;
        } else {
                inlop->lo_end = inlop->lo_first + len;
                outlop->lo_end = outlop->lo_first + len;
        }

        if ((inoff > OFF_MAX || outoff > OFF_MAX ||
            inlop->lo_end > OFF_MAX || outlop->lo_end > OFF_MAX ||
            inlop->lo_end < inlop->lo_first || outlop->lo_end <
            outlop->lo_first))
                nd->nd_repstat = NFSERR_INVAL;

        if (nd->nd_repstat == 0 && vp->v_type != VREG)
                nd->nd_repstat = NFSERR_WRONGTYPE;

        /* Check permissions for the input file. */
        NFSZERO_ATTRBIT(&attrbits);
        NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
        ret = nfsvno_getattr(vp, &at, nd, curthread, 1, &attrbits);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = ret;
        if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
             NFSVNO_EXSTRICTACCESS(exp)))
                nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred, exp,
                    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
                    NULL);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = nfsrv_lockctrl(vp, &instp, &inlop, NULL,
                    clientid, &stateid, exp, nd, curthread);
        if (vp != tovp) {
                NFSVOPUNLOCK(vp);
                if (nd->nd_repstat != 0)
                        goto out;

                error = NFSVOPLOCK(tovp, LK_SHARED);
                if (error != 0)
                        goto out;
                pathval = 0;
                if (VOP_PATHCONF(tovp, _PC_CLONE_BLKSIZE, &pathval) != 0 ||
                    pathval == 0)
                        nd->nd_repstat = NFSERR_NOTSUPP;
                else if (tovp->v_type != VREG)
                        nd->nd_repstat = NFSERR_WRONGTYPE;
        }

        /* For the output file, we only need the Owner attribute. */
        ret = nfsvno_getattr(tovp, &at, nd, curthread, 1, &attrbits);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = ret;
        if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
             NFSVNO_EXSTRICTACCESS(exp)))
                nd->nd_repstat = nfsvno_accchk(tovp, VWRITE, nd->nd_cred, toexp,
                    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
                    NULL);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = nfsrv_lockctrl(tovp, &outstp, &outlop, NULL,
                    clientid, &stateid, toexp, nd, curthread);
        NFSVOPUNLOCK(tovp);

        /* Range lock the byte ranges for both invp and outvp. */
        if (nd->nd_repstat == 0) {
                for (;;) {
                        if (len == 0)
                                rl_wcookie = vn_rangelock_wlock(tovp, outoff,
                                    OFF_MAX);
                        else
                                rl_wcookie = vn_rangelock_wlock(tovp, outoff,
                                    outoff + len);
                        if (vp != tovp) {
                                if (len == 0)
                                        rl_rcookie = vn_rangelock_tryrlock(vp,
                                            inoff, OFF_MAX);
                                else
                                        rl_rcookie = vn_rangelock_tryrlock(vp,
                                            inoff, inoff + len);
                                if (rl_rcookie != NULL)
                                        break;
                        } else {
                                rl_rcookie = NULL;
                                break;
                        }
                        vn_rangelock_unlock(tovp, rl_wcookie);
                        if (len == 0)
                                rl_rcookie = vn_rangelock_rlock(vp, inoff,
                                    OFF_MAX);
                        else
                                rl_rcookie = vn_rangelock_rlock(vp, inoff,
                                    inoff + len);
                        vn_rangelock_unlock(vp, rl_rcookie);
                }

                error = NFSVOPLOCK(vp, LK_SHARED);
                if (error == 0) {
                        ret = nfsvno_getattr(vp, &at, nd, curthread, 1, NULL);
                        if (ret == 0) {
                                /*
                                 * Since invp is range locked, na_size should
                                 * not change.
                                 */
                                if (len == 0 && at.na_size > inoff)
                                        len = SSIZE_MAX;        /* To EOF. */
                                else if (inoff + len > at.na_size)
                                        nd->nd_repstat = NFSERR_INVAL;
                        }
                        NFSVOPUNLOCK(vp);
                        if (ret != 0 && nd->nd_repstat == 0)
                                nd->nd_repstat = ret;
                } else if (nd->nd_repstat == 0)
                        nd->nd_repstat = error;
        }

        /*
         * Do the actual copy to an upper limit of vfs.nfsd.maxcopyrange.
         * This size limit can be set to limit the time a copy RPC will
         * take.
         */
        xfer = len;
        if (nd->nd_repstat == 0) {
                nd->nd_repstat = vn_copy_file_range(vp, &inoff, tovp, &outoff,
                    &xfer, COPY_FILE_RANGE_CLONE, nd->nd_cred, nd->nd_cred,
                    NULL);
                if (nd->nd_repstat == ENOSYS)
                        nd->nd_repstat = NFSERR_INVAL;
        }

        /* Unlock the ranges. */
        if (rl_rcookie != NULL)
                vn_rangelock_unlock(vp, rl_rcookie);
        if (rl_wcookie != NULL)
                vn_rangelock_unlock(tovp, rl_wcookie);

out:
        vrele(vp);
        vrele(tovp);
        NFSEXITCODE2(error, nd);
        return (error);
nfsmout:
        vput(vp);
        vrele(tovp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs seek service
 */
int
nfsrvd_seek(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, struct nfsexstuff *exp)
{
        uint32_t *tl;
        struct nfsvattr at;
        int content, error = 0;
        off_t off;
        u_long cmd;
        nfsattrbit_t attrbits;
        bool eof;

        NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + NFSX_HYPER + NFSX_UNSIGNED);
        /* Ignore the stateid for now. */
        tl += (NFSX_STATEID / NFSX_UNSIGNED);
        off = fxdr_hyper(tl); tl += 2;
        content = fxdr_unsigned(int, *tl);
        if (content == NFSV4CONTENT_DATA)
                cmd = FIOSEEKDATA;
        else if (content == NFSV4CONTENT_HOLE)
                cmd = FIOSEEKHOLE;
        else
                nd->nd_repstat = NFSERR_BADXDR;
        if (nd->nd_repstat == 0 && vp->v_type == VDIR)
                nd->nd_repstat = NFSERR_ISDIR;
        if (nd->nd_repstat == 0 && vp->v_type != VREG)
                nd->nd_repstat = NFSERR_WRONGTYPE;
        if (nd->nd_repstat == 0 && off < 0)
                nd->nd_repstat = NFSERR_NXIO;
        if (nd->nd_repstat == 0) {
                /* Check permissions for the input file. */
                NFSZERO_ATTRBIT(&attrbits);
                NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
                nd->nd_repstat = nfsvno_getattr(vp, &at, nd, curthread, 1,
                    &attrbits);
        }
        if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
             NFSVNO_EXSTRICTACCESS(exp)))
                nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred, exp,
                    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
                    NULL);
        if (nd->nd_repstat != 0)
                goto nfsmout;

        /* nfsvno_seek() unlocks and vrele()s the vp. */
        nd->nd_repstat = nfsvno_seek(nd, vp, cmd, &off, content, &eof,
            nd->nd_cred, curthread);
        if (nd->nd_repstat == 0 && eof && content == NFSV4CONTENT_DATA &&
            nfsrv_linux42server != 0)
                nd->nd_repstat = NFSERR_NXIO;
        if (nd->nd_repstat == 0) {
                NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED + NFSX_HYPER);
                if (eof)
                        *tl++ = newnfs_true;
                else
                        *tl++ = newnfs_false;
                txdr_hyper(off, tl);
        }
        NFSEXITCODE2(error, nd);
        return (error);
nfsmout:
        vput(vp);
        NFSEXITCODE2(error, nd);
        return (error);
}

/*
 * nfs get extended attribute service
 */
int
nfsrvd_getxattr(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        uint32_t *tl;
        struct mbuf *mp = NULL, *mpend = NULL;
        int error, len;
        char *name;
        struct thread *p = curthread;
        uint16_t off;

        error = 0;
        NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
        len = fxdr_unsigned(int, *tl);
        if (len <= 0) {
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        }
        if (len > EXTATTR_MAXNAMELEN) {
                nd->nd_repstat = NFSERR_NOXATTR;
                goto nfsmout;
        }
        name = malloc(len + 1, M_TEMP, M_WAITOK);
        nd->nd_repstat = nfsrv_mtostr(nd, name, len);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = nfsvno_getxattr(vp, name,
                    nd->nd_maxresp, nd->nd_cred, nd->nd_flag,
                    nd->nd_maxextsiz, p, &mp, &mpend, &len);
        if (nd->nd_repstat == ENOATTR)
                nd->nd_repstat = NFSERR_NOXATTR;
        else if (nd->nd_repstat == EOPNOTSUPP)
                nd->nd_repstat = NFSERR_NOTSUPP;
        if (nd->nd_repstat == 0) {
                NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                *tl = txdr_unsigned(len);
                if (len > 0) {
                        nd->nd_mb->m_next = mp;
                        nd->nd_mb = mpend;
                        if ((mpend->m_flags & M_EXTPG) != 0) {
                                nd->nd_flag |= ND_EXTPG;
                                nd->nd_bextpg = mpend->m_epg_npgs - 1;
                                nd->nd_bpos = (char *)(void *)
                                   PHYS_TO_DMAP(mpend->m_epg_pa[nd->nd_bextpg]);
                                off = (nd->nd_bextpg == 0) ?
                                    mpend->m_epg_1st_off : 0;
                                nd->nd_bpos += off + mpend->m_epg_last_len;
                                nd->nd_bextpgsiz = PAGE_SIZE -
                                    mpend->m_epg_last_len - off;
                        } else
                                nd->nd_bpos = mtod(mpend, char *) +
                                    mpend->m_len;
                }
        }
        free(name, M_TEMP);

nfsmout:
        if (nd->nd_repstat == 0)
                nd->nd_repstat = error;
        vput(vp);
        NFSEXITCODE2(0, nd);
        return (0);
}

/*
 * nfs set extended attribute service
 */
int
nfsrvd_setxattr(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        uint32_t *tl;
        struct nfsvattr ova, nva;
        nfsattrbit_t attrbits;
        int error, len, opt;
        char *name;
        size_t siz;
        struct thread *p = curthread;

        error = 0;
        name = NULL;
        NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
        opt = fxdr_unsigned(int, *tl++);
        len = fxdr_unsigned(int, *tl);
        if (len <= 0) {
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        }
        if (len > EXTATTR_MAXNAMELEN) {
                nd->nd_repstat = NFSERR_NOXATTR;
                goto nfsmout;
        }
        name = malloc(len + 1, M_TEMP, M_WAITOK);
        error = nfsrv_mtostr(nd, name, len);
        if (error != 0)
                goto nfsmout;
        NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
        len = fxdr_unsigned(int, *tl);
        if (len < 0 || len > IOSIZE_MAX) {
                nd->nd_repstat = NFSERR_XATTR2BIG;
                goto nfsmout;
        }
        switch (opt) {
        case NFSV4SXATTR_CREATE:
                error = VOP_GETEXTATTR(vp, EXTATTR_NAMESPACE_USER, name, NULL,
                    &siz, nd->nd_cred, p);
                if (error != ENOATTR)
                        nd->nd_repstat = NFSERR_EXIST;
                error = 0;
                break;
        case NFSV4SXATTR_REPLACE:
                error = VOP_GETEXTATTR(vp, EXTATTR_NAMESPACE_USER, name, NULL,
                    &siz, nd->nd_cred, p);
                if (error != 0)
                        nd->nd_repstat = NFSERR_NOXATTR;
                break;
        case NFSV4SXATTR_EITHER:
                break;
        default:
                nd->nd_repstat = NFSERR_BADXDR;
        }
        if (nd->nd_repstat != 0)
                goto nfsmout;

        /* Now, do the Set Extended attribute, with Change before and after. */
        NFSZERO_ATTRBIT(&attrbits);
        NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
        nd->nd_repstat = nfsvno_getattr(vp, &ova, nd, p, 1, &attrbits);
        if (nd->nd_repstat == 0) {
                nd->nd_repstat = nfsvno_setxattr(vp, name, len, nd->nd_md,
                    nd->nd_dpos, nd->nd_cred, p);
                if (nd->nd_repstat == ENXIO)
                        nd->nd_repstat = NFSERR_XATTR2BIG;
        }
        if (nd->nd_repstat == 0 && len > 0)
                nd->nd_repstat = nfsm_advance(nd, NFSM_RNDUP(len), -1);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, &attrbits);
        if (nd->nd_repstat == 0) {
                NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_UNSIGNED);
                *tl++ = newnfs_true;
                txdr_hyper(ova.na_filerev, tl); tl += 2;
                txdr_hyper(nva.na_filerev, tl);
        }

nfsmout:
        free(name, M_TEMP);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = error;
        vput(vp);
        NFSEXITCODE2(0, nd);
        return (0);
}

/*
 * nfs remove extended attribute service
 */
int
nfsrvd_rmxattr(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        uint32_t *tl;
        struct nfsvattr ova, nva;
        nfsattrbit_t attrbits;
        int error, len;
        char *name;
        struct thread *p = curthread;

        error = 0;
        name = NULL;
        NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
        len = fxdr_unsigned(int, *tl);
        if (len <= 0) {
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        }
        if (len > EXTATTR_MAXNAMELEN) {
                nd->nd_repstat = NFSERR_NOXATTR;
                goto nfsmout;
        }
        name = malloc(len + 1, M_TEMP, M_WAITOK);
        error = nfsrv_mtostr(nd, name, len);
        if (error != 0)
                goto nfsmout;

        if ((nd->nd_flag & ND_IMPLIEDCLID) == 0) {
                printf("EEK! nfsrvd_rmxattr: no implied clientid\n");
                error = NFSERR_NOXATTR;
                goto nfsmout;
        }
        /*
         * Now, do the Remove Extended attribute, with Change before and
         * after.
        */
        NFSZERO_ATTRBIT(&attrbits);
        NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
        nd->nd_repstat = nfsvno_getattr(vp, &ova, nd, p, 1, &attrbits);
        if (nd->nd_repstat == 0) {
                nd->nd_repstat = nfsvno_rmxattr(nd, vp, name, nd->nd_cred, p);
                if (nd->nd_repstat == ENOATTR)
                        nd->nd_repstat = NFSERR_NOXATTR;
        }
        if (nd->nd_repstat == 0)
                nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, &attrbits);
        if (nd->nd_repstat == 0) {
                NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_UNSIGNED);
                *tl++ = newnfs_true;
                txdr_hyper(ova.na_filerev, tl); tl += 2;
                txdr_hyper(nva.na_filerev, tl);
        }

nfsmout:
        free(name, M_TEMP);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = error;
        vput(vp);
        NFSEXITCODE2(0, nd);
        return (0);
}

/*
 * nfs list extended attribute service
 */
int
nfsrvd_listxattr(struct nfsrv_descript *nd, __unused int isdgram,
    vnode_t vp, __unused struct nfsexstuff *exp)
{
        uint32_t cnt, *tl, len, len2, i, pos, retlen;
        int error;
        uint64_t cookie, cookie2;
        u_char *buf;
        bool eof;
        struct thread *p = curthread;

        error = 0;
        buf = NULL;
        NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + NFSX_UNSIGNED);
        /*
         * The cookie doesn't need to be in net byte order, but FreeBSD
         * does so to make it more readable in packet traces.
         */
        cookie = fxdr_hyper(tl); tl += 2;
        len = fxdr_unsigned(uint32_t, *tl);
        if (len == 0 || cookie >= IOSIZE_MAX) {
                nd->nd_repstat = NFSERR_BADXDR;
                goto nfsmout;
        }
        if (len > nd->nd_maxresp - NFS_MAXXDR)
                len = nd->nd_maxresp - NFS_MAXXDR;
        len2 = len;
        nd->nd_repstat = nfsvno_listxattr(vp, cookie, nd->nd_cred, p, &buf,
            &len, &eof);
        if (nd->nd_repstat == EOPNOTSUPP)
                nd->nd_repstat = NFSERR_NOTSUPP;
        if (nd->nd_repstat == 0) {
                cookie2 = cookie + len;
                if (cookie2 < cookie)
                        nd->nd_repstat = NFSERR_BADXDR;
        }
        retlen = NFSX_HYPER + 2 * NFSX_UNSIGNED;
        if (nd->nd_repstat == 0 && len2 < retlen)
                nd->nd_repstat = NFSERR_TOOSMALL;
        if (nd->nd_repstat == 0) {
                /* Now copy the entries out. */
                if (len == 0) {
                        /* The cookie was at eof. */
                        NFSM_BUILD(tl, uint32_t *, NFSX_HYPER + 2 *
                            NFSX_UNSIGNED);
                        txdr_hyper(cookie2, tl); tl += 2;
                        *tl++ = txdr_unsigned(0);
                        *tl = newnfs_true;
                        goto nfsmout;
                }

                /* Sanity check the cookie. */
                for (pos = 0; pos < len; pos += (i + 1)) {
                        if (pos == cookie)
                                break;
                        i = buf[pos];
                }
                if (pos != cookie) {
                        nd->nd_repstat = NFSERR_INVAL;
                        goto nfsmout;
                }

                /* Loop around copying the entrie(s) out. */
                cnt = 0;
                len -= cookie;
                i = buf[pos];
                while (i < len && len2 >= retlen + NFSM_RNDUP(i) +
                    NFSX_UNSIGNED) {
                        if (cnt == 0) {
                                NFSM_BUILD(tl, uint32_t *, NFSX_HYPER +
                                    NFSX_UNSIGNED);
                                txdr_hyper(cookie2, tl); tl += 2;
                        }
                        retlen += nfsm_strtom(nd, &buf[pos + 1], i);
                        len -= (i + 1);
                        pos += (i + 1);
                        i = buf[pos];
                        cnt++;
                }
                /*
                 * eof is set true/false by nfsvno_listxattr(), but if we
                 * can't copy all entries returned by nfsvno_listxattr(),
                 * we are not at eof.
                 */
                if (len > 0)
                        eof = false;
                if (cnt > 0) {
                        /* *tl is set above. */
                        *tl = txdr_unsigned(cnt);
                        NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
                        if (eof)
                                *tl = newnfs_true;
                        else
                                *tl = newnfs_false;
                } else
                        nd->nd_repstat = NFSERR_TOOSMALL;
        }

nfsmout:
        free(buf, M_TEMP);
        if (nd->nd_repstat == 0)
                nd->nd_repstat = error;
        vput(vp);
        NFSEXITCODE2(0, nd);
        return (0);
}

/*
 * nfsv4 service not supported
 */
int
nfsrvd_notsupp(struct nfsrv_descript *nd, __unused int isdgram,
    __unused vnode_t vp, __unused struct nfsexstuff *exp)
{

        nd->nd_repstat = NFSERR_NOTSUPP;
        NFSEXITCODE2(0, nd);
        return (0);
}