xref: /freebsd/sys/fs/nfsserver/nfs_nfsdserv.c (revision 56e9d8e38e7eed84901acddca24170eb352d2ed6)
19ec7b004SRick Macklem /*-
251369649SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni  *
49ec7b004SRick Macklem  * Copyright (c) 1989, 1993
59ec7b004SRick Macklem  *	The Regents of the University of California.  All rights reserved.
69ec7b004SRick Macklem  *
79ec7b004SRick Macklem  * This code is derived from software contributed to Berkeley by
89ec7b004SRick Macklem  * Rick Macklem at The University of Guelph.
99ec7b004SRick Macklem  *
109ec7b004SRick Macklem  * Redistribution and use in source and binary forms, with or without
119ec7b004SRick Macklem  * modification, are permitted provided that the following conditions
129ec7b004SRick Macklem  * are met:
139ec7b004SRick Macklem  * 1. Redistributions of source code must retain the above copyright
149ec7b004SRick Macklem  *    notice, this list of conditions and the following disclaimer.
159ec7b004SRick Macklem  * 2. Redistributions in binary form must reproduce the above copyright
169ec7b004SRick Macklem  *    notice, this list of conditions and the following disclaimer in the
179ec7b004SRick Macklem  *    documentation and/or other materials provided with the distribution.
18fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
199ec7b004SRick Macklem  *    may be used to endorse or promote products derived from this software
209ec7b004SRick Macklem  *    without specific prior written permission.
219ec7b004SRick Macklem  *
229ec7b004SRick Macklem  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
239ec7b004SRick Macklem  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
249ec7b004SRick Macklem  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
259ec7b004SRick Macklem  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
269ec7b004SRick Macklem  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
279ec7b004SRick Macklem  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
289ec7b004SRick Macklem  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
299ec7b004SRick Macklem  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
309ec7b004SRick Macklem  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
319ec7b004SRick Macklem  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
329ec7b004SRick Macklem  * SUCH DAMAGE.
339ec7b004SRick Macklem  *
349ec7b004SRick Macklem  */
359ec7b004SRick Macklem 
369ec7b004SRick Macklem #include <sys/cdefs.h>
379ec7b004SRick Macklem __FBSDID("$FreeBSD$");
389ec7b004SRick Macklem 
39ed2f1001SRick Macklem #include "opt_inet.h"
40ed2f1001SRick Macklem #include "opt_inet6.h"
419ec7b004SRick Macklem /*
429ec7b004SRick Macklem  * nfs version 2, 3 and 4 server calls to vnode ops
439ec7b004SRick Macklem  * - these routines generally have 3 phases
449ec7b004SRick Macklem  *   1 - break down and validate rpc request in mbuf list
459ec7b004SRick Macklem  *   2 - do the vnode ops for the request, usually by calling a nfsvno_XXX()
469ec7b004SRick Macklem  *       function in nfsd_port.c
479ec7b004SRick Macklem  *   3 - build the rpc reply in an mbuf list
489ec7b004SRick Macklem  * For nfsv4, these functions are called for each Op within the Compound RPC.
499ec7b004SRick Macklem  */
509ec7b004SRick Macklem 
519ec7b004SRick Macklem #include <fs/nfs/nfsport.h>
52c057a378SRick Macklem #include <sys/extattr.h>
53c057a378SRick Macklem #include <sys/filio.h>
549ec7b004SRick Macklem 
559ec7b004SRick Macklem /* Global vars */
569ec7b004SRick Macklem extern u_int32_t newnfs_false, newnfs_true;
579ec7b004SRick Macklem extern enum vtype nv34tov_type[8];
589ec7b004SRick Macklem extern struct timeval nfsboottime;
5907c0c166SRick Macklem extern int nfsrv_enable_crossmntpt;
601f54e596SRick Macklem extern int nfsrv_statehashsize;
6190d2dfabSRick Macklem extern int nfsrv_layouthashsize;
6290d2dfabSRick Macklem extern time_t nfsdev_time;
6390d2dfabSRick Macklem extern volatile int nfsrv_devidcnt;
6490d2dfabSRick Macklem extern int nfsd_debuglevel;
6590d2dfabSRick Macklem extern u_long sb_max_adj;
6690d2dfabSRick Macklem extern int nfsrv_pnfsatime;
6790d2dfabSRick Macklem extern int nfsrv_maxpnfsmirror;
68c057a378SRick Macklem extern int nfs_maxcopyrange;
699ec7b004SRick Macklem 
70e4558aacSXin LI static int	nfs_async = 0;
71e4558aacSXin LI SYSCTL_DECL(_vfs_nfsd);
72e4558aacSXin LI SYSCTL_INT(_vfs_nfsd, OID_AUTO, async, CTLFLAG_RW, &nfs_async, 0,
73e4558aacSXin LI     "Tell client that writes were synced even though they were not");
7490d2dfabSRick Macklem extern int	nfsrv_doflexfile;
7590d2dfabSRick Macklem SYSCTL_INT(_vfs_nfsd, OID_AUTO, default_flexfile, CTLFLAG_RW,
7690d2dfabSRick Macklem     &nfsrv_doflexfile, 0, "Make Flex File Layout the default for pNFS");
77c057a378SRick Macklem static int	nfsrv_linux42server = 1;
78c057a378SRick Macklem SYSCTL_INT(_vfs_nfsd, OID_AUTO, linux42server, CTLFLAG_RW,
79c057a378SRick Macklem     &nfsrv_linux42server, 0,
80c057a378SRick Macklem     "Enable Linux style NFSv4.2 server (non-RFC compliant)");
81b0b7d978SRick Macklem static bool	nfsrv_openaccess = true;
82b0b7d978SRick Macklem SYSCTL_BOOL(_vfs_nfsd, OID_AUTO, v4openaccess, CTLFLAG_RW,
83b0b7d978SRick Macklem     &nfsrv_openaccess, 0,
84b0b7d978SRick Macklem     "Enable Linux style NFSv4 Open access check");
85e4558aacSXin LI 
869ec7b004SRick Macklem /*
879ec7b004SRick Macklem  * This list defines the GSS mechanisms supported.
889ec7b004SRick Macklem  * (Don't ask me how you get these strings from the RFC stuff like
899ec7b004SRick Macklem  *  iso(1), org(3)... but someone did it, so I don't need to know.)
909ec7b004SRick Macklem  */
919ec7b004SRick Macklem static struct nfsgss_mechlist nfsgss_mechlist[] = {
929ec7b004SRick Macklem 	{ 9, "\052\206\110\206\367\022\001\002\002", 11 },
939ec7b004SRick Macklem 	{ 0, "", 0 },
949ec7b004SRick Macklem };
959ec7b004SRick Macklem 
969ec7b004SRick Macklem /* local functions */
979ec7b004SRick Macklem static void nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
989ec7b004SRick Macklem     struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
999ec7b004SRick Macklem     vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
1009ec7b004SRick Macklem     int *diraft_retp, nfsattrbit_t *attrbitp,
1019ec7b004SRick Macklem     NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
1029ec7b004SRick Macklem     int pathlen);
1039ec7b004SRick Macklem static void nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
1049ec7b004SRick Macklem     struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
1059ec7b004SRick Macklem     vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
1069ec7b004SRick Macklem     int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
1079ec7b004SRick Macklem     NFSPROC_T *p, struct nfsexstuff *exp);
1089ec7b004SRick Macklem 
1099ec7b004SRick Macklem /*
1109ec7b004SRick Macklem  * nfs access service (not a part of NFS V2)
1119ec7b004SRick Macklem  */
112b9cc3262SRyan Moeller int
1139ec7b004SRick Macklem nfsrvd_access(struct nfsrv_descript *nd, __unused int isdgram,
114af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
1159ec7b004SRick Macklem {
1169ec7b004SRick Macklem 	u_int32_t *tl;
1179ec7b004SRick Macklem 	int getret, error = 0;
1189ec7b004SRick Macklem 	struct nfsvattr nva;
1199ec7b004SRick Macklem 	u_int32_t testmode, nfsmode, supported = 0;
1208da45f2cSRick Macklem 	accmode_t deletebit;
121af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
1229ec7b004SRick Macklem 
1239ec7b004SRick Macklem 	if (nd->nd_repstat) {
1249ec7b004SRick Macklem 		nfsrv_postopattr(nd, 1, &nva);
125a9285ae5SZack Kirsch 		goto out;
1269ec7b004SRick Macklem 	}
1279ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1289ec7b004SRick Macklem 	nfsmode = fxdr_unsigned(u_int32_t, *tl);
1299ec7b004SRick Macklem 	if ((nd->nd_flag & ND_NFSV4) &&
1309ec7b004SRick Macklem 	    (nfsmode & ~(NFSACCESS_READ | NFSACCESS_LOOKUP |
1319ec7b004SRick Macklem 	     NFSACCESS_MODIFY | NFSACCESS_EXTEND | NFSACCESS_DELETE |
132c057a378SRick Macklem 	     NFSACCESS_EXECUTE | NFSACCESS_XAREAD | NFSACCESS_XAWRITE |
133c057a378SRick Macklem 	     NFSACCESS_XALIST))) {
1349ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
1359ec7b004SRick Macklem 		vput(vp);
136a9285ae5SZack Kirsch 		goto out;
1379ec7b004SRick Macklem 	}
1389ec7b004SRick Macklem 	if (nfsmode & NFSACCESS_READ) {
1399ec7b004SRick Macklem 		supported |= NFSACCESS_READ;
1408da45f2cSRick Macklem 		if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
1418da45f2cSRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
1429ec7b004SRick Macklem 			nfsmode &= ~NFSACCESS_READ;
1439ec7b004SRick Macklem 	}
1449ec7b004SRick Macklem 	if (nfsmode & NFSACCESS_MODIFY) {
1459ec7b004SRick Macklem 		supported |= NFSACCESS_MODIFY;
1468da45f2cSRick Macklem 		if (nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp, p,
1478da45f2cSRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
1489ec7b004SRick Macklem 			nfsmode &= ~NFSACCESS_MODIFY;
1499ec7b004SRick Macklem 	}
1509ec7b004SRick Macklem 	if (nfsmode & NFSACCESS_EXTEND) {
1519ec7b004SRick Macklem 		supported |= NFSACCESS_EXTEND;
1528da45f2cSRick Macklem 		if (nfsvno_accchk(vp, VWRITE | VAPPEND, nd->nd_cred, exp, p,
1538da45f2cSRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
1549ec7b004SRick Macklem 			nfsmode &= ~NFSACCESS_EXTEND;
1559ec7b004SRick Macklem 	}
156c057a378SRick Macklem 	if (nfsmode & NFSACCESS_XAREAD) {
157c057a378SRick Macklem 		supported |= NFSACCESS_XAREAD;
158c057a378SRick Macklem 		if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
159c057a378SRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
160c057a378SRick Macklem 			nfsmode &= ~NFSACCESS_XAREAD;
161c057a378SRick Macklem 	}
162c057a378SRick Macklem 	if (nfsmode & NFSACCESS_XAWRITE) {
163c057a378SRick Macklem 		supported |= NFSACCESS_XAWRITE;
164c057a378SRick Macklem 		if (nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp, p,
165c057a378SRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
166c057a378SRick Macklem 			nfsmode &= ~NFSACCESS_XAWRITE;
167c057a378SRick Macklem 	}
168c057a378SRick Macklem 	if (nfsmode & NFSACCESS_XALIST) {
169c057a378SRick Macklem 		supported |= NFSACCESS_XALIST;
170c057a378SRick Macklem 		if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
171c057a378SRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
172c057a378SRick Macklem 			nfsmode &= ~NFSACCESS_XALIST;
173c057a378SRick Macklem 	}
1749ec7b004SRick Macklem 	if (nfsmode & NFSACCESS_DELETE) {
1759ec7b004SRick Macklem 		supported |= NFSACCESS_DELETE;
1768da45f2cSRick Macklem 		if (vp->v_type == VDIR)
1778da45f2cSRick Macklem 			deletebit = VDELETE_CHILD;
1788da45f2cSRick Macklem 		else
1798da45f2cSRick Macklem 			deletebit = VDELETE;
1808da45f2cSRick Macklem 		if (nfsvno_accchk(vp, deletebit, nd->nd_cred, exp, p,
1818da45f2cSRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
1829ec7b004SRick Macklem 			nfsmode &= ~NFSACCESS_DELETE;
1839ec7b004SRick Macklem 	}
1849ec7b004SRick Macklem 	if (vnode_vtype(vp) == VDIR)
1859ec7b004SRick Macklem 		testmode = NFSACCESS_LOOKUP;
1869ec7b004SRick Macklem 	else
1879ec7b004SRick Macklem 		testmode = NFSACCESS_EXECUTE;
1889ec7b004SRick Macklem 	if (nfsmode & testmode) {
1899ec7b004SRick Macklem 		supported |= (nfsmode & testmode);
1908da45f2cSRick Macklem 		if (nfsvno_accchk(vp, VEXEC, nd->nd_cred, exp, p,
1918da45f2cSRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
1929ec7b004SRick Macklem 			nfsmode &= ~testmode;
1939ec7b004SRick Macklem 	}
1949ec7b004SRick Macklem 	nfsmode &= supported;
1959ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
19690d2dfabSRick Macklem 		getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
1979ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &nva);
1989ec7b004SRick Macklem 	}
1999ec7b004SRick Macklem 	vput(vp);
2009ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
2019ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2029ec7b004SRick Macklem 		*tl++ = txdr_unsigned(supported);
2039ec7b004SRick Macklem 	} else
2049ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2059ec7b004SRick Macklem 	*tl = txdr_unsigned(nfsmode);
206a9285ae5SZack Kirsch 
207a9285ae5SZack Kirsch out:
208a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
2099ec7b004SRick Macklem 	return (0);
2109ec7b004SRick Macklem nfsmout:
2119ec7b004SRick Macklem 	vput(vp);
212a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
2139ec7b004SRick Macklem 	return (error);
2149ec7b004SRick Macklem }
2159ec7b004SRick Macklem 
2169ec7b004SRick Macklem /*
2179ec7b004SRick Macklem  * nfs getattr service
2189ec7b004SRick Macklem  */
219b9cc3262SRyan Moeller int
2209ec7b004SRick Macklem nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
221af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
2229ec7b004SRick Macklem {
2239ec7b004SRick Macklem 	struct nfsvattr nva;
2249ec7b004SRick Macklem 	fhandle_t fh;
225a09001a8SRick Macklem 	int at_root = 0, error = 0, supports_nfsv4acls;
2269ec7b004SRick Macklem 	struct nfsreferral *refp;
22753f476caSRick Macklem 	nfsattrbit_t attrbits, tmpbits;
22807c0c166SRick Macklem 	struct mount *mp;
22907c0c166SRick Macklem 	struct vnode *tvp = NULL;
23007c0c166SRick Macklem 	struct vattr va;
23107c0c166SRick Macklem 	uint64_t mounted_on_fileno = 0;
23253f476caSRick Macklem 	accmode_t accmode;
233af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
2349ec7b004SRick Macklem 
2359ec7b004SRick Macklem 	if (nd->nd_repstat)
236a9285ae5SZack Kirsch 		goto out;
2379ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
2389ec7b004SRick Macklem 		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
2399ec7b004SRick Macklem 		if (error) {
2409ec7b004SRick Macklem 			vput(vp);
241a9285ae5SZack Kirsch 			goto out;
2429ec7b004SRick Macklem 		}
2439ec7b004SRick Macklem 
2449ec7b004SRick Macklem 		/*
2459ec7b004SRick Macklem 		 * Check for a referral.
2469ec7b004SRick Macklem 		 */
2479ec7b004SRick Macklem 		refp = nfsv4root_getreferral(vp, NULL, 0);
2489ec7b004SRick Macklem 		if (refp != NULL) {
2499ec7b004SRick Macklem 			(void) nfsrv_putreferralattr(nd, &attrbits, refp, 1,
2509ec7b004SRick Macklem 			    &nd->nd_repstat);
2519ec7b004SRick Macklem 			vput(vp);
252a9285ae5SZack Kirsch 			goto out;
2539ec7b004SRick Macklem 		}
25453f476caSRick Macklem 		if (nd->nd_repstat == 0) {
25553f476caSRick Macklem 			accmode = 0;
25653f476caSRick Macklem 			NFSSET_ATTRBIT(&tmpbits, &attrbits);
257d8a5961fSMarcelo Araujo 
258d8a5961fSMarcelo Araujo 			/*
259d8a5961fSMarcelo Araujo 			 * GETATTR with write-only attr time_access_set and time_modify_set
260d8a5961fSMarcelo Araujo 			 * should return NFS4ERR_INVAL.
261d8a5961fSMarcelo Araujo 			 */
262d8a5961fSMarcelo Araujo 			if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_TIMEACCESSSET) ||
263d8a5961fSMarcelo Araujo 					NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_TIMEMODIFYSET)){
264d8a5961fSMarcelo Araujo 				error = NFSERR_INVAL;
265d8a5961fSMarcelo Araujo 				vput(vp);
266d8a5961fSMarcelo Araujo 				goto out;
267d8a5961fSMarcelo Araujo 			}
26853f476caSRick Macklem 			if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_ACL)) {
26953f476caSRick Macklem 				NFSCLRBIT_ATTRBIT(&tmpbits, NFSATTRBIT_ACL);
27053f476caSRick Macklem 				accmode |= VREAD_ACL;
27153f476caSRick Macklem 			}
27253f476caSRick Macklem 			if (NFSNONZERO_ATTRBIT(&tmpbits))
27353f476caSRick Macklem 				accmode |= VREAD_ATTRIBUTES;
27453f476caSRick Macklem 			if (accmode != 0)
27553f476caSRick Macklem 				nd->nd_repstat = nfsvno_accchk(vp, accmode,
2768da45f2cSRick Macklem 				    nd->nd_cred, exp, p, NFSACCCHK_NOOVERRIDE,
2778da45f2cSRick Macklem 				    NFSACCCHK_VPISLOCKED, NULL);
2789ec7b004SRick Macklem 		}
27953f476caSRick Macklem 	}
2809ec7b004SRick Macklem 	if (!nd->nd_repstat)
28190d2dfabSRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, &attrbits);
2829ec7b004SRick Macklem 	if (!nd->nd_repstat) {
2839ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV4) {
2849ec7b004SRick Macklem 			if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_FILEHANDLE))
2859ec7b004SRick Macklem 				nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
2869ec7b004SRick Macklem 			if (!nd->nd_repstat)
2879ec7b004SRick Macklem 				nd->nd_repstat = nfsrv_checkgetattr(nd, vp,
28890d2dfabSRick Macklem 				    &nva, &attrbits, p);
28907c0c166SRick Macklem 			if (nd->nd_repstat == 0) {
290a09001a8SRick Macklem 				supports_nfsv4acls = nfs_supportsnfsv4acls(vp);
29107c0c166SRick Macklem 				mp = vp->v_mount;
29207c0c166SRick Macklem 				if (nfsrv_enable_crossmntpt != 0 &&
29307c0c166SRick Macklem 				    vp->v_type == VDIR &&
29407c0c166SRick Macklem 				    (vp->v_vflag & VV_ROOT) != 0 &&
29507c0c166SRick Macklem 				    vp != rootvnode) {
29607c0c166SRick Macklem 					tvp = mp->mnt_vnodecovered;
29707c0c166SRick Macklem 					VREF(tvp);
29807c0c166SRick Macklem 					at_root = 1;
29907c0c166SRick Macklem 				} else
30007c0c166SRick Macklem 					at_root = 0;
30107c0c166SRick Macklem 				vfs_ref(mp);
302b249ce48SMateusz Guzik 				NFSVOPUNLOCK(vp);
30307c0c166SRick Macklem 				if (at_root != 0) {
30407c0c166SRick Macklem 					if ((nd->nd_repstat =
30598f234f3SZack Kirsch 					     NFSVOPLOCK(tvp, LK_SHARED)) == 0) {
30607c0c166SRick Macklem 						nd->nd_repstat = VOP_GETATTR(
30707c0c166SRick Macklem 						    tvp, &va, nd->nd_cred);
30807c0c166SRick Macklem 						vput(tvp);
30907c0c166SRick Macklem 					} else
31007c0c166SRick Macklem 						vrele(tvp);
31107c0c166SRick Macklem 					if (nd->nd_repstat == 0)
31207c0c166SRick Macklem 						mounted_on_fileno = (uint64_t)
31307c0c166SRick Macklem 						    va.va_fileid;
31407c0c166SRick Macklem 					else
31507c0c166SRick Macklem 						at_root = 0;
31607c0c166SRick Macklem 				}
31707c0c166SRick Macklem 				if (nd->nd_repstat == 0)
31807c0c166SRick Macklem 					nd->nd_repstat = vfs_busy(mp, 0);
31907c0c166SRick Macklem 				vfs_rel(mp);
32007c0c166SRick Macklem 				if (nd->nd_repstat == 0) {
32107c0c166SRick Macklem 					(void)nfsvno_fillattr(nd, mp, vp, &nva,
32207c0c166SRick Macklem 					    &fh, 0, &attrbits, nd->nd_cred, p,
323a09001a8SRick Macklem 					    isdgram, 1, supports_nfsv4acls,
324a09001a8SRick Macklem 					    at_root, mounted_on_fileno);
32507c0c166SRick Macklem 					vfs_unbusy(mp);
32607c0c166SRick Macklem 				}
3279ec7b004SRick Macklem 				vrele(vp);
32807c0c166SRick Macklem 			} else
32907c0c166SRick Macklem 				vput(vp);
3309ec7b004SRick Macklem 		} else {
3319ec7b004SRick Macklem 			nfsrv_fillattr(nd, &nva);
3329ec7b004SRick Macklem 			vput(vp);
3339ec7b004SRick Macklem 		}
3349ec7b004SRick Macklem 	} else {
3359ec7b004SRick Macklem 		vput(vp);
3369ec7b004SRick Macklem 	}
337a9285ae5SZack Kirsch 
338a9285ae5SZack Kirsch out:
339a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
340a9285ae5SZack Kirsch 	return (error);
3419ec7b004SRick Macklem }
3429ec7b004SRick Macklem 
3439ec7b004SRick Macklem /*
3449ec7b004SRick Macklem  * nfs setattr service
3459ec7b004SRick Macklem  */
346b9cc3262SRyan Moeller int
3479ec7b004SRick Macklem nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
348af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
3499ec7b004SRick Macklem {
3509ec7b004SRick Macklem 	struct nfsvattr nva, nva2;
3519ec7b004SRick Macklem 	u_int32_t *tl;
3529ec7b004SRick Macklem 	int preat_ret = 1, postat_ret = 1, gcheck = 0, error = 0;
35390d2dfabSRick Macklem 	int gotproxystateid;
3549ec7b004SRick Macklem 	struct timespec guard = { 0, 0 };
3559ec7b004SRick Macklem 	nfsattrbit_t attrbits, retbits;
3569ec7b004SRick Macklem 	nfsv4stateid_t stateid;
3579ec7b004SRick Macklem 	NFSACL_T *aclp = NULL;
358af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
3599ec7b004SRick Macklem 
3609ec7b004SRick Macklem 	if (nd->nd_repstat) {
3619ec7b004SRick Macklem 		nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
362a9285ae5SZack Kirsch 		goto out;
3639ec7b004SRick Macklem 	}
3649ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
365c3e22f83SRick Macklem 	aclp = acl_alloc(M_WAITOK);
3669ec7b004SRick Macklem 	aclp->acl_cnt = 0;
3679ec7b004SRick Macklem #endif
36890d2dfabSRick Macklem 	gotproxystateid = 0;
3699ec7b004SRick Macklem 	NFSVNO_ATTRINIT(&nva);
3709ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
3719ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
3729ec7b004SRick Macklem 		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
37390d2dfabSRick Macklem 		stateid.other[0] = *tl++;
37490d2dfabSRick Macklem 		stateid.other[1] = *tl++;
37590d2dfabSRick Macklem 		stateid.other[2] = *tl;
37690d2dfabSRick Macklem 		if (stateid.other[0] == 0x55555555 &&
37790d2dfabSRick Macklem 		    stateid.other[1] == 0x55555555 &&
37890d2dfabSRick Macklem 		    stateid.other[2] == 0x55555555 &&
37990d2dfabSRick Macklem 		    stateid.seqid == 0xffffffff)
38090d2dfabSRick Macklem 			gotproxystateid = 1;
3819ec7b004SRick Macklem 	}
382d8a5961fSMarcelo Araujo 	error = nfsrv_sattr(nd, vp, &nva, &attrbits, aclp, p);
3839ec7b004SRick Macklem 	if (error)
3849ec7b004SRick Macklem 		goto nfsmout;
38590d2dfabSRick Macklem 
38690d2dfabSRick Macklem 	/* For NFSv4, only va_uid is used from nva2. */
38790d2dfabSRick Macklem 	NFSZERO_ATTRBIT(&retbits);
38890d2dfabSRick Macklem 	NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
38990d2dfabSRick Macklem 	preat_ret = nfsvno_getattr(vp, &nva2, nd, p, 1, &retbits);
3909ec7b004SRick Macklem 	if (!nd->nd_repstat)
3919ec7b004SRick Macklem 		nd->nd_repstat = preat_ret;
39290d2dfabSRick Macklem 
39390d2dfabSRick Macklem 	NFSZERO_ATTRBIT(&retbits);
3949ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
3959ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3969ec7b004SRick Macklem 		gcheck = fxdr_unsigned(int, *tl);
3979ec7b004SRick Macklem 		if (gcheck) {
3989ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3999ec7b004SRick Macklem 			fxdr_nfsv3time(tl, &guard);
4009ec7b004SRick Macklem 		}
4019ec7b004SRick Macklem 		if (!nd->nd_repstat && gcheck &&
4029ec7b004SRick Macklem 		    (nva2.na_ctime.tv_sec != guard.tv_sec ||
4039ec7b004SRick Macklem 		     nva2.na_ctime.tv_nsec != guard.tv_nsec))
4049ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_NOT_SYNC;
4059ec7b004SRick Macklem 		if (nd->nd_repstat) {
4069ec7b004SRick Macklem 			vput(vp);
4079ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
4089ec7b004SRick Macklem 			acl_free(aclp);
4099ec7b004SRick Macklem #endif
4109ec7b004SRick Macklem 			nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
411a9285ae5SZack Kirsch 			goto out;
4129ec7b004SRick Macklem 		}
4139ec7b004SRick Macklem 	} else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
4149ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
4159ec7b004SRick Macklem 
4169ec7b004SRick Macklem 	/*
4179ec7b004SRick Macklem 	 * Now that we have all the fields, lets do it.
4189ec7b004SRick Macklem 	 * If the size is being changed write access is required, otherwise
4199ec7b004SRick Macklem 	 * just check for a read only file system.
4209ec7b004SRick Macklem 	 */
4219ec7b004SRick Macklem 	if (!nd->nd_repstat) {
4229ec7b004SRick Macklem 		if (NFSVNO_NOTSETSIZE(&nva)) {
4239ec7b004SRick Macklem 			if (NFSVNO_EXRDONLY(exp) ||
424eea79fdeSAlan Somers 			    (vfs_flags(vp->v_mount) & MNT_RDONLY))
4259ec7b004SRick Macklem 				nd->nd_repstat = EROFS;
4269ec7b004SRick Macklem 		} else {
4279ec7b004SRick Macklem 			if (vnode_vtype(vp) != VREG)
4289ec7b004SRick Macklem 				nd->nd_repstat = EINVAL;
4299ec7b004SRick Macklem 			else if (nva2.na_uid != nd->nd_cred->cr_uid ||
4309ec7b004SRick Macklem 			    NFSVNO_EXSTRICTACCESS(exp))
4319ec7b004SRick Macklem 				nd->nd_repstat = nfsvno_accchk(vp,
4328da45f2cSRick Macklem 				    VWRITE, nd->nd_cred, exp, p,
4338da45f2cSRick Macklem 				    NFSACCCHK_NOOVERRIDE,
4348da45f2cSRick Macklem 				    NFSACCCHK_VPISLOCKED, NULL);
4359ec7b004SRick Macklem 		}
4369ec7b004SRick Macklem 	}
43790d2dfabSRick Macklem 	/*
43890d2dfabSRick Macklem 	 * Proxy operations from the MDS are allowed via the all 0s special
43990d2dfabSRick Macklem 	 * stateid.
44090d2dfabSRick Macklem 	 */
44190d2dfabSRick Macklem 	if (nd->nd_repstat == 0 && (nd->nd_flag & ND_NFSV4) != 0 &&
44290d2dfabSRick Macklem 	    gotproxystateid == 0)
4439ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_checksetattr(vp, nd, &stateid,
4449ec7b004SRick Macklem 		    &nva, &attrbits, exp, p);
4459ec7b004SRick Macklem 
4469ec7b004SRick Macklem 	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
4479ec7b004SRick Macklem 	    /*
4489ec7b004SRick Macklem 	     * For V4, try setting the attrbutes in sets, so that the
4499ec7b004SRick Macklem 	     * reply bitmap will be correct for an error case.
4509ec7b004SRick Macklem 	     */
4519ec7b004SRick Macklem 	    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER) ||
4529ec7b004SRick Macklem 		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP)) {
4539ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva2);
4549ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, uid, nva.na_uid);
4559ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, gid, nva.na_gid);
4569ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
4579ec7b004SRick Macklem 		    exp);
4589ec7b004SRick Macklem 		if (!nd->nd_repstat) {
4599ec7b004SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER))
4609ec7b004SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
4619ec7b004SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP))
4629ec7b004SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNERGROUP);
4639ec7b004SRick Macklem 		}
4649ec7b004SRick Macklem 	    }
4659ec7b004SRick Macklem 	    if (!nd->nd_repstat &&
4669ec7b004SRick Macklem 		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SIZE)) {
4679ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva2);
4689ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, size, nva.na_size);
4699ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
4709ec7b004SRick Macklem 		    exp);
4719ec7b004SRick Macklem 		if (!nd->nd_repstat)
4729ec7b004SRick Macklem 		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SIZE);
4739ec7b004SRick Macklem 	    }
4749ec7b004SRick Macklem 	    if (!nd->nd_repstat &&
4759ec7b004SRick Macklem 		(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET) ||
4769ec7b004SRick Macklem 		 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))) {
4779ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva2);
4789ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, atime, nva.na_atime);
4799ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, mtime, nva.na_mtime);
4809ec7b004SRick Macklem 		if (nva.na_vaflags & VA_UTIMES_NULL) {
4819ec7b004SRick Macklem 			nva2.na_vaflags |= VA_UTIMES_NULL;
4829ec7b004SRick Macklem 			NFSVNO_SETACTIVE(&nva2, vaflags);
4839ec7b004SRick Macklem 		}
4849ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
4859ec7b004SRick Macklem 		    exp);
4869ec7b004SRick Macklem 		if (!nd->nd_repstat) {
4879ec7b004SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET))
4889ec7b004SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEACCESSSET);
4899ec7b004SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))
4909ec7b004SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEMODIFYSET);
4919ec7b004SRick Macklem 		}
4929ec7b004SRick Macklem 	    }
4939ec7b004SRick Macklem 	    if (!nd->nd_repstat &&
494dd02d9d6SRick Macklem 		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMECREATE)) {
495dd02d9d6SRick Macklem 		NFSVNO_ATTRINIT(&nva2);
496dd02d9d6SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, btime, nva.na_btime);
497dd02d9d6SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
498dd02d9d6SRick Macklem 		    exp);
499dd02d9d6SRick Macklem 		if (!nd->nd_repstat)
500dd02d9d6SRick Macklem 		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMECREATE);
501dd02d9d6SRick Macklem 	    }
502dd02d9d6SRick Macklem 	    if (!nd->nd_repstat &&
503b4372164SRick Macklem 		(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE) ||
504b4372164SRick Macklem 		 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODESETMASKED))) {
5059ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva2);
5069ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, mode, nva.na_mode);
5079ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
5089ec7b004SRick Macklem 		    exp);
509b4372164SRick Macklem 		if (!nd->nd_repstat) {
510b4372164SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE))
5119ec7b004SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODE);
512b4372164SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODESETMASKED))
513b4372164SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODESETMASKED);
514b4372164SRick Macklem 		}
5159ec7b004SRick Macklem 	    }
5169ec7b004SRick Macklem 
5179ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
5189ec7b004SRick Macklem 	    if (!nd->nd_repstat && aclp->acl_cnt > 0 &&
5199ec7b004SRick Macklem 		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ACL)) {
5209ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_setacl(vp, aclp, nd->nd_cred, p);
5219ec7b004SRick Macklem 		if (!nd->nd_repstat)
5229ec7b004SRick Macklem 		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_ACL);
5239ec7b004SRick Macklem 	    }
5249ec7b004SRick Macklem #endif
5259ec7b004SRick Macklem 	} else if (!nd->nd_repstat) {
5269ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva, nd->nd_cred, p,
5279ec7b004SRick Macklem 		    exp);
5289ec7b004SRick Macklem 	}
5299ec7b004SRick Macklem 	if (nd->nd_flag & (ND_NFSV2 | ND_NFSV3)) {
53090d2dfabSRick Macklem 		postat_ret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
5319ec7b004SRick Macklem 		if (!nd->nd_repstat)
5329ec7b004SRick Macklem 			nd->nd_repstat = postat_ret;
5339ec7b004SRick Macklem 	}
5349ec7b004SRick Macklem 	vput(vp);
5359ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
5369ec7b004SRick Macklem 	acl_free(aclp);
5379ec7b004SRick Macklem #endif
5389ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
5399ec7b004SRick Macklem 		nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
5409ec7b004SRick Macklem 	else if (nd->nd_flag & ND_NFSV4)
5419ec7b004SRick Macklem 		(void) nfsrv_putattrbit(nd, &retbits);
5429ec7b004SRick Macklem 	else if (!nd->nd_repstat)
5439ec7b004SRick Macklem 		nfsrv_fillattr(nd, &nva);
544a9285ae5SZack Kirsch 
545a9285ae5SZack Kirsch out:
546a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
5479ec7b004SRick Macklem 	return (0);
5489ec7b004SRick Macklem nfsmout:
5499ec7b004SRick Macklem 	vput(vp);
5509ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
5519ec7b004SRick Macklem 	acl_free(aclp);
5529ec7b004SRick Macklem #endif
5539ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
5549ec7b004SRick Macklem 		/*
5559ec7b004SRick Macklem 		 * For all nd_repstat, the V4 reply includes a bitmap,
5569ec7b004SRick Macklem 		 * even NFSERR_BADXDR, which is what this will end up
5579ec7b004SRick Macklem 		 * returning.
5589ec7b004SRick Macklem 		 */
5599ec7b004SRick Macklem 		(void) nfsrv_putattrbit(nd, &retbits);
5609ec7b004SRick Macklem 	}
561a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
5629ec7b004SRick Macklem 	return (error);
5639ec7b004SRick Macklem }
5649ec7b004SRick Macklem 
5659ec7b004SRick Macklem /*
5669ec7b004SRick Macklem  * nfs lookup rpc
5679ec7b004SRick Macklem  * (Also performs lookup parent for v4)
5689ec7b004SRick Macklem  */
569b9cc3262SRyan Moeller int
5709ec7b004SRick Macklem nfsrvd_lookup(struct nfsrv_descript *nd, __unused int isdgram,
571af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
5729ec7b004SRick Macklem {
5739ec7b004SRick Macklem 	struct nameidata named;
5749ec7b004SRick Macklem 	vnode_t vp, dirp = NULL;
575a9285ae5SZack Kirsch 	int error = 0, dattr_ret = 1;
5769ec7b004SRick Macklem 	struct nfsvattr nva, dattr;
5779ec7b004SRick Macklem 	char *bufp;
5789ec7b004SRick Macklem 	u_long *hashp;
579af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
5809ec7b004SRick Macklem 
5819ec7b004SRick Macklem 	if (nd->nd_repstat) {
5829ec7b004SRick Macklem 		nfsrv_postopattr(nd, dattr_ret, &dattr);
583a9285ae5SZack Kirsch 		goto out;
5849ec7b004SRick Macklem 	}
5859ec7b004SRick Macklem 
5869ec7b004SRick Macklem 	/*
5879ec7b004SRick Macklem 	 * For some reason, if dp is a symlink, the error
5889ec7b004SRick Macklem 	 * returned is supposed to be NFSERR_SYMLINK and not NFSERR_NOTDIR.
5899ec7b004SRick Macklem 	 */
5909ec7b004SRick Macklem 	if (dp->v_type == VLNK && (nd->nd_flag & ND_NFSV4)) {
5919ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_SYMLINK;
5929ec7b004SRick Macklem 		vrele(dp);
593a9285ae5SZack Kirsch 		goto out;
5949ec7b004SRick Macklem 	}
5959ec7b004SRick Macklem 
5969ec7b004SRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
5979ec7b004SRick Macklem 	    LOCKLEAF | SAVESTART);
5989ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
5999ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
6009ec7b004SRick Macklem 	if (error) {
6019ec7b004SRick Macklem 		vrele(dp);
6029ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
603a9285ae5SZack Kirsch 		goto out;
6049ec7b004SRick Macklem 	}
6059ec7b004SRick Macklem 	if (!nd->nd_repstat) {
6069ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
6079ec7b004SRick Macklem 	} else {
6089ec7b004SRick Macklem 		vrele(dp);
6099ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
6109ec7b004SRick Macklem 	}
6119ec7b004SRick Macklem 	if (nd->nd_repstat) {
6129ec7b004SRick Macklem 		if (dirp) {
6139ec7b004SRick Macklem 			if (nd->nd_flag & ND_NFSV3)
61490d2dfabSRick Macklem 				dattr_ret = nfsvno_getattr(dirp, &dattr, nd, p,
61590d2dfabSRick Macklem 				    0, NULL);
6169ec7b004SRick Macklem 			vrele(dirp);
6179ec7b004SRick Macklem 		}
6189ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
6199ec7b004SRick Macklem 			nfsrv_postopattr(nd, dattr_ret, &dattr);
620a9285ae5SZack Kirsch 		goto out;
6219ec7b004SRick Macklem 	}
6229ec7b004SRick Macklem 	if (named.ni_startdir)
6239ec7b004SRick Macklem 		vrele(named.ni_startdir);
6249ec7b004SRick Macklem 	nfsvno_relpathbuf(&named);
6259ec7b004SRick Macklem 	vp = named.ni_vp;
62637b88c2dSRick Macklem 	if ((nd->nd_flag & ND_NFSV4) != 0 && !NFSVNO_EXPORTED(exp) &&
62737b88c2dSRick Macklem 	    vp->v_type != VDIR && vp->v_type != VLNK)
62837b88c2dSRick Macklem 		/*
62937b88c2dSRick Macklem 		 * Only allow lookup of VDIR and VLNK for traversal of
63037b88c2dSRick Macklem 		 * non-exported volumes during NFSv4 mounting.
63137b88c2dSRick Macklem 		 */
63237b88c2dSRick Macklem 		nd->nd_repstat = ENOENT;
63337b88c2dSRick Macklem 	if (nd->nd_repstat == 0)
6349ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
6359ec7b004SRick Macklem 	if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
63690d2dfabSRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
63781f78d99SRick Macklem 	if (vpp != NULL && nd->nd_repstat == 0)
6389ec7b004SRick Macklem 		*vpp = vp;
63981f78d99SRick Macklem 	else
6409ec7b004SRick Macklem 		vput(vp);
6419ec7b004SRick Macklem 	if (dirp) {
6429ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
64390d2dfabSRick Macklem 			dattr_ret = nfsvno_getattr(dirp, &dattr, nd, p, 0,
64490d2dfabSRick Macklem 			    NULL);
6459ec7b004SRick Macklem 		vrele(dirp);
6469ec7b004SRick Macklem 	}
6479ec7b004SRick Macklem 	if (nd->nd_repstat) {
6489ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
6499ec7b004SRick Macklem 			nfsrv_postopattr(nd, dattr_ret, &dattr);
650a9285ae5SZack Kirsch 		goto out;
6519ec7b004SRick Macklem 	}
6529ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
6539ec7b004SRick Macklem 		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
6549ec7b004SRick Macklem 		nfsrv_fillattr(nd, &nva);
6559ec7b004SRick Macklem 	} else if (nd->nd_flag & ND_NFSV3) {
6569ec7b004SRick Macklem 		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
6579ec7b004SRick Macklem 		nfsrv_postopattr(nd, 0, &nva);
6589ec7b004SRick Macklem 		nfsrv_postopattr(nd, dattr_ret, &dattr);
6599ec7b004SRick Macklem 	}
660a9285ae5SZack Kirsch 
661a9285ae5SZack Kirsch out:
662a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
663a9285ae5SZack Kirsch 	return (error);
6649ec7b004SRick Macklem }
6659ec7b004SRick Macklem 
6669ec7b004SRick Macklem /*
6679ec7b004SRick Macklem  * nfs readlink service
6689ec7b004SRick Macklem  */
669b9cc3262SRyan Moeller int
6709ec7b004SRick Macklem nfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram,
671af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
6729ec7b004SRick Macklem {
6739ec7b004SRick Macklem 	u_int32_t *tl;
674ae070589SRick Macklem 	struct mbuf *mp = NULL, *mpend = NULL;
6759ec7b004SRick Macklem 	int getret = 1, len;
6769ec7b004SRick Macklem 	struct nfsvattr nva;
677af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
678cb889ce6SRick Macklem 	uint16_t off;
6799ec7b004SRick Macklem 
6809ec7b004SRick Macklem 	if (nd->nd_repstat) {
6819ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &nva);
682a9285ae5SZack Kirsch 		goto out;
6839ec7b004SRick Macklem 	}
6849ec7b004SRick Macklem 	if (vnode_vtype(vp) != VLNK) {
6859ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV2)
6869ec7b004SRick Macklem 			nd->nd_repstat = ENXIO;
6879ec7b004SRick Macklem 		else
6889ec7b004SRick Macklem 			nd->nd_repstat = EINVAL;
6899ec7b004SRick Macklem 	}
690cb889ce6SRick Macklem 	if (nd->nd_repstat == 0) {
691cb889ce6SRick Macklem 		if ((nd->nd_flag & ND_EXTPG) != 0)
692cb889ce6SRick Macklem 			nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred,
693cb889ce6SRick Macklem 			    nd->nd_maxextsiz, p, &mp, &mpend, &len);
694cb889ce6SRick Macklem 		else
695cb889ce6SRick Macklem 			nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred,
696cb889ce6SRick Macklem 			    0, p, &mp, &mpend, &len);
697cb889ce6SRick Macklem 	}
6989ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
69990d2dfabSRick Macklem 		getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
7009ec7b004SRick Macklem 	vput(vp);
7019ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
7029ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &nva);
7039ec7b004SRick Macklem 	if (nd->nd_repstat)
704a9285ae5SZack Kirsch 		goto out;
7059ec7b004SRick Macklem 	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
7069ec7b004SRick Macklem 	*tl = txdr_unsigned(len);
70718a48314SRick Macklem 	if (mp != NULL) {
7089f6624d3SRick Macklem 		nd->nd_mb->m_next = mp;
7099ec7b004SRick Macklem 		nd->nd_mb = mpend;
710cb889ce6SRick Macklem 		if ((mpend->m_flags & M_EXTPG) != 0) {
711cb889ce6SRick Macklem 			nd->nd_bextpg = mpend->m_epg_npgs - 1;
712cb889ce6SRick Macklem 			nd->nd_bpos = (char *)(void *)
713cb889ce6SRick Macklem 			    PHYS_TO_DMAP(mpend->m_epg_pa[nd->nd_bextpg]);
714cb889ce6SRick Macklem 			off = (nd->nd_bextpg == 0) ? mpend->m_epg_1st_off : 0;
715cb889ce6SRick Macklem 			nd->nd_bpos += off + mpend->m_epg_last_len;
716cb889ce6SRick Macklem 			nd->nd_bextpgsiz = PAGE_SIZE - mpend->m_epg_last_len -
717cb889ce6SRick Macklem 			    off;
718cb889ce6SRick Macklem 		} else
719cb889ce6SRick Macklem 			nd->nd_bpos = mtod(mpend, char *) + mpend->m_len;
72018a48314SRick Macklem 	}
721a9285ae5SZack Kirsch 
722a9285ae5SZack Kirsch out:
723a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
7249ec7b004SRick Macklem 	return (0);
7259ec7b004SRick Macklem }
7269ec7b004SRick Macklem 
7279ec7b004SRick Macklem /*
7289ec7b004SRick Macklem  * nfs read service
7299ec7b004SRick Macklem  */
730b9cc3262SRyan Moeller int
7319ec7b004SRick Macklem nfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram,
732af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
7339ec7b004SRick Macklem {
7349ec7b004SRick Macklem 	u_int32_t *tl;
73590d2dfabSRick Macklem 	int error = 0, cnt, getret = 1, gotproxystateid, reqlen, eof = 0;
736ae070589SRick Macklem 	struct mbuf *m2, *m3;
7379ec7b004SRick Macklem 	struct nfsvattr nva;
7389ec7b004SRick Macklem 	off_t off = 0x0;
7399ec7b004SRick Macklem 	struct nfsstate st, *stp = &st;
7409ec7b004SRick Macklem 	struct nfslock lo, *lop = &lo;
7419ec7b004SRick Macklem 	nfsv4stateid_t stateid;
7429ec7b004SRick Macklem 	nfsquad_t clientid;
743af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
744cb889ce6SRick Macklem 	uint16_t poff;
7459ec7b004SRick Macklem 
7469ec7b004SRick Macklem 	if (nd->nd_repstat) {
7479ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &nva);
748a9285ae5SZack Kirsch 		goto out;
7499ec7b004SRick Macklem 	}
7509ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
7519ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
7529ec7b004SRick Macklem 		off = (off_t)fxdr_unsigned(u_int32_t, *tl++);
7539ec7b004SRick Macklem 		reqlen = fxdr_unsigned(int, *tl);
7549ec7b004SRick Macklem 	} else if (nd->nd_flag & ND_NFSV3) {
7559ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
7569ec7b004SRick Macklem 		off = fxdr_hyper(tl);
7579ec7b004SRick Macklem 		tl += 2;
7589ec7b004SRick Macklem 		reqlen = fxdr_unsigned(int, *tl);
7599ec7b004SRick Macklem 	} else {
7609ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3*NFSX_UNSIGNED);
7619ec7b004SRick Macklem 		reqlen = fxdr_unsigned(int, *(tl + 6));
7629ec7b004SRick Macklem 	}
7639ec7b004SRick Macklem 	if (reqlen > NFS_SRVMAXDATA(nd)) {
7649ec7b004SRick Macklem 		reqlen = NFS_SRVMAXDATA(nd);
7659ec7b004SRick Macklem 	} else if (reqlen < 0) {
7669ec7b004SRick Macklem 		error = EBADRPC;
7679ec7b004SRick Macklem 		goto nfsmout;
7689ec7b004SRick Macklem 	}
76990d2dfabSRick Macklem 	gotproxystateid = 0;
7709ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
7719ec7b004SRick Macklem 		stp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
7729ec7b004SRick Macklem 		lop->lo_flags = NFSLCK_READ;
7739ec7b004SRick Macklem 		stp->ls_ownerlen = 0;
7749ec7b004SRick Macklem 		stp->ls_op = NULL;
7759ec7b004SRick Macklem 		stp->ls_uid = nd->nd_cred->cr_uid;
7769ec7b004SRick Macklem 		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
7779ec7b004SRick Macklem 		clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
7789ec7b004SRick Macklem 		clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
779c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
780c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
781c59e4cc3SRick Macklem 				clientid.qval = nd->nd_clientid.qval;
782c59e4cc3SRick Macklem 			else if (nd->nd_clientid.qval != clientid.qval)
783c59e4cc3SRick Macklem 				printf("EEK1 multiple clids\n");
7849ec7b004SRick Macklem 		} else {
785c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
786c59e4cc3SRick Macklem 				printf("EEK! no clientid from session\n");
7879ec7b004SRick Macklem 			nd->nd_flag |= ND_IMPLIEDCLID;
7889ec7b004SRick Macklem 			nd->nd_clientid.qval = clientid.qval;
7899ec7b004SRick Macklem 		}
7909ec7b004SRick Macklem 		stp->ls_stateid.other[2] = *tl++;
79190d2dfabSRick Macklem 		/*
79290d2dfabSRick Macklem 		 * Don't allow the client to use a special stateid for a DS op.
79390d2dfabSRick Macklem 		 */
79490d2dfabSRick Macklem 		if ((nd->nd_flag & ND_DSSERVER) != 0 &&
79590d2dfabSRick Macklem 		    ((stp->ls_stateid.other[0] == 0x0 &&
79690d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0x0 &&
79790d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0x0) ||
79890d2dfabSRick Macklem 		    (stp->ls_stateid.other[0] == 0xffffffff &&
79990d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0xffffffff &&
80090d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0xffffffff) ||
80190d2dfabSRick Macklem 		    stp->ls_stateid.seqid != 0))
80290d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
80390d2dfabSRick Macklem 		/* However, allow the proxy stateid. */
80490d2dfabSRick Macklem 		if (stp->ls_stateid.seqid == 0xffffffff &&
80590d2dfabSRick Macklem 		    stp->ls_stateid.other[0] == 0x55555555 &&
80690d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0x55555555 &&
80790d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0x55555555)
80890d2dfabSRick Macklem 			gotproxystateid = 1;
8099ec7b004SRick Macklem 		off = fxdr_hyper(tl);
8109ec7b004SRick Macklem 		lop->lo_first = off;
8119ec7b004SRick Macklem 		tl += 2;
8129ec7b004SRick Macklem 		lop->lo_end = off + reqlen;
8139ec7b004SRick Macklem 		/*
8149ec7b004SRick Macklem 		 * Paranoia, just in case it wraps around.
8159ec7b004SRick Macklem 		 */
8169ec7b004SRick Macklem 		if (lop->lo_end < off)
8179ec7b004SRick Macklem 			lop->lo_end = NFS64BITSSET;
8189ec7b004SRick Macklem 	}
8199ec7b004SRick Macklem 	if (vnode_vtype(vp) != VREG) {
8209ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
8219ec7b004SRick Macklem 			nd->nd_repstat = EINVAL;
8229ec7b004SRick Macklem 		else
8239ec7b004SRick Macklem 			nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
8249ec7b004SRick Macklem 			    EINVAL;
8259ec7b004SRick Macklem 	}
82690d2dfabSRick Macklem 	getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
8279ec7b004SRick Macklem 	if (!nd->nd_repstat)
8289ec7b004SRick Macklem 		nd->nd_repstat = getret;
8299ec7b004SRick Macklem 	if (!nd->nd_repstat &&
8309ec7b004SRick Macklem 	    (nva.na_uid != nd->nd_cred->cr_uid ||
8319ec7b004SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp))) {
8328da45f2cSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
8339ec7b004SRick Macklem 		    nd->nd_cred, exp, p,
8348da45f2cSRick Macklem 		    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
8359ec7b004SRick Macklem 		if (nd->nd_repstat)
8368da45f2cSRick Macklem 			nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
8378da45f2cSRick Macklem 			    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
8388da45f2cSRick Macklem 			    NFSACCCHK_VPISLOCKED, NULL);
8399ec7b004SRick Macklem 	}
84090d2dfabSRick Macklem 	/*
84190d2dfabSRick Macklem 	 * DS reads are marked by ND_DSSERVER or use the proxy special
84290d2dfabSRick Macklem 	 * stateid.
84390d2dfabSRick Macklem 	 */
84490d2dfabSRick Macklem 	if (nd->nd_repstat == 0 && (nd->nd_flag & (ND_NFSV4 | ND_DSSERVER)) ==
84590d2dfabSRick Macklem 	    ND_NFSV4 && gotproxystateid == 0)
8469ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
8479ec7b004SRick Macklem 		    &stateid, exp, nd, p);
8489ec7b004SRick Macklem 	if (nd->nd_repstat) {
8499ec7b004SRick Macklem 		vput(vp);
8509ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
8519ec7b004SRick Macklem 			nfsrv_postopattr(nd, getret, &nva);
852a9285ae5SZack Kirsch 		goto out;
8539ec7b004SRick Macklem 	}
8549ec7b004SRick Macklem 	if (off >= nva.na_size) {
8559ec7b004SRick Macklem 		cnt = 0;
8569ec7b004SRick Macklem 		eof = 1;
8579ec7b004SRick Macklem 	} else if (reqlen == 0)
8589ec7b004SRick Macklem 		cnt = 0;
85906521fbbSZack Kirsch 	else if ((off + reqlen) >= nva.na_size) {
8609ec7b004SRick Macklem 		cnt = nva.na_size - off;
86106521fbbSZack Kirsch 		eof = 1;
86206521fbbSZack Kirsch 	} else
8639ec7b004SRick Macklem 		cnt = reqlen;
8649ec7b004SRick Macklem 	m3 = NULL;
8659ec7b004SRick Macklem 	if (cnt > 0) {
866cb889ce6SRick Macklem 		/*
867cb889ce6SRick Macklem 		 * If cnt > MCLBYTES and the reply will not be saved, use
868cb889ce6SRick Macklem 		 * ext_pgs mbufs for TLS.
869cb889ce6SRick Macklem 		 * For NFSv4.0, we do not know for sure if the reply will
870cb889ce6SRick Macklem 		 * be saved, so do not use ext_pgs mbufs for NFSv4.0.
871cb889ce6SRick Macklem 		 * Always use ext_pgs mbufs if ND_EXTPG is set.
872cb889ce6SRick Macklem 		 */
873cb889ce6SRick Macklem 		if ((nd->nd_flag & ND_EXTPG) != 0 || (cnt > MCLBYTES &&
874cb889ce6SRick Macklem 		    (nd->nd_flag & (ND_TLS | ND_SAVEREPLY)) == ND_TLS &&
875cb889ce6SRick Macklem 		    (nd->nd_flag & (ND_NFSV4 | ND_NFSV41)) != ND_NFSV4))
876cb889ce6SRick Macklem 			nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred,
877cb889ce6SRick Macklem 			    nd->nd_maxextsiz, p, &m3, &m2);
878cb889ce6SRick Macklem 		else
879cb889ce6SRick Macklem 			nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred,
880cb889ce6SRick Macklem 			    0, p, &m3, &m2);
8819ec7b004SRick Macklem 		if (!(nd->nd_flag & ND_NFSV4)) {
88290d2dfabSRick Macklem 			getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
8839ec7b004SRick Macklem 			if (!nd->nd_repstat)
8849ec7b004SRick Macklem 				nd->nd_repstat = getret;
8859ec7b004SRick Macklem 		}
8869ec7b004SRick Macklem 		if (nd->nd_repstat) {
8879ec7b004SRick Macklem 			vput(vp);
8889ec7b004SRick Macklem 			if (m3)
8899f6624d3SRick Macklem 				m_freem(m3);
8909ec7b004SRick Macklem 			if (nd->nd_flag & ND_NFSV3)
8919ec7b004SRick Macklem 				nfsrv_postopattr(nd, getret, &nva);
892a9285ae5SZack Kirsch 			goto out;
8939ec7b004SRick Macklem 		}
8949ec7b004SRick Macklem 	}
8959ec7b004SRick Macklem 	vput(vp);
8969ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
8979ec7b004SRick Macklem 		nfsrv_fillattr(nd, &nva);
8989ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
8999ec7b004SRick Macklem 	} else {
9009ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
9019ec7b004SRick Macklem 			nfsrv_postopattr(nd, getret, &nva);
9029ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
9039ec7b004SRick Macklem 			*tl++ = txdr_unsigned(cnt);
9049ec7b004SRick Macklem 		} else
9059ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
90606521fbbSZack Kirsch 		if (eof)
9079ec7b004SRick Macklem 			*tl++ = newnfs_true;
9089ec7b004SRick Macklem 		else
9099ec7b004SRick Macklem 			*tl++ = newnfs_false;
9109ec7b004SRick Macklem 	}
9119ec7b004SRick Macklem 	*tl = txdr_unsigned(cnt);
9129ec7b004SRick Macklem 	if (m3) {
9139f6624d3SRick Macklem 		nd->nd_mb->m_next = m3;
9149ec7b004SRick Macklem 		nd->nd_mb = m2;
915cb889ce6SRick Macklem 		if ((m2->m_flags & M_EXTPG) != 0) {
916cb889ce6SRick Macklem 			nd->nd_flag |= ND_EXTPG;
917cb889ce6SRick Macklem 			nd->nd_bextpg = m2->m_epg_npgs - 1;
918cb889ce6SRick Macklem 			nd->nd_bpos = (char *)(void *)
919cb889ce6SRick Macklem 			    PHYS_TO_DMAP(m2->m_epg_pa[nd->nd_bextpg]);
920cb889ce6SRick Macklem 			poff = (nd->nd_bextpg == 0) ? m2->m_epg_1st_off : 0;
921cb889ce6SRick Macklem 			nd->nd_bpos += poff + m2->m_epg_last_len;
922cb889ce6SRick Macklem 			nd->nd_bextpgsiz = PAGE_SIZE - m2->m_epg_last_len -
923cb889ce6SRick Macklem 			    poff;
924cb889ce6SRick Macklem 		} else
925cb889ce6SRick Macklem 			nd->nd_bpos = mtod(m2, char *) + m2->m_len;
9269ec7b004SRick Macklem 	}
927a9285ae5SZack Kirsch 
928a9285ae5SZack Kirsch out:
929a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
9309ec7b004SRick Macklem 	return (0);
9319ec7b004SRick Macklem nfsmout:
9329ec7b004SRick Macklem 	vput(vp);
933a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
9349ec7b004SRick Macklem 	return (error);
9359ec7b004SRick Macklem }
9369ec7b004SRick Macklem 
9379ec7b004SRick Macklem /*
9389ec7b004SRick Macklem  * nfs write service
9399ec7b004SRick Macklem  */
940b9cc3262SRyan Moeller int
9419ec7b004SRick Macklem nfsrvd_write(struct nfsrv_descript *nd, __unused int isdgram,
942af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
9439ec7b004SRick Macklem {
9449ec7b004SRick Macklem 	u_int32_t *tl;
9459ec7b004SRick Macklem 	struct nfsvattr nva, forat;
9469ec7b004SRick Macklem 	int aftat_ret = 1, retlen, len, error = 0, forat_ret = 1;
94790d2dfabSRick Macklem 	int gotproxystateid, stable = NFSWRITE_FILESYNC;
9489ec7b004SRick Macklem 	off_t off;
9499ec7b004SRick Macklem 	struct nfsstate st, *stp = &st;
9509ec7b004SRick Macklem 	struct nfslock lo, *lop = &lo;
9519ec7b004SRick Macklem 	nfsv4stateid_t stateid;
9529ec7b004SRick Macklem 	nfsquad_t clientid;
95390d2dfabSRick Macklem 	nfsattrbit_t attrbits;
954af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
9559ec7b004SRick Macklem 
9569ec7b004SRick Macklem 	if (nd->nd_repstat) {
9579ec7b004SRick Macklem 		nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
958a9285ae5SZack Kirsch 		goto out;
9599ec7b004SRick Macklem 	}
96090d2dfabSRick Macklem 	gotproxystateid = 0;
9619ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
9629ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
9639ec7b004SRick Macklem 		off = (off_t)fxdr_unsigned(u_int32_t, *++tl);
9649ec7b004SRick Macklem 		tl += 2;
9659ec7b004SRick Macklem 		retlen = len = fxdr_unsigned(int32_t, *tl);
9669ec7b004SRick Macklem 	} else if (nd->nd_flag & ND_NFSV3) {
9679ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
9689ec7b004SRick Macklem 		off = fxdr_hyper(tl);
9699ec7b004SRick Macklem 		tl += 3;
9709ec7b004SRick Macklem 		stable = fxdr_unsigned(int, *tl++);
9719ec7b004SRick Macklem 		retlen = len = fxdr_unsigned(int32_t, *tl);
9729ec7b004SRick Macklem 	} else {
9739ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 4 * NFSX_UNSIGNED);
9749ec7b004SRick Macklem 		stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
9759ec7b004SRick Macklem 		lop->lo_flags = NFSLCK_WRITE;
9769ec7b004SRick Macklem 		stp->ls_ownerlen = 0;
9779ec7b004SRick Macklem 		stp->ls_op = NULL;
9789ec7b004SRick Macklem 		stp->ls_uid = nd->nd_cred->cr_uid;
9799ec7b004SRick Macklem 		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
9809ec7b004SRick Macklem 		clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
9819ec7b004SRick Macklem 		clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
982c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
983c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
984c59e4cc3SRick Macklem 				clientid.qval = nd->nd_clientid.qval;
985c59e4cc3SRick Macklem 			else if (nd->nd_clientid.qval != clientid.qval)
986c59e4cc3SRick Macklem 				printf("EEK2 multiple clids\n");
9879ec7b004SRick Macklem 		} else {
988c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
989c59e4cc3SRick Macklem 				printf("EEK! no clientid from session\n");
9909ec7b004SRick Macklem 			nd->nd_flag |= ND_IMPLIEDCLID;
9919ec7b004SRick Macklem 			nd->nd_clientid.qval = clientid.qval;
9929ec7b004SRick Macklem 		}
9939ec7b004SRick Macklem 		stp->ls_stateid.other[2] = *tl++;
99490d2dfabSRick Macklem 		/*
99590d2dfabSRick Macklem 		 * Don't allow the client to use a special stateid for a DS op.
99690d2dfabSRick Macklem 		 */
99790d2dfabSRick Macklem 		if ((nd->nd_flag & ND_DSSERVER) != 0 &&
99890d2dfabSRick Macklem 		    ((stp->ls_stateid.other[0] == 0x0 &&
99990d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0x0 &&
100090d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0x0) ||
100190d2dfabSRick Macklem 		    (stp->ls_stateid.other[0] == 0xffffffff &&
100290d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0xffffffff &&
100390d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0xffffffff) ||
100490d2dfabSRick Macklem 		    stp->ls_stateid.seqid != 0))
100590d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
100690d2dfabSRick Macklem 		/* However, allow the proxy stateid. */
100790d2dfabSRick Macklem 		if (stp->ls_stateid.seqid == 0xffffffff &&
100890d2dfabSRick Macklem 		    stp->ls_stateid.other[0] == 0x55555555 &&
100990d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0x55555555 &&
101090d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0x55555555)
101190d2dfabSRick Macklem 			gotproxystateid = 1;
10129ec7b004SRick Macklem 		off = fxdr_hyper(tl);
10139ec7b004SRick Macklem 		lop->lo_first = off;
10149ec7b004SRick Macklem 		tl += 2;
10159ec7b004SRick Macklem 		stable = fxdr_unsigned(int, *tl++);
10169ec7b004SRick Macklem 		retlen = len = fxdr_unsigned(int32_t, *tl);
10179ec7b004SRick Macklem 		lop->lo_end = off + len;
10189ec7b004SRick Macklem 		/*
10199ec7b004SRick Macklem 		 * Paranoia, just in case it wraps around, which shouldn't
10209ec7b004SRick Macklem 		 * ever happen anyhow.
10219ec7b004SRick Macklem 		 */
10229ec7b004SRick Macklem 		if (lop->lo_end < lop->lo_first)
10239ec7b004SRick Macklem 			lop->lo_end = NFS64BITSSET;
10249ec7b004SRick Macklem 	}
10259ec7b004SRick Macklem 
102666e80f77SRick Macklem 	if (retlen > NFS_SRVMAXIO || retlen < 0)
10279ec7b004SRick Macklem 		nd->nd_repstat = EIO;
10289ec7b004SRick Macklem 	if (vnode_vtype(vp) != VREG && !nd->nd_repstat) {
10299ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
10309ec7b004SRick Macklem 			nd->nd_repstat = EINVAL;
10319ec7b004SRick Macklem 		else
10329ec7b004SRick Macklem 			nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
10339ec7b004SRick Macklem 			    EINVAL;
10349ec7b004SRick Macklem 	}
103590d2dfabSRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
103690d2dfabSRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
103790d2dfabSRick Macklem 	forat_ret = nfsvno_getattr(vp, &forat, nd, p, 1, &attrbits);
10389ec7b004SRick Macklem 	if (!nd->nd_repstat)
10399ec7b004SRick Macklem 		nd->nd_repstat = forat_ret;
10409ec7b004SRick Macklem 	if (!nd->nd_repstat &&
10419ec7b004SRick Macklem 	    (forat.na_uid != nd->nd_cred->cr_uid ||
10429ec7b004SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
10438da45f2cSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
10449ec7b004SRick Macklem 		    nd->nd_cred, exp, p,
10458da45f2cSRick Macklem 		    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
104690d2dfabSRick Macklem 	/*
104790d2dfabSRick Macklem 	 * DS reads are marked by ND_DSSERVER or use the proxy special
104890d2dfabSRick Macklem 	 * stateid.
104990d2dfabSRick Macklem 	 */
105090d2dfabSRick Macklem 	if (nd->nd_repstat == 0 && (nd->nd_flag & (ND_NFSV4 | ND_DSSERVER)) ==
105190d2dfabSRick Macklem 	    ND_NFSV4 && gotproxystateid == 0)
10529ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
10539ec7b004SRick Macklem 		    &stateid, exp, nd, p);
10549ec7b004SRick Macklem 	if (nd->nd_repstat) {
10559ec7b004SRick Macklem 		vput(vp);
10569ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
10579ec7b004SRick Macklem 			nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
1058a9285ae5SZack Kirsch 		goto out;
10599ec7b004SRick Macklem 	}
10609ec7b004SRick Macklem 
10619ec7b004SRick Macklem 	/*
10629ec7b004SRick Macklem 	 * For NFS Version 2, it is not obvious what a write of zero length
10639ec7b004SRick Macklem 	 * should do, but I might as well be consistent with Version 3,
10649ec7b004SRick Macklem 	 * which is to return ok so long as there are no permission problems.
10659ec7b004SRick Macklem 	 */
10669ec7b004SRick Macklem 	if (retlen > 0) {
1067c057a378SRick Macklem 		nd->nd_repstat = nfsvno_write(vp, off, retlen, &stable,
10689ec7b004SRick Macklem 		    nd->nd_md, nd->nd_dpos, nd->nd_cred, p);
10699ec7b004SRick Macklem 		error = nfsm_advance(nd, NFSM_RNDUP(retlen), -1);
10709ec7b004SRick Macklem 		if (error)
1071ce8d06feSRick Macklem 			goto nfsmout;
10729ec7b004SRick Macklem 	}
10739ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4)
10749ec7b004SRick Macklem 		aftat_ret = 0;
10759ec7b004SRick Macklem 	else
107690d2dfabSRick Macklem 		aftat_ret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
10779ec7b004SRick Macklem 	vput(vp);
10789ec7b004SRick Macklem 	if (!nd->nd_repstat)
10799ec7b004SRick Macklem 		nd->nd_repstat = aftat_ret;
10809ec7b004SRick Macklem 	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
10819ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
10829ec7b004SRick Macklem 			nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
10839ec7b004SRick Macklem 		if (nd->nd_repstat)
1084a9285ae5SZack Kirsch 			goto out;
10859ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
10869ec7b004SRick Macklem 		*tl++ = txdr_unsigned(retlen);
1087e4558aacSXin LI 		/*
1088e4558aacSXin LI 		 * If nfs_async is set, then pretend the write was FILESYNC.
1089e4558aacSXin LI 		 * Warning: Doing this violates RFC1813 and runs a risk
1090e4558aacSXin LI 		 * of data written by a client being lost when the server
1091e4558aacSXin LI 		 * crashes/reboots.
1092e4558aacSXin LI 		 */
1093e4558aacSXin LI 		if (stable == NFSWRITE_UNSTABLE && nfs_async == 0)
10949ec7b004SRick Macklem 			*tl++ = txdr_unsigned(stable);
10959ec7b004SRick Macklem 		else
10969ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSWRITE_FILESYNC);
10979ec7b004SRick Macklem 		/*
10989ec7b004SRick Macklem 		 * Actually, there is no need to txdr these fields,
10999ec7b004SRick Macklem 		 * but it may make the values more human readable,
11009ec7b004SRick Macklem 		 * for debugging purposes.
11019ec7b004SRick Macklem 		 */
11029ec7b004SRick Macklem 		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
11039ec7b004SRick Macklem 		*tl = txdr_unsigned(nfsboottime.tv_usec);
11049ec7b004SRick Macklem 	} else if (!nd->nd_repstat)
11059ec7b004SRick Macklem 		nfsrv_fillattr(nd, &nva);
1106a9285ae5SZack Kirsch 
1107a9285ae5SZack Kirsch out:
1108a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
11099ec7b004SRick Macklem 	return (0);
11109ec7b004SRick Macklem nfsmout:
11119ec7b004SRick Macklem 	vput(vp);
1112a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
11139ec7b004SRick Macklem 	return (error);
11149ec7b004SRick Macklem }
11159ec7b004SRick Macklem 
11169ec7b004SRick Macklem /*
11179ec7b004SRick Macklem  * nfs create service (creates regular files for V2 and V3. Spec. files for V2.)
11189ec7b004SRick Macklem  * now does a truncate to 0 length via. setattr if it already exists
11199ec7b004SRick Macklem  * The core creation routine has been extracted out into nfsrv_creatsub(),
11209ec7b004SRick Macklem  * so it can also be used by nfsrv_open() for V4.
11219ec7b004SRick Macklem  */
1122b9cc3262SRyan Moeller int
11239ec7b004SRick Macklem nfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram,
1124af444b18SEdward Tomasz Napierala     vnode_t dp, struct nfsexstuff *exp)
11259ec7b004SRick Macklem {
11269ec7b004SRick Macklem 	struct nfsvattr nva, dirfor, diraft;
11279ec7b004SRick Macklem 	struct nfsv2_sattr *sp;
11289ec7b004SRick Macklem 	struct nameidata named;
11299ec7b004SRick Macklem 	u_int32_t *tl;
11309ec7b004SRick Macklem 	int error = 0, tsize, dirfor_ret = 1, diraft_ret = 1;
11319ec7b004SRick Macklem 	int how = NFSCREATE_UNCHECKED, exclusive_flag = 0;
11329ec7b004SRick Macklem 	NFSDEV_T rdev = 0;
11339ec7b004SRick Macklem 	vnode_t vp = NULL, dirp = NULL;
11349ec7b004SRick Macklem 	fhandle_t fh;
11359ec7b004SRick Macklem 	char *bufp;
11369ec7b004SRick Macklem 	u_long *hashp;
11379ec7b004SRick Macklem 	enum vtype vtyp;
1138086f6e0cSRick Macklem 	int32_t cverf[2], tverf[2] = { 0, 0 };
1139af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
11409ec7b004SRick Macklem 
11419ec7b004SRick Macklem 	if (nd->nd_repstat) {
11429ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1143a9285ae5SZack Kirsch 		goto out;
11449ec7b004SRick Macklem 	}
11459ec7b004SRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
11466c21f6edSKonstantin Belousov 	    LOCKPARENT | LOCKLEAF | SAVESTART | NOCACHE);
11479ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
11489ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1149a9285ae5SZack Kirsch 	if (error)
1150a9285ae5SZack Kirsch 		goto nfsmout;
11519ec7b004SRick Macklem 	if (!nd->nd_repstat) {
11529ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva);
11539ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV2) {
11549ec7b004SRick Macklem 			NFSM_DISSECT(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
11559ec7b004SRick Macklem 			vtyp = IFTOVT(fxdr_unsigned(u_int32_t, sp->sa_mode));
11569ec7b004SRick Macklem 			if (vtyp == VNON)
11579ec7b004SRick Macklem 				vtyp = VREG;
11589ec7b004SRick Macklem 			NFSVNO_SETATTRVAL(&nva, type, vtyp);
11599ec7b004SRick Macklem 			NFSVNO_SETATTRVAL(&nva, mode,
11609ec7b004SRick Macklem 			    nfstov_mode(sp->sa_mode));
11619ec7b004SRick Macklem 			switch (nva.na_type) {
11629ec7b004SRick Macklem 			case VREG:
11639ec7b004SRick Macklem 				tsize = fxdr_unsigned(int32_t, sp->sa_size);
11649ec7b004SRick Macklem 				if (tsize != -1)
11659ec7b004SRick Macklem 					NFSVNO_SETATTRVAL(&nva, size,
11669ec7b004SRick Macklem 					    (u_quad_t)tsize);
11679ec7b004SRick Macklem 				break;
11689ec7b004SRick Macklem 			case VCHR:
11699ec7b004SRick Macklem 			case VBLK:
11709ec7b004SRick Macklem 			case VFIFO:
11719ec7b004SRick Macklem 				rdev = fxdr_unsigned(NFSDEV_T, sp->sa_size);
11729ec7b004SRick Macklem 				break;
11739ec7b004SRick Macklem 			default:
11749ec7b004SRick Macklem 				break;
117574b8d63dSPedro F. Giffuni 			}
11769ec7b004SRick Macklem 		} else {
11779ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
11789ec7b004SRick Macklem 			how = fxdr_unsigned(int, *tl);
11799ec7b004SRick Macklem 			switch (how) {
11809ec7b004SRick Macklem 			case NFSCREATE_GUARDED:
11819ec7b004SRick Macklem 			case NFSCREATE_UNCHECKED:
1182d8a5961fSMarcelo Araujo 				error = nfsrv_sattr(nd, NULL, &nva, NULL, NULL, p);
11839ec7b004SRick Macklem 				if (error)
11849ec7b004SRick Macklem 					goto nfsmout;
11859ec7b004SRick Macklem 				break;
11869ec7b004SRick Macklem 			case NFSCREATE_EXCLUSIVE:
1187086f6e0cSRick Macklem 				NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
1188086f6e0cSRick Macklem 				cverf[0] = *tl++;
1189086f6e0cSRick Macklem 				cverf[1] = *tl;
11909ec7b004SRick Macklem 				exclusive_flag = 1;
11919ec7b004SRick Macklem 				break;
119274b8d63dSPedro F. Giffuni 			}
11939ec7b004SRick Macklem 			NFSVNO_SETATTRVAL(&nva, type, VREG);
11949ec7b004SRick Macklem 		}
11959ec7b004SRick Macklem 	}
11969ec7b004SRick Macklem 	if (nd->nd_repstat) {
11979ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
11989ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
119990d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dp, &dirfor, nd, p, 1,
120090d2dfabSRick Macklem 			    NULL);
12019ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
12029ec7b004SRick Macklem 			    &diraft);
12039ec7b004SRick Macklem 		}
12049ec7b004SRick Macklem 		vput(dp);
1205a9285ae5SZack Kirsch 		goto out;
12069ec7b004SRick Macklem 	}
12079ec7b004SRick Macklem 
12089ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
12099ec7b004SRick Macklem 	if (dirp) {
12109ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV2) {
12119ec7b004SRick Macklem 			vrele(dirp);
12129ec7b004SRick Macklem 			dirp = NULL;
12139ec7b004SRick Macklem 		} else {
121490d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
121590d2dfabSRick Macklem 			    NULL);
12169ec7b004SRick Macklem 		}
12179ec7b004SRick Macklem 	}
12189ec7b004SRick Macklem 	if (nd->nd_repstat) {
12199ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
12209ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
12219ec7b004SRick Macklem 			    &diraft);
12229ec7b004SRick Macklem 		if (dirp)
12239ec7b004SRick Macklem 			vrele(dirp);
1224a9285ae5SZack Kirsch 		goto out;
12259ec7b004SRick Macklem 	}
12269ec7b004SRick Macklem 
12279ec7b004SRick Macklem 	if (!(nd->nd_flag & ND_NFSV2)) {
12289ec7b004SRick Macklem 		switch (how) {
12299ec7b004SRick Macklem 		case NFSCREATE_GUARDED:
12309ec7b004SRick Macklem 			if (named.ni_vp)
12319ec7b004SRick Macklem 				nd->nd_repstat = EEXIST;
12329ec7b004SRick Macklem 			break;
12339ec7b004SRick Macklem 		case NFSCREATE_UNCHECKED:
12349ec7b004SRick Macklem 			break;
12359ec7b004SRick Macklem 		case NFSCREATE_EXCLUSIVE:
12369ec7b004SRick Macklem 			if (named.ni_vp == NULL)
12379ec7b004SRick Macklem 				NFSVNO_SETATTRVAL(&nva, mode, 0);
12389ec7b004SRick Macklem 			break;
123974b8d63dSPedro F. Giffuni 		}
12409ec7b004SRick Macklem 	}
12419ec7b004SRick Macklem 
12429ec7b004SRick Macklem 	/*
12439ec7b004SRick Macklem 	 * Iff doesn't exist, create it
12449ec7b004SRick Macklem 	 * otherwise just truncate to 0 length
12459ec7b004SRick Macklem 	 *   should I set the mode too ?
12469ec7b004SRick Macklem 	 */
12479ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_createsub(nd, &named, &vp, &nva,
1248127152feSEdward Tomasz Napierala 	    &exclusive_flag, cverf, rdev, exp);
12499ec7b004SRick Macklem 
12509ec7b004SRick Macklem 	if (!nd->nd_repstat) {
12519ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
12529ec7b004SRick Macklem 		if (!nd->nd_repstat)
125390d2dfabSRick Macklem 			nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1,
125490d2dfabSRick Macklem 			    NULL);
12559ec7b004SRick Macklem 		vput(vp);
1256086f6e0cSRick Macklem 		if (!nd->nd_repstat) {
1257086f6e0cSRick Macklem 			tverf[0] = nva.na_atime.tv_sec;
1258086f6e0cSRick Macklem 			tverf[1] = nva.na_atime.tv_nsec;
1259086f6e0cSRick Macklem 		}
12609ec7b004SRick Macklem 	}
12619ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
12629ec7b004SRick Macklem 		if (!nd->nd_repstat) {
12639ec7b004SRick Macklem 			(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
12649ec7b004SRick Macklem 			nfsrv_fillattr(nd, &nva);
12659ec7b004SRick Macklem 		}
12669ec7b004SRick Macklem 	} else {
1267086f6e0cSRick Macklem 		if (exclusive_flag && !nd->nd_repstat && (cverf[0] != tverf[0]
1268086f6e0cSRick Macklem 		    || cverf[1] != tverf[1]))
12699ec7b004SRick Macklem 			nd->nd_repstat = EEXIST;
127090d2dfabSRick Macklem 		diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
12719ec7b004SRick Macklem 		vrele(dirp);
12729ec7b004SRick Macklem 		if (!nd->nd_repstat) {
12739ec7b004SRick Macklem 			(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 1);
12749ec7b004SRick Macklem 			nfsrv_postopattr(nd, 0, &nva);
12759ec7b004SRick Macklem 		}
12769ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
12779ec7b004SRick Macklem 	}
1278a9285ae5SZack Kirsch 
1279a9285ae5SZack Kirsch out:
1280a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
12819ec7b004SRick Macklem 	return (0);
12829ec7b004SRick Macklem nfsmout:
12839ec7b004SRick Macklem 	vput(dp);
12849ec7b004SRick Macklem 	nfsvno_relpathbuf(&named);
1285a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
12869ec7b004SRick Macklem 	return (error);
12879ec7b004SRick Macklem }
12889ec7b004SRick Macklem 
12899ec7b004SRick Macklem /*
12909ec7b004SRick Macklem  * nfs v3 mknod service (and v4 create)
12919ec7b004SRick Macklem  */
1292b9cc3262SRyan Moeller int
12939ec7b004SRick Macklem nfsrvd_mknod(struct nfsrv_descript *nd, __unused int isdgram,
1294af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
12959ec7b004SRick Macklem {
12969ec7b004SRick Macklem 	struct nfsvattr nva, dirfor, diraft;
12979ec7b004SRick Macklem 	u_int32_t *tl;
12989ec7b004SRick Macklem 	struct nameidata named;
12999ec7b004SRick Macklem 	int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
13009ec7b004SRick Macklem 	u_int32_t major, minor;
13019ec7b004SRick Macklem 	enum vtype vtyp = VNON;
13029ec7b004SRick Macklem 	nfstype nfs4type = NFNON;
13039ec7b004SRick Macklem 	vnode_t vp, dirp = NULL;
13049ec7b004SRick Macklem 	nfsattrbit_t attrbits;
13059ec7b004SRick Macklem 	char *bufp = NULL, *pathcp = NULL;
13069ec7b004SRick Macklem 	u_long *hashp, cnflags;
13079ec7b004SRick Macklem 	NFSACL_T *aclp = NULL;
1308af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
13099ec7b004SRick Macklem 
13109ec7b004SRick Macklem 	NFSVNO_ATTRINIT(&nva);
13119ec7b004SRick Macklem 	cnflags = (LOCKPARENT | SAVESTART);
13129ec7b004SRick Macklem 	if (nd->nd_repstat) {
13139ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1314a9285ae5SZack Kirsch 		goto out;
13159ec7b004SRick Macklem 	}
13169ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
1317c3e22f83SRick Macklem 	aclp = acl_alloc(M_WAITOK);
13189ec7b004SRick Macklem 	aclp->acl_cnt = 0;
13199ec7b004SRick Macklem #endif
13209ec7b004SRick Macklem 
13219ec7b004SRick Macklem 	/*
13229ec7b004SRick Macklem 	 * For V4, the creation stuff is here, Yuck!
13239ec7b004SRick Macklem 	 */
13249ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
13259ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
13269ec7b004SRick Macklem 		vtyp = nfsv34tov_type(*tl);
13279ec7b004SRick Macklem 		nfs4type = fxdr_unsigned(nfstype, *tl);
13289ec7b004SRick Macklem 		switch (nfs4type) {
13299ec7b004SRick Macklem 		case NFLNK:
13309ec7b004SRick Macklem 			error = nfsvno_getsymlink(nd, &nva, p, &pathcp,
13319ec7b004SRick Macklem 			    &pathlen);
1332a9285ae5SZack Kirsch 			if (error)
1333a9285ae5SZack Kirsch 				goto nfsmout;
13349ec7b004SRick Macklem 			break;
13359ec7b004SRick Macklem 		case NFCHR:
13369ec7b004SRick Macklem 		case NFBLK:
13379ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
13389ec7b004SRick Macklem 			major = fxdr_unsigned(u_int32_t, *tl++);
13399ec7b004SRick Macklem 			minor = fxdr_unsigned(u_int32_t, *tl);
13409ec7b004SRick Macklem 			nva.na_rdev = NFSMAKEDEV(major, minor);
13419ec7b004SRick Macklem 			break;
13429ec7b004SRick Macklem 		case NFSOCK:
13439ec7b004SRick Macklem 		case NFFIFO:
13449ec7b004SRick Macklem 			break;
13459ec7b004SRick Macklem 		case NFDIR:
1346f61786cbSRick Macklem 			cnflags = (LOCKPARENT | SAVENAME);
13479ec7b004SRick Macklem 			break;
13489ec7b004SRick Macklem 		default:
13499ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_BADTYPE;
13509ec7b004SRick Macklem 			vrele(dp);
13519ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
13529ec7b004SRick Macklem 			acl_free(aclp);
13539ec7b004SRick Macklem #endif
1354a9285ae5SZack Kirsch 			goto out;
1355a9285ae5SZack Kirsch 		}
13569ec7b004SRick Macklem 	}
13576c21f6edSKonstantin Belousov 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, cnflags | NOCACHE);
13589ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
13599ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1360a9285ae5SZack Kirsch 	if (error)
1361a9285ae5SZack Kirsch 		goto nfsmout;
13629ec7b004SRick Macklem 	if (!nd->nd_repstat) {
13639ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
13649ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
13659ec7b004SRick Macklem 			vtyp = nfsv34tov_type(*tl);
13669ec7b004SRick Macklem 		}
1367d8a5961fSMarcelo Araujo 		error = nfsrv_sattr(nd, NULL, &nva, &attrbits, aclp, p);
1368a9285ae5SZack Kirsch 		if (error)
1369a9285ae5SZack Kirsch 			goto nfsmout;
13709ec7b004SRick Macklem 		nva.na_type = vtyp;
13719ec7b004SRick Macklem 		if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV3) &&
13729ec7b004SRick Macklem 		    (vtyp == VCHR || vtyp == VBLK)) {
13739ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
13749ec7b004SRick Macklem 			major = fxdr_unsigned(u_int32_t, *tl++);
13759ec7b004SRick Macklem 			minor = fxdr_unsigned(u_int32_t, *tl);
13769ec7b004SRick Macklem 			nva.na_rdev = NFSMAKEDEV(major, minor);
13779ec7b004SRick Macklem 		}
13789ec7b004SRick Macklem 	}
13799ec7b004SRick Macklem 
138090d2dfabSRick Macklem 	dirfor_ret = nfsvno_getattr(dp, &dirfor, nd, p, 0, NULL);
13819ec7b004SRick Macklem 	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
13829ec7b004SRick Macklem 		if (!dirfor_ret && NFSVNO_ISSETGID(&nva) &&
13839ec7b004SRick Macklem 		    dirfor.na_gid == nva.na_gid)
13849ec7b004SRick Macklem 			NFSVNO_UNSET(&nva, gid);
13859ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
13869ec7b004SRick Macklem 	}
13879ec7b004SRick Macklem 	if (nd->nd_repstat) {
13889ec7b004SRick Macklem 		vrele(dp);
13899ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
13909ec7b004SRick Macklem 		acl_free(aclp);
13919ec7b004SRick Macklem #endif
13929ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
13939ec7b004SRick Macklem 		if (pathcp)
1394222daa42SConrad Meyer 			free(pathcp, M_TEMP);
13959ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
13969ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
13979ec7b004SRick Macklem 			    &diraft);
1398a9285ae5SZack Kirsch 		goto out;
13999ec7b004SRick Macklem 	}
14009ec7b004SRick Macklem 
14019ec7b004SRick Macklem 	/*
14029ec7b004SRick Macklem 	 * Yuck! For V4, mkdir and link are here and some V4 clients don't fill
14039ec7b004SRick Macklem 	 * in va_mode, so we'll have to set a default here.
14049ec7b004SRick Macklem 	 */
14059ec7b004SRick Macklem 	if (NFSVNO_NOTSETMODE(&nva)) {
14069ec7b004SRick Macklem 		if (vtyp == VLNK)
14079ec7b004SRick Macklem 			nva.na_mode = 0755;
14089ec7b004SRick Macklem 		else
14099ec7b004SRick Macklem 			nva.na_mode = 0400;
14109ec7b004SRick Macklem 	}
14119ec7b004SRick Macklem 
14129ec7b004SRick Macklem 	if (vtyp == VDIR)
14139ec7b004SRick Macklem 		named.ni_cnd.cn_flags |= WILLBEDIR;
14149ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
14159ec7b004SRick Macklem 	if (nd->nd_repstat) {
14169ec7b004SRick Macklem 		if (dirp) {
14179ec7b004SRick Macklem 			if (nd->nd_flag & ND_NFSV3)
141890d2dfabSRick Macklem 				dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd,
141990d2dfabSRick Macklem 				    p, 0, NULL);
14209ec7b004SRick Macklem 			vrele(dirp);
14219ec7b004SRick Macklem 		}
14229ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
14239ec7b004SRick Macklem 		acl_free(aclp);
14249ec7b004SRick Macklem #endif
14259ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
14269ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
14279ec7b004SRick Macklem 			    &diraft);
1428a9285ae5SZack Kirsch 		goto out;
14299ec7b004SRick Macklem 	}
14309ec7b004SRick Macklem 	if (dirp)
143190d2dfabSRick Macklem 		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL);
14329ec7b004SRick Macklem 
14339ec7b004SRick Macklem 	if ((nd->nd_flag & ND_NFSV4) && (vtyp == VDIR || vtyp == VLNK)) {
14349ec7b004SRick Macklem 		if (vtyp == VDIR) {
14359ec7b004SRick Macklem 			nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp,
14369ec7b004SRick Macklem 			    &dirfor, &diraft, &diraft_ret, &attrbits, aclp, p,
14379ec7b004SRick Macklem 			    exp);
14389ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
14399ec7b004SRick Macklem 			acl_free(aclp);
14409ec7b004SRick Macklem #endif
1441a9285ae5SZack Kirsch 			goto out;
14429ec7b004SRick Macklem 		} else if (vtyp == VLNK) {
14439ec7b004SRick Macklem 			nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
14449ec7b004SRick Macklem 			    &dirfor, &diraft, &diraft_ret, &attrbits,
14459ec7b004SRick Macklem 			    aclp, p, exp, pathcp, pathlen);
14469ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
14479ec7b004SRick Macklem 			acl_free(aclp);
14489ec7b004SRick Macklem #endif
1449222daa42SConrad Meyer 			free(pathcp, M_TEMP);
1450a9285ae5SZack Kirsch 			goto out;
14519ec7b004SRick Macklem 		}
14529ec7b004SRick Macklem 	}
14539ec7b004SRick Macklem 
14549ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_mknod(&named, &nva, nd->nd_cred, p);
14559ec7b004SRick Macklem 	if (!nd->nd_repstat) {
14569ec7b004SRick Macklem 		vp = named.ni_vp;
14579ec7b004SRick Macklem 		nfsrv_fixattr(nd, vp, &nva, aclp, p, &attrbits, exp);
14589ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
14599ec7b004SRick Macklem 		if ((nd->nd_flag & ND_NFSV3) && !nd->nd_repstat)
146090d2dfabSRick Macklem 			nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1,
146190d2dfabSRick Macklem 			    NULL);
146281f78d99SRick Macklem 		if (vpp != NULL && nd->nd_repstat == 0) {
1463b249ce48SMateusz Guzik 			NFSVOPUNLOCK(vp);
14649ec7b004SRick Macklem 			*vpp = vp;
146581f78d99SRick Macklem 		} else
14669ec7b004SRick Macklem 			vput(vp);
14679ec7b004SRick Macklem 	}
14689ec7b004SRick Macklem 
146990d2dfabSRick Macklem 	diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
14709ec7b004SRick Macklem 	vrele(dirp);
14719ec7b004SRick Macklem 	if (!nd->nd_repstat) {
14729ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
14739ec7b004SRick Macklem 			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
14749ec7b004SRick Macklem 			nfsrv_postopattr(nd, 0, &nva);
14759ec7b004SRick Macklem 		} else {
14769ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
14779ec7b004SRick Macklem 			*tl++ = newnfs_false;
14789ec7b004SRick Macklem 			txdr_hyper(dirfor.na_filerev, tl);
14799ec7b004SRick Macklem 			tl += 2;
14809ec7b004SRick Macklem 			txdr_hyper(diraft.na_filerev, tl);
14819ec7b004SRick Macklem 			(void) nfsrv_putattrbit(nd, &attrbits);
14829ec7b004SRick Macklem 		}
14839ec7b004SRick Macklem 	}
14849ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
14859ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
14869ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
14879ec7b004SRick Macklem 	acl_free(aclp);
14889ec7b004SRick Macklem #endif
1489a9285ae5SZack Kirsch 
1490a9285ae5SZack Kirsch out:
1491a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
14929ec7b004SRick Macklem 	return (0);
14939ec7b004SRick Macklem nfsmout:
14949ec7b004SRick Macklem 	vrele(dp);
14959ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
14969ec7b004SRick Macklem 	acl_free(aclp);
14979ec7b004SRick Macklem #endif
14989ec7b004SRick Macklem 	if (bufp)
14999ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
15009ec7b004SRick Macklem 	if (pathcp)
1501222daa42SConrad Meyer 		free(pathcp, M_TEMP);
1502a9285ae5SZack Kirsch 
1503a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
15049ec7b004SRick Macklem 	return (error);
15059ec7b004SRick Macklem }
15069ec7b004SRick Macklem 
15079ec7b004SRick Macklem /*
15089ec7b004SRick Macklem  * nfs remove service
15099ec7b004SRick Macklem  */
1510b9cc3262SRyan Moeller int
15119ec7b004SRick Macklem nfsrvd_remove(struct nfsrv_descript *nd, __unused int isdgram,
1512af444b18SEdward Tomasz Napierala     vnode_t dp, struct nfsexstuff *exp)
15139ec7b004SRick Macklem {
15149ec7b004SRick Macklem 	struct nameidata named;
15159ec7b004SRick Macklem 	u_int32_t *tl;
1516a9285ae5SZack Kirsch 	int error = 0, dirfor_ret = 1, diraft_ret = 1;
15179ec7b004SRick Macklem 	vnode_t dirp = NULL;
15189ec7b004SRick Macklem 	struct nfsvattr dirfor, diraft;
15199ec7b004SRick Macklem 	char *bufp;
15209ec7b004SRick Macklem 	u_long *hashp;
1521af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
15229ec7b004SRick Macklem 
15239ec7b004SRick Macklem 	if (nd->nd_repstat) {
15249ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1525a9285ae5SZack Kirsch 		goto out;
15269ec7b004SRick Macklem 	}
15279ec7b004SRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, DELETE,
15289ec7b004SRick Macklem 	    LOCKPARENT | LOCKLEAF);
15299ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
15309ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
15319ec7b004SRick Macklem 	if (error) {
15329ec7b004SRick Macklem 		vput(dp);
15339ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
1534a9285ae5SZack Kirsch 		goto out;
15359ec7b004SRick Macklem 	}
15369ec7b004SRick Macklem 	if (!nd->nd_repstat) {
15379ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
15389ec7b004SRick Macklem 	} else {
15399ec7b004SRick Macklem 		vput(dp);
15409ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
15419ec7b004SRick Macklem 	}
15429ec7b004SRick Macklem 	if (dirp) {
15439ec7b004SRick Macklem 		if (!(nd->nd_flag & ND_NFSV2)) {
154490d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
154590d2dfabSRick Macklem 			    NULL);
15469ec7b004SRick Macklem 		} else {
15479ec7b004SRick Macklem 			vrele(dirp);
15489ec7b004SRick Macklem 			dirp = NULL;
15499ec7b004SRick Macklem 		}
15509ec7b004SRick Macklem 	}
15519ec7b004SRick Macklem 	if (!nd->nd_repstat) {
15529ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV4) {
15539ec7b004SRick Macklem 			if (vnode_vtype(named.ni_vp) == VDIR)
15549ec7b004SRick Macklem 				nd->nd_repstat = nfsvno_rmdirsub(&named, 1,
15559ec7b004SRick Macklem 				    nd->nd_cred, p, exp);
15569ec7b004SRick Macklem 			else
15579ec7b004SRick Macklem 				nd->nd_repstat = nfsvno_removesub(&named, 1,
15589ec7b004SRick Macklem 				    nd->nd_cred, p, exp);
15599ec7b004SRick Macklem 		} else if (nd->nd_procnum == NFSPROC_RMDIR) {
15609ec7b004SRick Macklem 			nd->nd_repstat = nfsvno_rmdirsub(&named, 0,
15619ec7b004SRick Macklem 			    nd->nd_cred, p, exp);
15629ec7b004SRick Macklem 		} else {
15639ec7b004SRick Macklem 			nd->nd_repstat = nfsvno_removesub(&named, 0,
15649ec7b004SRick Macklem 			    nd->nd_cred, p, exp);
15659ec7b004SRick Macklem 		}
15669ec7b004SRick Macklem 	}
15679ec7b004SRick Macklem 	if (!(nd->nd_flag & ND_NFSV2)) {
15689ec7b004SRick Macklem 		if (dirp) {
156990d2dfabSRick Macklem 			diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0,
157090d2dfabSRick Macklem 			    NULL);
15719ec7b004SRick Macklem 			vrele(dirp);
15729ec7b004SRick Macklem 		}
15739ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
15749ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
15759ec7b004SRick Macklem 			    &diraft);
15769ec7b004SRick Macklem 		} else if (!nd->nd_repstat) {
15779ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
15789ec7b004SRick Macklem 			*tl++ = newnfs_false;
15799ec7b004SRick Macklem 			txdr_hyper(dirfor.na_filerev, tl);
15809ec7b004SRick Macklem 			tl += 2;
15819ec7b004SRick Macklem 			txdr_hyper(diraft.na_filerev, tl);
15829ec7b004SRick Macklem 		}
15839ec7b004SRick Macklem 	}
1584a9285ae5SZack Kirsch 
1585a9285ae5SZack Kirsch out:
1586a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
1587a9285ae5SZack Kirsch 	return (error);
15889ec7b004SRick Macklem }
15899ec7b004SRick Macklem 
15909ec7b004SRick Macklem /*
15919ec7b004SRick Macklem  * nfs rename service
15929ec7b004SRick Macklem  */
1593b9cc3262SRyan Moeller int
15949ec7b004SRick Macklem nfsrvd_rename(struct nfsrv_descript *nd, int isdgram,
1595af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t todp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
15969ec7b004SRick Macklem {
15979ec7b004SRick Macklem 	u_int32_t *tl;
1598a9285ae5SZack Kirsch 	int error = 0, fdirfor_ret = 1, fdiraft_ret = 1;
15999ec7b004SRick Macklem 	int tdirfor_ret = 1, tdiraft_ret = 1;
16009ec7b004SRick Macklem 	struct nameidata fromnd, tond;
16019ec7b004SRick Macklem 	vnode_t fdirp = NULL, tdirp = NULL, tdp = NULL;
16029ec7b004SRick Macklem 	struct nfsvattr fdirfor, fdiraft, tdirfor, tdiraft;
16039ec7b004SRick Macklem 	struct nfsexstuff tnes;
16049ec7b004SRick Macklem 	struct nfsrvfh tfh;
16059ec7b004SRick Macklem 	char *bufp, *tbufp = NULL;
16069ec7b004SRick Macklem 	u_long *hashp;
16076b3dfc6aSRick Macklem 	fhandle_t fh;
1608af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
16099ec7b004SRick Macklem 
16109ec7b004SRick Macklem 	if (nd->nd_repstat) {
16119ec7b004SRick Macklem 		nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
16129ec7b004SRick Macklem 		nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
1613a9285ae5SZack Kirsch 		goto out;
16149ec7b004SRick Macklem 	}
16159ec7b004SRick Macklem 	if (!(nd->nd_flag & ND_NFSV2))
161690d2dfabSRick Macklem 		fdirfor_ret = nfsvno_getattr(dp, &fdirfor, nd, p, 1, NULL);
16179ec7b004SRick Macklem 	tond.ni_cnd.cn_nameiop = 0;
16189ec7b004SRick Macklem 	tond.ni_startdir = NULL;
16199ec7b004SRick Macklem 	NFSNAMEICNDSET(&fromnd.ni_cnd, nd->nd_cred, DELETE, WANTPARENT | SAVESTART);
16209ec7b004SRick Macklem 	nfsvno_setpathbuf(&fromnd, &bufp, &hashp);
16219ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &fromnd.ni_pathlen);
16229ec7b004SRick Macklem 	if (error) {
16239ec7b004SRick Macklem 		vput(dp);
16249ec7b004SRick Macklem 		if (todp)
16259ec7b004SRick Macklem 			vrele(todp);
16269ec7b004SRick Macklem 		nfsvno_relpathbuf(&fromnd);
1627a9285ae5SZack Kirsch 		goto out;
16289ec7b004SRick Macklem 	}
162925bfde79SXin LI 	/*
163025bfde79SXin LI 	 * Unlock dp in this code section, so it is unlocked before
163125bfde79SXin LI 	 * tdp gets locked. This avoids a potential LOR if tdp is the
163225bfde79SXin LI 	 * parent directory of dp.
163325bfde79SXin LI 	 */
16349ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
16359ec7b004SRick Macklem 		tdp = todp;
16369ec7b004SRick Macklem 		tnes = *toexp;
163725bfde79SXin LI 		if (dp != tdp) {
1638b249ce48SMateusz Guzik 			NFSVOPUNLOCK(dp);
163990d2dfabSRick Macklem 			/* Might lock tdp. */
164090d2dfabSRick Macklem 			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd, p, 0,
164190d2dfabSRick Macklem 			    NULL);
164225bfde79SXin LI 		} else {
164390d2dfabSRick Macklem 			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd, p, 1,
164490d2dfabSRick Macklem 			    NULL);
1645b249ce48SMateusz Guzik 			NFSVOPUNLOCK(dp);
164625bfde79SXin LI 		}
16479ec7b004SRick Macklem 	} else {
16486b3dfc6aSRick Macklem 		tfh.nfsrvfh_len = 0;
16499ec7b004SRick Macklem 		error = nfsrv_mtofh(nd, &tfh);
16506b3dfc6aSRick Macklem 		if (error == 0)
16516b3dfc6aSRick Macklem 			error = nfsvno_getfh(dp, &fh, p);
16529ec7b004SRick Macklem 		if (error) {
16539ec7b004SRick Macklem 			vput(dp);
16549ec7b004SRick Macklem 			/* todp is always NULL except NFSv4 */
16559ec7b004SRick Macklem 			nfsvno_relpathbuf(&fromnd);
1656a9285ae5SZack Kirsch 			goto out;
16579ec7b004SRick Macklem 		}
16586b3dfc6aSRick Macklem 
16596b3dfc6aSRick Macklem 		/* If this is the same file handle, just VREF() the vnode. */
16606b3dfc6aSRick Macklem 		if (tfh.nfsrvfh_len == NFSX_MYFH &&
16616b3dfc6aSRick Macklem 		    !NFSBCMP(tfh.nfsrvfh_data, &fh, NFSX_MYFH)) {
16626b3dfc6aSRick Macklem 			VREF(dp);
16636b3dfc6aSRick Macklem 			tdp = dp;
16646b3dfc6aSRick Macklem 			tnes = *exp;
166590d2dfabSRick Macklem 			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd, p, 1,
166690d2dfabSRick Macklem 			    NULL);
1667b249ce48SMateusz Guzik 			NFSVOPUNLOCK(dp);
16686b3dfc6aSRick Macklem 		} else {
1669b249ce48SMateusz Guzik 			NFSVOPUNLOCK(dp);
16706b3dfc6aSRick Macklem 			nd->nd_cred->cr_uid = nd->nd_saveduid;
16716b3dfc6aSRick Macklem 			nfsd_fhtovp(nd, &tfh, LK_EXCLUSIVE, &tdp, &tnes, NULL,
16725edc9102SEdward Tomasz Napierala 			    0);	/* Locks tdp. */
16736b3dfc6aSRick Macklem 			if (tdp) {
167490d2dfabSRick Macklem 				tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd,
167590d2dfabSRick Macklem 				    p, 1, NULL);
1676b249ce48SMateusz Guzik 				NFSVOPUNLOCK(tdp);
16779ec7b004SRick Macklem 			}
16789ec7b004SRick Macklem 		}
16796b3dfc6aSRick Macklem 	}
16809ec7b004SRick Macklem 	NFSNAMEICNDSET(&tond.ni_cnd, nd->nd_cred, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART);
16819ec7b004SRick Macklem 	nfsvno_setpathbuf(&tond, &tbufp, &hashp);
16829ec7b004SRick Macklem 	if (!nd->nd_repstat) {
16839ec7b004SRick Macklem 		error = nfsrv_parsename(nd, tbufp, hashp, &tond.ni_pathlen);
16849ec7b004SRick Macklem 		if (error) {
16858974bc2fSRick Macklem 			if (tdp)
16869ec7b004SRick Macklem 				vrele(tdp);
168725bfde79SXin LI 			vrele(dp);
16889ec7b004SRick Macklem 			nfsvno_relpathbuf(&fromnd);
16899ec7b004SRick Macklem 			nfsvno_relpathbuf(&tond);
1690a9285ae5SZack Kirsch 			goto out;
16919ec7b004SRick Macklem 		}
16929ec7b004SRick Macklem 	}
16939ec7b004SRick Macklem 	if (nd->nd_repstat) {
16949ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
16959ec7b004SRick Macklem 			nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
16969ec7b004SRick Macklem 			    &fdiraft);
16979ec7b004SRick Macklem 			nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
16989ec7b004SRick Macklem 			    &tdiraft);
16999ec7b004SRick Macklem 		}
17008974bc2fSRick Macklem 		if (tdp)
17019ec7b004SRick Macklem 			vrele(tdp);
170225bfde79SXin LI 		vrele(dp);
17039ec7b004SRick Macklem 		nfsvno_relpathbuf(&fromnd);
17049ec7b004SRick Macklem 		nfsvno_relpathbuf(&tond);
1705a9285ae5SZack Kirsch 		goto out;
17069ec7b004SRick Macklem 	}
17079ec7b004SRick Macklem 
17089ec7b004SRick Macklem 	/*
17099ec7b004SRick Macklem 	 * Done parsing, now down to business.
17109ec7b004SRick Macklem 	 */
171125bfde79SXin LI 	nd->nd_repstat = nfsvno_namei(nd, &fromnd, dp, 0, exp, p, &fdirp);
17129ec7b004SRick Macklem 	if (nd->nd_repstat) {
17139ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
17149ec7b004SRick Macklem 			nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
17159ec7b004SRick Macklem 			    &fdiraft);
17169ec7b004SRick Macklem 			nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
17179ec7b004SRick Macklem 			    &tdiraft);
17189ec7b004SRick Macklem 		}
17199ec7b004SRick Macklem 		if (fdirp)
17209ec7b004SRick Macklem 			vrele(fdirp);
17218974bc2fSRick Macklem 		if (tdp)
17229ec7b004SRick Macklem 			vrele(tdp);
17239ec7b004SRick Macklem 		nfsvno_relpathbuf(&tond);
1724a9285ae5SZack Kirsch 		goto out;
17259ec7b004SRick Macklem 	}
17269ec7b004SRick Macklem 	if (vnode_vtype(fromnd.ni_vp) == VDIR)
17279ec7b004SRick Macklem 		tond.ni_cnd.cn_flags |= WILLBEDIR;
17289ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_namei(nd, &tond, tdp, 0, &tnes, p, &tdirp);
17299ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_rename(&fromnd, &tond, nd->nd_repstat,
17309ec7b004SRick Macklem 	    nd->nd_flag, nd->nd_cred, p);
17319ec7b004SRick Macklem 	if (fdirp)
173290d2dfabSRick Macklem 		fdiraft_ret = nfsvno_getattr(fdirp, &fdiraft, nd, p, 0, NULL);
17339ec7b004SRick Macklem 	if (tdirp)
173490d2dfabSRick Macklem 		tdiraft_ret = nfsvno_getattr(tdirp, &tdiraft, nd, p, 0, NULL);
17359ec7b004SRick Macklem 	if (fdirp)
17369ec7b004SRick Macklem 		vrele(fdirp);
17379ec7b004SRick Macklem 	if (tdirp)
17389ec7b004SRick Macklem 		vrele(tdirp);
17399ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
17409ec7b004SRick Macklem 		nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
17419ec7b004SRick Macklem 		nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
17429ec7b004SRick Macklem 	} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
17439ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 10 * NFSX_UNSIGNED);
17449ec7b004SRick Macklem 		*tl++ = newnfs_false;
17459ec7b004SRick Macklem 		txdr_hyper(fdirfor.na_filerev, tl);
17469ec7b004SRick Macklem 		tl += 2;
17479ec7b004SRick Macklem 		txdr_hyper(fdiraft.na_filerev, tl);
17489ec7b004SRick Macklem 		tl += 2;
17499ec7b004SRick Macklem 		*tl++ = newnfs_false;
17509ec7b004SRick Macklem 		txdr_hyper(tdirfor.na_filerev, tl);
17519ec7b004SRick Macklem 		tl += 2;
17529ec7b004SRick Macklem 		txdr_hyper(tdiraft.na_filerev, tl);
17539ec7b004SRick Macklem 	}
1754a9285ae5SZack Kirsch 
1755a9285ae5SZack Kirsch out:
1756a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
1757a9285ae5SZack Kirsch 	return (error);
17589ec7b004SRick Macklem }
17599ec7b004SRick Macklem 
17609ec7b004SRick Macklem /*
17619ec7b004SRick Macklem  * nfs link service
17629ec7b004SRick Macklem  */
1763b9cc3262SRyan Moeller int
17649ec7b004SRick Macklem nfsrvd_link(struct nfsrv_descript *nd, int isdgram,
1765af444b18SEdward Tomasz Napierala     vnode_t vp, vnode_t tovp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
17669ec7b004SRick Macklem {
17679ec7b004SRick Macklem 	struct nameidata named;
17689ec7b004SRick Macklem 	u_int32_t *tl;
17699ec7b004SRick Macklem 	int error = 0, dirfor_ret = 1, diraft_ret = 1, getret = 1;
17709ec7b004SRick Macklem 	vnode_t dirp = NULL, dp = NULL;
17719ec7b004SRick Macklem 	struct nfsvattr dirfor, diraft, at;
17729ec7b004SRick Macklem 	struct nfsexstuff tnes;
17739ec7b004SRick Macklem 	struct nfsrvfh dfh;
17749ec7b004SRick Macklem 	char *bufp;
17759ec7b004SRick Macklem 	u_long *hashp;
1776af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
17779ec7b004SRick Macklem 
17789ec7b004SRick Macklem 	if (nd->nd_repstat) {
17799ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
17809ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1781a9285ae5SZack Kirsch 		goto out;
17829ec7b004SRick Macklem 	}
1783b249ce48SMateusz Guzik 	NFSVOPUNLOCK(vp);
17849ec7b004SRick Macklem 	if (vnode_vtype(vp) == VDIR) {
17859ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV4)
17869ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_ISDIR;
17879ec7b004SRick Macklem 		else
17889ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
17899ec7b004SRick Macklem 		if (tovp)
17909ec7b004SRick Macklem 			vrele(tovp);
17919ec7b004SRick Macklem 	}
17929ec7b004SRick Macklem 	if (!nd->nd_repstat) {
17939ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV4) {
17949ec7b004SRick Macklem 			dp = tovp;
17959ec7b004SRick Macklem 			tnes = *toexp;
17969ec7b004SRick Macklem 		} else {
17979ec7b004SRick Macklem 			error = nfsrv_mtofh(nd, &dfh);
17989ec7b004SRick Macklem 			if (error) {
17999ec7b004SRick Macklem 				vrele(vp);
18009ec7b004SRick Macklem 				/* tovp is always NULL unless NFSv4 */
1801a9285ae5SZack Kirsch 				goto out;
18029ec7b004SRick Macklem 			}
18035edc9102SEdward Tomasz Napierala 			nfsd_fhtovp(nd, &dfh, LK_EXCLUSIVE, &dp, &tnes, NULL, 0);
18049ec7b004SRick Macklem 			if (dp)
1805b249ce48SMateusz Guzik 				NFSVOPUNLOCK(dp);
18069ec7b004SRick Macklem 		}
18079ec7b004SRick Macklem 	}
1808f61786cbSRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
18096c21f6edSKonstantin Belousov 	    LOCKPARENT | SAVENAME | NOCACHE);
18109ec7b004SRick Macklem 	if (!nd->nd_repstat) {
18119ec7b004SRick Macklem 		nfsvno_setpathbuf(&named, &bufp, &hashp);
18129ec7b004SRick Macklem 		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
18139ec7b004SRick Macklem 		if (error) {
18149ec7b004SRick Macklem 			vrele(vp);
18158974bc2fSRick Macklem 			if (dp)
18169ec7b004SRick Macklem 				vrele(dp);
18179ec7b004SRick Macklem 			nfsvno_relpathbuf(&named);
1818a9285ae5SZack Kirsch 			goto out;
18199ec7b004SRick Macklem 		}
18209ec7b004SRick Macklem 		if (!nd->nd_repstat) {
18219ec7b004SRick Macklem 			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, &tnes,
18229ec7b004SRick Macklem 			    p, &dirp);
18239ec7b004SRick Macklem 		} else {
18249ec7b004SRick Macklem 			if (dp)
18259ec7b004SRick Macklem 				vrele(dp);
18269ec7b004SRick Macklem 			nfsvno_relpathbuf(&named);
18279ec7b004SRick Macklem 		}
18289ec7b004SRick Macklem 	}
18299ec7b004SRick Macklem 	if (dirp) {
18309ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV2) {
18319ec7b004SRick Macklem 			vrele(dirp);
18329ec7b004SRick Macklem 			dirp = NULL;
18339ec7b004SRick Macklem 		} else {
183490d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
183590d2dfabSRick Macklem 			    NULL);
18369ec7b004SRick Macklem 		}
18379ec7b004SRick Macklem 	}
18389ec7b004SRick Macklem 	if (!nd->nd_repstat)
18399ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_link(&named, vp, nd->nd_cred, p, exp);
18409ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
184190d2dfabSRick Macklem 		getret = nfsvno_getattr(vp, &at, nd, p, 0, NULL);
18429ec7b004SRick Macklem 	if (dirp) {
184390d2dfabSRick Macklem 		diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
18449ec7b004SRick Macklem 		vrele(dirp);
18459ec7b004SRick Macklem 	}
18469ec7b004SRick Macklem 	vrele(vp);
18479ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
18489ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
18499ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
18509ec7b004SRick Macklem 	} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
18519ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
18529ec7b004SRick Macklem 		*tl++ = newnfs_false;
18539ec7b004SRick Macklem 		txdr_hyper(dirfor.na_filerev, tl);
18549ec7b004SRick Macklem 		tl += 2;
18559ec7b004SRick Macklem 		txdr_hyper(diraft.na_filerev, tl);
18569ec7b004SRick Macklem 	}
1857a9285ae5SZack Kirsch 
1858a9285ae5SZack Kirsch out:
1859a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
1860a9285ae5SZack Kirsch 	return (error);
18619ec7b004SRick Macklem }
18629ec7b004SRick Macklem 
18639ec7b004SRick Macklem /*
18649ec7b004SRick Macklem  * nfs symbolic link service
18659ec7b004SRick Macklem  */
1866b9cc3262SRyan Moeller int
18679ec7b004SRick Macklem nfsrvd_symlink(struct nfsrv_descript *nd, __unused int isdgram,
1868af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
18699ec7b004SRick Macklem {
18709ec7b004SRick Macklem 	struct nfsvattr nva, dirfor, diraft;
18719ec7b004SRick Macklem 	struct nameidata named;
1872a9285ae5SZack Kirsch 	int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
18739ec7b004SRick Macklem 	vnode_t dirp = NULL;
18749ec7b004SRick Macklem 	char *bufp, *pathcp = NULL;
18759ec7b004SRick Macklem 	u_long *hashp;
1876af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
18779ec7b004SRick Macklem 
18789ec7b004SRick Macklem 	if (nd->nd_repstat) {
18799ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1880a9285ae5SZack Kirsch 		goto out;
18819ec7b004SRick Macklem 	}
18829ec7b004SRick Macklem 	if (vpp)
18839ec7b004SRick Macklem 		*vpp = NULL;
18849ec7b004SRick Macklem 	NFSVNO_ATTRINIT(&nva);
18859ec7b004SRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
18866c21f6edSKonstantin Belousov 	    LOCKPARENT | SAVESTART | NOCACHE);
18879ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
18889ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
18899ec7b004SRick Macklem 	if (!error && !nd->nd_repstat)
18909ec7b004SRick Macklem 		error = nfsvno_getsymlink(nd, &nva, p, &pathcp, &pathlen);
18919ec7b004SRick Macklem 	if (error) {
18929ec7b004SRick Macklem 		vrele(dp);
18939ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
1894a9285ae5SZack Kirsch 		goto out;
18959ec7b004SRick Macklem 	}
18969ec7b004SRick Macklem 	if (!nd->nd_repstat) {
18979ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
18989ec7b004SRick Macklem 	} else {
18999ec7b004SRick Macklem 		vrele(dp);
19009ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
19019ec7b004SRick Macklem 	}
19029ec7b004SRick Macklem 	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
19039ec7b004SRick Macklem 		vrele(dirp);
19049ec7b004SRick Macklem 		dirp = NULL;
19059ec7b004SRick Macklem 	}
19069ec7b004SRick Macklem 
19079ec7b004SRick Macklem 	/*
19089ec7b004SRick Macklem 	 * And call nfsrvd_symlinksub() to do the common code. It will
19099ec7b004SRick Macklem 	 * return EBADRPC upon a parsing error, 0 otherwise.
19109ec7b004SRick Macklem 	 */
19119ec7b004SRick Macklem 	if (!nd->nd_repstat) {
19129ec7b004SRick Macklem 		if (dirp != NULL)
191390d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
191490d2dfabSRick Macklem 			    NULL);
19159ec7b004SRick Macklem 		nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
19169ec7b004SRick Macklem 		    &dirfor, &diraft, &diraft_ret, NULL, NULL, p, exp,
19179ec7b004SRick Macklem 		    pathcp, pathlen);
19189ec7b004SRick Macklem 	} else if (dirp != NULL) {
191990d2dfabSRick Macklem 		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL);
19209ec7b004SRick Macklem 		vrele(dirp);
19219ec7b004SRick Macklem 	}
19229ec7b004SRick Macklem 	if (pathcp)
1923222daa42SConrad Meyer 		free(pathcp, M_TEMP);
19249ec7b004SRick Macklem 
19259ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
19269ec7b004SRick Macklem 		if (!nd->nd_repstat) {
19279ec7b004SRick Macklem 			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
19289ec7b004SRick Macklem 			nfsrv_postopattr(nd, 0, &nva);
19299ec7b004SRick Macklem 		}
19309ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
19319ec7b004SRick Macklem 	}
1932a9285ae5SZack Kirsch 
1933a9285ae5SZack Kirsch out:
1934a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
1935a9285ae5SZack Kirsch 	return (error);
19369ec7b004SRick Macklem }
19379ec7b004SRick Macklem 
19389ec7b004SRick Macklem /*
19399ec7b004SRick Macklem  * Common code for creating a symbolic link.
19409ec7b004SRick Macklem  */
19419ec7b004SRick Macklem static void
19429ec7b004SRick Macklem nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
19439ec7b004SRick Macklem     struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
19449ec7b004SRick Macklem     vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
19459ec7b004SRick Macklem     int *diraft_retp, nfsattrbit_t *attrbitp,
19469ec7b004SRick Macklem     NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
19479ec7b004SRick Macklem     int pathlen)
19489ec7b004SRick Macklem {
19499ec7b004SRick Macklem 	u_int32_t *tl;
19509ec7b004SRick Macklem 
19519ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_symlink(ndp, nvap, pathcp, pathlen,
19529ec7b004SRick Macklem 	    !(nd->nd_flag & ND_NFSV2), nd->nd_saveduid, nd->nd_cred, p, exp);
19539ec7b004SRick Macklem 	if (!nd->nd_repstat && !(nd->nd_flag & ND_NFSV2)) {
19549ec7b004SRick Macklem 		nfsrv_fixattr(nd, ndp->ni_vp, nvap, aclp, p, attrbitp, exp);
19559ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
19569ec7b004SRick Macklem 			nd->nd_repstat = nfsvno_getfh(ndp->ni_vp, fhp, p);
19579ec7b004SRick Macklem 			if (!nd->nd_repstat)
19589ec7b004SRick Macklem 				nd->nd_repstat = nfsvno_getattr(ndp->ni_vp,
195990d2dfabSRick Macklem 				    nvap, nd, p, 1, NULL);
19609ec7b004SRick Macklem 		}
196181f78d99SRick Macklem 		if (vpp != NULL && nd->nd_repstat == 0) {
1962b249ce48SMateusz Guzik 			NFSVOPUNLOCK(ndp->ni_vp);
19639ec7b004SRick Macklem 			*vpp = ndp->ni_vp;
196481f78d99SRick Macklem 		} else
19659ec7b004SRick Macklem 			vput(ndp->ni_vp);
19669ec7b004SRick Macklem 	}
19679ec7b004SRick Macklem 	if (dirp) {
196890d2dfabSRick Macklem 		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd, p, 0, NULL);
19699ec7b004SRick Macklem 		vrele(dirp);
19709ec7b004SRick Macklem 	}
19719ec7b004SRick Macklem 	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
19729ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
19739ec7b004SRick Macklem 		*tl++ = newnfs_false;
19749ec7b004SRick Macklem 		txdr_hyper(dirforp->na_filerev, tl);
19759ec7b004SRick Macklem 		tl += 2;
19769ec7b004SRick Macklem 		txdr_hyper(diraftp->na_filerev, tl);
19779ec7b004SRick Macklem 		(void) nfsrv_putattrbit(nd, attrbitp);
19789ec7b004SRick Macklem 	}
1979a9285ae5SZack Kirsch 
1980a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
19819ec7b004SRick Macklem }
19829ec7b004SRick Macklem 
19839ec7b004SRick Macklem /*
19849ec7b004SRick Macklem  * nfs mkdir service
19859ec7b004SRick Macklem  */
1986b9cc3262SRyan Moeller int
19879ec7b004SRick Macklem nfsrvd_mkdir(struct nfsrv_descript *nd, __unused int isdgram,
1988af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
19899ec7b004SRick Macklem {
19909ec7b004SRick Macklem 	struct nfsvattr nva, dirfor, diraft;
19919ec7b004SRick Macklem 	struct nameidata named;
19929ec7b004SRick Macklem 	u_int32_t *tl;
1993a9285ae5SZack Kirsch 	int error = 0, dirfor_ret = 1, diraft_ret = 1;
19949ec7b004SRick Macklem 	vnode_t dirp = NULL;
19959ec7b004SRick Macklem 	char *bufp;
19969ec7b004SRick Macklem 	u_long *hashp;
1997af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
19989ec7b004SRick Macklem 
19999ec7b004SRick Macklem 	if (nd->nd_repstat) {
20009ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
2001a9285ae5SZack Kirsch 		goto out;
20029ec7b004SRick Macklem 	}
2003f61786cbSRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
20046c21f6edSKonstantin Belousov 	    LOCKPARENT | SAVENAME | NOCACHE);
20059ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
20069ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
2007a9285ae5SZack Kirsch 	if (error)
2008a9285ae5SZack Kirsch 		goto nfsmout;
20099ec7b004SRick Macklem 	if (!nd->nd_repstat) {
20109ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva);
20119ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
2012d8a5961fSMarcelo Araujo 			error = nfsrv_sattr(nd, NULL, &nva, NULL, NULL, p);
2013a9285ae5SZack Kirsch 			if (error)
2014a9285ae5SZack Kirsch 				goto nfsmout;
20159ec7b004SRick Macklem 		} else {
20169ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
20179ec7b004SRick Macklem 			nva.na_mode = nfstov_mode(*tl++);
20189ec7b004SRick Macklem 		}
20199ec7b004SRick Macklem 	}
20209ec7b004SRick Macklem 	if (!nd->nd_repstat) {
20219ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
20229ec7b004SRick Macklem 	} else {
20239ec7b004SRick Macklem 		vrele(dp);
20249ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
20259ec7b004SRick Macklem 	}
20269ec7b004SRick Macklem 	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
20279ec7b004SRick Macklem 		vrele(dirp);
20289ec7b004SRick Macklem 		dirp = NULL;
20299ec7b004SRick Macklem 	}
20309ec7b004SRick Macklem 	if (nd->nd_repstat) {
20319ec7b004SRick Macklem 		if (dirp != NULL) {
203290d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
203390d2dfabSRick Macklem 			    NULL);
20349ec7b004SRick Macklem 			vrele(dirp);
20359ec7b004SRick Macklem 		}
20369ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
20379ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
20389ec7b004SRick Macklem 			    &diraft);
2039a9285ae5SZack Kirsch 		goto out;
20409ec7b004SRick Macklem 	}
20419ec7b004SRick Macklem 	if (dirp != NULL)
204290d2dfabSRick Macklem 		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL);
20439ec7b004SRick Macklem 
20449ec7b004SRick Macklem 	/*
20459ec7b004SRick Macklem 	 * Call nfsrvd_mkdirsub() for the code common to V4 as well.
20469ec7b004SRick Macklem 	 */
20479ec7b004SRick Macklem 	nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp, &dirfor, &diraft,
20489ec7b004SRick Macklem 	    &diraft_ret, NULL, NULL, p, exp);
20499ec7b004SRick Macklem 
20509ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
20519ec7b004SRick Macklem 		if (!nd->nd_repstat) {
20529ec7b004SRick Macklem 			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
20539ec7b004SRick Macklem 			nfsrv_postopattr(nd, 0, &nva);
20549ec7b004SRick Macklem 		}
20559ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
20569ec7b004SRick Macklem 	} else if (!nd->nd_repstat) {
20579ec7b004SRick Macklem 		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
20589ec7b004SRick Macklem 		nfsrv_fillattr(nd, &nva);
20599ec7b004SRick Macklem 	}
2060a9285ae5SZack Kirsch 
2061a9285ae5SZack Kirsch out:
2062a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
20639ec7b004SRick Macklem 	return (0);
20649ec7b004SRick Macklem nfsmout:
20659ec7b004SRick Macklem 	vrele(dp);
20669ec7b004SRick Macklem 	nfsvno_relpathbuf(&named);
2067a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
20689ec7b004SRick Macklem 	return (error);
20699ec7b004SRick Macklem }
20709ec7b004SRick Macklem 
20719ec7b004SRick Macklem /*
20729ec7b004SRick Macklem  * Code common to mkdir for V2,3 and 4.
20739ec7b004SRick Macklem  */
20749ec7b004SRick Macklem static void
20759ec7b004SRick Macklem nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
20769ec7b004SRick Macklem     struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
20779ec7b004SRick Macklem     vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
20789ec7b004SRick Macklem     int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
20799ec7b004SRick Macklem     NFSPROC_T *p, struct nfsexstuff *exp)
20809ec7b004SRick Macklem {
20819ec7b004SRick Macklem 	vnode_t vp;
20829ec7b004SRick Macklem 	u_int32_t *tl;
20839ec7b004SRick Macklem 
20849ec7b004SRick Macklem 	NFSVNO_SETATTRVAL(nvap, type, VDIR);
20859ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_mkdir(ndp, nvap, nd->nd_saveduid,
20869ec7b004SRick Macklem 	    nd->nd_cred, p, exp);
20879ec7b004SRick Macklem 	if (!nd->nd_repstat) {
20889ec7b004SRick Macklem 		vp = ndp->ni_vp;
20899ec7b004SRick Macklem 		nfsrv_fixattr(nd, vp, nvap, aclp, p, attrbitp, exp);
20909ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
20919ec7b004SRick Macklem 		if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
209290d2dfabSRick Macklem 			nd->nd_repstat = nfsvno_getattr(vp, nvap, nd, p, 1,
209390d2dfabSRick Macklem 			    NULL);
20949ec7b004SRick Macklem 		if (vpp && !nd->nd_repstat) {
2095b249ce48SMateusz Guzik 			NFSVOPUNLOCK(vp);
20969ec7b004SRick Macklem 			*vpp = vp;
20979ec7b004SRick Macklem 		} else {
20989ec7b004SRick Macklem 			vput(vp);
20999ec7b004SRick Macklem 		}
21009ec7b004SRick Macklem 	}
21019ec7b004SRick Macklem 	if (dirp) {
210290d2dfabSRick Macklem 		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd, p, 0, NULL);
21039ec7b004SRick Macklem 		vrele(dirp);
21049ec7b004SRick Macklem 	}
21059ec7b004SRick Macklem 	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
21069ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
21079ec7b004SRick Macklem 		*tl++ = newnfs_false;
21089ec7b004SRick Macklem 		txdr_hyper(dirforp->na_filerev, tl);
21099ec7b004SRick Macklem 		tl += 2;
21109ec7b004SRick Macklem 		txdr_hyper(diraftp->na_filerev, tl);
21119ec7b004SRick Macklem 		(void) nfsrv_putattrbit(nd, attrbitp);
21129ec7b004SRick Macklem 	}
2113a9285ae5SZack Kirsch 
2114a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
21159ec7b004SRick Macklem }
21169ec7b004SRick Macklem 
21179ec7b004SRick Macklem /*
21189ec7b004SRick Macklem  * nfs commit service
21199ec7b004SRick Macklem  */
2120b9cc3262SRyan Moeller int
21219ec7b004SRick Macklem nfsrvd_commit(struct nfsrv_descript *nd, __unused int isdgram,
2122af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
21239ec7b004SRick Macklem {
21249ec7b004SRick Macklem 	struct nfsvattr bfor, aft;
21259ec7b004SRick Macklem 	u_int32_t *tl;
21269ec7b004SRick Macklem 	int error = 0, for_ret = 1, aft_ret = 1, cnt;
21279ec7b004SRick Macklem 	u_int64_t off;
2128af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
21299ec7b004SRick Macklem 
21309ec7b004SRick Macklem        if (nd->nd_repstat) {
21319ec7b004SRick Macklem 		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
2132a9285ae5SZack Kirsch 		goto out;
21339ec7b004SRick Macklem 	}
2134d8a5961fSMarcelo Araujo 
2135d8a5961fSMarcelo Araujo 	/* Return NFSERR_ISDIR in NFSv4 when commit on a directory. */
2136d8a5961fSMarcelo Araujo 	if (vp->v_type != VREG) {
2137d8a5961fSMarcelo Araujo 		if (nd->nd_flag & ND_NFSV3)
2138d8a5961fSMarcelo Araujo 			error = NFSERR_NOTSUPP;
2139d8a5961fSMarcelo Araujo 		else
2140d8a5961fSMarcelo Araujo 			error = (vp->v_type == VDIR) ? NFSERR_ISDIR : NFSERR_INVAL;
2141d8a5961fSMarcelo Araujo 		goto nfsmout;
2142d8a5961fSMarcelo Araujo 	}
21439ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
2144d8a5961fSMarcelo Araujo 
21459ec7b004SRick Macklem 	/*
21469ec7b004SRick Macklem 	 * XXX At this time VOP_FSYNC() does not accept offset and byte
21479ec7b004SRick Macklem 	 * count parameters, so these arguments are useless (someday maybe).
21489ec7b004SRick Macklem 	 */
21499ec7b004SRick Macklem 	off = fxdr_hyper(tl);
21509ec7b004SRick Macklem 	tl += 2;
21519ec7b004SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
21529ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
215390d2dfabSRick Macklem 		for_ret = nfsvno_getattr(vp, &bfor, nd, p, 1, NULL);
21549ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_fsync(vp, off, cnt, nd->nd_cred, p);
21559ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
215690d2dfabSRick Macklem 		aft_ret = nfsvno_getattr(vp, &aft, nd, p, 1, NULL);
21579ec7b004SRick Macklem 		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
21589ec7b004SRick Macklem 	}
21599ec7b004SRick Macklem 	vput(vp);
21609ec7b004SRick Macklem 	if (!nd->nd_repstat) {
21619ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
21629ec7b004SRick Macklem 		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
21639ec7b004SRick Macklem 		*tl = txdr_unsigned(nfsboottime.tv_usec);
21649ec7b004SRick Macklem 	}
2165a9285ae5SZack Kirsch 
2166a9285ae5SZack Kirsch out:
2167a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
21689ec7b004SRick Macklem 	return (0);
21699ec7b004SRick Macklem nfsmout:
21709ec7b004SRick Macklem 	vput(vp);
2171a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
21729ec7b004SRick Macklem 	return (error);
21739ec7b004SRick Macklem }
21749ec7b004SRick Macklem 
21759ec7b004SRick Macklem /*
21769ec7b004SRick Macklem  * nfs statfs service
21779ec7b004SRick Macklem  */
2178b9cc3262SRyan Moeller int
21799ec7b004SRick Macklem nfsrvd_statfs(struct nfsrv_descript *nd, __unused int isdgram,
2180af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
21819ec7b004SRick Macklem {
21829ec7b004SRick Macklem 	struct statfs *sf;
21839ec7b004SRick Macklem 	u_int32_t *tl;
21849ec7b004SRick Macklem 	int getret = 1;
21859ec7b004SRick Macklem 	struct nfsvattr at;
21869ec7b004SRick Macklem 	u_quad_t tval;
2187af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
21889ec7b004SRick Macklem 
21892f304845SKonstantin Belousov 	sf = NULL;
21909ec7b004SRick Macklem 	if (nd->nd_repstat) {
21919ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
2192a9285ae5SZack Kirsch 		goto out;
21939ec7b004SRick Macklem 	}
21942f304845SKonstantin Belousov 	sf = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
2195dfd233edSAttilio Rao 	nd->nd_repstat = nfsvno_statfs(vp, sf);
219690d2dfabSRick Macklem 	getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL);
21979ec7b004SRick Macklem 	vput(vp);
21989ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
21999ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
22009ec7b004SRick Macklem 	if (nd->nd_repstat)
2201a9285ae5SZack Kirsch 		goto out;
22029ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
22039ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_V2STATFS);
22049ec7b004SRick Macklem 		*tl++ = txdr_unsigned(NFS_V2MAXDATA);
22059ec7b004SRick Macklem 		*tl++ = txdr_unsigned(sf->f_bsize);
22069ec7b004SRick Macklem 		*tl++ = txdr_unsigned(sf->f_blocks);
22079ec7b004SRick Macklem 		*tl++ = txdr_unsigned(sf->f_bfree);
22089ec7b004SRick Macklem 		*tl = txdr_unsigned(sf->f_bavail);
22099ec7b004SRick Macklem 	} else {
22109ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_V3STATFS);
22119ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_blocks;
22129ec7b004SRick Macklem 		tval *= (u_quad_t)sf->f_bsize;
22139ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
22149ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_bfree;
22159ec7b004SRick Macklem 		tval *= (u_quad_t)sf->f_bsize;
22169ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
22179ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_bavail;
22189ec7b004SRick Macklem 		tval *= (u_quad_t)sf->f_bsize;
22199ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
22209ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_files;
22219ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
22229ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_ffree;
22239ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
22249ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_ffree;
22259ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
22269ec7b004SRick Macklem 		*tl = 0;
22279ec7b004SRick Macklem 	}
2228a9285ae5SZack Kirsch 
2229a9285ae5SZack Kirsch out:
22302f304845SKonstantin Belousov 	free(sf, M_STATFS);
2231a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
22329ec7b004SRick Macklem 	return (0);
22339ec7b004SRick Macklem }
22349ec7b004SRick Macklem 
22359ec7b004SRick Macklem /*
22369ec7b004SRick Macklem  * nfs fsinfo service
22379ec7b004SRick Macklem  */
2238b9cc3262SRyan Moeller int
22399ec7b004SRick Macklem nfsrvd_fsinfo(struct nfsrv_descript *nd, int isdgram,
2240af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
22419ec7b004SRick Macklem {
22429ec7b004SRick Macklem 	u_int32_t *tl;
22439ec7b004SRick Macklem 	struct nfsfsinfo fs;
22449ec7b004SRick Macklem 	int getret = 1;
22459ec7b004SRick Macklem 	struct nfsvattr at;
2246af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
22479ec7b004SRick Macklem 
22489ec7b004SRick Macklem 	if (nd->nd_repstat) {
22499ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
2250a9285ae5SZack Kirsch 		goto out;
22519ec7b004SRick Macklem 	}
225290d2dfabSRick Macklem 	getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL);
22539ec7b004SRick Macklem 	nfsvno_getfs(&fs, isdgram);
22549ec7b004SRick Macklem 	vput(vp);
22559ec7b004SRick Macklem 	nfsrv_postopattr(nd, getret, &at);
22569ec7b004SRick Macklem 	NFSM_BUILD(tl, u_int32_t *, NFSX_V3FSINFO);
22579ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_rtmax);
22589ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_rtpref);
22599ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_rtmult);
22609ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_wtmax);
22619ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_wtpref);
22629ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_wtmult);
22639ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_dtpref);
22649ec7b004SRick Macklem 	txdr_hyper(fs.fs_maxfilesize, tl);
22659ec7b004SRick Macklem 	tl += 2;
22669ec7b004SRick Macklem 	txdr_nfsv3time(&fs.fs_timedelta, tl);
22679ec7b004SRick Macklem 	tl += 2;
22689ec7b004SRick Macklem 	*tl = txdr_unsigned(fs.fs_properties);
2269a9285ae5SZack Kirsch 
2270a9285ae5SZack Kirsch out:
2271a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
22729ec7b004SRick Macklem 	return (0);
22739ec7b004SRick Macklem }
22749ec7b004SRick Macklem 
22759ec7b004SRick Macklem /*
22769ec7b004SRick Macklem  * nfs pathconf service
22779ec7b004SRick Macklem  */
2278b9cc3262SRyan Moeller int
22799ec7b004SRick Macklem nfsrvd_pathconf(struct nfsrv_descript *nd, __unused int isdgram,
2280af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
22819ec7b004SRick Macklem {
22829ec7b004SRick Macklem 	struct nfsv3_pathconf *pc;
22839ec7b004SRick Macklem 	int getret = 1;
2284b1288166SJohn Baldwin 	long linkmax, namemax, chownres, notrunc;
22859ec7b004SRick Macklem 	struct nfsvattr at;
2286af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
22879ec7b004SRick Macklem 
22889ec7b004SRick Macklem 	if (nd->nd_repstat) {
22899ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
2290a9285ae5SZack Kirsch 		goto out;
22919ec7b004SRick Macklem 	}
22929ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_pathconf(vp, _PC_LINK_MAX, &linkmax,
22939ec7b004SRick Macklem 	    nd->nd_cred, p);
22949ec7b004SRick Macklem 	if (!nd->nd_repstat)
22959ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NAME_MAX, &namemax,
22969ec7b004SRick Macklem 		    nd->nd_cred, p);
22979ec7b004SRick Macklem 	if (!nd->nd_repstat)
22989ec7b004SRick Macklem 		nd->nd_repstat=nfsvno_pathconf(vp, _PC_CHOWN_RESTRICTED,
22999ec7b004SRick Macklem 		    &chownres, nd->nd_cred, p);
23009ec7b004SRick Macklem 	if (!nd->nd_repstat)
23019ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NO_TRUNC, &notrunc,
23029ec7b004SRick Macklem 		    nd->nd_cred, p);
230390d2dfabSRick Macklem 	getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL);
23049ec7b004SRick Macklem 	vput(vp);
23059ec7b004SRick Macklem 	nfsrv_postopattr(nd, getret, &at);
23069ec7b004SRick Macklem 	if (!nd->nd_repstat) {
23079ec7b004SRick Macklem 		NFSM_BUILD(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
23089ec7b004SRick Macklem 		pc->pc_linkmax = txdr_unsigned(linkmax);
23099ec7b004SRick Macklem 		pc->pc_namemax = txdr_unsigned(namemax);
23109ec7b004SRick Macklem 		pc->pc_notrunc = txdr_unsigned(notrunc);
23119ec7b004SRick Macklem 		pc->pc_chownrestricted = txdr_unsigned(chownres);
23129ec7b004SRick Macklem 
23139ec7b004SRick Macklem 		/*
23149ec7b004SRick Macklem 		 * These should probably be supported by VOP_PATHCONF(), but
23159ec7b004SRick Macklem 		 * until msdosfs is exportable (why would you want to?), the
23169ec7b004SRick Macklem 		 * Unix defaults should be ok.
23179ec7b004SRick Macklem 		 */
23189ec7b004SRick Macklem 		pc->pc_caseinsensitive = newnfs_false;
23199ec7b004SRick Macklem 		pc->pc_casepreserving = newnfs_true;
23209ec7b004SRick Macklem 	}
2321a9285ae5SZack Kirsch 
2322a9285ae5SZack Kirsch out:
2323a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
23249ec7b004SRick Macklem 	return (0);
23259ec7b004SRick Macklem }
23269ec7b004SRick Macklem 
23279ec7b004SRick Macklem /*
23289ec7b004SRick Macklem  * nfsv4 lock service
23299ec7b004SRick Macklem  */
2330b9cc3262SRyan Moeller int
23319ec7b004SRick Macklem nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram,
2332af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
23339ec7b004SRick Macklem {
23349ec7b004SRick Macklem 	u_int32_t *tl;
23359ec7b004SRick Macklem 	int i;
23369ec7b004SRick Macklem 	struct nfsstate *stp = NULL;
23379ec7b004SRick Macklem 	struct nfslock *lop;
23389ec7b004SRick Macklem 	struct nfslockconflict cf;
23399ec7b004SRick Macklem 	int error = 0;
23409ec7b004SRick Macklem 	u_short flags = NFSLCK_LOCK, lflags;
23419ec7b004SRick Macklem 	u_int64_t offset, len;
23429ec7b004SRick Macklem 	nfsv4stateid_t stateid;
23439ec7b004SRick Macklem 	nfsquad_t clientid;
2344af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
23459ec7b004SRick Macklem 
23469ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
23479ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
23489ec7b004SRick Macklem 	switch (i) {
23499ec7b004SRick Macklem 	case NFSV4LOCKT_READW:
23509ec7b004SRick Macklem 		flags |= NFSLCK_BLOCKING;
23519ec7b004SRick Macklem 	case NFSV4LOCKT_READ:
23529ec7b004SRick Macklem 		lflags = NFSLCK_READ;
23539ec7b004SRick Macklem 		break;
23549ec7b004SRick Macklem 	case NFSV4LOCKT_WRITEW:
23559ec7b004SRick Macklem 		flags |= NFSLCK_BLOCKING;
23569ec7b004SRick Macklem 	case NFSV4LOCKT_WRITE:
23579ec7b004SRick Macklem 		lflags = NFSLCK_WRITE;
23589ec7b004SRick Macklem 		break;
23599ec7b004SRick Macklem 	default:
23609ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
23619ec7b004SRick Macklem 		goto nfsmout;
236274b8d63dSPedro F. Giffuni 	}
23639ec7b004SRick Macklem 	if (*tl++ == newnfs_true)
23649ec7b004SRick Macklem 		flags |= NFSLCK_RECLAIM;
23659ec7b004SRick Macklem 	offset = fxdr_hyper(tl);
23669ec7b004SRick Macklem 	tl += 2;
23679ec7b004SRick Macklem 	len = fxdr_hyper(tl);
23689ec7b004SRick Macklem 	tl += 2;
23699ec7b004SRick Macklem 	if (*tl == newnfs_true)
23709ec7b004SRick Macklem 		flags |= NFSLCK_OPENTOLOCK;
23719ec7b004SRick Macklem 	if (flags & NFSLCK_OPENTOLOCK) {
23729ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID);
23739ec7b004SRick Macklem 		i = fxdr_unsigned(int, *(tl+4+(NFSX_STATEID / NFSX_UNSIGNED)));
23742a45247cSRick Macklem 		if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
23752a45247cSRick Macklem 			nd->nd_repstat = NFSERR_BADXDR;
23762a45247cSRick Macklem 			goto nfsmout;
23772a45247cSRick Macklem 		}
2378222daa42SConrad Meyer 		stp = malloc(sizeof (struct nfsstate) + i,
23799ec7b004SRick Macklem 			M_NFSDSTATE, M_WAITOK);
23809ec7b004SRick Macklem 		stp->ls_ownerlen = i;
23819ec7b004SRick Macklem 		stp->ls_op = nd->nd_rp;
23829ec7b004SRick Macklem 		stp->ls_seq = fxdr_unsigned(int, *tl++);
23839ec7b004SRick Macklem 		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
23849ec7b004SRick Macklem 		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
23859ec7b004SRick Macklem 			NFSX_STATEIDOTHER);
23869ec7b004SRick Macklem 		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
238790d2dfabSRick Macklem 
238890d2dfabSRick Macklem 		/*
238990d2dfabSRick Macklem 		 * For the special stateid of other all 0s and seqid == 1, set
239090d2dfabSRick Macklem 		 * the stateid to the current stateid, if it is set.
239190d2dfabSRick Macklem 		 */
239290d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0 &&
239390d2dfabSRick Macklem 		    stp->ls_stateid.seqid == 1 &&
239490d2dfabSRick Macklem 		    stp->ls_stateid.other[0] == 0 &&
239590d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0 &&
239690d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0) {
239790d2dfabSRick Macklem 			if ((nd->nd_flag & ND_CURSTATEID) != 0) {
239890d2dfabSRick Macklem 				stp->ls_stateid = nd->nd_curstateid;
239990d2dfabSRick Macklem 				stp->ls_stateid.seqid = 0;
240090d2dfabSRick Macklem 			} else {
240190d2dfabSRick Macklem 				nd->nd_repstat = NFSERR_BADSTATEID;
240290d2dfabSRick Macklem 				goto nfsmout;
240390d2dfabSRick Macklem 			}
240490d2dfabSRick Macklem 		}
240590d2dfabSRick Macklem 
24069ec7b004SRick Macklem 		stp->ls_opentolockseq = fxdr_unsigned(int, *tl++);
24079ec7b004SRick Macklem 		clientid.lval[0] = *tl++;
24089ec7b004SRick Macklem 		clientid.lval[1] = *tl++;
2409c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2410c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
2411c59e4cc3SRick Macklem 				clientid.qval = nd->nd_clientid.qval;
2412c59e4cc3SRick Macklem 			else if (nd->nd_clientid.qval != clientid.qval)
2413c59e4cc3SRick Macklem 				printf("EEK3 multiple clids\n");
24149ec7b004SRick Macklem 		} else {
2415c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
2416c59e4cc3SRick Macklem 				printf("EEK! no clientid from session\n");
24179ec7b004SRick Macklem 			nd->nd_flag |= ND_IMPLIEDCLID;
24189ec7b004SRick Macklem 			nd->nd_clientid.qval = clientid.qval;
24199ec7b004SRick Macklem 		}
24209ec7b004SRick Macklem 		error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
24219ec7b004SRick Macklem 		if (error)
24229ec7b004SRick Macklem 			goto nfsmout;
24239ec7b004SRick Macklem 	} else {
24249ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
2425222daa42SConrad Meyer 		stp = malloc(sizeof (struct nfsstate),
24269ec7b004SRick Macklem 			M_NFSDSTATE, M_WAITOK);
24279ec7b004SRick Macklem 		stp->ls_ownerlen = 0;
24289ec7b004SRick Macklem 		stp->ls_op = nd->nd_rp;
24299ec7b004SRick Macklem 		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
24309ec7b004SRick Macklem 		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
24319ec7b004SRick Macklem 			NFSX_STATEIDOTHER);
24329ec7b004SRick Macklem 		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
243390d2dfabSRick Macklem 
243490d2dfabSRick Macklem 		/*
243590d2dfabSRick Macklem 		 * For the special stateid of other all 0s and seqid == 1, set
243690d2dfabSRick Macklem 		 * the stateid to the current stateid, if it is set.
243790d2dfabSRick Macklem 		 */
243890d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0 &&
243990d2dfabSRick Macklem 		    stp->ls_stateid.seqid == 1 &&
244090d2dfabSRick Macklem 		    stp->ls_stateid.other[0] == 0 &&
244190d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0 &&
244290d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0) {
244390d2dfabSRick Macklem 			if ((nd->nd_flag & ND_CURSTATEID) != 0) {
244490d2dfabSRick Macklem 				stp->ls_stateid = nd->nd_curstateid;
244590d2dfabSRick Macklem 				stp->ls_stateid.seqid = 0;
244690d2dfabSRick Macklem 			} else {
244790d2dfabSRick Macklem 				nd->nd_repstat = NFSERR_BADSTATEID;
244890d2dfabSRick Macklem 				goto nfsmout;
244990d2dfabSRick Macklem 			}
245090d2dfabSRick Macklem 		}
245190d2dfabSRick Macklem 
24529ec7b004SRick Macklem 		stp->ls_seq = fxdr_unsigned(int, *tl);
24539ec7b004SRick Macklem 		clientid.lval[0] = stp->ls_stateid.other[0];
24549ec7b004SRick Macklem 		clientid.lval[1] = stp->ls_stateid.other[1];
2455c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2456c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
2457c59e4cc3SRick Macklem 				clientid.qval = nd->nd_clientid.qval;
2458c59e4cc3SRick Macklem 			else if (nd->nd_clientid.qval != clientid.qval)
2459c59e4cc3SRick Macklem 				printf("EEK4 multiple clids\n");
24609ec7b004SRick Macklem 		} else {
2461c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
2462c59e4cc3SRick Macklem 				printf("EEK! no clientid from session\n");
24639ec7b004SRick Macklem 			nd->nd_flag |= ND_IMPLIEDCLID;
24649ec7b004SRick Macklem 			nd->nd_clientid.qval = clientid.qval;
24659ec7b004SRick Macklem 		}
24669ec7b004SRick Macklem 	}
2467222daa42SConrad Meyer 	lop = malloc(sizeof (struct nfslock),
24689ec7b004SRick Macklem 		M_NFSDLOCK, M_WAITOK);
24699ec7b004SRick Macklem 	lop->lo_first = offset;
24709ec7b004SRick Macklem 	if (len == NFS64BITSSET) {
24719ec7b004SRick Macklem 		lop->lo_end = NFS64BITSSET;
24729ec7b004SRick Macklem 	} else {
24739ec7b004SRick Macklem 		lop->lo_end = offset + len;
24749ec7b004SRick Macklem 		if (lop->lo_end <= lop->lo_first)
24759ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
24769ec7b004SRick Macklem 	}
24779ec7b004SRick Macklem 	lop->lo_flags = lflags;
24789ec7b004SRick Macklem 	stp->ls_flags = flags;
24799ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
24809ec7b004SRick Macklem 
24819ec7b004SRick Macklem 	/*
24829ec7b004SRick Macklem 	 * Do basic access checking.
24839ec7b004SRick Macklem 	 */
24849ec7b004SRick Macklem 	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
24859ec7b004SRick Macklem 	    if (vnode_vtype(vp) == VDIR)
24869ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_ISDIR;
24879ec7b004SRick Macklem 	    else
24889ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
24899ec7b004SRick Macklem 	}
24909ec7b004SRick Macklem 	if (!nd->nd_repstat) {
24919ec7b004SRick Macklem 	    if (lflags & NFSLCK_WRITE) {
24928da45f2cSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
24939ec7b004SRick Macklem 		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
24948da45f2cSRick Macklem 		    NFSACCCHK_VPISLOCKED, NULL);
24959ec7b004SRick Macklem 	    } else {
24968da45f2cSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
24979ec7b004SRick Macklem 		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
24988da45f2cSRick Macklem 		    NFSACCCHK_VPISLOCKED, NULL);
24999ec7b004SRick Macklem 		if (nd->nd_repstat)
25008da45f2cSRick Macklem 		    nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
25019ec7b004SRick Macklem 			nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
25028da45f2cSRick Macklem 			NFSACCCHK_VPISLOCKED, NULL);
25039ec7b004SRick Macklem 	    }
25049ec7b004SRick Macklem 	}
25059ec7b004SRick Macklem 
25069ec7b004SRick Macklem 	/*
25079ec7b004SRick Macklem 	 * We call nfsrv_lockctrl() even if nd_repstat set, so that the
25089ec7b004SRick Macklem 	 * seqid# gets updated. nfsrv_lockctrl() will return the value
25099ec7b004SRick Macklem 	 * of nd_repstat, if it gets that far.
25109ec7b004SRick Macklem 	 */
25119ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
25129ec7b004SRick Macklem 		&stateid, exp, nd, p);
25139ec7b004SRick Macklem 	if (lop)
2514222daa42SConrad Meyer 		free(lop, M_NFSDLOCK);
25159ec7b004SRick Macklem 	if (stp)
2516222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
25179ec7b004SRick Macklem 	if (!nd->nd_repstat) {
251890d2dfabSRick Macklem 		/* For NFSv4.1, set the Current StateID. */
251990d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0) {
252090d2dfabSRick Macklem 			nd->nd_curstateid = stateid;
252190d2dfabSRick Macklem 			nd->nd_flag |= ND_CURSTATEID;
252290d2dfabSRick Macklem 		}
25239ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
25249ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
25259ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
25269ec7b004SRick Macklem 	} else if (nd->nd_repstat == NFSERR_DENIED) {
25279ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
25289ec7b004SRick Macklem 		txdr_hyper(cf.cl_first, tl);
25299ec7b004SRick Macklem 		tl += 2;
25309ec7b004SRick Macklem 		if (cf.cl_end == NFS64BITSSET)
25319ec7b004SRick Macklem 			len = NFS64BITSSET;
25329ec7b004SRick Macklem 		else
25339ec7b004SRick Macklem 			len = cf.cl_end - cf.cl_first;
25349ec7b004SRick Macklem 		txdr_hyper(len, tl);
25359ec7b004SRick Macklem 		tl += 2;
25369ec7b004SRick Macklem 		if (cf.cl_flags == NFSLCK_WRITE)
25379ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
25389ec7b004SRick Macklem 		else
25399ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
25409ec7b004SRick Macklem 		*tl++ = stateid.other[0];
25419ec7b004SRick Macklem 		*tl = stateid.other[1];
25429ec7b004SRick Macklem 		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
25439ec7b004SRick Macklem 	}
25449ec7b004SRick Macklem 	vput(vp);
2545a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
25469ec7b004SRick Macklem 	return (0);
25479ec7b004SRick Macklem nfsmout:
25489ec7b004SRick Macklem 	vput(vp);
25499ec7b004SRick Macklem 	if (stp)
2550222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
2551a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
25529ec7b004SRick Macklem 	return (error);
25539ec7b004SRick Macklem }
25549ec7b004SRick Macklem 
25559ec7b004SRick Macklem /*
25569ec7b004SRick Macklem  * nfsv4 lock test service
25579ec7b004SRick Macklem  */
2558b9cc3262SRyan Moeller int
25599ec7b004SRick Macklem nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram,
2560af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
25619ec7b004SRick Macklem {
25629ec7b004SRick Macklem 	u_int32_t *tl;
25639ec7b004SRick Macklem 	int i;
25649ec7b004SRick Macklem 	struct nfsstate *stp = NULL;
25659ec7b004SRick Macklem 	struct nfslock lo, *lop = &lo;
25669ec7b004SRick Macklem 	struct nfslockconflict cf;
25679ec7b004SRick Macklem 	int error = 0;
25689ec7b004SRick Macklem 	nfsv4stateid_t stateid;
25699ec7b004SRick Macklem 	nfsquad_t clientid;
25709ec7b004SRick Macklem 	u_int64_t len;
2571af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
25729ec7b004SRick Macklem 
25739ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
25749ec7b004SRick Macklem 	i = fxdr_unsigned(int, *(tl + 7));
25752a45247cSRick Macklem 	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
25762a45247cSRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
25772a45247cSRick Macklem 		goto nfsmout;
25782a45247cSRick Macklem 	}
2579222daa42SConrad Meyer 	stp = malloc(sizeof (struct nfsstate) + i,
25809ec7b004SRick Macklem 	    M_NFSDSTATE, M_WAITOK);
25819ec7b004SRick Macklem 	stp->ls_ownerlen = i;
25829ec7b004SRick Macklem 	stp->ls_op = NULL;
25839ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_TEST;
25849ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
25859ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
25869ec7b004SRick Macklem 	switch (i) {
25879ec7b004SRick Macklem 	case NFSV4LOCKT_READW:
25889ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_BLOCKING;
25899ec7b004SRick Macklem 	case NFSV4LOCKT_READ:
25909ec7b004SRick Macklem 		lo.lo_flags = NFSLCK_READ;
25919ec7b004SRick Macklem 		break;
25929ec7b004SRick Macklem 	case NFSV4LOCKT_WRITEW:
25939ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_BLOCKING;
25949ec7b004SRick Macklem 	case NFSV4LOCKT_WRITE:
25959ec7b004SRick Macklem 		lo.lo_flags = NFSLCK_WRITE;
25969ec7b004SRick Macklem 		break;
25979ec7b004SRick Macklem 	default:
25989ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
25999ec7b004SRick Macklem 		goto nfsmout;
260074b8d63dSPedro F. Giffuni 	}
26019ec7b004SRick Macklem 	lo.lo_first = fxdr_hyper(tl);
26029ec7b004SRick Macklem 	tl += 2;
26039ec7b004SRick Macklem 	len = fxdr_hyper(tl);
26049ec7b004SRick Macklem 	if (len == NFS64BITSSET) {
26059ec7b004SRick Macklem 		lo.lo_end = NFS64BITSSET;
26069ec7b004SRick Macklem 	} else {
26079ec7b004SRick Macklem 		lo.lo_end = lo.lo_first + len;
26089ec7b004SRick Macklem 		if (lo.lo_end <= lo.lo_first)
26099ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
26109ec7b004SRick Macklem 	}
26119ec7b004SRick Macklem 	tl += 2;
26129ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
26139ec7b004SRick Macklem 	clientid.lval[1] = *tl;
2614c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2615c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2616c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
2617c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
2618c59e4cc3SRick Macklem 			printf("EEK5 multiple clids\n");
26199ec7b004SRick Macklem 	} else {
2620c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2621c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
26229ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
26239ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
26249ec7b004SRick Macklem 	}
26259ec7b004SRick Macklem 	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
26269ec7b004SRick Macklem 	if (error)
26279ec7b004SRick Macklem 		goto nfsmout;
26289ec7b004SRick Macklem 	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
26299ec7b004SRick Macklem 	    if (vnode_vtype(vp) == VDIR)
26309ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_ISDIR;
26319ec7b004SRick Macklem 	    else
26329ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
26339ec7b004SRick Macklem 	}
26349ec7b004SRick Macklem 	if (!nd->nd_repstat)
26359ec7b004SRick Macklem 	  nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
26369ec7b004SRick Macklem 	    &stateid, exp, nd, p);
26379ec7b004SRick Macklem 	if (nd->nd_repstat) {
26389ec7b004SRick Macklem 	    if (nd->nd_repstat == NFSERR_DENIED) {
26399ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
26409ec7b004SRick Macklem 		txdr_hyper(cf.cl_first, tl);
26419ec7b004SRick Macklem 		tl += 2;
26429ec7b004SRick Macklem 		if (cf.cl_end == NFS64BITSSET)
26439ec7b004SRick Macklem 			len = NFS64BITSSET;
26449ec7b004SRick Macklem 		else
26459ec7b004SRick Macklem 			len = cf.cl_end - cf.cl_first;
26469ec7b004SRick Macklem 		txdr_hyper(len, tl);
26479ec7b004SRick Macklem 		tl += 2;
26489ec7b004SRick Macklem 		if (cf.cl_flags == NFSLCK_WRITE)
26499ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
26509ec7b004SRick Macklem 		else
26519ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
26529ec7b004SRick Macklem 		*tl++ = stp->ls_stateid.other[0];
26539ec7b004SRick Macklem 		*tl = stp->ls_stateid.other[1];
26549ec7b004SRick Macklem 		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
26559ec7b004SRick Macklem 	    }
26569ec7b004SRick Macklem 	}
26579ec7b004SRick Macklem 	vput(vp);
26585ecc225fSConrad Meyer 	if (stp)
2659222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
2660a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
26619ec7b004SRick Macklem 	return (0);
26629ec7b004SRick Macklem nfsmout:
26639ec7b004SRick Macklem 	vput(vp);
26649ec7b004SRick Macklem 	if (stp)
2665222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
2666a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
26679ec7b004SRick Macklem 	return (error);
26689ec7b004SRick Macklem }
26699ec7b004SRick Macklem 
26709ec7b004SRick Macklem /*
26719ec7b004SRick Macklem  * nfsv4 unlock service
26729ec7b004SRick Macklem  */
2673b9cc3262SRyan Moeller int
26749ec7b004SRick Macklem nfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram,
2675af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
26769ec7b004SRick Macklem {
26779ec7b004SRick Macklem 	u_int32_t *tl;
26789ec7b004SRick Macklem 	int i;
26799ec7b004SRick Macklem 	struct nfsstate *stp;
26809ec7b004SRick Macklem 	struct nfslock *lop;
26819ec7b004SRick Macklem 	int error = 0;
26829ec7b004SRick Macklem 	nfsv4stateid_t stateid;
26839ec7b004SRick Macklem 	nfsquad_t clientid;
26849ec7b004SRick Macklem 	u_int64_t len;
2685af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
26869ec7b004SRick Macklem 
26879ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED + NFSX_STATEID);
2688222daa42SConrad Meyer 	stp = malloc(sizeof (struct nfsstate),
26899ec7b004SRick Macklem 	    M_NFSDSTATE, M_WAITOK);
2690222daa42SConrad Meyer 	lop = malloc(sizeof (struct nfslock),
26919ec7b004SRick Macklem 	    M_NFSDLOCK, M_WAITOK);
26929ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_UNLOCK;
26939ec7b004SRick Macklem 	lop->lo_flags = NFSLCK_UNLOCK;
26949ec7b004SRick Macklem 	stp->ls_op = nd->nd_rp;
26959ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
26969ec7b004SRick Macklem 	switch (i) {
26979ec7b004SRick Macklem 	case NFSV4LOCKT_READW:
26989ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_BLOCKING;
26999ec7b004SRick Macklem 	case NFSV4LOCKT_READ:
27009ec7b004SRick Macklem 		break;
27019ec7b004SRick Macklem 	case NFSV4LOCKT_WRITEW:
27029ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_BLOCKING;
27039ec7b004SRick Macklem 	case NFSV4LOCKT_WRITE:
27049ec7b004SRick Macklem 		break;
27059ec7b004SRick Macklem 	default:
27069ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
27072a45247cSRick Macklem 		free(stp, M_NFSDSTATE);
27082a45247cSRick Macklem 		free(lop, M_NFSDLOCK);
27099ec7b004SRick Macklem 		goto nfsmout;
271074b8d63dSPedro F. Giffuni 	}
27119ec7b004SRick Macklem 	stp->ls_ownerlen = 0;
27129ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
27139ec7b004SRick Macklem 	stp->ls_seq = fxdr_unsigned(int, *tl++);
27149ec7b004SRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
27159ec7b004SRick Macklem 	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
27169ec7b004SRick Macklem 	    NFSX_STATEIDOTHER);
27179ec7b004SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
271890d2dfabSRick Macklem 
271990d2dfabSRick Macklem 	/*
272090d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
272190d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
272290d2dfabSRick Macklem 	 */
272390d2dfabSRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 1 &&
272490d2dfabSRick Macklem 	    stp->ls_stateid.other[0] == 0 && stp->ls_stateid.other[1] == 0 &&
272590d2dfabSRick Macklem 	    stp->ls_stateid.other[2] == 0) {
272690d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
272790d2dfabSRick Macklem 			stp->ls_stateid = nd->nd_curstateid;
272890d2dfabSRick Macklem 			stp->ls_stateid.seqid = 0;
272990d2dfabSRick Macklem 		} else {
273090d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
27310bb42627SEric van Gyzen 			free(stp, M_NFSDSTATE);
27320bb42627SEric van Gyzen 			free(lop, M_NFSDLOCK);
273390d2dfabSRick Macklem 			goto nfsmout;
273490d2dfabSRick Macklem 		}
273590d2dfabSRick Macklem 	}
273690d2dfabSRick Macklem 
27379ec7b004SRick Macklem 	lop->lo_first = fxdr_hyper(tl);
27389ec7b004SRick Macklem 	tl += 2;
27399ec7b004SRick Macklem 	len = fxdr_hyper(tl);
27409ec7b004SRick Macklem 	if (len == NFS64BITSSET) {
27419ec7b004SRick Macklem 		lop->lo_end = NFS64BITSSET;
27429ec7b004SRick Macklem 	} else {
27439ec7b004SRick Macklem 		lop->lo_end = lop->lo_first + len;
27449ec7b004SRick Macklem 		if (lop->lo_end <= lop->lo_first)
27459ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
27469ec7b004SRick Macklem 	}
27479ec7b004SRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0];
27489ec7b004SRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1];
2749c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2750c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2751c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
2752c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
2753c59e4cc3SRick Macklem 			printf("EEK6 multiple clids\n");
27549ec7b004SRick Macklem 	} else {
2755c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2756c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
27579ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
27589ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
27599ec7b004SRick Macklem 	}
27609ec7b004SRick Macklem 	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
27619ec7b004SRick Macklem 	    if (vnode_vtype(vp) == VDIR)
27629ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_ISDIR;
27639ec7b004SRick Macklem 	    else
27649ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
27659ec7b004SRick Macklem 	}
27669ec7b004SRick Macklem 	/*
27679ec7b004SRick Macklem 	 * Call nfsrv_lockctrl() even if nd_repstat is set, so that the
27689ec7b004SRick Macklem 	 * seqid# gets incremented. nfsrv_lockctrl() will return the
27699ec7b004SRick Macklem 	 * value of nd_repstat, if it gets that far.
27709ec7b004SRick Macklem 	 */
27719ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
27729ec7b004SRick Macklem 	    &stateid, exp, nd, p);
27739ec7b004SRick Macklem 	if (stp)
2774222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
27759ec7b004SRick Macklem 	if (lop)
2776222daa42SConrad Meyer 		free(lop, M_NFSDLOCK);
27779ec7b004SRick Macklem 	if (!nd->nd_repstat) {
27789ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
27799ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
27809ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
27819ec7b004SRick Macklem 	}
27829ec7b004SRick Macklem nfsmout:
27839ec7b004SRick Macklem 	vput(vp);
2784a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
27859ec7b004SRick Macklem 	return (error);
27869ec7b004SRick Macklem }
27879ec7b004SRick Macklem 
27889ec7b004SRick Macklem /*
27899ec7b004SRick Macklem  * nfsv4 open service
27909ec7b004SRick Macklem  */
2791b9cc3262SRyan Moeller int
27929ec7b004SRick Macklem nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
2793af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t *vpp, __unused fhandle_t *fhp, struct nfsexstuff *exp)
27949ec7b004SRick Macklem {
27959ec7b004SRick Macklem 	u_int32_t *tl;
2796c59e4cc3SRick Macklem 	int i, retext;
27979ec7b004SRick Macklem 	struct nfsstate *stp = NULL;
2798b0b7d978SRick Macklem 	int error = 0, create, claim, exclusive_flag = 0, override;
27999ec7b004SRick Macklem 	u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask;
28009ec7b004SRick Macklem 	int how = NFSCREATE_UNCHECKED;
2801086f6e0cSRick Macklem 	int32_t cverf[2], tverf[2] = { 0, 0 };
28029ec7b004SRick Macklem 	vnode_t vp = NULL, dirp = NULL;
28039ec7b004SRick Macklem 	struct nfsvattr nva, dirfor, diraft;
28049ec7b004SRick Macklem 	struct nameidata named;
28059ec7b004SRick Macklem 	nfsv4stateid_t stateid, delegstateid;
28069ec7b004SRick Macklem 	nfsattrbit_t attrbits;
28079ec7b004SRick Macklem 	nfsquad_t clientid;
28089ec7b004SRick Macklem 	char *bufp = NULL;
28099ec7b004SRick Macklem 	u_long *hashp;
28109ec7b004SRick Macklem 	NFSACL_T *aclp = NULL;
2811af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
28129ec7b004SRick Macklem 
28139ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
2814c3e22f83SRick Macklem 	aclp = acl_alloc(M_WAITOK);
28159ec7b004SRick Macklem 	aclp->acl_cnt = 0;
28169ec7b004SRick Macklem #endif
28179ec7b004SRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
28189ec7b004SRick Macklem 	named.ni_startdir = NULL;
28199ec7b004SRick Macklem 	named.ni_cnd.cn_nameiop = 0;
28209ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
28219ec7b004SRick Macklem 	i = fxdr_unsigned(int, *(tl + 5));
28222a45247cSRick Macklem 	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
28232a45247cSRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
2824a9285ae5SZack Kirsch 		goto nfsmout;
28252a45247cSRick Macklem 	}
2826222daa42SConrad Meyer 	stp = malloc(sizeof (struct nfsstate) + i,
28279ec7b004SRick Macklem 	    M_NFSDSTATE, M_WAITOK);
28289ec7b004SRick Macklem 	stp->ls_ownerlen = i;
28299ec7b004SRick Macklem 	stp->ls_op = nd->nd_rp;
28309ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_OPEN;
28319ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
28329ec7b004SRick Macklem 	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
28339ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
2834c59e4cc3SRick Macklem 	retext = 0;
2835c59e4cc3SRick Macklem 	if ((i & (NFSV4OPEN_WANTDELEGMASK | NFSV4OPEN_WANTSIGNALDELEG |
2836c59e4cc3SRick Macklem 	    NFSV4OPEN_WANTPUSHDELEG)) != 0 && (nd->nd_flag & ND_NFSV41) != 0) {
2837c59e4cc3SRick Macklem 		retext = 1;
2838c59e4cc3SRick Macklem 		/* For now, ignore these. */
2839c59e4cc3SRick Macklem 		i &= ~(NFSV4OPEN_WANTPUSHDELEG | NFSV4OPEN_WANTSIGNALDELEG);
2840c59e4cc3SRick Macklem 		switch (i & NFSV4OPEN_WANTDELEGMASK) {
2841c59e4cc3SRick Macklem 		case NFSV4OPEN_WANTANYDELEG:
2842c59e4cc3SRick Macklem 			stp->ls_flags |= (NFSLCK_WANTRDELEG |
2843c59e4cc3SRick Macklem 			    NFSLCK_WANTWDELEG);
2844c59e4cc3SRick Macklem 			i &= ~NFSV4OPEN_WANTDELEGMASK;
2845c59e4cc3SRick Macklem 			break;
2846c59e4cc3SRick Macklem 		case NFSV4OPEN_WANTREADDELEG:
2847c59e4cc3SRick Macklem 			stp->ls_flags |= NFSLCK_WANTRDELEG;
2848c59e4cc3SRick Macklem 			i &= ~NFSV4OPEN_WANTDELEGMASK;
2849c59e4cc3SRick Macklem 			break;
2850c59e4cc3SRick Macklem 		case NFSV4OPEN_WANTWRITEDELEG:
2851c59e4cc3SRick Macklem 			stp->ls_flags |= NFSLCK_WANTWDELEG;
2852c59e4cc3SRick Macklem 			i &= ~NFSV4OPEN_WANTDELEGMASK;
2853c59e4cc3SRick Macklem 			break;
2854c59e4cc3SRick Macklem 		case NFSV4OPEN_WANTNODELEG:
2855c59e4cc3SRick Macklem 			stp->ls_flags |= NFSLCK_WANTNODELEG;
2856c59e4cc3SRick Macklem 			i &= ~NFSV4OPEN_WANTDELEGMASK;
2857c59e4cc3SRick Macklem 			break;
2858c59e4cc3SRick Macklem 		case NFSV4OPEN_WANTCANCEL:
2859c59e4cc3SRick Macklem 			printf("NFSv4: ignore Open WantCancel\n");
2860c59e4cc3SRick Macklem 			i &= ~NFSV4OPEN_WANTDELEGMASK;
2861c59e4cc3SRick Macklem 			break;
2862c59e4cc3SRick Macklem 		default:
2863c59e4cc3SRick Macklem 			/* nd_repstat will be set to NFSERR_INVAL below. */
2864c59e4cc3SRick Macklem 			break;
286574b8d63dSPedro F. Giffuni 		}
2866c59e4cc3SRick Macklem 	}
28679ec7b004SRick Macklem 	switch (i) {
28689ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSREAD:
28699ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_READACCESS;
28709ec7b004SRick Macklem 		break;
28719ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSWRITE:
28729ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_WRITEACCESS;
28739ec7b004SRick Macklem 		break;
28749ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSBOTH:
28759ec7b004SRick Macklem 		stp->ls_flags |= (NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
28769ec7b004SRick Macklem 		break;
28779ec7b004SRick Macklem 	default:
28789ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
287974b8d63dSPedro F. Giffuni 	}
28809ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
28819ec7b004SRick Macklem 	switch (i) {
28829ec7b004SRick Macklem 	case NFSV4OPEN_DENYNONE:
28839ec7b004SRick Macklem 		break;
28849ec7b004SRick Macklem 	case NFSV4OPEN_DENYREAD:
28859ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_READDENY;
28869ec7b004SRick Macklem 		break;
28879ec7b004SRick Macklem 	case NFSV4OPEN_DENYWRITE:
28889ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_WRITEDENY;
28899ec7b004SRick Macklem 		break;
28909ec7b004SRick Macklem 	case NFSV4OPEN_DENYBOTH:
28919ec7b004SRick Macklem 		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
28929ec7b004SRick Macklem 		break;
28939ec7b004SRick Macklem 	default:
28949ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
289574b8d63dSPedro F. Giffuni 	}
28969ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
28979ec7b004SRick Macklem 	clientid.lval[1] = *tl;
2898c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2899c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2900c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
2901c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
2902c59e4cc3SRick Macklem 			printf("EEK7 multiple clids\n");
29039ec7b004SRick Macklem 	} else {
2904c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2905c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
29069ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
29079ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
29089ec7b004SRick Macklem 	}
29099ec7b004SRick Macklem 	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2910a9285ae5SZack Kirsch 	if (error)
2911a9285ae5SZack Kirsch 		goto nfsmout;
29129ec7b004SRick Macklem 	NFSVNO_ATTRINIT(&nva);
29139ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
29149ec7b004SRick Macklem 	create = fxdr_unsigned(int, *tl);
29159ec7b004SRick Macklem 	if (!nd->nd_repstat)
291690d2dfabSRick Macklem 		nd->nd_repstat = nfsvno_getattr(dp, &dirfor, nd, p, 0, NULL);
29179ec7b004SRick Macklem 	if (create == NFSV4OPEN_CREATE) {
29189ec7b004SRick Macklem 		nva.na_type = VREG;
29199ec7b004SRick Macklem 		nva.na_mode = 0;
29209ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
29219ec7b004SRick Macklem 		how = fxdr_unsigned(int, *tl);
29229ec7b004SRick Macklem 		switch (how) {
29239ec7b004SRick Macklem 		case NFSCREATE_UNCHECKED:
29249ec7b004SRick Macklem 		case NFSCREATE_GUARDED:
2925d8a5961fSMarcelo Araujo 			error = nfsv4_sattr(nd, NULL, &nva, &attrbits, aclp, p);
2926a9285ae5SZack Kirsch 			if (error)
2927a9285ae5SZack Kirsch 				goto nfsmout;
29289ec7b004SRick Macklem 			/*
29299ec7b004SRick Macklem 			 * If the na_gid being set is the same as that of
29309ec7b004SRick Macklem 			 * the directory it is going in, clear it, since
29319ec7b004SRick Macklem 			 * that is what will be set by default. This allows
29329ec7b004SRick Macklem 			 * a user that isn't in that group to do the create.
29339ec7b004SRick Macklem 			 */
29349ec7b004SRick Macklem 			if (!nd->nd_repstat && NFSVNO_ISSETGID(&nva) &&
29359ec7b004SRick Macklem 			    nva.na_gid == dirfor.na_gid)
29369ec7b004SRick Macklem 				NFSVNO_UNSET(&nva, gid);
29379ec7b004SRick Macklem 			if (!nd->nd_repstat)
29389ec7b004SRick Macklem 				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
29399ec7b004SRick Macklem 			break;
29409ec7b004SRick Macklem 		case NFSCREATE_EXCLUSIVE:
29419ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2942086f6e0cSRick Macklem 			cverf[0] = *tl++;
2943086f6e0cSRick Macklem 			cverf[1] = *tl;
29449ec7b004SRick Macklem 			break;
2945c59e4cc3SRick Macklem 		case NFSCREATE_EXCLUSIVE41:
2946c59e4cc3SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2947c59e4cc3SRick Macklem 			cverf[0] = *tl++;
2948c59e4cc3SRick Macklem 			cverf[1] = *tl;
2949b4645807SRick Macklem 			error = nfsv4_sattr(nd, NULL, &nva, &attrbits, aclp, p);
2950c59e4cc3SRick Macklem 			if (error != 0)
2951c59e4cc3SRick Macklem 				goto nfsmout;
2952c59e4cc3SRick Macklem 			if (NFSISSET_ATTRBIT(&attrbits,
2953c59e4cc3SRick Macklem 			    NFSATTRBIT_TIMEACCESSSET))
2954c59e4cc3SRick Macklem 				nd->nd_repstat = NFSERR_INVAL;
2955c59e4cc3SRick Macklem 			/*
2956c59e4cc3SRick Macklem 			 * If the na_gid being set is the same as that of
2957c59e4cc3SRick Macklem 			 * the directory it is going in, clear it, since
2958c59e4cc3SRick Macklem 			 * that is what will be set by default. This allows
2959c59e4cc3SRick Macklem 			 * a user that isn't in that group to do the create.
2960c59e4cc3SRick Macklem 			 */
2961c59e4cc3SRick Macklem 			if (nd->nd_repstat == 0 && NFSVNO_ISSETGID(&nva) &&
2962c59e4cc3SRick Macklem 			    nva.na_gid == dirfor.na_gid)
2963c59e4cc3SRick Macklem 				NFSVNO_UNSET(&nva, gid);
2964c59e4cc3SRick Macklem 			if (nd->nd_repstat == 0)
2965c59e4cc3SRick Macklem 				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
2966c59e4cc3SRick Macklem 			break;
29679ec7b004SRick Macklem 		default:
29689ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_BADXDR;
2969a9285ae5SZack Kirsch 			goto nfsmout;
297074b8d63dSPedro F. Giffuni 		}
29719ec7b004SRick Macklem 	} else if (create != NFSV4OPEN_NOCREATE) {
29729ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
2973a9285ae5SZack Kirsch 		goto nfsmout;
29749ec7b004SRick Macklem 	}
29759ec7b004SRick Macklem 
29769ec7b004SRick Macklem 	/*
29779ec7b004SRick Macklem 	 * Now, handle the claim, which usually includes looking up a
29789ec7b004SRick Macklem 	 * name in the directory referenced by dp. The exception is
29799ec7b004SRick Macklem 	 * NFSV4OPEN_CLAIMPREVIOUS.
29809ec7b004SRick Macklem 	 */
29819ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
29829ec7b004SRick Macklem 	claim = fxdr_unsigned(int, *tl);
2983b3d4c70dSRick Macklem 	if (claim == NFSV4OPEN_CLAIMDELEGATECUR || claim ==
2984b3d4c70dSRick Macklem 	    NFSV4OPEN_CLAIMDELEGATECURFH) {
29859ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
29869ec7b004SRick Macklem 		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
29879ec7b004SRick Macklem 		NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
29889ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_DELEGCUR;
2989d80a903aSRick Macklem 	} else if (claim == NFSV4OPEN_CLAIMDELEGATEPREV || claim ==
2990d80a903aSRick Macklem 	    NFSV4OPEN_CLAIMDELEGATEPREVFH) {
29919ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_DELEGPREV;
29929ec7b004SRick Macklem 	}
29939ec7b004SRick Macklem 	if (claim == NFSV4OPEN_CLAIMNULL || claim == NFSV4OPEN_CLAIMDELEGATECUR
29949ec7b004SRick Macklem 	    || claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
29959ec7b004SRick Macklem 		if (!nd->nd_repstat && create == NFSV4OPEN_CREATE &&
29969ec7b004SRick Macklem 		    claim != NFSV4OPEN_CLAIMNULL)
29979ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
29989ec7b004SRick Macklem 		if (nd->nd_repstat) {
29999ec7b004SRick Macklem 			nd->nd_repstat = nfsrv_opencheck(clientid,
30009ec7b004SRick Macklem 			    &stateid, stp, NULL, nd, p, nd->nd_repstat);
3001a9285ae5SZack Kirsch 			goto nfsmout;
30029ec7b004SRick Macklem 		}
30039ec7b004SRick Macklem 		if (create == NFSV4OPEN_CREATE)
30049ec7b004SRick Macklem 		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
30056c21f6edSKonstantin Belousov 			LOCKPARENT | LOCKLEAF | SAVESTART | NOCACHE);
30069ec7b004SRick Macklem 		else
30079ec7b004SRick Macklem 		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
30089ec7b004SRick Macklem 			LOCKLEAF | SAVESTART);
30099ec7b004SRick Macklem 		nfsvno_setpathbuf(&named, &bufp, &hashp);
30109ec7b004SRick Macklem 		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
30119ec7b004SRick Macklem 		if (error) {
30129ec7b004SRick Macklem 			vrele(dp);
30139ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
30149ec7b004SRick Macklem 			acl_free(aclp);
30159ec7b004SRick Macklem #endif
3016222daa42SConrad Meyer 			free(stp, M_NFSDSTATE);
30179ec7b004SRick Macklem 			nfsvno_relpathbuf(&named);
3018a9285ae5SZack Kirsch 			NFSEXITCODE2(error, nd);
30199ec7b004SRick Macklem 			return (error);
30209ec7b004SRick Macklem 		}
30219ec7b004SRick Macklem 		if (!nd->nd_repstat) {
30229ec7b004SRick Macklem 			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp,
30239ec7b004SRick Macklem 			    p, &dirp);
30249ec7b004SRick Macklem 		} else {
30259ec7b004SRick Macklem 			vrele(dp);
30269ec7b004SRick Macklem 			nfsvno_relpathbuf(&named);
30279ec7b004SRick Macklem 		}
30289ec7b004SRick Macklem 		if (create == NFSV4OPEN_CREATE) {
30299ec7b004SRick Macklem 		    switch (how) {
30309ec7b004SRick Macklem 		    case NFSCREATE_UNCHECKED:
30319ec7b004SRick Macklem 			if (named.ni_vp) {
30329ec7b004SRick Macklem 				/*
30339ec7b004SRick Macklem 				 * Clear the setable attribute bits, except
30349ec7b004SRick Macklem 				 * for Size, if it is being truncated.
30359ec7b004SRick Macklem 				 */
30369ec7b004SRick Macklem 				NFSZERO_ATTRBIT(&attrbits);
30379ec7b004SRick Macklem 				if (NFSVNO_ISSETSIZE(&nva))
30389ec7b004SRick Macklem 					NFSSETBIT_ATTRBIT(&attrbits,
30399ec7b004SRick Macklem 					    NFSATTRBIT_SIZE);
30409ec7b004SRick Macklem 			}
30419ec7b004SRick Macklem 			break;
30429ec7b004SRick Macklem 		    case NFSCREATE_GUARDED:
30439ec7b004SRick Macklem 			if (named.ni_vp && !nd->nd_repstat)
30449ec7b004SRick Macklem 				nd->nd_repstat = EEXIST;
30459ec7b004SRick Macklem 			break;
30469ec7b004SRick Macklem 		    case NFSCREATE_EXCLUSIVE:
30479ec7b004SRick Macklem 			exclusive_flag = 1;
30489ec7b004SRick Macklem 			if (!named.ni_vp)
30499ec7b004SRick Macklem 				nva.na_mode = 0;
3050c59e4cc3SRick Macklem 			break;
3051c59e4cc3SRick Macklem 		    case NFSCREATE_EXCLUSIVE41:
3052c59e4cc3SRick Macklem 			exclusive_flag = 1;
3053c59e4cc3SRick Macklem 			break;
305474b8d63dSPedro F. Giffuni 		    }
30559ec7b004SRick Macklem 		}
30569ec7b004SRick Macklem 		nfsvno_open(nd, &named, clientid, &stateid, stp,
30579ec7b004SRick Macklem 		    &exclusive_flag, &nva, cverf, create, aclp, &attrbits,
305801c27978SEdward Tomasz Napierala 		    nd->nd_cred, exp, &vp);
3059c59e4cc3SRick Macklem 	} else if (claim == NFSV4OPEN_CLAIMPREVIOUS || claim ==
3060d80a903aSRick Macklem 	    NFSV4OPEN_CLAIMFH || claim == NFSV4OPEN_CLAIMDELEGATECURFH ||
3061d80a903aSRick Macklem 	    claim == NFSV4OPEN_CLAIMDELEGATEPREVFH) {
3062c59e4cc3SRick Macklem 		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
30639ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
30649ec7b004SRick Macklem 			i = fxdr_unsigned(int, *tl);
30659ec7b004SRick Macklem 			switch (i) {
30669ec7b004SRick Macklem 			case NFSV4OPEN_DELEGATEREAD:
30679ec7b004SRick Macklem 				stp->ls_flags |= NFSLCK_DELEGREAD;
30689ec7b004SRick Macklem 				break;
30699ec7b004SRick Macklem 			case NFSV4OPEN_DELEGATEWRITE:
30709ec7b004SRick Macklem 				stp->ls_flags |= NFSLCK_DELEGWRITE;
30719ec7b004SRick Macklem 			case NFSV4OPEN_DELEGATENONE:
30729ec7b004SRick Macklem 				break;
30739ec7b004SRick Macklem 			default:
30749ec7b004SRick Macklem 				nd->nd_repstat = NFSERR_BADXDR;
3075a9285ae5SZack Kirsch 				goto nfsmout;
307674b8d63dSPedro F. Giffuni 			}
30779ec7b004SRick Macklem 			stp->ls_flags |= NFSLCK_RECLAIM;
3078c59e4cc3SRick Macklem 		} else {
3079c59e4cc3SRick Macklem 			if (nd->nd_repstat == 0 && create == NFSV4OPEN_CREATE)
3080c59e4cc3SRick Macklem 				nd->nd_repstat = NFSERR_INVAL;
3081c59e4cc3SRick Macklem 		}
30829ec7b004SRick Macklem 		vp = dp;
308398f234f3SZack Kirsch 		NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
3084abd80ddbSMateusz Guzik 		if (!VN_IS_DOOMED(vp))
3085629fa50eSRick Macklem 			nd->nd_repstat = nfsrv_opencheck(clientid, &stateid,
3086629fa50eSRick Macklem 			    stp, vp, nd, p, nd->nd_repstat);
3087629fa50eSRick Macklem 		else
3088629fa50eSRick Macklem 			nd->nd_repstat = NFSERR_PERM;
30899ec7b004SRick Macklem 	} else {
30909ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
3091a9285ae5SZack Kirsch 		goto nfsmout;
30929ec7b004SRick Macklem 	}
30939ec7b004SRick Macklem 
30949ec7b004SRick Macklem 	/*
30959ec7b004SRick Macklem 	 * Do basic access checking.
30969ec7b004SRick Macklem 	 */
30979ec7b004SRick Macklem 	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
3098de67b496SRick Macklem 		/*
3099de67b496SRick Macklem 		 * The IETF working group decided that this is the correct
3100de67b496SRick Macklem 		 * error return for all non-regular files.
3101de67b496SRick Macklem 		 */
3102d8a5961fSMarcelo Araujo 		nd->nd_repstat = (vp->v_type == VDIR) ? NFSERR_ISDIR : NFSERR_SYMLINK;
31039ec7b004SRick Macklem 	}
3104b0b7d978SRick Macklem 
3105b0b7d978SRick Macklem 	/*
3106b0b7d978SRick Macklem 	 * If the Open is being done for a file that already exists, apply
3107b0b7d978SRick Macklem 	 * normal permission checking including for the file owner, if
3108b0b7d978SRick Macklem 	 * vfs.nfsd.v4openaccess is set.
3109b0b7d978SRick Macklem 	 * Previously, the owner was always allowed to open the file to
3110b0b7d978SRick Macklem 	 * be consistent with the NFS tradition of always allowing the
3111b0b7d978SRick Macklem 	 * owner of the file to write to the file regardless of permissions.
3112b0b7d978SRick Macklem 	 * It now appears that the Linux client expects the owner
3113b0b7d978SRick Macklem 	 * permissions to be checked for opens that are not creating the
3114b0b7d978SRick Macklem 	 * file.  I believe the correct approach is to use the Access
3115b0b7d978SRick Macklem 	 * operation's results to be consistent with NFSv3, but that is
3116b0b7d978SRick Macklem 	 * not what the current Linux client appears to be doing.
3117b0b7d978SRick Macklem 	 * Since both the Linux and OpenSolaris NFSv4 servers do this check,
3118b0b7d978SRick Macklem 	 * I have enabled it by default.
3119b0b7d978SRick Macklem 	 * If this semantic change causes a problem, it can be disabled by
3120b0b7d978SRick Macklem 	 * setting the sysctl vfs.nfsd.v4openaccess to 0 to re-enable the
3121b0b7d978SRick Macklem 	 * previous semantics.
3122b0b7d978SRick Macklem 	 */
3123b0b7d978SRick Macklem 	if (nfsrv_openaccess && create == NFSV4OPEN_NOCREATE)
3124b0b7d978SRick Macklem 		override = NFSACCCHK_NOOVERRIDE;
3125b0b7d978SRick Macklem 	else
3126b0b7d978SRick Macklem 		override = NFSACCCHK_ALLOWOWNER;
31279ec7b004SRick Macklem 	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_WRITEACCESS))
31288da45f2cSRick Macklem 	    nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred,
3129b0b7d978SRick Macklem 	        exp, p, override, NFSACCCHK_VPISLOCKED, NULL);
31309ec7b004SRick Macklem 	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_READACCESS)) {
31318da45f2cSRick Macklem 	    nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred,
3132b0b7d978SRick Macklem 	        exp, p, override, NFSACCCHK_VPISLOCKED, NULL);
31339ec7b004SRick Macklem 	    if (nd->nd_repstat)
31348da45f2cSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
3135b0b7d978SRick Macklem 		    nd->nd_cred, exp, p, override,
31368da45f2cSRick Macklem 		    NFSACCCHK_VPISLOCKED, NULL);
31379ec7b004SRick Macklem 	}
31389ec7b004SRick Macklem 
3139086f6e0cSRick Macklem 	if (!nd->nd_repstat) {
314090d2dfabSRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
3141086f6e0cSRick Macklem 		if (!nd->nd_repstat) {
3142086f6e0cSRick Macklem 			tverf[0] = nva.na_atime.tv_sec;
3143086f6e0cSRick Macklem 			tverf[1] = nva.na_atime.tv_nsec;
3144086f6e0cSRick Macklem 		}
3145086f6e0cSRick Macklem 	}
3146086f6e0cSRick Macklem 	if (!nd->nd_repstat && exclusive_flag && (cverf[0] != tverf[0] ||
3147086f6e0cSRick Macklem 	    cverf[1] != tverf[1]))
31489ec7b004SRick Macklem 		nd->nd_repstat = EEXIST;
31499ec7b004SRick Macklem 	/*
31509ec7b004SRick Macklem 	 * Do the open locking/delegation stuff.
31519ec7b004SRick Macklem 	 */
31529ec7b004SRick Macklem 	if (!nd->nd_repstat)
31539ec7b004SRick Macklem 	    nd->nd_repstat = nfsrv_openctrl(nd, vp, &stp, clientid, &stateid,
31549ec7b004SRick Macklem 		&delegstateid, &rflags, exp, p, nva.na_filerev);
31559ec7b004SRick Macklem 
31569ec7b004SRick Macklem 	/*
31579ec7b004SRick Macklem 	 * vp must be unlocked before the call to nfsvno_getattr(dirp,...)
31589ec7b004SRick Macklem 	 * below, to avoid a deadlock with the lookup in nfsvno_namei() above.
31599ec7b004SRick Macklem 	 * (ie: Leave the NFSVOPUNLOCK() about here.)
31609ec7b004SRick Macklem 	 */
31619ec7b004SRick Macklem 	if (vp)
3162b249ce48SMateusz Guzik 		NFSVOPUNLOCK(vp);
31639ec7b004SRick Macklem 	if (stp)
3164222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
31659ec7b004SRick Macklem 	if (!nd->nd_repstat && dirp)
316690d2dfabSRick Macklem 		nd->nd_repstat = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
31679ec7b004SRick Macklem 	if (!nd->nd_repstat) {
316890d2dfabSRick Macklem 		/* For NFSv4.1, set the Current StateID. */
316990d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0) {
317090d2dfabSRick Macklem 			nd->nd_curstateid = stateid;
317190d2dfabSRick Macklem 			nd->nd_flag |= ND_CURSTATEID;
317290d2dfabSRick Macklem 		}
31739ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
31749ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
31759ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
31769ec7b004SRick Macklem 		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
31779ec7b004SRick Macklem 		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
31789ec7b004SRick Macklem 			*tl++ = newnfs_true;
31799ec7b004SRick Macklem 			*tl++ = 0;
31809ec7b004SRick Macklem 			*tl++ = 0;
31819ec7b004SRick Macklem 			*tl++ = 0;
31829ec7b004SRick Macklem 			*tl++ = 0;
31839ec7b004SRick Macklem 		} else {
31849ec7b004SRick Macklem 			*tl++ = newnfs_false;	/* Since dirp is not locked */
31859ec7b004SRick Macklem 			txdr_hyper(dirfor.na_filerev, tl);
31869ec7b004SRick Macklem 			tl += 2;
31879ec7b004SRick Macklem 			txdr_hyper(diraft.na_filerev, tl);
31889ec7b004SRick Macklem 			tl += 2;
31899ec7b004SRick Macklem 		}
31909ec7b004SRick Macklem 		*tl = txdr_unsigned(rflags & NFSV4OPEN_RFLAGS);
31919ec7b004SRick Macklem 		(void) nfsrv_putattrbit(nd, &attrbits);
31929ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
31939ec7b004SRick Macklem 		if (rflags & NFSV4OPEN_READDELEGATE)
31949ec7b004SRick Macklem 			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEREAD);
31959ec7b004SRick Macklem 		else if (rflags & NFSV4OPEN_WRITEDELEGATE)
31969ec7b004SRick Macklem 			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEWRITE);
3197c59e4cc3SRick Macklem 		else if (retext != 0) {
3198c59e4cc3SRick Macklem 			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONEEXT);
31995d54f186SRick Macklem 			if ((rflags & NFSV4OPEN_WDNOTWANTED) != 0) {
32005d54f186SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
32015d54f186SRick Macklem 				*tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
32025d54f186SRick Macklem 			} else if ((rflags & NFSV4OPEN_WDSUPPFTYPE) != 0) {
32035d54f186SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
32045d54f186SRick Macklem 				*tl = txdr_unsigned(NFSV4OPEN_NOTSUPPFTYPE);
32055d54f186SRick Macklem 			} else if ((rflags & NFSV4OPEN_WDCONTENTION) != 0) {
3206c59e4cc3SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3207c59e4cc3SRick Macklem 				*tl++ = txdr_unsigned(NFSV4OPEN_CONTENTION);
3208c59e4cc3SRick Macklem 				*tl = newnfs_false;
3209c59e4cc3SRick Macklem 			} else if ((rflags & NFSV4OPEN_WDRESOURCE) != 0) {
3210c59e4cc3SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3211c59e4cc3SRick Macklem 				*tl++ = txdr_unsigned(NFSV4OPEN_RESOURCE);
3212c59e4cc3SRick Macklem 				*tl = newnfs_false;
3213c59e4cc3SRick Macklem 			} else {
3214c59e4cc3SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3215c59e4cc3SRick Macklem 				*tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
3216c59e4cc3SRick Macklem 			}
3217c59e4cc3SRick Macklem 		} else
32189ec7b004SRick Macklem 			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONE);
32199ec7b004SRick Macklem 		if (rflags & (NFSV4OPEN_READDELEGATE|NFSV4OPEN_WRITEDELEGATE)) {
32209ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID+NFSX_UNSIGNED);
32219ec7b004SRick Macklem 			*tl++ = txdr_unsigned(delegstateid.seqid);
32229ec7b004SRick Macklem 			NFSBCOPY((caddr_t)delegstateid.other, (caddr_t)tl,
32239ec7b004SRick Macklem 			    NFSX_STATEIDOTHER);
32249ec7b004SRick Macklem 			tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
32259ec7b004SRick Macklem 			if (rflags & NFSV4OPEN_RECALL)
32269ec7b004SRick Macklem 				*tl = newnfs_true;
32279ec7b004SRick Macklem 			else
32289ec7b004SRick Macklem 				*tl = newnfs_false;
32299ec7b004SRick Macklem 			if (rflags & NFSV4OPEN_WRITEDELEGATE) {
32309ec7b004SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
32319ec7b004SRick Macklem 				*tl++ = txdr_unsigned(NFSV4OPEN_LIMITSIZE);
32329ec7b004SRick Macklem 				txdr_hyper(nva.na_size, tl);
32339ec7b004SRick Macklem 			}
32349ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
32359ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSV4ACE_ALLOWEDTYPE);
32369ec7b004SRick Macklem 			*tl++ = txdr_unsigned(0x0);
32379ec7b004SRick Macklem 			acemask = NFSV4ACE_ALLFILESMASK;
32389ec7b004SRick Macklem 			if (nva.na_mode & S_IRUSR)
32399ec7b004SRick Macklem 			    acemask |= NFSV4ACE_READMASK;
32409ec7b004SRick Macklem 			if (nva.na_mode & S_IWUSR)
32419ec7b004SRick Macklem 			    acemask |= NFSV4ACE_WRITEMASK;
32429ec7b004SRick Macklem 			if (nva.na_mode & S_IXUSR)
32439ec7b004SRick Macklem 			    acemask |= NFSV4ACE_EXECUTEMASK;
32449ec7b004SRick Macklem 			*tl = txdr_unsigned(acemask);
32459ec7b004SRick Macklem 			(void) nfsm_strtom(nd, "OWNER@", 6);
32469ec7b004SRick Macklem 		}
32479ec7b004SRick Macklem 		*vpp = vp;
32489ec7b004SRick Macklem 	} else if (vp) {
32499ec7b004SRick Macklem 		vrele(vp);
32509ec7b004SRick Macklem 	}
32519ec7b004SRick Macklem 	if (dirp)
32529ec7b004SRick Macklem 		vrele(dirp);
32539ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
32549ec7b004SRick Macklem 	acl_free(aclp);
32559ec7b004SRick Macklem #endif
3256a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
32579ec7b004SRick Macklem 	return (0);
32589ec7b004SRick Macklem nfsmout:
32599ec7b004SRick Macklem 	vrele(dp);
32609ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
32619ec7b004SRick Macklem 	acl_free(aclp);
32629ec7b004SRick Macklem #endif
32639ec7b004SRick Macklem 	if (stp)
3264222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
3265a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
32669ec7b004SRick Macklem 	return (error);
32679ec7b004SRick Macklem }
32689ec7b004SRick Macklem 
32699ec7b004SRick Macklem /*
32709ec7b004SRick Macklem  * nfsv4 close service
32719ec7b004SRick Macklem  */
3272b9cc3262SRyan Moeller int
32739ec7b004SRick Macklem nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram,
3274af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
32759ec7b004SRick Macklem {
32769ec7b004SRick Macklem 	u_int32_t *tl;
32779ec7b004SRick Macklem 	struct nfsstate st, *stp = &st;
327890d2dfabSRick Macklem 	int error = 0, writeacc;
32799ec7b004SRick Macklem 	nfsv4stateid_t stateid;
32809ec7b004SRick Macklem 	nfsquad_t clientid;
328190d2dfabSRick Macklem 	struct nfsvattr na;
3282af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
32839ec7b004SRick Macklem 
32849ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
32859ec7b004SRick Macklem 	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
32869ec7b004SRick Macklem 	stp->ls_ownerlen = 0;
32879ec7b004SRick Macklem 	stp->ls_op = nd->nd_rp;
32889ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
32899ec7b004SRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
32909ec7b004SRick Macklem 	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
32919ec7b004SRick Macklem 	    NFSX_STATEIDOTHER);
329290d2dfabSRick Macklem 
329390d2dfabSRick Macklem 	/*
329490d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
329590d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
329690d2dfabSRick Macklem 	 */
329790d2dfabSRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 1 &&
329890d2dfabSRick Macklem 	    stp->ls_stateid.other[0] == 0 && stp->ls_stateid.other[1] == 0 &&
329990d2dfabSRick Macklem 	    stp->ls_stateid.other[2] == 0) {
330090d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0)
330190d2dfabSRick Macklem 			stp->ls_stateid = nd->nd_curstateid;
330290d2dfabSRick Macklem 		else {
330390d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
330490d2dfabSRick Macklem 			goto nfsmout;
330590d2dfabSRick Macklem 		}
330690d2dfabSRick Macklem 	}
330790d2dfabSRick Macklem 
33089ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_CLOSE;
33099ec7b004SRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0];
33109ec7b004SRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1];
3311c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3312c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3313c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3314c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3315c59e4cc3SRick Macklem 			printf("EEK8 multiple clids\n");
33169ec7b004SRick Macklem 	} else {
3317c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3318c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
33199ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
33209ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
33219ec7b004SRick Macklem 	}
332290d2dfabSRick Macklem 	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p,
332390d2dfabSRick Macklem 	    &writeacc);
332490d2dfabSRick Macklem 	/* For pNFS, update the attributes. */
332590d2dfabSRick Macklem 	if (writeacc != 0 || nfsrv_pnfsatime != 0)
332690d2dfabSRick Macklem 		nfsrv_updatemdsattr(vp, &na, p);
33279ec7b004SRick Macklem 	vput(vp);
33289ec7b004SRick Macklem 	if (!nd->nd_repstat) {
332990d2dfabSRick Macklem 		/*
333090d2dfabSRick Macklem 		 * If the stateid that has been closed is the current stateid,
333190d2dfabSRick Macklem 		 * unset it.
333290d2dfabSRick Macklem 		 */
333390d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0 &&
333490d2dfabSRick Macklem 		    stateid.other[0] == nd->nd_curstateid.other[0] &&
333590d2dfabSRick Macklem 		    stateid.other[1] == nd->nd_curstateid.other[1] &&
333690d2dfabSRick Macklem 		    stateid.other[2] == nd->nd_curstateid.other[2])
333790d2dfabSRick Macklem 			nd->nd_flag &= ~ND_CURSTATEID;
33389ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
33399ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
33409ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
33419ec7b004SRick Macklem 	}
3342a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
33439ec7b004SRick Macklem 	return (0);
33449ec7b004SRick Macklem nfsmout:
33459ec7b004SRick Macklem 	vput(vp);
3346a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
33479ec7b004SRick Macklem 	return (error);
33489ec7b004SRick Macklem }
33499ec7b004SRick Macklem 
33509ec7b004SRick Macklem /*
33519ec7b004SRick Macklem  * nfsv4 delegpurge service
33529ec7b004SRick Macklem  */
3353b9cc3262SRyan Moeller int
33549ec7b004SRick Macklem nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram,
3355af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
33569ec7b004SRick Macklem {
33579ec7b004SRick Macklem 	u_int32_t *tl;
33589ec7b004SRick Macklem 	int error = 0;
33599ec7b004SRick Macklem 	nfsquad_t clientid;
3360af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
33619ec7b004SRick Macklem 
3362984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
3363a9285ae5SZack Kirsch 		goto nfsmout;
33649ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
33659ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
33669ec7b004SRick Macklem 	clientid.lval[1] = *tl;
3367c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3368c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3369c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3370c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3371c59e4cc3SRick Macklem 			printf("EEK9 multiple clids\n");
33729ec7b004SRick Macklem 	} else {
3373c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3374c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
33759ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
33769ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
33779ec7b004SRick Macklem 	}
3378c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_delegupdate(nd, clientid, NULL, NULL,
337990d2dfabSRick Macklem 	    NFSV4OP_DELEGPURGE, nd->nd_cred, p, NULL);
33809ec7b004SRick Macklem nfsmout:
3381a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
33829ec7b004SRick Macklem 	return (error);
33839ec7b004SRick Macklem }
33849ec7b004SRick Macklem 
33859ec7b004SRick Macklem /*
33869ec7b004SRick Macklem  * nfsv4 delegreturn service
33879ec7b004SRick Macklem  */
3388b9cc3262SRyan Moeller int
33899ec7b004SRick Macklem nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram,
3390af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
33919ec7b004SRick Macklem {
33929ec7b004SRick Macklem 	u_int32_t *tl;
339390d2dfabSRick Macklem 	int error = 0, writeacc;
33949ec7b004SRick Macklem 	nfsv4stateid_t stateid;
33959ec7b004SRick Macklem 	nfsquad_t clientid;
339690d2dfabSRick Macklem 	struct nfsvattr na;
3397af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
33989ec7b004SRick Macklem 
33999ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
34009ec7b004SRick Macklem 	stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
34019ec7b004SRick Macklem 	NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, NFSX_STATEIDOTHER);
34029ec7b004SRick Macklem 	clientid.lval[0] = stateid.other[0];
34039ec7b004SRick Macklem 	clientid.lval[1] = stateid.other[1];
3404c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3405c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3406c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3407c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3408c59e4cc3SRick Macklem 			printf("EEK10 multiple clids\n");
34099ec7b004SRick Macklem 	} else {
3410c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3411c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
34129ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
34139ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
34149ec7b004SRick Macklem 	}
3415c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_delegupdate(nd, clientid, &stateid, vp,
341690d2dfabSRick Macklem 	    NFSV4OP_DELEGRETURN, nd->nd_cred, p, &writeacc);
341790d2dfabSRick Macklem 	/* For pNFS, update the attributes. */
341890d2dfabSRick Macklem 	if (writeacc != 0 || nfsrv_pnfsatime != 0)
341990d2dfabSRick Macklem 		nfsrv_updatemdsattr(vp, &na, p);
34209ec7b004SRick Macklem nfsmout:
34219ec7b004SRick Macklem 	vput(vp);
3422a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
34239ec7b004SRick Macklem 	return (error);
34249ec7b004SRick Macklem }
34259ec7b004SRick Macklem 
34269ec7b004SRick Macklem /*
34279ec7b004SRick Macklem  * nfsv4 get file handle service
34289ec7b004SRick Macklem  */
3429b9cc3262SRyan Moeller int
34309ec7b004SRick Macklem nfsrvd_getfh(struct nfsrv_descript *nd, __unused int isdgram,
3431af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
34329ec7b004SRick Macklem {
34339ec7b004SRick Macklem 	fhandle_t fh;
3434af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
34359ec7b004SRick Macklem 
34369ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
34379ec7b004SRick Macklem 	vput(vp);
34389ec7b004SRick Macklem 	if (!nd->nd_repstat)
34399ec7b004SRick Macklem 		(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
3440a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
34419ec7b004SRick Macklem 	return (0);
34429ec7b004SRick Macklem }
34439ec7b004SRick Macklem 
34449ec7b004SRick Macklem /*
34459ec7b004SRick Macklem  * nfsv4 open confirm service
34469ec7b004SRick Macklem  */
3447b9cc3262SRyan Moeller int
34489ec7b004SRick Macklem nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram,
3449af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
34509ec7b004SRick Macklem {
34519ec7b004SRick Macklem 	u_int32_t *tl;
34529ec7b004SRick Macklem 	struct nfsstate st, *stp = &st;
34539ec7b004SRick Macklem 	int error = 0;
34549ec7b004SRick Macklem 	nfsv4stateid_t stateid;
34559ec7b004SRick Macklem 	nfsquad_t clientid;
3456af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
34579ec7b004SRick Macklem 
3458c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0) {
3459c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
3460c59e4cc3SRick Macklem 		goto nfsmout;
3461c59e4cc3SRick Macklem 	}
34629ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
34639ec7b004SRick Macklem 	stp->ls_ownerlen = 0;
34649ec7b004SRick Macklem 	stp->ls_op = nd->nd_rp;
34659ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
34669ec7b004SRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
34679ec7b004SRick Macklem 	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
34689ec7b004SRick Macklem 	    NFSX_STATEIDOTHER);
34699ec7b004SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
34709ec7b004SRick Macklem 	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl);
34719ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_CONFIRM;
34729ec7b004SRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0];
34739ec7b004SRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1];
3474c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3475c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3476c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3477c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3478c59e4cc3SRick Macklem 			printf("EEK11 multiple clids\n");
34799ec7b004SRick Macklem 	} else {
3480c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3481c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
34829ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
34839ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
34849ec7b004SRick Macklem 	}
348590d2dfabSRick Macklem 	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p,
348690d2dfabSRick Macklem 	    NULL);
34879ec7b004SRick Macklem 	if (!nd->nd_repstat) {
34889ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
34899ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
34909ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
34919ec7b004SRick Macklem 	}
34929ec7b004SRick Macklem nfsmout:
34939ec7b004SRick Macklem 	vput(vp);
3494a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
34959ec7b004SRick Macklem 	return (error);
34969ec7b004SRick Macklem }
34979ec7b004SRick Macklem 
34989ec7b004SRick Macklem /*
34999ec7b004SRick Macklem  * nfsv4 open downgrade service
35009ec7b004SRick Macklem  */
3501b9cc3262SRyan Moeller int
35029ec7b004SRick Macklem nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram,
3503af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
35049ec7b004SRick Macklem {
35059ec7b004SRick Macklem 	u_int32_t *tl;
35069ec7b004SRick Macklem 	int i;
35079ec7b004SRick Macklem 	struct nfsstate st, *stp = &st;
35089ec7b004SRick Macklem 	int error = 0;
35099ec7b004SRick Macklem 	nfsv4stateid_t stateid;
35109ec7b004SRick Macklem 	nfsquad_t clientid;
3511af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
35129ec7b004SRick Macklem 
3513d8a5961fSMarcelo Araujo 	/* opendowngrade can only work on a file object.*/
3514d8a5961fSMarcelo Araujo 	if (vp->v_type != VREG) {
3515d8a5961fSMarcelo Araujo 		error = NFSERR_INVAL;
3516d8a5961fSMarcelo Araujo 		goto nfsmout;
3517d8a5961fSMarcelo Araujo 	}
35189ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
35199ec7b004SRick Macklem 	stp->ls_ownerlen = 0;
35209ec7b004SRick Macklem 	stp->ls_op = nd->nd_rp;
35219ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
35229ec7b004SRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
35239ec7b004SRick Macklem 	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
35249ec7b004SRick Macklem 	    NFSX_STATEIDOTHER);
35259ec7b004SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
352690d2dfabSRick Macklem 
352790d2dfabSRick Macklem 	/*
352890d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
352990d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
353090d2dfabSRick Macklem 	 */
353190d2dfabSRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 1 &&
353290d2dfabSRick Macklem 	    stp->ls_stateid.other[0] == 0 && stp->ls_stateid.other[1] == 0 &&
353390d2dfabSRick Macklem 	    stp->ls_stateid.other[2] == 0) {
353490d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0)
353590d2dfabSRick Macklem 			stp->ls_stateid = nd->nd_curstateid;
353690d2dfabSRick Macklem 		else {
353790d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
353890d2dfabSRick Macklem 			goto nfsmout;
353990d2dfabSRick Macklem 		}
354090d2dfabSRick Macklem 	}
354190d2dfabSRick Macklem 
35429ec7b004SRick Macklem 	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
35439ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
35446269d663SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0)
35456269d663SRick Macklem 		i &= ~NFSV4OPEN_WANTDELEGMASK;
35469ec7b004SRick Macklem 	switch (i) {
35479ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSREAD:
35489ec7b004SRick Macklem 		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_DOWNGRADE);
35499ec7b004SRick Macklem 		break;
35509ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSWRITE:
35519ec7b004SRick Macklem 		stp->ls_flags = (NFSLCK_WRITEACCESS | NFSLCK_DOWNGRADE);
35529ec7b004SRick Macklem 		break;
35539ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSBOTH:
35549ec7b004SRick Macklem 		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_WRITEACCESS |
35559ec7b004SRick Macklem 		    NFSLCK_DOWNGRADE);
35569ec7b004SRick Macklem 		break;
35579ec7b004SRick Macklem 	default:
35586269d663SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
355974b8d63dSPedro F. Giffuni 	}
35609ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl);
35619ec7b004SRick Macklem 	switch (i) {
35629ec7b004SRick Macklem 	case NFSV4OPEN_DENYNONE:
35639ec7b004SRick Macklem 		break;
35649ec7b004SRick Macklem 	case NFSV4OPEN_DENYREAD:
35659ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_READDENY;
35669ec7b004SRick Macklem 		break;
35679ec7b004SRick Macklem 	case NFSV4OPEN_DENYWRITE:
35689ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_WRITEDENY;
35699ec7b004SRick Macklem 		break;
35709ec7b004SRick Macklem 	case NFSV4OPEN_DENYBOTH:
35719ec7b004SRick Macklem 		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
35729ec7b004SRick Macklem 		break;
35739ec7b004SRick Macklem 	default:
35746269d663SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
357574b8d63dSPedro F. Giffuni 	}
35769ec7b004SRick Macklem 
35779ec7b004SRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0];
35789ec7b004SRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1];
3579c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3580c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3581c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3582c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3583c59e4cc3SRick Macklem 			printf("EEK12 multiple clids\n");
35849ec7b004SRick Macklem 	} else {
3585c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3586c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
35879ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
35889ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
35899ec7b004SRick Macklem 	}
35909ec7b004SRick Macklem 	if (!nd->nd_repstat)
35919ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid,
359290d2dfabSRick Macklem 		    nd, p, NULL);
35939ec7b004SRick Macklem 	if (!nd->nd_repstat) {
359490d2dfabSRick Macklem 		/* For NFSv4.1, set the Current StateID. */
359590d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0) {
359690d2dfabSRick Macklem 			nd->nd_curstateid = stateid;
359790d2dfabSRick Macklem 			nd->nd_flag |= ND_CURSTATEID;
359890d2dfabSRick Macklem 		}
35999ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
36009ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
36019ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
36029ec7b004SRick Macklem 	}
36039ec7b004SRick Macklem nfsmout:
36049ec7b004SRick Macklem 	vput(vp);
3605a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
36069ec7b004SRick Macklem 	return (error);
36079ec7b004SRick Macklem }
36089ec7b004SRick Macklem 
36099ec7b004SRick Macklem /*
36109ec7b004SRick Macklem  * nfsv4 renew lease service
36119ec7b004SRick Macklem  */
3612b9cc3262SRyan Moeller int
36139ec7b004SRick Macklem nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram,
3614af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
36159ec7b004SRick Macklem {
36169ec7b004SRick Macklem 	u_int32_t *tl;
36179ec7b004SRick Macklem 	int error = 0;
36189ec7b004SRick Macklem 	nfsquad_t clientid;
3619af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
36209ec7b004SRick Macklem 
3621c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0) {
3622c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
3623c59e4cc3SRick Macklem 		goto nfsmout;
3624c59e4cc3SRick Macklem 	}
3625984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
3626a9285ae5SZack Kirsch 		goto nfsmout;
36279ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
36289ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
36299ec7b004SRick Macklem 	clientid.lval[1] = *tl;
3630c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3631c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3632c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3633c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3634c59e4cc3SRick Macklem 			printf("EEK13 multiple clids\n");
36359ec7b004SRick Macklem 	} else {
3636c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3637c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
36389ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
36399ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
36409ec7b004SRick Macklem 	}
36419ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW),
3642c59e4cc3SRick Macklem 	    NULL, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p);
36439ec7b004SRick Macklem nfsmout:
3644a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
36459ec7b004SRick Macklem 	return (error);
36469ec7b004SRick Macklem }
36479ec7b004SRick Macklem 
36489ec7b004SRick Macklem /*
36499ec7b004SRick Macklem  * nfsv4 security info service
36509ec7b004SRick Macklem  */
3651b9cc3262SRyan Moeller int
36529ec7b004SRick Macklem nfsrvd_secinfo(struct nfsrv_descript *nd, int isdgram,
3653af444b18SEdward Tomasz Napierala     vnode_t dp, struct nfsexstuff *exp)
36549ec7b004SRick Macklem {
36559ec7b004SRick Macklem 	u_int32_t *tl;
36569ec7b004SRick Macklem 	int len;
36579ec7b004SRick Macklem 	struct nameidata named;
36589ec7b004SRick Macklem 	vnode_t dirp = NULL, vp;
36599ec7b004SRick Macklem 	struct nfsrvfh fh;
36609ec7b004SRick Macklem 	struct nfsexstuff retnes;
36619ec7b004SRick Macklem 	u_int32_t *sizp;
3662947bd247SRick Macklem 	int error = 0, i;
3663947bd247SRick Macklem 	uint64_t savflag;
36649ec7b004SRick Macklem 	char *bufp;
36659ec7b004SRick Macklem 	u_long *hashp;
3666af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
36679ec7b004SRick Macklem 
36689ec7b004SRick Macklem 	/*
36699ec7b004SRick Macklem 	 * All this just to get the export flags for the name.
36709ec7b004SRick Macklem 	 */
36719ec7b004SRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
36729ec7b004SRick Macklem 	    LOCKLEAF | SAVESTART);
36739ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
36749ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
36759ec7b004SRick Macklem 	if (error) {
36769ec7b004SRick Macklem 		vput(dp);
36779ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
3678a9285ae5SZack Kirsch 		goto out;
36799ec7b004SRick Macklem 	}
36809ec7b004SRick Macklem 	if (!nd->nd_repstat) {
36819ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
36829ec7b004SRick Macklem 	} else {
36839ec7b004SRick Macklem 		vput(dp);
36849ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
36859ec7b004SRick Macklem 	}
36869ec7b004SRick Macklem 	if (dirp)
36879ec7b004SRick Macklem 		vrele(dirp);
36889ec7b004SRick Macklem 	if (nd->nd_repstat)
3689a9285ae5SZack Kirsch 		goto out;
36909ec7b004SRick Macklem 	vrele(named.ni_startdir);
36919ec7b004SRick Macklem 	nfsvno_relpathbuf(&named);
36929ec7b004SRick Macklem 	fh.nfsrvfh_len = NFSX_MYFH;
36939ec7b004SRick Macklem 	vp = named.ni_vp;
36949ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
36959ec7b004SRick Macklem 	vput(vp);
36969ec7b004SRick Macklem 	savflag = nd->nd_flag;
36979ec7b004SRick Macklem 	if (!nd->nd_repstat) {
36985edc9102SEdward Tomasz Napierala 		nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0);
36999ec7b004SRick Macklem 		if (vp)
37009ec7b004SRick Macklem 			vput(vp);
37019ec7b004SRick Macklem 	}
37029ec7b004SRick Macklem 	nd->nd_flag = savflag;
37039ec7b004SRick Macklem 	if (nd->nd_repstat)
3704a9285ae5SZack Kirsch 		goto out;
37059ec7b004SRick Macklem 
37069ec7b004SRick Macklem 	/*
37079ec7b004SRick Macklem 	 * Finally have the export flags for name, so we can create
37089ec7b004SRick Macklem 	 * the security info.
37099ec7b004SRick Macklem 	 */
37109ec7b004SRick Macklem 	len = 0;
37119ec7b004SRick Macklem 	NFSM_BUILD(sizp, u_int32_t *, NFSX_UNSIGNED);
3712*56e9d8e3SRick Macklem 
3713*56e9d8e3SRick Macklem 	/* If nes_numsecflavor == 0, all are allowed. */
3714*56e9d8e3SRick Macklem 	if (retnes.nes_numsecflavor == 0) {
3715*56e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
3716*56e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(RPCAUTH_UNIX);
3717*56e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTH_GSS);
3718*56e9d8e3SRick Macklem 		nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3719*56e9d8e3SRick Macklem 		    nfsgss_mechlist[KERBV_MECH].len);
3720*56e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
3721*56e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3722*56e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(RPCAUTHGSS_SVCNONE);
3723*56e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTH_GSS);
3724*56e9d8e3SRick Macklem 		nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3725*56e9d8e3SRick Macklem 		    nfsgss_mechlist[KERBV_MECH].len);
3726*56e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
3727*56e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3728*56e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
3729*56e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTH_GSS);
3730*56e9d8e3SRick Macklem 		nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3731*56e9d8e3SRick Macklem 		    nfsgss_mechlist[KERBV_MECH].len);
3732*56e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
3733*56e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3734*56e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
3735*56e9d8e3SRick Macklem 		len = 4;
3736*56e9d8e3SRick Macklem 	}
373798ad4453SRick Macklem 	for (i = 0; i < retnes.nes_numsecflavor; i++) {
373898ad4453SRick Macklem 		if (retnes.nes_secflavors[i] == AUTH_SYS) {
37399ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
37409ec7b004SRick Macklem 			*tl = txdr_unsigned(RPCAUTH_UNIX);
37419ec7b004SRick Macklem 			len++;
374298ad4453SRick Macklem 		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) {
37439ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
37449ec7b004SRick Macklem 			*tl++ = txdr_unsigned(RPCAUTH_GSS);
37459ec7b004SRick Macklem 			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
37469ec7b004SRick Macklem 			    nfsgss_mechlist[KERBV_MECH].len);
37479ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
37489ec7b004SRick Macklem 			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
374998ad4453SRick Macklem 			*tl = txdr_unsigned(RPCAUTHGSS_SVCNONE);
37509ec7b004SRick Macklem 			len++;
375198ad4453SRick Macklem 		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) {
375298ad4453SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
375398ad4453SRick Macklem 			*tl++ = txdr_unsigned(RPCAUTH_GSS);
375498ad4453SRick Macklem 			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
375598ad4453SRick Macklem 			    nfsgss_mechlist[KERBV_MECH].len);
375698ad4453SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
375798ad4453SRick Macklem 			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
375898ad4453SRick Macklem 			*tl = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
375998ad4453SRick Macklem 			len++;
376098ad4453SRick Macklem 		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) {
376198ad4453SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
376298ad4453SRick Macklem 			*tl++ = txdr_unsigned(RPCAUTH_GSS);
376398ad4453SRick Macklem 			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
376498ad4453SRick Macklem 			    nfsgss_mechlist[KERBV_MECH].len);
376598ad4453SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
376698ad4453SRick Macklem 			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
376798ad4453SRick Macklem 			*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
376898ad4453SRick Macklem 			len++;
376998ad4453SRick Macklem 		}
37709ec7b004SRick Macklem 	}
37719ec7b004SRick Macklem 	*sizp = txdr_unsigned(len);
3772a9285ae5SZack Kirsch 
3773a9285ae5SZack Kirsch out:
3774a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
3775a9285ae5SZack Kirsch 	return (error);
37769ec7b004SRick Macklem }
37779ec7b004SRick Macklem 
37789ec7b004SRick Macklem /*
3779947bd247SRick Macklem  * nfsv4 security info no name service
3780947bd247SRick Macklem  */
3781947bd247SRick Macklem int
3782947bd247SRick Macklem nfsrvd_secinfononame(struct nfsrv_descript *nd, int isdgram,
3783947bd247SRick Macklem     vnode_t dp, struct nfsexstuff *exp)
3784947bd247SRick Macklem {
3785947bd247SRick Macklem 	uint32_t *tl, *sizp;
3786947bd247SRick Macklem 	struct nameidata named;
3787947bd247SRick Macklem 	vnode_t dirp = NULL, vp;
3788947bd247SRick Macklem 	struct nfsrvfh fh;
3789947bd247SRick Macklem 	struct nfsexstuff retnes;
3790947bd247SRick Macklem 	int error = 0, fhstyle, i, len;
3791947bd247SRick Macklem 	uint64_t savflag;
3792947bd247SRick Macklem 	char *bufp;
3793947bd247SRick Macklem 	u_long *hashp;
3794947bd247SRick Macklem 	struct thread *p = curthread;
3795947bd247SRick Macklem 
3796947bd247SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
3797947bd247SRick Macklem 	fhstyle = fxdr_unsigned(int, *tl);
3798947bd247SRick Macklem 	switch (fhstyle) {
3799947bd247SRick Macklem 	case NFSSECINFONONAME_PARENT:
3800947bd247SRick Macklem 		NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
3801947bd247SRick Macklem 		    LOCKLEAF | SAVESTART);
3802947bd247SRick Macklem 		nfsvno_setpathbuf(&named, &bufp, &hashp);
3803947bd247SRick Macklem 		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
3804947bd247SRick Macklem 		if (error != 0) {
3805947bd247SRick Macklem 			vput(dp);
3806947bd247SRick Macklem 			nfsvno_relpathbuf(&named);
3807947bd247SRick Macklem 			goto nfsmout;
3808947bd247SRick Macklem 		}
3809947bd247SRick Macklem 		if (nd->nd_repstat == 0)
3810947bd247SRick Macklem 			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
3811947bd247SRick Macklem 		else
3812947bd247SRick Macklem 			vput(dp);
3813947bd247SRick Macklem 		if (dirp != NULL)
3814947bd247SRick Macklem 			vrele(dirp);
3815947bd247SRick Macklem 		vrele(named.ni_startdir);
3816947bd247SRick Macklem 		nfsvno_relpathbuf(&named);
3817947bd247SRick Macklem 		vp = named.ni_vp;
3818947bd247SRick Macklem 		break;
3819947bd247SRick Macklem 	case NFSSECINFONONAME_CURFH:
3820947bd247SRick Macklem 		vp = dp;
3821947bd247SRick Macklem 		break;
3822947bd247SRick Macklem 	default:
3823947bd247SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
3824947bd247SRick Macklem 		vput(dp);
3825947bd247SRick Macklem 	}
3826947bd247SRick Macklem 	if (nd->nd_repstat != 0)
3827947bd247SRick Macklem 		goto nfsmout;
3828947bd247SRick Macklem 	fh.nfsrvfh_len = NFSX_MYFH;
3829947bd247SRick Macklem 	nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
3830947bd247SRick Macklem 	vput(vp);
3831947bd247SRick Macklem 	savflag = nd->nd_flag;
3832947bd247SRick Macklem 	if (nd->nd_repstat == 0) {
3833947bd247SRick Macklem 		nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0);
3834947bd247SRick Macklem 		if (vp != NULL)
3835947bd247SRick Macklem 			vput(vp);
3836947bd247SRick Macklem 	}
3837947bd247SRick Macklem 	nd->nd_flag = savflag;
3838947bd247SRick Macklem 	if (nd->nd_repstat != 0)
3839947bd247SRick Macklem 		goto nfsmout;
3840947bd247SRick Macklem 
3841947bd247SRick Macklem 	/*
3842947bd247SRick Macklem 	 * Finally have the export flags for fh/parent, so we can create
3843947bd247SRick Macklem 	 * the security info.
3844947bd247SRick Macklem 	 */
3845947bd247SRick Macklem 	len = 0;
3846947bd247SRick Macklem 	NFSM_BUILD(sizp, uint32_t *, NFSX_UNSIGNED);
3847*56e9d8e3SRick Macklem 
3848*56e9d8e3SRick Macklem 	/* If nes_numsecflavor == 0, all are allowed. */
3849*56e9d8e3SRick Macklem 	if (retnes.nes_numsecflavor == 0) {
3850*56e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
3851*56e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(RPCAUTH_UNIX);
3852*56e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTH_GSS);
3853*56e9d8e3SRick Macklem 		nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3854*56e9d8e3SRick Macklem 		    nfsgss_mechlist[KERBV_MECH].len);
3855*56e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
3856*56e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3857*56e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(RPCAUTHGSS_SVCNONE);
3858*56e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTH_GSS);
3859*56e9d8e3SRick Macklem 		nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3860*56e9d8e3SRick Macklem 		    nfsgss_mechlist[KERBV_MECH].len);
3861*56e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
3862*56e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3863*56e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
3864*56e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTH_GSS);
3865*56e9d8e3SRick Macklem 		nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3866*56e9d8e3SRick Macklem 		    nfsgss_mechlist[KERBV_MECH].len);
3867*56e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
3868*56e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3869*56e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
3870*56e9d8e3SRick Macklem 		len = 4;
3871*56e9d8e3SRick Macklem 	}
3872947bd247SRick Macklem 	for (i = 0; i < retnes.nes_numsecflavor; i++) {
3873947bd247SRick Macklem 		if (retnes.nes_secflavors[i] == AUTH_SYS) {
3874947bd247SRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
3875947bd247SRick Macklem 			*tl = txdr_unsigned(RPCAUTH_UNIX);
3876947bd247SRick Macklem 			len++;
3877947bd247SRick Macklem 		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) {
3878947bd247SRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
3879947bd247SRick Macklem 			*tl = txdr_unsigned(RPCAUTH_GSS);
3880947bd247SRick Macklem 			nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3881947bd247SRick Macklem 			    nfsgss_mechlist[KERBV_MECH].len);
3882947bd247SRick Macklem 			NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
3883947bd247SRick Macklem 			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3884947bd247SRick Macklem 			*tl = txdr_unsigned(RPCAUTHGSS_SVCNONE);
3885947bd247SRick Macklem 			len++;
3886947bd247SRick Macklem 		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) {
3887947bd247SRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
3888947bd247SRick Macklem 			*tl = txdr_unsigned(RPCAUTH_GSS);
3889947bd247SRick Macklem 			nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3890947bd247SRick Macklem 			    nfsgss_mechlist[KERBV_MECH].len);
3891947bd247SRick Macklem 			NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
3892947bd247SRick Macklem 			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3893947bd247SRick Macklem 			*tl = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
3894947bd247SRick Macklem 			len++;
3895947bd247SRick Macklem 		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) {
3896947bd247SRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
3897947bd247SRick Macklem 			*tl = txdr_unsigned(RPCAUTH_GSS);
3898947bd247SRick Macklem 			nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3899947bd247SRick Macklem 			    nfsgss_mechlist[KERBV_MECH].len);
3900947bd247SRick Macklem 			NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
3901947bd247SRick Macklem 			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3902947bd247SRick Macklem 			*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
3903947bd247SRick Macklem 			len++;
3904947bd247SRick Macklem 		}
3905947bd247SRick Macklem 	}
3906947bd247SRick Macklem 	*sizp = txdr_unsigned(len);
3907947bd247SRick Macklem 
3908947bd247SRick Macklem nfsmout:
3909947bd247SRick Macklem 	NFSEXITCODE2(error, nd);
3910947bd247SRick Macklem 	return (error);
3911947bd247SRick Macklem }
3912947bd247SRick Macklem 
3913947bd247SRick Macklem /*
39149ec7b004SRick Macklem  * nfsv4 set client id service
39159ec7b004SRick Macklem  */
3916b9cc3262SRyan Moeller int
39179ec7b004SRick Macklem nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram,
3918af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
39199ec7b004SRick Macklem {
39209ec7b004SRick Macklem 	u_int32_t *tl;
39219ec7b004SRick Macklem 	int i;
39229ec7b004SRick Macklem 	int error = 0, idlen;
39239ec7b004SRick Macklem 	struct nfsclient *clp = NULL;
3924ed2f1001SRick Macklem #ifdef INET
3925ed2f1001SRick Macklem 	struct sockaddr_in *rin;
3926ed2f1001SRick Macklem #endif
3927ed2f1001SRick Macklem #ifdef INET6
3928ed2f1001SRick Macklem 	struct sockaddr_in6 *rin6;
3929ed2f1001SRick Macklem #endif
3930ed2f1001SRick Macklem #if defined(INET) || defined(INET6)
3931ed2f1001SRick Macklem 	u_char *ucp, *ucp2;
3932ed2f1001SRick Macklem #endif
3933ed2f1001SRick Macklem 	u_char *verf, *addrbuf;
39349ec7b004SRick Macklem 	nfsquad_t clientid, confirm;
3935af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
39369ec7b004SRick Macklem 
3937c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0) {
3938c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
3939c59e4cc3SRick Macklem 		goto nfsmout;
3940c59e4cc3SRick Macklem 	}
3941984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
3942a9285ae5SZack Kirsch 		goto out;
39439ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
39449ec7b004SRick Macklem 	verf = (u_char *)tl;
39459ec7b004SRick Macklem 	tl += (NFSX_VERF / NFSX_UNSIGNED);
39469ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl);
39479ec7b004SRick Macklem 	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
39489ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
3949a9285ae5SZack Kirsch 		goto nfsmout;
39509ec7b004SRick Macklem 	}
39519ec7b004SRick Macklem 	idlen = i;
39529ec7b004SRick Macklem 	if (nd->nd_flag & ND_GSS)
39539ec7b004SRick Macklem 		i += nd->nd_princlen;
39541f54e596SRick Macklem 	clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
39551f54e596SRick Macklem 	    M_ZERO);
39561f54e596SRick Macklem 	clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
39571f54e596SRick Macklem 	    nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
39589ec7b004SRick Macklem 	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
3959ed2f1001SRick Macklem 	/* Allocated large enough for an AF_INET or AF_INET6 socket. */
3960ed2f1001SRick Macklem 	clp->lc_req.nr_nam = malloc(sizeof(struct sockaddr_in6), M_SONAME,
3961b97b91b5SConrad Meyer 	    M_WAITOK | M_ZERO);
39629ec7b004SRick Macklem 	clp->lc_req.nr_cred = NULL;
39639ec7b004SRick Macklem 	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
39649ec7b004SRick Macklem 	clp->lc_idlen = idlen;
39659ec7b004SRick Macklem 	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
39669ec7b004SRick Macklem 	if (error)
39679ec7b004SRick Macklem 		goto nfsmout;
39689ec7b004SRick Macklem 	if (nd->nd_flag & ND_GSS) {
39699ec7b004SRick Macklem 		clp->lc_flags = LCL_GSS;
39709ec7b004SRick Macklem 		if (nd->nd_flag & ND_GSSINTEGRITY)
39719ec7b004SRick Macklem 			clp->lc_flags |= LCL_GSSINTEGRITY;
39729ec7b004SRick Macklem 		else if (nd->nd_flag & ND_GSSPRIVACY)
39739ec7b004SRick Macklem 			clp->lc_flags |= LCL_GSSPRIVACY;
39749ec7b004SRick Macklem 	} else {
39759ec7b004SRick Macklem 		clp->lc_flags = 0;
39769ec7b004SRick Macklem 	}
39779ec7b004SRick Macklem 	if ((nd->nd_flag & ND_GSS) && nd->nd_princlen > 0) {
39789ec7b004SRick Macklem 		clp->lc_flags |= LCL_NAME;
39799ec7b004SRick Macklem 		clp->lc_namelen = nd->nd_princlen;
39809ec7b004SRick Macklem 		clp->lc_name = &clp->lc_id[idlen];
39819ec7b004SRick Macklem 		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
39829ec7b004SRick Macklem 	} else {
39839ec7b004SRick Macklem 		clp->lc_uid = nd->nd_cred->cr_uid;
39849ec7b004SRick Macklem 		clp->lc_gid = nd->nd_cred->cr_gid;
39859ec7b004SRick Macklem 	}
39866e4b6ff8SRick Macklem 
39876e4b6ff8SRick Macklem 	/* If the client is using TLS, do so for the callback connection. */
39886e4b6ff8SRick Macklem 	if (nd->nd_flag & ND_TLS)
39896e4b6ff8SRick Macklem 		clp->lc_flags |= LCL_TLSCB;
39906e4b6ff8SRick Macklem 
39919ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
39929ec7b004SRick Macklem 	clp->lc_program = fxdr_unsigned(u_int32_t, *tl);
39939ec7b004SRick Macklem 	error = nfsrv_getclientipaddr(nd, clp);
39949ec7b004SRick Macklem 	if (error)
39959ec7b004SRick Macklem 		goto nfsmout;
39969ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
39979ec7b004SRick Macklem 	clp->lc_callback = fxdr_unsigned(u_int32_t, *tl);
39989ec7b004SRick Macklem 
39999ec7b004SRick Macklem 	/*
40009ec7b004SRick Macklem 	 * nfsrv_setclient() does the actual work of adding it to the
40019ec7b004SRick Macklem 	 * client list. If there is no error, the structure has been
40029ec7b004SRick Macklem 	 * linked into the client list and clp should no longer be used
40039ec7b004SRick Macklem 	 * here. When an error is returned, it has not been linked in,
40049ec7b004SRick Macklem 	 * so it should be free'd.
40059ec7b004SRick Macklem 	 */
40069ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
40079ec7b004SRick Macklem 	if (nd->nd_repstat == NFSERR_CLIDINUSE) {
4008ed2f1001SRick Macklem 		/*
4009ed2f1001SRick Macklem 		 * 8 is the maximum length of the port# string.
4010ed2f1001SRick Macklem 		 */
4011ed2f1001SRick Macklem 		addrbuf = malloc(INET6_ADDRSTRLEN + 8, M_TEMP, M_WAITOK);
4012ed2f1001SRick Macklem 		switch (clp->lc_req.nr_nam->sa_family) {
4013ed2f1001SRick Macklem #ifdef INET
4014ed2f1001SRick Macklem 		case AF_INET:
40159ec7b004SRick Macklem 			if (clp->lc_flags & LCL_TCPCALLBACK)
40169ec7b004SRick Macklem 				(void) nfsm_strtom(nd, "tcp", 3);
40179ec7b004SRick Macklem 			else
40189ec7b004SRick Macklem 				(void) nfsm_strtom(nd, "udp", 3);
4019ed2f1001SRick Macklem 			rin = (struct sockaddr_in *)clp->lc_req.nr_nam;
4020ed2f1001SRick Macklem 			ucp = (u_char *)&rin->sin_addr.s_addr;
4021ed2f1001SRick Macklem 			ucp2 = (u_char *)&rin->sin_port;
40229ec7b004SRick Macklem 			sprintf(addrbuf, "%d.%d.%d.%d.%d.%d", ucp[0] & 0xff,
40239ec7b004SRick Macklem 			    ucp[1] & 0xff, ucp[2] & 0xff, ucp[3] & 0xff,
40249ec7b004SRick Macklem 			    ucp2[0] & 0xff, ucp2[1] & 0xff);
4025ed2f1001SRick Macklem 			break;
4026ed2f1001SRick Macklem #endif
4027ed2f1001SRick Macklem #ifdef INET6
4028ed2f1001SRick Macklem 		case AF_INET6:
4029ed2f1001SRick Macklem 			if (clp->lc_flags & LCL_TCPCALLBACK)
4030ed2f1001SRick Macklem 				(void) nfsm_strtom(nd, "tcp6", 4);
4031ed2f1001SRick Macklem 			else
4032ed2f1001SRick Macklem 				(void) nfsm_strtom(nd, "udp6", 4);
4033ed2f1001SRick Macklem 			rin6 = (struct sockaddr_in6 *)clp->lc_req.nr_nam;
4034ed2f1001SRick Macklem 			ucp = inet_ntop(AF_INET6, &rin6->sin6_addr, addrbuf,
4035ed2f1001SRick Macklem 			    INET6_ADDRSTRLEN);
4036ed2f1001SRick Macklem 			if (ucp != NULL)
4037ed2f1001SRick Macklem 				i = strlen(ucp);
4038ed2f1001SRick Macklem 			else
4039ed2f1001SRick Macklem 				i = 0;
4040ed2f1001SRick Macklem 			ucp2 = (u_char *)&rin6->sin6_port;
4041ed2f1001SRick Macklem 			sprintf(&addrbuf[i], ".%d.%d", ucp2[0] & 0xff,
4042ed2f1001SRick Macklem 			    ucp2[1] & 0xff);
4043ed2f1001SRick Macklem 			break;
4044ed2f1001SRick Macklem #endif
4045ed2f1001SRick Macklem 		}
40469ec7b004SRick Macklem 		(void) nfsm_strtom(nd, addrbuf, strlen(addrbuf));
4047ed2f1001SRick Macklem 		free(addrbuf, M_TEMP);
40489ec7b004SRick Macklem 	}
40499ec7b004SRick Macklem 	if (clp) {
4050b97b91b5SConrad Meyer 		free(clp->lc_req.nr_nam, M_SONAME);
40519ec7b004SRick Macklem 		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
40521f54e596SRick Macklem 		free(clp->lc_stateid, M_NFSDCLIENT);
40531f54e596SRick Macklem 		free(clp, M_NFSDCLIENT);
40549ec7b004SRick Macklem 	}
40559ec7b004SRick Macklem 	if (!nd->nd_repstat) {
40569ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_HYPER);
40579ec7b004SRick Macklem 		*tl++ = clientid.lval[0];
40589ec7b004SRick Macklem 		*tl++ = clientid.lval[1];
40599ec7b004SRick Macklem 		*tl++ = confirm.lval[0];
40609ec7b004SRick Macklem 		*tl = confirm.lval[1];
40619ec7b004SRick Macklem 	}
4062a9285ae5SZack Kirsch 
4063a9285ae5SZack Kirsch out:
4064a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
40659ec7b004SRick Macklem 	return (0);
40669ec7b004SRick Macklem nfsmout:
40679ec7b004SRick Macklem 	if (clp) {
4068b97b91b5SConrad Meyer 		free(clp->lc_req.nr_nam, M_SONAME);
40699ec7b004SRick Macklem 		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
40701f54e596SRick Macklem 		free(clp->lc_stateid, M_NFSDCLIENT);
40711f54e596SRick Macklem 		free(clp, M_NFSDCLIENT);
40729ec7b004SRick Macklem 	}
4073a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
40749ec7b004SRick Macklem 	return (error);
40759ec7b004SRick Macklem }
40769ec7b004SRick Macklem 
40779ec7b004SRick Macklem /*
40789ec7b004SRick Macklem  * nfsv4 set client id confirm service
40799ec7b004SRick Macklem  */
4080b9cc3262SRyan Moeller int
40819ec7b004SRick Macklem nfsrvd_setclientidcfrm(struct nfsrv_descript *nd,
4082af444b18SEdward Tomasz Napierala     __unused int isdgram, __unused vnode_t vp,
40839ec7b004SRick Macklem     __unused struct nfsexstuff *exp)
40849ec7b004SRick Macklem {
40859ec7b004SRick Macklem 	u_int32_t *tl;
40869ec7b004SRick Macklem 	int error = 0;
40879ec7b004SRick Macklem 	nfsquad_t clientid, confirm;
4088af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
40899ec7b004SRick Macklem 
4090c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0) {
4091c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
4092c59e4cc3SRick Macklem 		goto nfsmout;
4093c59e4cc3SRick Macklem 	}
4094984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
4095a9285ae5SZack Kirsch 		goto nfsmout;
40969ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER);
40979ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
40989ec7b004SRick Macklem 	clientid.lval[1] = *tl++;
40999ec7b004SRick Macklem 	confirm.lval[0] = *tl++;
41009ec7b004SRick Macklem 	confirm.lval[1] = *tl;
41019ec7b004SRick Macklem 
41029ec7b004SRick Macklem 	/*
41039ec7b004SRick Macklem 	 * nfsrv_getclient() searches the client list for a match and
41049ec7b004SRick Macklem 	 * returns the appropriate NFSERR status.
41059ec7b004SRick Macklem 	 */
41069ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_CONFIRM|CLOPS_RENEW),
4107c59e4cc3SRick Macklem 	    NULL, NULL, confirm, 0, nd, p);
41089ec7b004SRick Macklem nfsmout:
4109a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
41109ec7b004SRick Macklem 	return (error);
41119ec7b004SRick Macklem }
41129ec7b004SRick Macklem 
41139ec7b004SRick Macklem /*
41149ec7b004SRick Macklem  * nfsv4 verify service
41159ec7b004SRick Macklem  */
4116b9cc3262SRyan Moeller int
41179ec7b004SRick Macklem nfsrvd_verify(struct nfsrv_descript *nd, int isdgram,
4118af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
41199ec7b004SRick Macklem {
41209ec7b004SRick Macklem 	int error = 0, ret, fhsize = NFSX_MYFH;
41219ec7b004SRick Macklem 	struct nfsvattr nva;
41222f304845SKonstantin Belousov 	struct statfs *sf;
41239ec7b004SRick Macklem 	struct nfsfsinfo fs;
41249ec7b004SRick Macklem 	fhandle_t fh;
4125af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
41269ec7b004SRick Macklem 
41272f304845SKonstantin Belousov 	sf = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
412890d2dfabSRick Macklem 	nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
41299ec7b004SRick Macklem 	if (!nd->nd_repstat)
41302f304845SKonstantin Belousov 		nd->nd_repstat = nfsvno_statfs(vp, sf);
41319ec7b004SRick Macklem 	if (!nd->nd_repstat)
41329ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
41339ec7b004SRick Macklem 	if (!nd->nd_repstat) {
41349ec7b004SRick Macklem 		nfsvno_getfs(&fs, isdgram);
41359ec7b004SRick Macklem 		error = nfsv4_loadattr(nd, vp, &nva, NULL, &fh, fhsize, NULL,
41362f304845SKonstantin Belousov 		    sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, p, nd->nd_cred);
41379ec7b004SRick Macklem 		if (!error) {
41389ec7b004SRick Macklem 			if (nd->nd_procnum == NFSV4OP_NVERIFY) {
41399ec7b004SRick Macklem 				if (ret == 0)
41409ec7b004SRick Macklem 					nd->nd_repstat = NFSERR_SAME;
41419ec7b004SRick Macklem 				else if (ret != NFSERR_NOTSAME)
41429ec7b004SRick Macklem 					nd->nd_repstat = ret;
41439ec7b004SRick Macklem 			} else if (ret)
41449ec7b004SRick Macklem 				nd->nd_repstat = ret;
41459ec7b004SRick Macklem 		}
41469ec7b004SRick Macklem 	}
41479ec7b004SRick Macklem 	vput(vp);
41482f304845SKonstantin Belousov 	free(sf, M_STATFS);
4149a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
41509ec7b004SRick Macklem 	return (error);
41519ec7b004SRick Macklem }
41529ec7b004SRick Macklem 
41539ec7b004SRick Macklem /*
41549ec7b004SRick Macklem  * nfs openattr rpc
41559ec7b004SRick Macklem  */
4156b9cc3262SRyan Moeller int
41579ec7b004SRick Macklem nfsrvd_openattr(struct nfsrv_descript *nd, __unused int isdgram,
41589ec7b004SRick Macklem     vnode_t dp, __unused vnode_t *vpp, __unused fhandle_t *fhp,
4159af444b18SEdward Tomasz Napierala     __unused struct nfsexstuff *exp)
41609ec7b004SRick Macklem {
41619ec7b004SRick Macklem 	u_int32_t *tl;
41628014c971SRick Macklem 	int error = 0, createdir __unused;
41639ec7b004SRick Macklem 
41649ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
41659ec7b004SRick Macklem 	createdir = fxdr_unsigned(int, *tl);
41669ec7b004SRick Macklem 	nd->nd_repstat = NFSERR_NOTSUPP;
41679ec7b004SRick Macklem nfsmout:
41689ec7b004SRick Macklem 	vrele(dp);
4169a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
41709ec7b004SRick Macklem 	return (error);
41719ec7b004SRick Macklem }
41729ec7b004SRick Macklem 
41739ec7b004SRick Macklem /*
41749ec7b004SRick Macklem  * nfsv4 release lock owner service
41759ec7b004SRick Macklem  */
4176b9cc3262SRyan Moeller int
41779ec7b004SRick Macklem nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram,
4178af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
41799ec7b004SRick Macklem {
41809ec7b004SRick Macklem 	u_int32_t *tl;
41819ec7b004SRick Macklem 	struct nfsstate *stp = NULL;
41829ec7b004SRick Macklem 	int error = 0, len;
41839ec7b004SRick Macklem 	nfsquad_t clientid;
4184af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
41859ec7b004SRick Macklem 
4186c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0) {
4187c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
4188c59e4cc3SRick Macklem 		goto nfsmout;
4189c59e4cc3SRick Macklem 	}
4190984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
4191a9285ae5SZack Kirsch 		goto nfsmout;
41929ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
41939ec7b004SRick Macklem 	len = fxdr_unsigned(int, *(tl + 2));
41942a45247cSRick Macklem 	if (len <= 0 || len > NFSV4_OPAQUELIMIT) {
41952a45247cSRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
4196a9285ae5SZack Kirsch 		goto nfsmout;
41972a45247cSRick Macklem 	}
4198222daa42SConrad Meyer 	stp = malloc(sizeof (struct nfsstate) + len,
41999ec7b004SRick Macklem 	    M_NFSDSTATE, M_WAITOK);
42009ec7b004SRick Macklem 	stp->ls_ownerlen = len;
42019ec7b004SRick Macklem 	stp->ls_op = NULL;
42029ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_RELEASE;
42039ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
42049ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
42059ec7b004SRick Macklem 	clientid.lval[1] = *tl;
4206c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
4207c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
4208c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
4209c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
4210c59e4cc3SRick Macklem 			printf("EEK14 multiple clids\n");
42119ec7b004SRick Macklem 	} else {
4212c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
4213c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
42149ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
42159ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
42169ec7b004SRick Macklem 	}
42179ec7b004SRick Macklem 	error = nfsrv_mtostr(nd, stp->ls_owner, len);
42189ec7b004SRick Macklem 	if (error)
42199ec7b004SRick Macklem 		goto nfsmout;
42209ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_releaselckown(stp, clientid, p);
4221222daa42SConrad Meyer 	free(stp, M_NFSDSTATE);
4222a9285ae5SZack Kirsch 
4223a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
42249ec7b004SRick Macklem 	return (0);
42259ec7b004SRick Macklem nfsmout:
42269ec7b004SRick Macklem 	if (stp)
4227222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
4228a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
42299ec7b004SRick Macklem 	return (error);
42309ec7b004SRick Macklem }
4231c59e4cc3SRick Macklem 
4232c59e4cc3SRick Macklem /*
4233c59e4cc3SRick Macklem  * nfsv4 exchange_id service
4234c59e4cc3SRick Macklem  */
4235b9cc3262SRyan Moeller int
4236c59e4cc3SRick Macklem nfsrvd_exchangeid(struct nfsrv_descript *nd, __unused int isdgram,
4237af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4238c59e4cc3SRick Macklem {
4239c59e4cc3SRick Macklem 	uint32_t *tl;
4240c59e4cc3SRick Macklem 	int error = 0, i, idlen;
4241c59e4cc3SRick Macklem 	struct nfsclient *clp = NULL;
4242c59e4cc3SRick Macklem 	nfsquad_t clientid, confirm;
4243c59e4cc3SRick Macklem 	uint8_t *verf;
4244c59e4cc3SRick Macklem 	uint32_t sp4type, v41flags;
4245c59e4cc3SRick Macklem 	uint64_t owner_minor;
4246c59e4cc3SRick Macklem 	struct timespec verstime;
4247ed2f1001SRick Macklem #ifdef INET
4248ed2f1001SRick Macklem 	struct sockaddr_in *sin, *rin;
4249ed2f1001SRick Macklem #endif
4250ed2f1001SRick Macklem #ifdef INET6
4251ed2f1001SRick Macklem 	struct sockaddr_in6 *sin6, *rin6;
4252ed2f1001SRick Macklem #endif
4253af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
4254c59e4cc3SRick Macklem 
4255984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
4256c59e4cc3SRick Macklem 		goto nfsmout;
4257c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
4258c59e4cc3SRick Macklem 	verf = (uint8_t *)tl;
4259c59e4cc3SRick Macklem 	tl += (NFSX_VERF / NFSX_UNSIGNED);
4260c59e4cc3SRick Macklem 	i = fxdr_unsigned(int, *tl);
4261c59e4cc3SRick Macklem 	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
4262c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
4263c59e4cc3SRick Macklem 		goto nfsmout;
4264c59e4cc3SRick Macklem 	}
4265c59e4cc3SRick Macklem 	idlen = i;
4266c59e4cc3SRick Macklem 	if (nd->nd_flag & ND_GSS)
4267c59e4cc3SRick Macklem 		i += nd->nd_princlen;
42681f54e596SRick Macklem 	clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
42691f54e596SRick Macklem 	    M_ZERO);
42701f54e596SRick Macklem 	clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
42711f54e596SRick Macklem 	    nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
4272c59e4cc3SRick Macklem 	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
4273ed2f1001SRick Macklem 	/* Allocated large enough for an AF_INET or AF_INET6 socket. */
4274ed2f1001SRick Macklem 	clp->lc_req.nr_nam = malloc(sizeof(struct sockaddr_in6), M_SONAME,
4275b97b91b5SConrad Meyer 	    M_WAITOK | M_ZERO);
4276ed2f1001SRick Macklem 	switch (nd->nd_nam->sa_family) {
4277ed2f1001SRick Macklem #ifdef INET
4278ed2f1001SRick Macklem 	case AF_INET:
4279ed2f1001SRick Macklem 		rin = (struct sockaddr_in *)clp->lc_req.nr_nam;
4280ed2f1001SRick Macklem 		sin = (struct sockaddr_in *)nd->nd_nam;
4281ed2f1001SRick Macklem 		rin->sin_family = AF_INET;
4282ed2f1001SRick Macklem 		rin->sin_len = sizeof(struct sockaddr_in);
4283ed2f1001SRick Macklem 		rin->sin_port = 0;
4284ed2f1001SRick Macklem 		rin->sin_addr.s_addr = sin->sin_addr.s_addr;
4285ed2f1001SRick Macklem 		break;
4286ed2f1001SRick Macklem #endif
4287ed2f1001SRick Macklem #ifdef INET6
4288ed2f1001SRick Macklem 	case AF_INET6:
4289ed2f1001SRick Macklem 		rin6 = (struct sockaddr_in6 *)clp->lc_req.nr_nam;
4290ed2f1001SRick Macklem 		sin6 = (struct sockaddr_in6 *)nd->nd_nam;
4291ed2f1001SRick Macklem 		rin6->sin6_family = AF_INET6;
4292ed2f1001SRick Macklem 		rin6->sin6_len = sizeof(struct sockaddr_in6);
4293ed2f1001SRick Macklem 		rin6->sin6_port = 0;
4294ed2f1001SRick Macklem 		rin6->sin6_addr = sin6->sin6_addr;
4295ed2f1001SRick Macklem 		break;
4296ed2f1001SRick Macklem #endif
4297ed2f1001SRick Macklem 	}
4298c59e4cc3SRick Macklem 	clp->lc_req.nr_cred = NULL;
4299c59e4cc3SRick Macklem 	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
4300c59e4cc3SRick Macklem 	clp->lc_idlen = idlen;
4301c59e4cc3SRick Macklem 	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
4302c59e4cc3SRick Macklem 	if (error != 0)
4303c59e4cc3SRick Macklem 		goto nfsmout;
4304c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_GSS) != 0) {
4305c59e4cc3SRick Macklem 		clp->lc_flags = LCL_GSS | LCL_NFSV41;
4306c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_GSSINTEGRITY) != 0)
4307c59e4cc3SRick Macklem 			clp->lc_flags |= LCL_GSSINTEGRITY;
4308c59e4cc3SRick Macklem 		else if ((nd->nd_flag & ND_GSSPRIVACY) != 0)
4309c59e4cc3SRick Macklem 			clp->lc_flags |= LCL_GSSPRIVACY;
4310c59e4cc3SRick Macklem 	} else
4311c59e4cc3SRick Macklem 		clp->lc_flags = LCL_NFSV41;
4312c057a378SRick Macklem 	if ((nd->nd_flag & ND_NFSV42) != 0)
4313c057a378SRick Macklem 		clp->lc_flags |= LCL_NFSV42;
4314c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_GSS) != 0 && nd->nd_princlen > 0) {
4315c59e4cc3SRick Macklem 		clp->lc_flags |= LCL_NAME;
4316c59e4cc3SRick Macklem 		clp->lc_namelen = nd->nd_princlen;
4317c59e4cc3SRick Macklem 		clp->lc_name = &clp->lc_id[idlen];
4318c59e4cc3SRick Macklem 		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
4319c59e4cc3SRick Macklem 	} else {
4320c59e4cc3SRick Macklem 		clp->lc_uid = nd->nd_cred->cr_uid;
4321c59e4cc3SRick Macklem 		clp->lc_gid = nd->nd_cred->cr_gid;
4322c59e4cc3SRick Macklem 	}
4323c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
4324c59e4cc3SRick Macklem 	v41flags = fxdr_unsigned(uint32_t, *tl++);
4325c59e4cc3SRick Macklem 	if ((v41flags & ~(NFSV4EXCH_SUPPMOVEDREFER | NFSV4EXCH_SUPPMOVEDMIGR |
4326c59e4cc3SRick Macklem 	    NFSV4EXCH_BINDPRINCSTATEID | NFSV4EXCH_MASKPNFS |
4327c59e4cc3SRick Macklem 	    NFSV4EXCH_UPDCONFIRMEDRECA)) != 0) {
4328c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
4329c59e4cc3SRick Macklem 		goto nfsmout;
4330c59e4cc3SRick Macklem 	}
4331c59e4cc3SRick Macklem 	if ((v41flags & NFSV4EXCH_UPDCONFIRMEDRECA) != 0)
4332c59e4cc3SRick Macklem 		confirm.lval[1] = 1;
4333c59e4cc3SRick Macklem 	else
4334c59e4cc3SRick Macklem 		confirm.lval[1] = 0;
433590d2dfabSRick Macklem 	if (nfsrv_devidcnt == 0)
433690d2dfabSRick Macklem 		v41flags = NFSV4EXCH_USENONPNFS | NFSV4EXCH_USEPNFSDS;
433790d2dfabSRick Macklem  	else
433890d2dfabSRick Macklem  		v41flags = NFSV4EXCH_USEPNFSMDS;
4339c59e4cc3SRick Macklem 	sp4type = fxdr_unsigned(uint32_t, *tl);
4340c59e4cc3SRick Macklem 	if (sp4type != NFSV4EXCH_SP4NONE) {
4341c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
4342c59e4cc3SRick Macklem 		goto nfsmout;
4343c59e4cc3SRick Macklem 	}
4344c59e4cc3SRick Macklem 
4345c59e4cc3SRick Macklem 	/*
4346c59e4cc3SRick Macklem 	 * nfsrv_setclient() does the actual work of adding it to the
4347c59e4cc3SRick Macklem 	 * client list. If there is no error, the structure has been
4348c59e4cc3SRick Macklem 	 * linked into the client list and clp should no longer be used
4349c59e4cc3SRick Macklem 	 * here. When an error is returned, it has not been linked in,
4350c59e4cc3SRick Macklem 	 * so it should be free'd.
4351c59e4cc3SRick Macklem 	 */
4352c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
4353c59e4cc3SRick Macklem 	if (clp != NULL) {
4354b97b91b5SConrad Meyer 		free(clp->lc_req.nr_nam, M_SONAME);
4355c59e4cc3SRick Macklem 		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
43561f54e596SRick Macklem 		free(clp->lc_stateid, M_NFSDCLIENT);
4357c59e4cc3SRick Macklem 		free(clp, M_NFSDCLIENT);
4358c59e4cc3SRick Macklem 	}
4359c59e4cc3SRick Macklem 	if (nd->nd_repstat == 0) {
4360c59e4cc3SRick Macklem 		if (confirm.lval[1] != 0)
4361c59e4cc3SRick Macklem 			v41flags |= NFSV4EXCH_CONFIRMEDR;
4362c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + 3 * NFSX_UNSIGNED);
4363c59e4cc3SRick Macklem 		*tl++ = clientid.lval[0];			/* ClientID */
4364c59e4cc3SRick Macklem 		*tl++ = clientid.lval[1];
4365c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(confirm.lval[0]);		/* SequenceID */
4366c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(v41flags);		/* Exch flags */
4367c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(NFSV4EXCH_SP4NONE);	/* No SSV */
4368c59e4cc3SRick Macklem 		owner_minor = 0;				/* Owner */
4369c59e4cc3SRick Macklem 		txdr_hyper(owner_minor, tl);			/* Minor */
4370c59e4cc3SRick Macklem 		(void)nfsm_strtom(nd, nd->nd_cred->cr_prison->pr_hostuuid,
4371c59e4cc3SRick Macklem 		    strlen(nd->nd_cred->cr_prison->pr_hostuuid)); /* Major */
43728932a483SRick Macklem 		(void)nfsm_strtom(nd, nd->nd_cred->cr_prison->pr_hostuuid,
43738932a483SRick Macklem 		    strlen(nd->nd_cred->cr_prison->pr_hostuuid)); /* Scope */
43748932a483SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
4375c59e4cc3SRick Macklem 		*tl = txdr_unsigned(1);
4376c59e4cc3SRick Macklem 		(void)nfsm_strtom(nd, "freebsd.org", strlen("freebsd.org"));
4377c59e4cc3SRick Macklem 		(void)nfsm_strtom(nd, version, strlen(version));
4378c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_V4TIME);
4379c59e4cc3SRick Macklem 		verstime.tv_sec = 1293840000;		/* Jan 1, 2011 */
4380c59e4cc3SRick Macklem 		verstime.tv_nsec = 0;
4381c59e4cc3SRick Macklem 		txdr_nfsv4time(&verstime, tl);
4382c59e4cc3SRick Macklem 	}
4383c59e4cc3SRick Macklem 	NFSEXITCODE2(0, nd);
4384c59e4cc3SRick Macklem 	return (0);
4385c59e4cc3SRick Macklem nfsmout:
4386c59e4cc3SRick Macklem 	if (clp != NULL) {
4387b97b91b5SConrad Meyer 		free(clp->lc_req.nr_nam, M_SONAME);
4388c59e4cc3SRick Macklem 		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
43891f54e596SRick Macklem 		free(clp->lc_stateid, M_NFSDCLIENT);
4390c59e4cc3SRick Macklem 		free(clp, M_NFSDCLIENT);
4391c59e4cc3SRick Macklem 	}
4392c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4393c59e4cc3SRick Macklem 	return (error);
4394c59e4cc3SRick Macklem }
4395c59e4cc3SRick Macklem 
4396c59e4cc3SRick Macklem /*
4397c59e4cc3SRick Macklem  * nfsv4 create session service
4398c59e4cc3SRick Macklem  */
4399b9cc3262SRyan Moeller int
4400c59e4cc3SRick Macklem nfsrvd_createsession(struct nfsrv_descript *nd, __unused int isdgram,
4401af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4402c59e4cc3SRick Macklem {
4403c59e4cc3SRick Macklem 	uint32_t *tl;
4404c59e4cc3SRick Macklem 	int error = 0;
4405c59e4cc3SRick Macklem 	nfsquad_t clientid, confirm;
4406c59e4cc3SRick Macklem 	struct nfsdsession *sep = NULL;
4407c59e4cc3SRick Macklem 	uint32_t rdmacnt;
4408af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
4409c59e4cc3SRick Macklem 
4410984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
4411c59e4cc3SRick Macklem 		goto nfsmout;
4412c59e4cc3SRick Macklem 	sep = (struct nfsdsession *)malloc(sizeof(struct nfsdsession),
4413c59e4cc3SRick Macklem 	    M_NFSDSESSION, M_WAITOK | M_ZERO);
4414c59e4cc3SRick Macklem 	sep->sess_refcnt = 1;
4415c59e4cc3SRick Macklem 	mtx_init(&sep->sess_cbsess.nfsess_mtx, "nfscbsession", NULL, MTX_DEF);
4416c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + 2 * NFSX_UNSIGNED);
4417c59e4cc3SRick Macklem 	clientid.lval[0] = *tl++;
4418c59e4cc3SRick Macklem 	clientid.lval[1] = *tl++;
4419c59e4cc3SRick Macklem 	confirm.lval[0] = fxdr_unsigned(uint32_t, *tl++);
4420c59e4cc3SRick Macklem 	sep->sess_crflags = fxdr_unsigned(uint32_t, *tl);
4421c59e4cc3SRick Macklem 	/* Persistent sessions and RDMA are not supported. */
4422c59e4cc3SRick Macklem 	sep->sess_crflags &= NFSV4CRSESS_CONNBACKCHAN;
4423c59e4cc3SRick Macklem 
4424c59e4cc3SRick Macklem 	/* Fore channel attributes. */
4425c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
4426c59e4cc3SRick Macklem 	tl++;					/* Header pad always 0. */
4427c59e4cc3SRick Macklem 	sep->sess_maxreq = fxdr_unsigned(uint32_t, *tl++);
442890d2dfabSRick Macklem 	if (sep->sess_maxreq > sb_max_adj - NFS_MAXXDR) {
442990d2dfabSRick Macklem 		sep->sess_maxreq = sb_max_adj - NFS_MAXXDR;
443090d2dfabSRick Macklem 		printf("Consider increasing kern.ipc.maxsockbuf\n");
443190d2dfabSRick Macklem 	}
4432c59e4cc3SRick Macklem 	sep->sess_maxresp = fxdr_unsigned(uint32_t, *tl++);
443390d2dfabSRick Macklem 	if (sep->sess_maxresp > sb_max_adj - NFS_MAXXDR) {
443490d2dfabSRick Macklem 		sep->sess_maxresp = sb_max_adj - NFS_MAXXDR;
443590d2dfabSRick Macklem 		printf("Consider increasing kern.ipc.maxsockbuf\n");
443690d2dfabSRick Macklem 	}
4437c59e4cc3SRick Macklem 	sep->sess_maxrespcached = fxdr_unsigned(uint32_t, *tl++);
4438c59e4cc3SRick Macklem 	sep->sess_maxops = fxdr_unsigned(uint32_t, *tl++);
4439c59e4cc3SRick Macklem 	sep->sess_maxslots = fxdr_unsigned(uint32_t, *tl++);
4440c59e4cc3SRick Macklem 	if (sep->sess_maxslots > NFSV4_SLOTS)
4441c59e4cc3SRick Macklem 		sep->sess_maxslots = NFSV4_SLOTS;
4442c59e4cc3SRick Macklem 	rdmacnt = fxdr_unsigned(uint32_t, *tl);
4443c59e4cc3SRick Macklem 	if (rdmacnt > 1) {
4444c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
4445c59e4cc3SRick Macklem 		goto nfsmout;
4446c59e4cc3SRick Macklem 	} else if (rdmacnt == 1)
4447c59e4cc3SRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4448c59e4cc3SRick Macklem 
4449c59e4cc3SRick Macklem 	/* Back channel attributes. */
4450c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
4451c59e4cc3SRick Macklem 	tl++;					/* Header pad always 0. */
4452c59e4cc3SRick Macklem 	sep->sess_cbmaxreq = fxdr_unsigned(uint32_t, *tl++);
4453c59e4cc3SRick Macklem 	sep->sess_cbmaxresp = fxdr_unsigned(uint32_t, *tl++);
4454c59e4cc3SRick Macklem 	sep->sess_cbmaxrespcached = fxdr_unsigned(uint32_t, *tl++);
4455c59e4cc3SRick Macklem 	sep->sess_cbmaxops = fxdr_unsigned(uint32_t, *tl++);
4456c59e4cc3SRick Macklem 	sep->sess_cbsess.nfsess_foreslots = fxdr_unsigned(uint32_t, *tl++);
4457c59e4cc3SRick Macklem 	rdmacnt = fxdr_unsigned(uint32_t, *tl);
4458c59e4cc3SRick Macklem 	if (rdmacnt > 1) {
4459c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
4460c59e4cc3SRick Macklem 		goto nfsmout;
4461c59e4cc3SRick Macklem 	} else if (rdmacnt == 1)
4462c59e4cc3SRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4463c59e4cc3SRick Macklem 
4464c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4465c59e4cc3SRick Macklem 	sep->sess_cbprogram = fxdr_unsigned(uint32_t, *tl);
4466c59e4cc3SRick Macklem 
4467c59e4cc3SRick Macklem 	/*
4468c59e4cc3SRick Macklem 	 * nfsrv_getclient() searches the client list for a match and
4469c59e4cc3SRick Macklem 	 * returns the appropriate NFSERR status.
4470c59e4cc3SRick Macklem 	 */
4471c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_getclient(clientid, CLOPS_CONFIRM | CLOPS_RENEW,
4472c59e4cc3SRick Macklem 	    NULL, sep, confirm, sep->sess_cbprogram, nd, p);
4473c59e4cc3SRick Macklem 	if (nd->nd_repstat == 0) {
4474c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
4475c59e4cc3SRick Macklem 		NFSBCOPY(sep->sess_sessionid, tl, NFSX_V4SESSIONID);
4476c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 18 * NFSX_UNSIGNED);
4477c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(confirm.lval[0]);	/* sequenceid */
4478c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_crflags);
4479c59e4cc3SRick Macklem 
4480c59e4cc3SRick Macklem 		/* Fore channel attributes. */
4481c59e4cc3SRick Macklem 		*tl++ = 0;
4482c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_maxreq);
4483c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_maxresp);
4484c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_maxrespcached);
4485c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_maxops);
4486c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_maxslots);
4487c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(1);
4488c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(0);			/* No RDMA. */
4489c59e4cc3SRick Macklem 
4490c59e4cc3SRick Macklem 		/* Back channel attributes. */
4491c59e4cc3SRick Macklem 		*tl++ = 0;
4492c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_cbmaxreq);
4493c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_cbmaxresp);
4494c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_cbmaxrespcached);
4495c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_cbmaxops);
4496c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_cbsess.nfsess_foreslots);
4497c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(1);
4498c59e4cc3SRick Macklem 		*tl = txdr_unsigned(0);			/* No RDMA. */
4499c59e4cc3SRick Macklem 	}
4500c59e4cc3SRick Macklem nfsmout:
4501c59e4cc3SRick Macklem 	if (nd->nd_repstat != 0 && sep != NULL)
4502c59e4cc3SRick Macklem 		free(sep, M_NFSDSESSION);
4503c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4504c59e4cc3SRick Macklem 	return (error);
4505c59e4cc3SRick Macklem }
4506c59e4cc3SRick Macklem 
4507c59e4cc3SRick Macklem /*
4508c59e4cc3SRick Macklem  * nfsv4 sequence service
4509c59e4cc3SRick Macklem  */
4510b9cc3262SRyan Moeller int
4511c59e4cc3SRick Macklem nfsrvd_sequence(struct nfsrv_descript *nd, __unused int isdgram,
4512af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4513c59e4cc3SRick Macklem {
4514c59e4cc3SRick Macklem 	uint32_t *tl;
4515c59e4cc3SRick Macklem 	uint32_t highest_slotid, sequenceid, sflags, target_highest_slotid;
4516c59e4cc3SRick Macklem 	int cache_this, error = 0;
4517af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
4518c59e4cc3SRick Macklem 
4519984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
4520c59e4cc3SRick Macklem 		goto nfsmout;
4521c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID);
4522c59e4cc3SRick Macklem 	NFSBCOPY(tl, nd->nd_sessionid, NFSX_V4SESSIONID);
4523c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
4524c59e4cc3SRick Macklem 	sequenceid = fxdr_unsigned(uint32_t, *tl++);
4525c59e4cc3SRick Macklem 	nd->nd_slotid = fxdr_unsigned(uint32_t, *tl++);
4526c59e4cc3SRick Macklem 	highest_slotid = fxdr_unsigned(uint32_t, *tl++);
4527c59e4cc3SRick Macklem 	if (*tl == newnfs_true)
4528c59e4cc3SRick Macklem 		cache_this = 1;
4529c59e4cc3SRick Macklem 	else
4530c59e4cc3SRick Macklem 		cache_this = 0;
4531c59e4cc3SRick Macklem 	nd->nd_flag |= ND_HASSEQUENCE;
4532c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_checksequence(nd, sequenceid, &highest_slotid,
4533c59e4cc3SRick Macklem 	    &target_highest_slotid, cache_this, &sflags, p);
4534c59e4cc3SRick Macklem 	if (nd->nd_repstat == 0) {
4535c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
4536c59e4cc3SRick Macklem 		NFSBCOPY(nd->nd_sessionid, tl, NFSX_V4SESSIONID);
4537c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 5 * NFSX_UNSIGNED);
4538c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sequenceid);
4539c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(nd->nd_slotid);
4540c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(highest_slotid);
4541c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(target_highest_slotid);
4542c59e4cc3SRick Macklem 		*tl = txdr_unsigned(sflags);
4543c59e4cc3SRick Macklem 	}
4544c59e4cc3SRick Macklem nfsmout:
4545c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4546c59e4cc3SRick Macklem 	return (error);
4547c59e4cc3SRick Macklem }
4548c59e4cc3SRick Macklem 
4549c59e4cc3SRick Macklem /*
4550c59e4cc3SRick Macklem  * nfsv4 reclaim complete service
4551c59e4cc3SRick Macklem  */
4552b9cc3262SRyan Moeller int
4553c59e4cc3SRick Macklem nfsrvd_reclaimcomplete(struct nfsrv_descript *nd, __unused int isdgram,
4554af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4555c59e4cc3SRick Macklem {
4556c59e4cc3SRick Macklem 	uint32_t *tl;
4557a3e709cdSRick Macklem 	int error = 0, onefs;
4558c59e4cc3SRick Macklem 
4559c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4560a3e709cdSRick Macklem 	/*
4561a3e709cdSRick Macklem 	 * I believe that a ReclaimComplete with rca_one_fs == TRUE is only
4562a3e709cdSRick Macklem 	 * to be used after a file system has been transferred to a different
4563a3e709cdSRick Macklem 	 * file server.  However, RFC5661 is somewhat vague w.r.t. this and
4564a3e709cdSRick Macklem 	 * the ESXi 6.7 client does both a ReclaimComplete with rca_one_fs
4565a3e709cdSRick Macklem 	 * == TRUE and one with ReclaimComplete with rca_one_fs == FALSE.
4566a3e709cdSRick Macklem 	 * Therefore, just ignore the rca_one_fs == TRUE operation and return
4567a3e709cdSRick Macklem 	 * NFS_OK without doing anything.
4568a3e709cdSRick Macklem 	 */
4569a3e709cdSRick Macklem 	onefs = 0;
4570c59e4cc3SRick Macklem 	if (*tl == newnfs_true)
4571a3e709cdSRick Macklem 		onefs = 1;
4572a3e709cdSRick Macklem 	nd->nd_repstat = nfsrv_checkreclaimcomplete(nd, onefs);
4573c59e4cc3SRick Macklem nfsmout:
4574c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4575c59e4cc3SRick Macklem 	return (error);
4576c59e4cc3SRick Macklem }
4577c59e4cc3SRick Macklem 
4578c59e4cc3SRick Macklem /*
4579c59e4cc3SRick Macklem  * nfsv4 destroy clientid service
4580c59e4cc3SRick Macklem  */
4581b9cc3262SRyan Moeller int
4582c59e4cc3SRick Macklem nfsrvd_destroyclientid(struct nfsrv_descript *nd, __unused int isdgram,
4583af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4584c59e4cc3SRick Macklem {
4585c59e4cc3SRick Macklem 	uint32_t *tl;
4586c59e4cc3SRick Macklem 	nfsquad_t clientid;
4587c59e4cc3SRick Macklem 	int error = 0;
4588af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
4589c59e4cc3SRick Macklem 
4590984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
4591c59e4cc3SRick Macklem 		goto nfsmout;
4592c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
4593c59e4cc3SRick Macklem 	clientid.lval[0] = *tl++;
4594c59e4cc3SRick Macklem 	clientid.lval[1] = *tl;
4595c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_destroyclient(clientid, p);
4596c59e4cc3SRick Macklem nfsmout:
4597c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4598c59e4cc3SRick Macklem 	return (error);
4599c59e4cc3SRick Macklem }
4600c59e4cc3SRick Macklem 
4601c59e4cc3SRick Macklem /*
46029442a64eSRick Macklem  * nfsv4 bind connection to session service
46039442a64eSRick Macklem  */
4604b9cc3262SRyan Moeller int
46059442a64eSRick Macklem nfsrvd_bindconnsess(struct nfsrv_descript *nd, __unused int isdgram,
4606af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
46079442a64eSRick Macklem {
46089442a64eSRick Macklem 	uint32_t *tl;
46099442a64eSRick Macklem 	uint8_t sessid[NFSX_V4SESSIONID];
46109442a64eSRick Macklem 	int error = 0, foreaft;
46119442a64eSRick Macklem 
4612984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
46139442a64eSRick Macklem 		goto nfsmout;
46149442a64eSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID + 2 * NFSX_UNSIGNED);
46159442a64eSRick Macklem 	NFSBCOPY(tl, sessid, NFSX_V4SESSIONID);
46169442a64eSRick Macklem 	tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
46179442a64eSRick Macklem 	foreaft = fxdr_unsigned(int, *tl++);
46189442a64eSRick Macklem 	if (*tl == newnfs_true) {
46199442a64eSRick Macklem 		/* RDMA is not supported. */
46209442a64eSRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
46219442a64eSRick Macklem 		goto nfsmout;
46229442a64eSRick Macklem 	}
46239442a64eSRick Macklem 
46249442a64eSRick Macklem 	nd->nd_repstat = nfsrv_bindconnsess(nd, sessid, &foreaft);
46259442a64eSRick Macklem 	if (nd->nd_repstat == 0) {
46269442a64eSRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 2 *
46279442a64eSRick Macklem 		    NFSX_UNSIGNED);
46289442a64eSRick Macklem 		NFSBCOPY(sessid, tl, NFSX_V4SESSIONID);
46299442a64eSRick Macklem 		tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
46309442a64eSRick Macklem 		*tl++ = txdr_unsigned(foreaft);
46319442a64eSRick Macklem 		*tl = newnfs_false;
46329442a64eSRick Macklem 	}
46339442a64eSRick Macklem nfsmout:
46349442a64eSRick Macklem 	NFSEXITCODE2(error, nd);
46359442a64eSRick Macklem 	return (error);
46369442a64eSRick Macklem }
46379442a64eSRick Macklem 
46389442a64eSRick Macklem /*
4639c59e4cc3SRick Macklem  * nfsv4 destroy session service
4640c59e4cc3SRick Macklem  */
4641b9cc3262SRyan Moeller int
4642c59e4cc3SRick Macklem nfsrvd_destroysession(struct nfsrv_descript *nd, __unused int isdgram,
4643af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4644c59e4cc3SRick Macklem {
4645c59e4cc3SRick Macklem 	uint8_t *cp, sessid[NFSX_V4SESSIONID];
4646c59e4cc3SRick Macklem 	int error = 0;
4647c59e4cc3SRick Macklem 
4648984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
4649c59e4cc3SRick Macklem 		goto nfsmout;
4650c59e4cc3SRick Macklem 	NFSM_DISSECT(cp, uint8_t *, NFSX_V4SESSIONID);
4651c59e4cc3SRick Macklem 	NFSBCOPY(cp, sessid, NFSX_V4SESSIONID);
4652c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_destroysession(nd, sessid);
4653c59e4cc3SRick Macklem nfsmout:
4654c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4655c59e4cc3SRick Macklem 	return (error);
4656c59e4cc3SRick Macklem }
4657c59e4cc3SRick Macklem 
4658c59e4cc3SRick Macklem /*
4659c59e4cc3SRick Macklem  * nfsv4 free stateid service
4660c59e4cc3SRick Macklem  */
4661b9cc3262SRyan Moeller int
4662c59e4cc3SRick Macklem nfsrvd_freestateid(struct nfsrv_descript *nd, __unused int isdgram,
4663af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4664c59e4cc3SRick Macklem {
4665c59e4cc3SRick Macklem 	uint32_t *tl;
4666c59e4cc3SRick Macklem 	nfsv4stateid_t stateid;
4667c59e4cc3SRick Macklem 	int error = 0;
4668af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
4669c59e4cc3SRick Macklem 
4670c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
4671c59e4cc3SRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
4672c59e4cc3SRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
467390d2dfabSRick Macklem 
467490d2dfabSRick Macklem 	/*
467590d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
467690d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
467790d2dfabSRick Macklem 	 */
467890d2dfabSRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
467990d2dfabSRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
468090d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
468190d2dfabSRick Macklem 			stateid = nd->nd_curstateid;
468290d2dfabSRick Macklem 			stateid.seqid = 0;
468390d2dfabSRick Macklem 		} else {
468490d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
468590d2dfabSRick Macklem 			goto nfsmout;
468690d2dfabSRick Macklem 		}
468790d2dfabSRick Macklem 	}
468890d2dfabSRick Macklem 
4689c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_freestateid(nd, &stateid, p);
469090d2dfabSRick Macklem 
469190d2dfabSRick Macklem 	/* If the current stateid has been free'd, unset it. */
469290d2dfabSRick Macklem 	if (nd->nd_repstat == 0 && (nd->nd_flag & ND_CURSTATEID) != 0 &&
469390d2dfabSRick Macklem 	    stateid.other[0] == nd->nd_curstateid.other[0] &&
469490d2dfabSRick Macklem 	    stateid.other[1] == nd->nd_curstateid.other[1] &&
469590d2dfabSRick Macklem 	    stateid.other[2] == nd->nd_curstateid.other[2])
469690d2dfabSRick Macklem 		nd->nd_flag &= ~ND_CURSTATEID;
469790d2dfabSRick Macklem nfsmout:
469890d2dfabSRick Macklem 	NFSEXITCODE2(error, nd);
469990d2dfabSRick Macklem 	return (error);
470090d2dfabSRick Macklem }
470190d2dfabSRick Macklem 
470290d2dfabSRick Macklem /*
470390d2dfabSRick Macklem  * nfsv4 layoutget service
470490d2dfabSRick Macklem  */
4705b9cc3262SRyan Moeller int
470690d2dfabSRick Macklem nfsrvd_layoutget(struct nfsrv_descript *nd, __unused int isdgram,
4707af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
470890d2dfabSRick Macklem {
470990d2dfabSRick Macklem 	uint32_t *tl;
471090d2dfabSRick Macklem 	nfsv4stateid_t stateid;
471190d2dfabSRick Macklem 	int error = 0, layoutlen, layouttype, iomode, maxcnt, retonclose;
471290d2dfabSRick Macklem 	uint64_t offset, len, minlen;
471390d2dfabSRick Macklem 	char *layp;
4714af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
471590d2dfabSRick Macklem 
471690d2dfabSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED + 3 * NFSX_HYPER +
471790d2dfabSRick Macklem 	    NFSX_STATEID);
471890d2dfabSRick Macklem 	tl++;		/* Signal layout available. Ignore for now. */
471990d2dfabSRick Macklem 	layouttype = fxdr_unsigned(int, *tl++);
472090d2dfabSRick Macklem 	iomode = fxdr_unsigned(int, *tl++);
472190d2dfabSRick Macklem 	offset = fxdr_hyper(tl); tl += 2;
472290d2dfabSRick Macklem 	len = fxdr_hyper(tl); tl += 2;
472390d2dfabSRick Macklem 	minlen = fxdr_hyper(tl); tl += 2;
472490d2dfabSRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
472590d2dfabSRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
472690d2dfabSRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
472790d2dfabSRick Macklem 	maxcnt = fxdr_unsigned(int, *tl);
472890d2dfabSRick Macklem 	NFSD_DEBUG(4, "layoutget ltyp=%d iom=%d off=%ju len=%ju mlen=%ju\n",
472990d2dfabSRick Macklem 	    layouttype, iomode, (uintmax_t)offset, (uintmax_t)len,
473090d2dfabSRick Macklem 	    (uintmax_t)minlen);
473190d2dfabSRick Macklem 	if (len < minlen ||
473290d2dfabSRick Macklem 	    (minlen != UINT64_MAX && offset + minlen < offset) ||
473390d2dfabSRick Macklem 	    (len != UINT64_MAX && offset + len < offset)) {
473490d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
473590d2dfabSRick Macklem 		goto nfsmout;
473690d2dfabSRick Macklem 	}
473790d2dfabSRick Macklem 
473890d2dfabSRick Macklem 	/*
473990d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
474090d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
474190d2dfabSRick Macklem 	 */
474290d2dfabSRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
474390d2dfabSRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
474490d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
474590d2dfabSRick Macklem 			stateid = nd->nd_curstateid;
474690d2dfabSRick Macklem 			stateid.seqid = 0;
474790d2dfabSRick Macklem 		} else {
474890d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
474990d2dfabSRick Macklem 			goto nfsmout;
475090d2dfabSRick Macklem 		}
475190d2dfabSRick Macklem 	}
475290d2dfabSRick Macklem 
475390d2dfabSRick Macklem 	layp = NULL;
475490d2dfabSRick Macklem 	if (layouttype == NFSLAYOUT_NFSV4_1_FILES && nfsrv_maxpnfsmirror == 1)
475590d2dfabSRick Macklem 		layp = malloc(NFSX_V4FILELAYOUT, M_TEMP, M_WAITOK);
475690d2dfabSRick Macklem 	else if (layouttype == NFSLAYOUT_FLEXFILE)
475790d2dfabSRick Macklem 		layp = malloc(NFSX_V4FLEXLAYOUT(nfsrv_maxpnfsmirror), M_TEMP,
475890d2dfabSRick Macklem 		    M_WAITOK);
475990d2dfabSRick Macklem 	else
476090d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_UNKNLAYOUTTYPE;
476190d2dfabSRick Macklem 	if (layp != NULL)
476290d2dfabSRick Macklem 		nd->nd_repstat = nfsrv_layoutget(nd, vp, exp, layouttype,
476390d2dfabSRick Macklem 		    &iomode, &offset, &len, minlen, &stateid, maxcnt,
476490d2dfabSRick Macklem 		    &retonclose, &layoutlen, layp, nd->nd_cred, p);
476590d2dfabSRick Macklem 	NFSD_DEBUG(4, "nfsrv_layoutget stat=%u layoutlen=%d\n", nd->nd_repstat,
476690d2dfabSRick Macklem 	    layoutlen);
476790d2dfabSRick Macklem 	if (nd->nd_repstat == 0) {
476890d2dfabSRick Macklem 		/* For NFSv4.1, set the Current StateID. */
476990d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0) {
477090d2dfabSRick Macklem 			nd->nd_curstateid = stateid;
477190d2dfabSRick Macklem 			nd->nd_flag |= ND_CURSTATEID;
477290d2dfabSRick Macklem 		}
477390d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED + NFSX_STATEID +
477490d2dfabSRick Macklem 		    2 * NFSX_HYPER);
477590d2dfabSRick Macklem 		*tl++ = txdr_unsigned(retonclose);
477690d2dfabSRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
477790d2dfabSRick Macklem 		NFSBCOPY(stateid.other, tl, NFSX_STATEIDOTHER);
477890d2dfabSRick Macklem 		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
477990d2dfabSRick Macklem 		*tl++ = txdr_unsigned(1);	/* Only returns one layout. */
478090d2dfabSRick Macklem 		txdr_hyper(offset, tl); tl += 2;
478190d2dfabSRick Macklem 		txdr_hyper(len, tl); tl += 2;
478290d2dfabSRick Macklem 		*tl++ = txdr_unsigned(iomode);
478390d2dfabSRick Macklem 		*tl = txdr_unsigned(layouttype);
478490d2dfabSRick Macklem 		nfsm_strtom(nd, layp, layoutlen);
478590d2dfabSRick Macklem 	} else if (nd->nd_repstat == NFSERR_LAYOUTTRYLATER) {
478690d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
478790d2dfabSRick Macklem 		*tl = newnfs_false;
478890d2dfabSRick Macklem 	}
478990d2dfabSRick Macklem 	free(layp, M_TEMP);
479090d2dfabSRick Macklem nfsmout:
479190d2dfabSRick Macklem 	vput(vp);
479290d2dfabSRick Macklem 	NFSEXITCODE2(error, nd);
479390d2dfabSRick Macklem 	return (error);
479490d2dfabSRick Macklem }
479590d2dfabSRick Macklem 
479690d2dfabSRick Macklem /*
479790d2dfabSRick Macklem  * nfsv4 layoutcommit service
479890d2dfabSRick Macklem  */
4799b9cc3262SRyan Moeller int
480090d2dfabSRick Macklem nfsrvd_layoutcommit(struct nfsrv_descript *nd, __unused int isdgram,
4801af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
480290d2dfabSRick Macklem {
480390d2dfabSRick Macklem 	uint32_t *tl;
480490d2dfabSRick Macklem 	nfsv4stateid_t stateid;
480590d2dfabSRick Macklem 	int error = 0, hasnewoff, hasnewmtime, layouttype, maxcnt, reclaim;
480690d2dfabSRick Macklem 	int hasnewsize;
4807f808cf72SRick Macklem 	uint64_t offset, len, newoff = 0, newsize;
480890d2dfabSRick Macklem 	struct timespec newmtime;
480990d2dfabSRick Macklem 	char *layp;
4810af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
481190d2dfabSRick Macklem 
481290d2dfabSRick Macklem 	layp = NULL;
481390d2dfabSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED + 2 * NFSX_HYPER +
481490d2dfabSRick Macklem 	    NFSX_STATEID);
481590d2dfabSRick Macklem 	offset = fxdr_hyper(tl); tl += 2;
481690d2dfabSRick Macklem 	len = fxdr_hyper(tl); tl += 2;
481790d2dfabSRick Macklem 	reclaim = fxdr_unsigned(int, *tl++);
481890d2dfabSRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
481990d2dfabSRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
482090d2dfabSRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
482190d2dfabSRick Macklem 	/*
482290d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
482390d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
482490d2dfabSRick Macklem 	 */
482590d2dfabSRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
482690d2dfabSRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
482790d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
482890d2dfabSRick Macklem 			stateid = nd->nd_curstateid;
482990d2dfabSRick Macklem 			stateid.seqid = 0;
483090d2dfabSRick Macklem 		} else {
483190d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
483290d2dfabSRick Macklem 			goto nfsmout;
483390d2dfabSRick Macklem 		}
483490d2dfabSRick Macklem 	}
483590d2dfabSRick Macklem 
483690d2dfabSRick Macklem 	hasnewoff = fxdr_unsigned(int, *tl);
483790d2dfabSRick Macklem 	if (hasnewoff != 0) {
483890d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + NFSX_UNSIGNED);
483990d2dfabSRick Macklem 		newoff = fxdr_hyper(tl); tl += 2;
484090d2dfabSRick Macklem 	} else
484190d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
484290d2dfabSRick Macklem 	hasnewmtime = fxdr_unsigned(int, *tl);
484390d2dfabSRick Macklem 	if (hasnewmtime != 0) {
484490d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_V4TIME + 2 * NFSX_UNSIGNED);
484590d2dfabSRick Macklem 		fxdr_nfsv4time(tl, &newmtime);
484690d2dfabSRick Macklem 		tl += (NFSX_V4TIME / NFSX_UNSIGNED);
484790d2dfabSRick Macklem 	} else
484890d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
484990d2dfabSRick Macklem 	layouttype = fxdr_unsigned(int, *tl++);
485090d2dfabSRick Macklem 	maxcnt = fxdr_unsigned(int, *tl);
485190d2dfabSRick Macklem 	if (maxcnt > 0) {
485290d2dfabSRick Macklem 		layp = malloc(maxcnt + 1, M_TEMP, M_WAITOK);
485390d2dfabSRick Macklem 		error = nfsrv_mtostr(nd, layp, maxcnt);
485490d2dfabSRick Macklem 		if (error != 0)
485590d2dfabSRick Macklem 			goto nfsmout;
485690d2dfabSRick Macklem 	}
485790d2dfabSRick Macklem 	nd->nd_repstat = nfsrv_layoutcommit(nd, vp, layouttype, hasnewoff,
485890d2dfabSRick Macklem 	    newoff, offset, len, hasnewmtime, &newmtime, reclaim, &stateid,
485990d2dfabSRick Macklem 	    maxcnt, layp, &hasnewsize, &newsize, nd->nd_cred, p);
486090d2dfabSRick Macklem 	NFSD_DEBUG(4, "nfsrv_layoutcommit stat=%u\n", nd->nd_repstat);
486190d2dfabSRick Macklem 	if (nd->nd_repstat == 0) {
486290d2dfabSRick Macklem 		if (hasnewsize != 0) {
486390d2dfabSRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED + NFSX_HYPER);
486490d2dfabSRick Macklem 			*tl++ = newnfs_true;
486590d2dfabSRick Macklem 			txdr_hyper(newsize, tl);
486690d2dfabSRick Macklem 		} else {
486790d2dfabSRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
486890d2dfabSRick Macklem 			*tl = newnfs_false;
486990d2dfabSRick Macklem 		}
487090d2dfabSRick Macklem 	}
487190d2dfabSRick Macklem nfsmout:
487290d2dfabSRick Macklem 	free(layp, M_TEMP);
487390d2dfabSRick Macklem 	vput(vp);
487490d2dfabSRick Macklem 	NFSEXITCODE2(error, nd);
487590d2dfabSRick Macklem 	return (error);
487690d2dfabSRick Macklem }
487790d2dfabSRick Macklem 
487890d2dfabSRick Macklem /*
487990d2dfabSRick Macklem  * nfsv4 layoutreturn service
488090d2dfabSRick Macklem  */
4881b9cc3262SRyan Moeller int
488290d2dfabSRick Macklem nfsrvd_layoutreturn(struct nfsrv_descript *nd, __unused int isdgram,
4883af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
488490d2dfabSRick Macklem {
488590d2dfabSRick Macklem 	uint32_t *tl, *layp;
488690d2dfabSRick Macklem 	nfsv4stateid_t stateid;
488790d2dfabSRick Macklem 	int error = 0, fnd, kind, layouttype, iomode, maxcnt, reclaim;
488890d2dfabSRick Macklem 	uint64_t offset, len;
4889af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
489090d2dfabSRick Macklem 
489190d2dfabSRick Macklem 	layp = NULL;
489290d2dfabSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
489390d2dfabSRick Macklem 	reclaim = *tl++;
489490d2dfabSRick Macklem 	layouttype = fxdr_unsigned(int, *tl++);
489590d2dfabSRick Macklem 	iomode = fxdr_unsigned(int, *tl++);
489690d2dfabSRick Macklem 	kind = fxdr_unsigned(int, *tl);
489790d2dfabSRick Macklem 	NFSD_DEBUG(4, "layoutreturn recl=%d ltyp=%d iom=%d kind=%d\n", reclaim,
489890d2dfabSRick Macklem 	    layouttype, iomode, kind);
489990d2dfabSRick Macklem 	if (kind == NFSV4LAYOUTRET_FILE) {
490090d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_STATEID +
490190d2dfabSRick Macklem 		    NFSX_UNSIGNED);
490290d2dfabSRick Macklem 		offset = fxdr_hyper(tl); tl += 2;
490390d2dfabSRick Macklem 		len = fxdr_hyper(tl); tl += 2;
490490d2dfabSRick Macklem 		stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
490590d2dfabSRick Macklem 		NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
490690d2dfabSRick Macklem 		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
490790d2dfabSRick Macklem 
490890d2dfabSRick Macklem 		/*
490990d2dfabSRick Macklem 		 * For the special stateid of other all 0s and seqid == 1, set
491090d2dfabSRick Macklem 		 * the stateid to the current stateid, if it is set.
491190d2dfabSRick Macklem 		 */
491290d2dfabSRick Macklem 		if (stateid.seqid == 1 && stateid.other[0] == 0 &&
491390d2dfabSRick Macklem 		    stateid.other[1] == 0 && stateid.other[2] == 0) {
491490d2dfabSRick Macklem 			if ((nd->nd_flag & ND_CURSTATEID) != 0) {
491590d2dfabSRick Macklem 				stateid = nd->nd_curstateid;
491690d2dfabSRick Macklem 				stateid.seqid = 0;
491790d2dfabSRick Macklem 			} else {
491890d2dfabSRick Macklem 				nd->nd_repstat = NFSERR_BADSTATEID;
491990d2dfabSRick Macklem 				goto nfsmout;
492090d2dfabSRick Macklem 			}
492190d2dfabSRick Macklem 		}
492290d2dfabSRick Macklem 
492390d2dfabSRick Macklem 		maxcnt = fxdr_unsigned(int, *tl);
492490d2dfabSRick Macklem 		if (maxcnt > 0) {
492590d2dfabSRick Macklem 			layp = malloc(maxcnt + 1, M_TEMP, M_WAITOK);
492690d2dfabSRick Macklem 			error = nfsrv_mtostr(nd, (char *)layp, maxcnt);
492790d2dfabSRick Macklem 			if (error != 0)
492890d2dfabSRick Macklem 				goto nfsmout;
492990d2dfabSRick Macklem 		}
493090d2dfabSRick Macklem 	} else {
493190d2dfabSRick Macklem 		if (reclaim == newnfs_true) {
493290d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
493390d2dfabSRick Macklem 			goto nfsmout;
493490d2dfabSRick Macklem 		}
493590d2dfabSRick Macklem 		offset = len = 0;
493690d2dfabSRick Macklem 		maxcnt = 0;
493790d2dfabSRick Macklem 	}
493890d2dfabSRick Macklem 	nd->nd_repstat = nfsrv_layoutreturn(nd, vp, layouttype, iomode,
493990d2dfabSRick Macklem 	    offset, len, reclaim, kind, &stateid, maxcnt, layp, &fnd,
494090d2dfabSRick Macklem 	    nd->nd_cred, p);
494190d2dfabSRick Macklem 	NFSD_DEBUG(4, "nfsrv_layoutreturn stat=%u fnd=%d\n", nd->nd_repstat,
494290d2dfabSRick Macklem 	    fnd);
494390d2dfabSRick Macklem 	if (nd->nd_repstat == 0) {
494490d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
494590d2dfabSRick Macklem 		if (fnd != 0) {
494690d2dfabSRick Macklem 			*tl = newnfs_true;
494790d2dfabSRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_STATEID);
494890d2dfabSRick Macklem 			*tl++ = txdr_unsigned(stateid.seqid);
494990d2dfabSRick Macklem 			NFSBCOPY(stateid.other, tl, NFSX_STATEIDOTHER);
495090d2dfabSRick Macklem 		} else
495190d2dfabSRick Macklem 			*tl = newnfs_false;
495290d2dfabSRick Macklem 	}
495390d2dfabSRick Macklem nfsmout:
495490d2dfabSRick Macklem 	free(layp, M_TEMP);
495590d2dfabSRick Macklem 	vput(vp);
495690d2dfabSRick Macklem 	NFSEXITCODE2(error, nd);
495790d2dfabSRick Macklem 	return (error);
495890d2dfabSRick Macklem }
495990d2dfabSRick Macklem 
496090d2dfabSRick Macklem /*
4961c057a378SRick Macklem  * nfsv4 layout error service
4962c057a378SRick Macklem  */
4963b9cc3262SRyan Moeller int
4964c057a378SRick Macklem nfsrvd_layouterror(struct nfsrv_descript *nd, __unused int isdgram,
4965c057a378SRick Macklem     vnode_t vp, struct nfsexstuff *exp)
4966c057a378SRick Macklem {
4967c057a378SRick Macklem 	uint32_t *tl;
4968c057a378SRick Macklem 	nfsv4stateid_t stateid;
4969c057a378SRick Macklem 	int cnt, error = 0, i, stat;
4970c057a378SRick Macklem 	int opnum __unused;
4971c057a378SRick Macklem 	char devid[NFSX_V4DEVICEID];
4972c057a378SRick Macklem 	uint64_t offset, len;
4973c057a378SRick Macklem 
4974c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_STATEID +
4975c057a378SRick Macklem 	    NFSX_UNSIGNED);
4976c057a378SRick Macklem 	offset = fxdr_hyper(tl); tl += 2;
4977c057a378SRick Macklem 	len = fxdr_hyper(tl); tl += 2;
4978c057a378SRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
4979c057a378SRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
4980c057a378SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
4981c057a378SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
4982c057a378SRick Macklem 	NFSD_DEBUG(4, "layouterror off=%ju len=%ju cnt=%d\n", (uintmax_t)offset,
4983c057a378SRick Macklem 	    (uintmax_t)len, cnt);
4984c057a378SRick Macklem 	/*
4985c057a378SRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set
4986c057a378SRick Macklem 	 * the stateid to the current stateid, if it is set.
4987c057a378SRick Macklem 	 */
4988c057a378SRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
4989c057a378SRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
4990c057a378SRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
4991c057a378SRick Macklem 			stateid = nd->nd_curstateid;
4992c057a378SRick Macklem 			stateid.seqid = 0;
4993c057a378SRick Macklem 		} else {
4994c057a378SRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
4995c057a378SRick Macklem 			goto nfsmout;
4996c057a378SRick Macklem 		}
4997c057a378SRick Macklem 	}
4998c057a378SRick Macklem 
4999c057a378SRick Macklem 	/*
5000c057a378SRick Macklem 	 * Ignore offset, len and stateid for now.
5001c057a378SRick Macklem 	 */
5002c057a378SRick Macklem 	for (i = 0; i < cnt; i++) {
5003c057a378SRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_V4DEVICEID + 2 *
5004c057a378SRick Macklem 		    NFSX_UNSIGNED);
5005c057a378SRick Macklem 		NFSBCOPY(tl, devid, NFSX_V4DEVICEID);
5006c057a378SRick Macklem 		tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
5007c057a378SRick Macklem 		stat = fxdr_unsigned(int, *tl++);
5008c057a378SRick Macklem 		opnum = fxdr_unsigned(int, *tl);
5009c057a378SRick Macklem 		NFSD_DEBUG(4, "nfsrvd_layouterr op=%d stat=%d\n", opnum, stat);
5010c057a378SRick Macklem 		/*
5011c057a378SRick Macklem 		 * Except for NFSERR_ACCES and NFSERR_STALE errors,
5012c057a378SRick Macklem 		 * disable the mirror.
5013c057a378SRick Macklem 		 */
5014c057a378SRick Macklem 		if (stat != NFSERR_ACCES && stat != NFSERR_STALE)
5015c057a378SRick Macklem 			nfsrv_delds(devid, curthread);
5016c057a378SRick Macklem 	}
5017c057a378SRick Macklem nfsmout:
5018c057a378SRick Macklem 	vput(vp);
5019c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5020c057a378SRick Macklem 	return (error);
5021c057a378SRick Macklem }
5022c057a378SRick Macklem 
5023c057a378SRick Macklem /*
5024c057a378SRick Macklem  * nfsv4 layout stats service
5025c057a378SRick Macklem  */
5026b9cc3262SRyan Moeller int
5027c057a378SRick Macklem nfsrvd_layoutstats(struct nfsrv_descript *nd, __unused int isdgram,
5028c057a378SRick Macklem     vnode_t vp, struct nfsexstuff *exp)
5029c057a378SRick Macklem {
5030c057a378SRick Macklem 	uint32_t *tl;
5031c057a378SRick Macklem 	nfsv4stateid_t stateid;
5032c057a378SRick Macklem 	int cnt, error = 0;
5033c057a378SRick Macklem 	int layouttype __unused;
5034c057a378SRick Macklem 	char devid[NFSX_V4DEVICEID] __unused;
5035c057a378SRick Macklem 	uint64_t offset, len, readcount, readbytes, writecount, writebytes
5036c057a378SRick Macklem 	    __unused;
5037c057a378SRick Macklem 
5038c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 6 * NFSX_HYPER + NFSX_STATEID +
5039c057a378SRick Macklem 	    NFSX_V4DEVICEID + 2 * NFSX_UNSIGNED);
5040c057a378SRick Macklem 	offset = fxdr_hyper(tl); tl += 2;
5041c057a378SRick Macklem 	len = fxdr_hyper(tl); tl += 2;
5042c057a378SRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
5043c057a378SRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
5044c057a378SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
5045c057a378SRick Macklem 	readcount = fxdr_hyper(tl); tl += 2;
5046c057a378SRick Macklem 	readbytes = fxdr_hyper(tl); tl += 2;
5047c057a378SRick Macklem 	writecount = fxdr_hyper(tl); tl += 2;
5048c057a378SRick Macklem 	writebytes = fxdr_hyper(tl); tl += 2;
5049c057a378SRick Macklem 	NFSBCOPY(tl, devid, NFSX_V4DEVICEID);
5050c057a378SRick Macklem 	tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
5051c057a378SRick Macklem 	layouttype = fxdr_unsigned(int, *tl++);
5052c057a378SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
5053c057a378SRick Macklem 	error = nfsm_advance(nd, NFSM_RNDUP(cnt), -1);
5054c057a378SRick Macklem 	if (error != 0)
5055c057a378SRick Macklem 		goto nfsmout;
5056c057a378SRick Macklem 	NFSD_DEBUG(4, "layoutstats cnt=%d\n", cnt);
5057c057a378SRick Macklem 	/*
5058c057a378SRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set
5059c057a378SRick Macklem 	 * the stateid to the current stateid, if it is set.
5060c057a378SRick Macklem 	 */
5061c057a378SRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
5062c057a378SRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
5063c057a378SRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
5064c057a378SRick Macklem 			stateid = nd->nd_curstateid;
5065c057a378SRick Macklem 			stateid.seqid = 0;
5066c057a378SRick Macklem 		} else {
5067c057a378SRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
5068c057a378SRick Macklem 			goto nfsmout;
5069c057a378SRick Macklem 		}
5070c057a378SRick Macklem 	}
5071c057a378SRick Macklem 
5072c057a378SRick Macklem 	/*
5073c057a378SRick Macklem 	 * No use for the stats for now.
5074c057a378SRick Macklem 	 */
5075c057a378SRick Macklem nfsmout:
5076c057a378SRick Macklem 	vput(vp);
5077c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5078c057a378SRick Macklem 	return (error);
5079c057a378SRick Macklem }
5080c057a378SRick Macklem 
5081c057a378SRick Macklem /*
5082c057a378SRick Macklem  * nfsv4 io_advise service
5083c057a378SRick Macklem  */
5084b9cc3262SRyan Moeller int
5085c057a378SRick Macklem nfsrvd_ioadvise(struct nfsrv_descript *nd, __unused int isdgram,
5086c057a378SRick Macklem     vnode_t vp, struct nfsexstuff *exp)
5087c057a378SRick Macklem {
5088c057a378SRick Macklem 	uint32_t *tl;
5089c057a378SRick Macklem 	nfsv4stateid_t stateid;
5090c057a378SRick Macklem 	nfsattrbit_t hints;
5091c057a378SRick Macklem 	int error = 0, ret;
5092c057a378SRick Macklem 	off_t offset, len;
5093c057a378SRick Macklem 
5094c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + 2 * NFSX_HYPER);
5095c057a378SRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
5096c057a378SRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
5097c057a378SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
5098c057a378SRick Macklem 	offset = fxdr_hyper(tl); tl += 2;
5099c057a378SRick Macklem 	len = fxdr_hyper(tl);
5100c057a378SRick Macklem 	error = nfsrv_getattrbits(nd, &hints, NULL, NULL);
5101c057a378SRick Macklem 	if (error != 0)
5102c057a378SRick Macklem 		goto nfsmout;
5103c057a378SRick Macklem 	/*
5104c057a378SRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set
5105c057a378SRick Macklem 	 * the stateid to the current stateid, if it is set.
5106c057a378SRick Macklem 	 */
5107c057a378SRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
5108c057a378SRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
5109c057a378SRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
5110c057a378SRick Macklem 			stateid = nd->nd_curstateid;
5111c057a378SRick Macklem 			stateid.seqid = 0;
5112c057a378SRick Macklem 		} else {
5113c057a378SRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
5114c057a378SRick Macklem 			goto nfsmout;
5115c057a378SRick Macklem 		}
5116c057a378SRick Macklem 	}
5117c057a378SRick Macklem 
5118c057a378SRick Macklem 	if (offset < 0) {
5119c057a378SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
5120c057a378SRick Macklem 		goto nfsmout;
5121c057a378SRick Macklem 	}
5122c057a378SRick Macklem 	if (len < 0)
5123c057a378SRick Macklem 		len = 0;
5124c057a378SRick Macklem 	if (vp->v_type != VREG) {
5125c057a378SRick Macklem 		if (vp->v_type == VDIR)
5126c057a378SRick Macklem 			nd->nd_repstat = NFSERR_ISDIR;
5127c057a378SRick Macklem 		else
5128c057a378SRick Macklem 			nd->nd_repstat = NFSERR_WRONGTYPE;
5129c057a378SRick Macklem 		goto nfsmout;
5130c057a378SRick Macklem 	}
5131c057a378SRick Macklem 
5132c057a378SRick Macklem 	/*
5133c057a378SRick Macklem 	 * For now, we can only handle WILLNEED and DONTNEED and don't use
5134c057a378SRick Macklem 	 * the stateid.
5135c057a378SRick Macklem 	 */
5136c057a378SRick Macklem 	if ((NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED) &&
5137c057a378SRick Macklem 	    !NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_DONTNEED)) ||
5138c057a378SRick Macklem 	    (NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_DONTNEED) &&
5139c057a378SRick Macklem 	    !NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED))) {
5140b249ce48SMateusz Guzik 		NFSVOPUNLOCK(vp);
5141c057a378SRick Macklem 		if (NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED)) {
5142c057a378SRick Macklem 			ret = VOP_ADVISE(vp, offset, len, POSIX_FADV_WILLNEED);
5143c057a378SRick Macklem 			NFSZERO_ATTRBIT(&hints);
5144c057a378SRick Macklem 			if (ret == 0)
5145c057a378SRick Macklem 				NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED);
5146c057a378SRick Macklem 			else
5147c057a378SRick Macklem 				NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_NORMAL);
5148c057a378SRick Macklem 		} else {
5149c057a378SRick Macklem 			ret = VOP_ADVISE(vp, offset, len, POSIX_FADV_DONTNEED);
5150c057a378SRick Macklem 			NFSZERO_ATTRBIT(&hints);
5151c057a378SRick Macklem 			if (ret == 0)
5152c057a378SRick Macklem 				NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_DONTNEED);
5153c057a378SRick Macklem 			else
5154c057a378SRick Macklem 				NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_NORMAL);
5155c057a378SRick Macklem 		}
5156c057a378SRick Macklem 		vrele(vp);
5157c057a378SRick Macklem 	} else {
5158c057a378SRick Macklem 		NFSZERO_ATTRBIT(&hints);
5159c057a378SRick Macklem 		NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_NORMAL);
5160c057a378SRick Macklem 		vput(vp);
5161c057a378SRick Macklem 	}
5162c057a378SRick Macklem 	nfsrv_putattrbit(nd, &hints);
5163c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5164c057a378SRick Macklem 	return (error);
5165c057a378SRick Macklem nfsmout:
5166c057a378SRick Macklem 	vput(vp);
5167c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5168c057a378SRick Macklem 	return (error);
5169c057a378SRick Macklem }
5170c057a378SRick Macklem 
5171c057a378SRick Macklem /*
517290d2dfabSRick Macklem  * nfsv4 getdeviceinfo service
517390d2dfabSRick Macklem  */
5174b9cc3262SRyan Moeller int
517590d2dfabSRick Macklem nfsrvd_getdevinfo(struct nfsrv_descript *nd, __unused int isdgram,
5176af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
517790d2dfabSRick Macklem {
517890d2dfabSRick Macklem 	uint32_t *tl, maxcnt, notify[NFSV4_NOTIFYBITMAP];
517990d2dfabSRick Macklem 	int cnt, devaddrlen, error = 0, i, layouttype;
518090d2dfabSRick Macklem 	char devid[NFSX_V4DEVICEID], *devaddr;
518190d2dfabSRick Macklem 	time_t dev_time;
518290d2dfabSRick Macklem 
518390d2dfabSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 3 * NFSX_UNSIGNED + NFSX_V4DEVICEID);
518490d2dfabSRick Macklem 	NFSBCOPY(tl, devid, NFSX_V4DEVICEID);
518590d2dfabSRick Macklem 	tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
518690d2dfabSRick Macklem 	layouttype = fxdr_unsigned(int, *tl++);
518790d2dfabSRick Macklem 	maxcnt = fxdr_unsigned(uint32_t, *tl++);
518890d2dfabSRick Macklem 	cnt = fxdr_unsigned(int, *tl);
518990d2dfabSRick Macklem 	NFSD_DEBUG(4, "getdevinfo ltyp=%d maxcnt=%u bitcnt=%d\n", layouttype,
519090d2dfabSRick Macklem 	    maxcnt, cnt);
519190d2dfabSRick Macklem 	if (cnt > NFSV4_NOTIFYBITMAP || cnt < 0) {
519290d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
519390d2dfabSRick Macklem 		goto nfsmout;
519490d2dfabSRick Macklem 	}
519590d2dfabSRick Macklem 	if (cnt > 0) {
519690d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, cnt * NFSX_UNSIGNED);
519790d2dfabSRick Macklem 		for (i = 0; i < cnt; i++)
519890d2dfabSRick Macklem 			notify[i] = fxdr_unsigned(uint32_t, *tl++);
519990d2dfabSRick Macklem 	}
520090d2dfabSRick Macklem 	for (i = cnt; i < NFSV4_NOTIFYBITMAP; i++)
520190d2dfabSRick Macklem 		notify[i] = 0;
520290d2dfabSRick Macklem 
520390d2dfabSRick Macklem 	/*
520490d2dfabSRick Macklem 	 * Check that the device id is not stale.  Device ids are recreated
520590d2dfabSRick Macklem 	 * each time the nfsd threads are restarted.
520690d2dfabSRick Macklem 	 */
520790d2dfabSRick Macklem 	NFSBCOPY(devid, &dev_time, sizeof(dev_time));
520890d2dfabSRick Macklem 	if (dev_time != nfsdev_time) {
520990d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_NOENT;
521090d2dfabSRick Macklem 		goto nfsmout;
521190d2dfabSRick Macklem 	}
521290d2dfabSRick Macklem 
521390d2dfabSRick Macklem 	/* Look for the device id. */
521490d2dfabSRick Macklem 	nd->nd_repstat = nfsrv_getdevinfo(devid, layouttype, &maxcnt,
521590d2dfabSRick Macklem 	    notify, &devaddrlen, &devaddr);
521690d2dfabSRick Macklem 	NFSD_DEBUG(4, "nfsrv_getdevinfo stat=%u\n", nd->nd_repstat);
521790d2dfabSRick Macklem 	if (nd->nd_repstat == 0) {
521890d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
521990d2dfabSRick Macklem 		*tl = txdr_unsigned(layouttype);
522090d2dfabSRick Macklem 		nfsm_strtom(nd, devaddr, devaddrlen);
522190d2dfabSRick Macklem 		cnt = 0;
522290d2dfabSRick Macklem 		for (i = 0; i < NFSV4_NOTIFYBITMAP; i++) {
522390d2dfabSRick Macklem 			if (notify[i] != 0)
522490d2dfabSRick Macklem 				cnt = i + 1;
522590d2dfabSRick Macklem 		}
522690d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, (cnt + 1) * NFSX_UNSIGNED);
522790d2dfabSRick Macklem 		*tl++ = txdr_unsigned(cnt);
522890d2dfabSRick Macklem 		for (i = 0; i < cnt; i++)
522990d2dfabSRick Macklem 			*tl++ = txdr_unsigned(notify[i]);
523090d2dfabSRick Macklem 	} else if (nd->nd_repstat == NFSERR_TOOSMALL) {
523190d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
523290d2dfabSRick Macklem 		*tl = txdr_unsigned(maxcnt);
523390d2dfabSRick Macklem 	}
5234c59e4cc3SRick Macklem nfsmout:
5235c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
5236c59e4cc3SRick Macklem 	return (error);
5237c59e4cc3SRick Macklem }
5238c59e4cc3SRick Macklem 
5239c59e4cc3SRick Macklem /*
52405d4835e4SRick Macklem  * nfsv4 test stateid service
52415d4835e4SRick Macklem  */
5242b9cc3262SRyan Moeller int
52435d4835e4SRick Macklem nfsrvd_teststateid(struct nfsrv_descript *nd, __unused int isdgram,
5244af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
52455d4835e4SRick Macklem {
52465d4835e4SRick Macklem 	uint32_t *tl;
52475d4835e4SRick Macklem 	nfsv4stateid_t *stateidp = NULL, *tstateidp;
52485d4835e4SRick Macklem 	int cnt, error = 0, i, ret;
5249af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
52505d4835e4SRick Macklem 
52515d4835e4SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
52525d4835e4SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
52535d4835e4SRick Macklem 	if (cnt <= 0 || cnt > 1024) {
52545d4835e4SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
52555d4835e4SRick Macklem 		goto nfsmout;
52565d4835e4SRick Macklem 	}
52575d4835e4SRick Macklem 	stateidp = mallocarray(cnt, sizeof(nfsv4stateid_t), M_TEMP, M_WAITOK);
52585d4835e4SRick Macklem 	tstateidp = stateidp;
52595d4835e4SRick Macklem 	for (i = 0; i < cnt; i++) {
52605d4835e4SRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
52615d4835e4SRick Macklem 		tstateidp->seqid = fxdr_unsigned(uint32_t, *tl++);
52625d4835e4SRick Macklem 		NFSBCOPY(tl, tstateidp->other, NFSX_STATEIDOTHER);
52635d4835e4SRick Macklem 		tstateidp++;
52645d4835e4SRick Macklem 	}
52655d4835e4SRick Macklem 	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
52665d4835e4SRick Macklem 	*tl = txdr_unsigned(cnt);
52675d4835e4SRick Macklem 	tstateidp = stateidp;
52685d4835e4SRick Macklem 	for (i = 0; i < cnt; i++) {
52695d4835e4SRick Macklem 		ret = nfsrv_teststateid(nd, tstateidp, p);
52705d4835e4SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
52715d4835e4SRick Macklem 		*tl = txdr_unsigned(ret);
52725d4835e4SRick Macklem 		tstateidp++;
52735d4835e4SRick Macklem 	}
52745d4835e4SRick Macklem nfsmout:
52755d4835e4SRick Macklem 	free(stateidp, M_TEMP);
52765d4835e4SRick Macklem 	NFSEXITCODE2(error, nd);
52775d4835e4SRick Macklem 	return (error);
52785d4835e4SRick Macklem }
52795d4835e4SRick Macklem 
52805d4835e4SRick Macklem /*
5281c057a378SRick Macklem  * nfs allocate service
5282c057a378SRick Macklem  */
5283b9cc3262SRyan Moeller int
5284c057a378SRick Macklem nfsrvd_allocate(struct nfsrv_descript *nd, __unused int isdgram,
5285c057a378SRick Macklem     vnode_t vp, struct nfsexstuff *exp)
5286c057a378SRick Macklem {
5287c057a378SRick Macklem 	uint32_t *tl;
5288c057a378SRick Macklem 	struct nfsvattr forat;
5289c057a378SRick Macklem 	int error = 0, forat_ret = 1, gotproxystateid;
5290c057a378SRick Macklem 	off_t off, len;
5291c057a378SRick Macklem 	struct nfsstate st, *stp = &st;
5292c057a378SRick Macklem 	struct nfslock lo, *lop = &lo;
5293c057a378SRick Macklem 	nfsv4stateid_t stateid;
5294c057a378SRick Macklem 	nfsquad_t clientid;
5295c057a378SRick Macklem 	nfsattrbit_t attrbits;
5296c057a378SRick Macklem 
5297c057a378SRick Macklem 	gotproxystateid = 0;
5298c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + 2 * NFSX_HYPER);
5299c057a378SRick Macklem 	stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
5300c057a378SRick Macklem 	lop->lo_flags = NFSLCK_WRITE;
5301c057a378SRick Macklem 	stp->ls_ownerlen = 0;
5302c057a378SRick Macklem 	stp->ls_op = NULL;
5303c057a378SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
5304c057a378SRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
5305c057a378SRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
5306c057a378SRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
5307c057a378SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
5308c057a378SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
5309c057a378SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
5310c057a378SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
5311c057a378SRick Macklem 			printf("EEK2 multiple clids\n");
5312c057a378SRick Macklem 	} else {
5313c057a378SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
5314c057a378SRick Macklem 			printf("EEK! no clientid from session\n");
5315c057a378SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
5316c057a378SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
5317c057a378SRick Macklem 	}
5318c057a378SRick Macklem 	stp->ls_stateid.other[2] = *tl++;
5319c057a378SRick Macklem 	/*
5320c057a378SRick Macklem 	 * Don't allow this to be done for a DS.
5321c057a378SRick Macklem 	 */
5322c057a378SRick Macklem 	if ((nd->nd_flag & ND_DSSERVER) != 0)
5323c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5324c057a378SRick Macklem 	/* However, allow the proxy stateid. */
5325c057a378SRick Macklem 	if (stp->ls_stateid.seqid == 0xffffffff &&
5326c057a378SRick Macklem 	    stp->ls_stateid.other[0] == 0x55555555 &&
5327c057a378SRick Macklem 	    stp->ls_stateid.other[1] == 0x55555555 &&
5328c057a378SRick Macklem 	    stp->ls_stateid.other[2] == 0x55555555)
5329c057a378SRick Macklem 		gotproxystateid = 1;
5330c057a378SRick Macklem 	off = fxdr_hyper(tl); tl += 2;
5331c057a378SRick Macklem 	lop->lo_first = off;
5332c057a378SRick Macklem 	len = fxdr_hyper(tl);
5333c057a378SRick Macklem 	lop->lo_end = off + len;
5334c057a378SRick Macklem 	/*
5335c057a378SRick Macklem 	 * Paranoia, just in case it wraps around, which shouldn't
5336c057a378SRick Macklem 	 * ever happen anyhow.
5337c057a378SRick Macklem 	 */
5338c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (lop->lo_end < lop->lo_first || len <= 0))
5339c057a378SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
5340c057a378SRick Macklem 
5341c057a378SRick Macklem 	if (nd->nd_repstat == 0 && vnode_vtype(vp) != VREG)
5342c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGTYPE;
5343c057a378SRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
5344c057a378SRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
5345c057a378SRick Macklem 	forat_ret = nfsvno_getattr(vp, &forat, nd, curthread, 1, &attrbits);
5346c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5347c057a378SRick Macklem 		nd->nd_repstat = forat_ret;
5348c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (forat.na_uid != nd->nd_cred->cr_uid ||
5349c057a378SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
5350c057a378SRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp,
5351c057a378SRick Macklem 		    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
5352c057a378SRick Macklem 		    NULL);
5353c057a378SRick Macklem 	if (nd->nd_repstat == 0 && gotproxystateid == 0)
5354c057a378SRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
5355c057a378SRick Macklem 		    &stateid, exp, nd, curthread);
5356c057a378SRick Macklem 
5357c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5358c057a378SRick Macklem 		nd->nd_repstat = nfsvno_allocate(vp, off, len, nd->nd_cred,
5359c057a378SRick Macklem 		    curthread);
5360c057a378SRick Macklem 	vput(vp);
5361c057a378SRick Macklem 	NFSEXITCODE2(0, nd);
5362c057a378SRick Macklem 	return (0);
5363c057a378SRick Macklem nfsmout:
5364c057a378SRick Macklem 	vput(vp);
5365c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5366c057a378SRick Macklem 	return (error);
5367c057a378SRick Macklem }
5368c057a378SRick Macklem 
5369c057a378SRick Macklem /*
5370c057a378SRick Macklem  * nfs copy service
5371c057a378SRick Macklem  */
5372b9cc3262SRyan Moeller int
5373c057a378SRick Macklem nfsrvd_copy_file_range(struct nfsrv_descript *nd, __unused int isdgram,
5374c057a378SRick Macklem     vnode_t vp, vnode_t tovp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
5375c057a378SRick Macklem {
5376c057a378SRick Macklem 	uint32_t *tl;
5377c057a378SRick Macklem 	struct nfsvattr at;
5378c057a378SRick Macklem 	int cnt, error = 0, ret;
5379c057a378SRick Macklem 	off_t inoff, outoff;
5380c057a378SRick Macklem 	uint64_t len;
5381c057a378SRick Macklem 	size_t xfer;
5382c057a378SRick Macklem 	struct nfsstate inst, outst, *instp = &inst, *outstp = &outst;
5383c057a378SRick Macklem 	struct nfslock inlo, outlo, *inlop = &inlo, *outlop = &outlo;
5384c057a378SRick Macklem 	nfsquad_t clientid;
5385c057a378SRick Macklem 	nfsv4stateid_t stateid;
5386c057a378SRick Macklem 	nfsattrbit_t attrbits;
5387c057a378SRick Macklem 	void *rl_rcookie, *rl_wcookie;
5388c057a378SRick Macklem 
5389c057a378SRick Macklem 	rl_rcookie = rl_wcookie = NULL;
5390c057a378SRick Macklem 	if (nfsrv_devidcnt > 0) {
5391c057a378SRick Macklem 		/*
5392c057a378SRick Macklem 		 * For a pNFS server, reply NFSERR_NOTSUPP so that the client
5393c057a378SRick Macklem 		 * will do the copy via I/O on the DS(s).
5394c057a378SRick Macklem 		 */
5395c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5396c057a378SRick Macklem 		goto nfsmout;
5397c057a378SRick Macklem 	}
5398c057a378SRick Macklem 	if (vp == tovp) {
5399c057a378SRick Macklem 		/* Copying a byte range within the same file is not allowed. */
5400c057a378SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
5401c057a378SRick Macklem 		goto nfsmout;
5402c057a378SRick Macklem 	}
5403c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_STATEID + 3 * NFSX_HYPER +
5404c057a378SRick Macklem 	    3 * NFSX_UNSIGNED);
5405c057a378SRick Macklem 	instp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
5406c057a378SRick Macklem 	inlop->lo_flags = NFSLCK_READ;
5407c057a378SRick Macklem 	instp->ls_ownerlen = 0;
5408c057a378SRick Macklem 	instp->ls_op = NULL;
5409c057a378SRick Macklem 	instp->ls_uid = nd->nd_cred->cr_uid;
5410c057a378SRick Macklem 	instp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
5411c057a378SRick Macklem 	clientid.lval[0] = instp->ls_stateid.other[0] = *tl++;
5412c057a378SRick Macklem 	clientid.lval[1] = instp->ls_stateid.other[1] = *tl++;
5413c057a378SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0)
5414c057a378SRick Macklem 		clientid.qval = nd->nd_clientid.qval;
5415c057a378SRick Macklem 	instp->ls_stateid.other[2] = *tl++;
5416c057a378SRick Macklem 	outstp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
5417c057a378SRick Macklem 	outlop->lo_flags = NFSLCK_WRITE;
5418c057a378SRick Macklem 	outstp->ls_ownerlen = 0;
5419c057a378SRick Macklem 	outstp->ls_op = NULL;
5420c057a378SRick Macklem 	outstp->ls_uid = nd->nd_cred->cr_uid;
5421c057a378SRick Macklem 	outstp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
5422c057a378SRick Macklem 	outstp->ls_stateid.other[0] = *tl++;
5423c057a378SRick Macklem 	outstp->ls_stateid.other[1] = *tl++;
5424c057a378SRick Macklem 	outstp->ls_stateid.other[2] = *tl++;
5425c057a378SRick Macklem 	inoff = fxdr_hyper(tl); tl += 2;
5426c057a378SRick Macklem 	inlop->lo_first = inoff;
5427c057a378SRick Macklem 	outoff = fxdr_hyper(tl); tl += 2;
5428c057a378SRick Macklem 	outlop->lo_first = outoff;
5429c057a378SRick Macklem 	len = fxdr_hyper(tl); tl += 2;
5430c057a378SRick Macklem 	if (len == 0) {
5431c057a378SRick Macklem 		/* len == 0 means to EOF. */
5432c057a378SRick Macklem 		inlop->lo_end = OFF_MAX;
5433c057a378SRick Macklem 		outlop->lo_end = OFF_MAX;
5434c057a378SRick Macklem 	} else {
5435c057a378SRick Macklem 		inlop->lo_end = inlop->lo_first + len;
5436c057a378SRick Macklem 		outlop->lo_end = outlop->lo_first + len;
5437c057a378SRick Macklem 	}
5438c057a378SRick Macklem 
5439c057a378SRick Macklem 	/*
5440c057a378SRick Macklem 	 * At this time only consecutive, synchronous copy is supported,
5441c057a378SRick Macklem 	 * so ca_consecutive and ca_synchronous can be ignored.
5442c057a378SRick Macklem 	 */
5443c057a378SRick Macklem 	tl += 2;
5444c057a378SRick Macklem 
5445c057a378SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
5446c057a378SRick Macklem 	if ((nd->nd_flag & ND_DSSERVER) != 0 || cnt != 0)
5447c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5448c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (inoff > OFF_MAX || outoff > OFF_MAX ||
5449c057a378SRick Macklem 	    inlop->lo_end > OFF_MAX || outlop->lo_end > OFF_MAX ||
5450c057a378SRick Macklem 	    inlop->lo_end < inlop->lo_first || outlop->lo_end <
5451c057a378SRick Macklem 	    outlop->lo_first))
5452c057a378SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
5453c057a378SRick Macklem 
5454c057a378SRick Macklem 	if (nd->nd_repstat == 0 && vnode_vtype(vp) != VREG)
5455c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGTYPE;
5456c057a378SRick Macklem 
5457c057a378SRick Macklem 	/* Check permissions for the input file. */
5458c057a378SRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
5459c057a378SRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
5460c057a378SRick Macklem 	ret = nfsvno_getattr(vp, &at, nd, curthread, 1, &attrbits);
5461c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5462c057a378SRick Macklem 		nd->nd_repstat = ret;
5463c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
5464c057a378SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
5465c057a378SRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred, exp,
5466c057a378SRick Macklem 		    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
5467c057a378SRick Macklem 		    NULL);
5468c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5469c057a378SRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(vp, &instp, &inlop, NULL,
5470c057a378SRick Macklem 		    clientid, &stateid, exp, nd, curthread);
5471b249ce48SMateusz Guzik 	NFSVOPUNLOCK(vp);
5472c057a378SRick Macklem 	if (nd->nd_repstat != 0)
5473c057a378SRick Macklem 		goto out;
5474c057a378SRick Macklem 
5475c057a378SRick Macklem 	error = NFSVOPLOCK(tovp, LK_SHARED);
5476c057a378SRick Macklem 	if (error != 0)
5477c057a378SRick Macklem 		goto out;
5478c057a378SRick Macklem 	if (vnode_vtype(tovp) != VREG)
5479c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGTYPE;
5480c057a378SRick Macklem 
5481c057a378SRick Macklem 	/* For the output file, we only need the Owner attribute. */
5482c057a378SRick Macklem 	ret = nfsvno_getattr(tovp, &at, nd, curthread, 1, &attrbits);
5483c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5484c057a378SRick Macklem 		nd->nd_repstat = ret;
5485c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
5486c057a378SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
5487c057a378SRick Macklem 		nd->nd_repstat = nfsvno_accchk(tovp, VWRITE, nd->nd_cred, toexp,
5488c057a378SRick Macklem 		    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
5489c057a378SRick Macklem 		    NULL);
5490c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5491c057a378SRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(tovp, &outstp, &outlop, NULL,
5492c057a378SRick Macklem 		    clientid, &stateid, toexp, nd, curthread);
5493b249ce48SMateusz Guzik 	NFSVOPUNLOCK(tovp);
5494c057a378SRick Macklem 
5495c057a378SRick Macklem 	/* Range lock the byte ranges for both invp and outvp. */
5496c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5497c057a378SRick Macklem 		for (;;) {
5498c057a378SRick Macklem 			if (len == 0) {
5499c057a378SRick Macklem 				rl_wcookie = vn_rangelock_wlock(tovp, outoff,
5500c057a378SRick Macklem 				    OFF_MAX);
5501c057a378SRick Macklem 				rl_rcookie = vn_rangelock_tryrlock(vp, inoff,
5502c057a378SRick Macklem 				    OFF_MAX);
5503c057a378SRick Macklem 			} else {
5504c057a378SRick Macklem 				rl_wcookie = vn_rangelock_wlock(tovp, outoff,
5505c057a378SRick Macklem 				    outoff + len);
5506c057a378SRick Macklem 				rl_rcookie = vn_rangelock_tryrlock(vp, inoff,
5507c057a378SRick Macklem 				    inoff + len);
5508c057a378SRick Macklem 			}
5509c057a378SRick Macklem 			if (rl_rcookie != NULL)
5510c057a378SRick Macklem 				break;
5511c057a378SRick Macklem 			vn_rangelock_unlock(tovp, rl_wcookie);
5512c057a378SRick Macklem 			if (len == 0)
5513c057a378SRick Macklem 				rl_rcookie = vn_rangelock_rlock(vp, inoff,
5514c057a378SRick Macklem 				    OFF_MAX);
5515c057a378SRick Macklem 			else
5516c057a378SRick Macklem 				rl_rcookie = vn_rangelock_rlock(vp, inoff,
5517c057a378SRick Macklem 				    inoff + len);
5518c057a378SRick Macklem 			vn_rangelock_unlock(vp, rl_rcookie);
5519c057a378SRick Macklem 		}
5520c057a378SRick Macklem 
5521c057a378SRick Macklem 		error = NFSVOPLOCK(vp, LK_SHARED);
5522c057a378SRick Macklem 		if (error == 0) {
5523c057a378SRick Macklem 			ret = nfsvno_getattr(vp, &at, nd, curthread, 1, NULL);
5524c057a378SRick Macklem 			if (ret == 0) {
5525c057a378SRick Macklem 				/*
5526c057a378SRick Macklem 				 * Since invp is range locked, na_size should
5527c057a378SRick Macklem 				 * not change.
5528c057a378SRick Macklem 				 */
5529c057a378SRick Macklem 				if (len == 0 && at.na_size > inoff) {
5530c057a378SRick Macklem 					/*
5531c057a378SRick Macklem 					 * If len == 0, set it based on invp's
5532c057a378SRick Macklem 					 * size. If offset is past EOF, just
5533c057a378SRick Macklem 					 * leave len == 0.
5534c057a378SRick Macklem 					 */
5535c057a378SRick Macklem 					len = at.na_size - inoff;
5536c057a378SRick Macklem 				} else if (nfsrv_linux42server == 0 &&
5537c057a378SRick Macklem 				    inoff + len > at.na_size) {
5538c057a378SRick Macklem 					/*
5539c057a378SRick Macklem 					 * RFC-7862 says that NFSERR_INVAL must
5540c057a378SRick Macklem 					 * be returned when inoff + len exceeds
5541c057a378SRick Macklem 					 * the file size, however the NFSv4.2
5542c057a378SRick Macklem 					 * Linux client likes to do this, so
5543c057a378SRick Macklem 					 * only check if nfsrv_linux42server
5544c057a378SRick Macklem 					 * is not set.
5545c057a378SRick Macklem 					 */
5546c057a378SRick Macklem 					nd->nd_repstat = NFSERR_INVAL;
5547c057a378SRick Macklem 				}
5548c057a378SRick Macklem 			}
5549b249ce48SMateusz Guzik 			NFSVOPUNLOCK(vp);
5550c057a378SRick Macklem 			if (ret != 0 && nd->nd_repstat == 0)
5551c057a378SRick Macklem 				nd->nd_repstat = ret;
5552c057a378SRick Macklem 		} else if (nd->nd_repstat == 0)
5553c057a378SRick Macklem 			nd->nd_repstat = error;
5554c057a378SRick Macklem 	}
5555c057a378SRick Macklem 
5556c057a378SRick Macklem 	/*
5557c057a378SRick Macklem 	 * Do the actual copy to an upper limit of vfs.nfs.maxcopyrange.
5558c057a378SRick Macklem 	 * This limit is applied to ensure that the RPC replies in a
5559c057a378SRick Macklem 	 * reasonable time.
5560c057a378SRick Macklem 	 */
5561c057a378SRick Macklem 	if (len > nfs_maxcopyrange)
5562c057a378SRick Macklem 		xfer = nfs_maxcopyrange;
5563c057a378SRick Macklem 	else
5564c057a378SRick Macklem 		xfer = len;
5565c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5566c057a378SRick Macklem 		nd->nd_repstat = vn_copy_file_range(vp, &inoff, tovp, &outoff,
5567c057a378SRick Macklem 		    &xfer, 0, nd->nd_cred, nd->nd_cred, NULL);
5568c057a378SRick Macklem 		if (nd->nd_repstat == 0)
5569c057a378SRick Macklem 			len = xfer;
5570c057a378SRick Macklem 	}
5571c057a378SRick Macklem 
5572c057a378SRick Macklem 	/* Unlock the ranges. */
5573c057a378SRick Macklem 	if (rl_rcookie != NULL)
5574c057a378SRick Macklem 		vn_rangelock_unlock(vp, rl_rcookie);
5575c057a378SRick Macklem 	if (rl_wcookie != NULL)
5576c057a378SRick Macklem 		vn_rangelock_unlock(tovp, rl_wcookie);
5577c057a378SRick Macklem 
5578c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5579c057a378SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED + NFSX_HYPER +
5580c057a378SRick Macklem 		    NFSX_VERF);
5581c057a378SRick Macklem 		*tl++ = txdr_unsigned(0);	/* No callback ids. */
5582c057a378SRick Macklem 		txdr_hyper(len, tl); tl += 2;
5583c057a378SRick Macklem 		*tl++ = txdr_unsigned(NFSWRITE_UNSTABLE);
5584c057a378SRick Macklem 		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
5585c057a378SRick Macklem 		*tl++ = txdr_unsigned(nfsboottime.tv_usec);
5586c057a378SRick Macklem 		*tl++ = newnfs_true;
5587c057a378SRick Macklem 		*tl = newnfs_true;
5588c057a378SRick Macklem 	}
5589c057a378SRick Macklem out:
5590c057a378SRick Macklem 	vrele(vp);
5591c057a378SRick Macklem 	vrele(tovp);
5592c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5593c057a378SRick Macklem 	return (error);
5594c057a378SRick Macklem nfsmout:
5595c057a378SRick Macklem 	vput(vp);
5596c057a378SRick Macklem 	vrele(tovp);
5597c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5598c057a378SRick Macklem 	return (error);
5599c057a378SRick Macklem }
5600c057a378SRick Macklem 
5601c057a378SRick Macklem /*
5602c057a378SRick Macklem  * nfs seek service
5603c057a378SRick Macklem  */
5604b9cc3262SRyan Moeller int
5605c057a378SRick Macklem nfsrvd_seek(struct nfsrv_descript *nd, __unused int isdgram,
5606c057a378SRick Macklem     vnode_t vp, struct nfsexstuff *exp)
5607c057a378SRick Macklem {
5608c057a378SRick Macklem 	uint32_t *tl;
5609c057a378SRick Macklem 	struct nfsvattr at;
5610c057a378SRick Macklem 	int content, error = 0;
5611c057a378SRick Macklem 	off_t off;
5612c057a378SRick Macklem 	u_long cmd;
5613c057a378SRick Macklem 	nfsattrbit_t attrbits;
5614c057a378SRick Macklem 	bool eof;
5615c057a378SRick Macklem 
5616c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + NFSX_HYPER + NFSX_UNSIGNED);
5617c057a378SRick Macklem 	/* Ignore the stateid for now. */
5618c057a378SRick Macklem 	tl += (NFSX_STATEID / NFSX_UNSIGNED);
5619c057a378SRick Macklem 	off = fxdr_hyper(tl); tl += 2;
5620c057a378SRick Macklem 	content = fxdr_unsigned(int, *tl);
5621c057a378SRick Macklem 	if (content == NFSV4CONTENT_DATA)
5622c057a378SRick Macklem 		cmd = FIOSEEKDATA;
5623c057a378SRick Macklem 	else if (content == NFSV4CONTENT_HOLE)
5624c057a378SRick Macklem 		cmd = FIOSEEKHOLE;
5625c057a378SRick Macklem 	else
5626c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
5627c057a378SRick Macklem 	if (nd->nd_repstat == 0 && vnode_vtype(vp) == VDIR)
5628c057a378SRick Macklem 		nd->nd_repstat = NFSERR_ISDIR;
5629c057a378SRick Macklem 	if (nd->nd_repstat == 0 && vnode_vtype(vp) != VREG)
5630c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGTYPE;
5631c057a378SRick Macklem 	if (nd->nd_repstat == 0 && off < 0)
5632c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NXIO;
5633c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5634c057a378SRick Macklem 		/* Check permissions for the input file. */
5635c057a378SRick Macklem 		NFSZERO_ATTRBIT(&attrbits);
5636c057a378SRick Macklem 		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
5637c057a378SRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &at, nd, curthread, 1,
5638c057a378SRick Macklem 		    &attrbits);
5639c057a378SRick Macklem 	}
5640c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
5641c057a378SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
5642c057a378SRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred, exp,
5643c057a378SRick Macklem 		    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
5644c057a378SRick Macklem 		    NULL);
5645c057a378SRick Macklem 	if (nd->nd_repstat != 0)
5646c057a378SRick Macklem 		goto nfsmout;
5647c057a378SRick Macklem 
5648c057a378SRick Macklem 	/* nfsvno_seek() unlocks and vrele()s the vp. */
5649c057a378SRick Macklem 	nd->nd_repstat = nfsvno_seek(nd, vp, cmd, &off, content, &eof,
5650c057a378SRick Macklem 	    nd->nd_cred, curthread);
5651c057a378SRick Macklem 	if (nd->nd_repstat == 0 && eof && content == NFSV4CONTENT_DATA &&
5652c057a378SRick Macklem 	    nfsrv_linux42server != 0)
5653c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NXIO;
5654c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5655c057a378SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED + NFSX_HYPER);
5656c057a378SRick Macklem 		if (eof)
5657c057a378SRick Macklem 			*tl++ = newnfs_true;
5658c057a378SRick Macklem 		else
5659c057a378SRick Macklem 			*tl++ = newnfs_false;
5660c057a378SRick Macklem 		txdr_hyper(off, tl);
5661c057a378SRick Macklem 	}
5662c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5663c057a378SRick Macklem 	return (error);
5664c057a378SRick Macklem nfsmout:
5665c057a378SRick Macklem 	vput(vp);
5666c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5667c057a378SRick Macklem 	return (error);
5668c057a378SRick Macklem }
5669c057a378SRick Macklem 
5670c057a378SRick Macklem /*
5671c057a378SRick Macklem  * nfs get extended attribute service
5672c057a378SRick Macklem  */
5673b9cc3262SRyan Moeller int
5674c057a378SRick Macklem nfsrvd_getxattr(struct nfsrv_descript *nd, __unused int isdgram,
5675c057a378SRick Macklem     vnode_t vp, __unused struct nfsexstuff *exp)
5676c057a378SRick Macklem {
5677c057a378SRick Macklem 	uint32_t *tl;
5678ae070589SRick Macklem 	struct mbuf *mp = NULL, *mpend = NULL;
5679c057a378SRick Macklem 	int error, len;
5680c057a378SRick Macklem 	char *name;
5681c057a378SRick Macklem 	struct thread *p = curthread;
5682cb889ce6SRick Macklem 	uint16_t off;
5683c057a378SRick Macklem 
5684c057a378SRick Macklem 	error = 0;
5685c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
5686c057a378SRick Macklem 	len = fxdr_unsigned(int, *tl);
5687c057a378SRick Macklem 	if (len <= 0) {
5688c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
5689c057a378SRick Macklem 		goto nfsmout;
5690c057a378SRick Macklem 	}
5691c057a378SRick Macklem 	if (len > EXTATTR_MAXNAMELEN) {
5692c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOXATTR;
5693c057a378SRick Macklem 		goto nfsmout;
5694c057a378SRick Macklem 	}
5695c057a378SRick Macklem 	name = malloc(len + 1, M_TEMP, M_WAITOK);
5696c057a378SRick Macklem 	nd->nd_repstat = nfsrv_mtostr(nd, name, len);
5697c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5698cb889ce6SRick Macklem 		nd->nd_repstat = nfsvno_getxattr(vp, name,
5699cb889ce6SRick Macklem 		    nd->nd_maxresp, nd->nd_cred, nd->nd_flag,
5700cb889ce6SRick Macklem 		    nd->nd_maxextsiz, p, &mp, &mpend, &len);
5701c057a378SRick Macklem 	if (nd->nd_repstat == ENOATTR)
5702c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOXATTR;
5703c057a378SRick Macklem 	else if (nd->nd_repstat == EOPNOTSUPP)
5704c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5705c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5706c057a378SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
5707c057a378SRick Macklem 		*tl = txdr_unsigned(len);
5708fb8ed4c5SRick Macklem 		if (len > 0) {
57099f6624d3SRick Macklem 			nd->nd_mb->m_next = mp;
5710c057a378SRick Macklem 			nd->nd_mb = mpend;
5711cb889ce6SRick Macklem 			if ((mpend->m_flags & M_EXTPG) != 0) {
5712cb889ce6SRick Macklem 				nd->nd_flag |= ND_EXTPG;
5713cb889ce6SRick Macklem 				nd->nd_bextpg = mpend->m_epg_npgs - 1;
5714cb889ce6SRick Macklem 				nd->nd_bpos = (char *)(void *)
5715cb889ce6SRick Macklem 				   PHYS_TO_DMAP(mpend->m_epg_pa[nd->nd_bextpg]);
5716cb889ce6SRick Macklem 				off = (nd->nd_bextpg == 0) ?
5717cb889ce6SRick Macklem 				    mpend->m_epg_1st_off : 0;
5718cb889ce6SRick Macklem 				nd->nd_bpos += off + mpend->m_epg_last_len;
5719cb889ce6SRick Macklem 				nd->nd_bextpgsiz = PAGE_SIZE -
5720cb889ce6SRick Macklem 				    mpend->m_epg_last_len - off;
5721cb889ce6SRick Macklem 			} else
5722cb889ce6SRick Macklem 				nd->nd_bpos = mtod(mpend, char *) +
5723cb889ce6SRick Macklem 				    mpend->m_len;
5724c057a378SRick Macklem 		}
5725fb8ed4c5SRick Macklem 	}
5726c057a378SRick Macklem 	free(name, M_TEMP);
5727c057a378SRick Macklem 
5728c057a378SRick Macklem nfsmout:
5729c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5730c057a378SRick Macklem 		nd->nd_repstat = error;
5731c057a378SRick Macklem 	vput(vp);
5732c057a378SRick Macklem 	NFSEXITCODE2(0, nd);
5733c057a378SRick Macklem 	return (0);
5734c057a378SRick Macklem }
5735c057a378SRick Macklem 
5736c057a378SRick Macklem /*
5737c057a378SRick Macklem  * nfs set extended attribute service
5738c057a378SRick Macklem  */
5739b9cc3262SRyan Moeller int
5740c057a378SRick Macklem nfsrvd_setxattr(struct nfsrv_descript *nd, __unused int isdgram,
5741c057a378SRick Macklem     vnode_t vp, __unused struct nfsexstuff *exp)
5742c057a378SRick Macklem {
5743c057a378SRick Macklem 	uint32_t *tl;
5744c057a378SRick Macklem 	struct nfsvattr ova, nva;
5745c057a378SRick Macklem 	nfsattrbit_t attrbits;
5746c057a378SRick Macklem 	int error, len, opt;
5747c057a378SRick Macklem 	char *name;
5748c057a378SRick Macklem 	size_t siz;
5749c057a378SRick Macklem 	struct thread *p = curthread;
5750c057a378SRick Macklem 
5751c057a378SRick Macklem 	error = 0;
5752c057a378SRick Macklem 	name = NULL;
5753c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
5754c057a378SRick Macklem 	opt = fxdr_unsigned(int, *tl++);
5755c057a378SRick Macklem 	len = fxdr_unsigned(int, *tl);
5756c057a378SRick Macklem 	if (len <= 0) {
5757c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
5758c057a378SRick Macklem 		goto nfsmout;
5759c057a378SRick Macklem 	}
5760c057a378SRick Macklem 	if (len > EXTATTR_MAXNAMELEN) {
5761c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOXATTR;
5762c057a378SRick Macklem 		goto nfsmout;
5763c057a378SRick Macklem 	}
5764c057a378SRick Macklem 	name = malloc(len + 1, M_TEMP, M_WAITOK);
5765c057a378SRick Macklem 	error = nfsrv_mtostr(nd, name, len);
5766c057a378SRick Macklem 	if (error != 0)
5767c057a378SRick Macklem 		goto nfsmout;
5768c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
5769c057a378SRick Macklem 	len = fxdr_unsigned(int, *tl);
5770fb8ed4c5SRick Macklem 	if (len < 0 || len > IOSIZE_MAX) {
5771c057a378SRick Macklem 		nd->nd_repstat = NFSERR_XATTR2BIG;
5772c057a378SRick Macklem 		goto nfsmout;
5773c057a378SRick Macklem 	}
5774c057a378SRick Macklem 	switch (opt) {
5775c057a378SRick Macklem 	case NFSV4SXATTR_CREATE:
5776c057a378SRick Macklem 		error = VOP_GETEXTATTR(vp, EXTATTR_NAMESPACE_USER, name, NULL,
5777c057a378SRick Macklem 		    &siz, nd->nd_cred, p);
5778c057a378SRick Macklem 		if (error != ENOATTR)
5779c057a378SRick Macklem 			nd->nd_repstat = NFSERR_EXIST;
5780c057a378SRick Macklem 		error = 0;
5781c057a378SRick Macklem 		break;
5782c057a378SRick Macklem 	case NFSV4SXATTR_REPLACE:
5783c057a378SRick Macklem 		error = VOP_GETEXTATTR(vp, EXTATTR_NAMESPACE_USER, name, NULL,
5784c057a378SRick Macklem 		    &siz, nd->nd_cred, p);
5785c057a378SRick Macklem 		if (error != 0)
5786c057a378SRick Macklem 			nd->nd_repstat = NFSERR_NOXATTR;
5787c057a378SRick Macklem 		break;
5788c057a378SRick Macklem 	case NFSV4SXATTR_EITHER:
5789c057a378SRick Macklem 		break;
5790c057a378SRick Macklem 	default:
5791c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
5792c057a378SRick Macklem 	}
5793c057a378SRick Macklem 	if (nd->nd_repstat != 0)
5794c057a378SRick Macklem 		goto nfsmout;
5795c057a378SRick Macklem 
5796c057a378SRick Macklem 	/* Now, do the Set Extended attribute, with Change before and after. */
5797c057a378SRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
5798c057a378SRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
5799c057a378SRick Macklem 	nd->nd_repstat = nfsvno_getattr(vp, &ova, nd, p, 1, &attrbits);
5800c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5801c057a378SRick Macklem 		nd->nd_repstat = nfsvno_setxattr(vp, name, len, nd->nd_md,
5802c057a378SRick Macklem 		    nd->nd_dpos, nd->nd_cred, p);
5803c057a378SRick Macklem 		if (nd->nd_repstat == ENXIO)
5804c057a378SRick Macklem 			nd->nd_repstat = NFSERR_XATTR2BIG;
5805c057a378SRick Macklem 	}
5806fb8ed4c5SRick Macklem 	if (nd->nd_repstat == 0 && len > 0)
5807c057a378SRick Macklem 		nd->nd_repstat = nfsm_advance(nd, NFSM_RNDUP(len), -1);
5808c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5809c057a378SRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, &attrbits);
5810c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5811c057a378SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_UNSIGNED);
5812c057a378SRick Macklem 		*tl++ = newnfs_true;
5813c057a378SRick Macklem 		txdr_hyper(ova.na_filerev, tl); tl += 2;
5814c057a378SRick Macklem 		txdr_hyper(nva.na_filerev, tl);
5815c057a378SRick Macklem 	}
5816c057a378SRick Macklem 
5817c057a378SRick Macklem nfsmout:
5818c057a378SRick Macklem 	free(name, M_TEMP);
5819c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5820c057a378SRick Macklem 		nd->nd_repstat = error;
5821c057a378SRick Macklem 	vput(vp);
5822c057a378SRick Macklem 	NFSEXITCODE2(0, nd);
5823c057a378SRick Macklem 	return (0);
5824c057a378SRick Macklem }
5825c057a378SRick Macklem 
5826c057a378SRick Macklem /*
5827c057a378SRick Macklem  * nfs remove extended attribute service
5828c057a378SRick Macklem  */
5829b9cc3262SRyan Moeller int
5830c057a378SRick Macklem nfsrvd_rmxattr(struct nfsrv_descript *nd, __unused int isdgram,
5831c057a378SRick Macklem     vnode_t vp, __unused struct nfsexstuff *exp)
5832c057a378SRick Macklem {
5833c057a378SRick Macklem 	uint32_t *tl;
5834c057a378SRick Macklem 	struct nfsvattr ova, nva;
5835c057a378SRick Macklem 	nfsattrbit_t attrbits;
5836c057a378SRick Macklem 	int error, len;
5837c057a378SRick Macklem 	char *name;
5838c057a378SRick Macklem 	struct thread *p = curthread;
5839c057a378SRick Macklem 
5840c057a378SRick Macklem 	error = 0;
5841c057a378SRick Macklem 	name = NULL;
5842c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
5843c057a378SRick Macklem 	len = fxdr_unsigned(int, *tl);
5844c057a378SRick Macklem 	if (len <= 0) {
5845c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
5846c057a378SRick Macklem 		goto nfsmout;
5847c057a378SRick Macklem 	}
5848c057a378SRick Macklem 	if (len > EXTATTR_MAXNAMELEN) {
5849c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOXATTR;
5850c057a378SRick Macklem 		goto nfsmout;
5851c057a378SRick Macklem 	}
5852c057a378SRick Macklem 	name = malloc(len + 1, M_TEMP, M_WAITOK);
5853c057a378SRick Macklem 	error = nfsrv_mtostr(nd, name, len);
5854c057a378SRick Macklem 	if (error != 0)
5855c057a378SRick Macklem 		goto nfsmout;
5856c057a378SRick Macklem 
5857c057a378SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) == 0) {
5858c057a378SRick Macklem 		printf("EEK! nfsrvd_rmxattr: no implied clientid\n");
5859c057a378SRick Macklem 		error = NFSERR_NOXATTR;
5860c057a378SRick Macklem 		goto nfsmout;
5861c057a378SRick Macklem 	}
5862c057a378SRick Macklem 	/*
5863c057a378SRick Macklem 	 * Now, do the Remove Extended attribute, with Change before and
5864c057a378SRick Macklem 	 * after.
5865c057a378SRick Macklem 	*/
5866c057a378SRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
5867c057a378SRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
5868c057a378SRick Macklem 	nd->nd_repstat = nfsvno_getattr(vp, &ova, nd, p, 1, &attrbits);
5869c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5870c057a378SRick Macklem 		nd->nd_repstat = nfsvno_rmxattr(nd, vp, name, nd->nd_cred, p);
5871c057a378SRick Macklem 		if (nd->nd_repstat == ENOATTR)
5872c057a378SRick Macklem 			nd->nd_repstat = NFSERR_NOXATTR;
5873c057a378SRick Macklem 	}
5874c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5875c057a378SRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, &attrbits);
5876c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
58770bda1dddSRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_UNSIGNED);
58780bda1dddSRick Macklem 		*tl++ = newnfs_true;
5879c057a378SRick Macklem 		txdr_hyper(ova.na_filerev, tl); tl += 2;
5880c057a378SRick Macklem 		txdr_hyper(nva.na_filerev, tl);
5881c057a378SRick Macklem 	}
5882c057a378SRick Macklem 
5883c057a378SRick Macklem nfsmout:
5884c057a378SRick Macklem 	free(name, M_TEMP);
5885c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5886c057a378SRick Macklem 		nd->nd_repstat = error;
5887c057a378SRick Macklem 	vput(vp);
5888c057a378SRick Macklem 	NFSEXITCODE2(0, nd);
5889c057a378SRick Macklem 	return (0);
5890c057a378SRick Macklem }
5891c057a378SRick Macklem 
5892c057a378SRick Macklem /*
5893c057a378SRick Macklem  * nfs list extended attribute service
5894c057a378SRick Macklem  */
5895b9cc3262SRyan Moeller int
5896c057a378SRick Macklem nfsrvd_listxattr(struct nfsrv_descript *nd, __unused int isdgram,
5897c057a378SRick Macklem     vnode_t vp, __unused struct nfsexstuff *exp)
5898c057a378SRick Macklem {
5899c057a378SRick Macklem 	uint32_t cnt, *tl, len, len2, i, pos, retlen;
5900c057a378SRick Macklem 	int error;
5901c057a378SRick Macklem 	uint64_t cookie, cookie2;
5902c057a378SRick Macklem 	u_char *buf;
5903c057a378SRick Macklem 	bool eof;
5904c057a378SRick Macklem 	struct thread *p = curthread;
5905c057a378SRick Macklem 
5906c057a378SRick Macklem 	error = 0;
5907c057a378SRick Macklem 	buf = NULL;
5908c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + NFSX_UNSIGNED);
5909c057a378SRick Macklem 	/*
5910c057a378SRick Macklem 	 * The cookie doesn't need to be in net byte order, but FreeBSD
5911c057a378SRick Macklem 	 * does so to make it more readable in packet traces.
5912c057a378SRick Macklem 	 */
5913c057a378SRick Macklem 	cookie = fxdr_hyper(tl); tl += 2;
5914c057a378SRick Macklem 	len = fxdr_unsigned(uint32_t, *tl);
5915c057a378SRick Macklem 	if (len == 0 || cookie >= IOSIZE_MAX) {
5916c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
5917c057a378SRick Macklem 		goto nfsmout;
5918c057a378SRick Macklem 	}
5919c057a378SRick Macklem 	if (len > nd->nd_maxresp - NFS_MAXXDR)
5920c057a378SRick Macklem 		len = nd->nd_maxresp - NFS_MAXXDR;
5921c057a378SRick Macklem 	len2 = len;
5922c057a378SRick Macklem 	nd->nd_repstat = nfsvno_listxattr(vp, cookie, nd->nd_cred, p, &buf,
5923c057a378SRick Macklem 	    &len, &eof);
5924c057a378SRick Macklem 	if (nd->nd_repstat == EOPNOTSUPP)
5925c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5926c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5927c057a378SRick Macklem 		cookie2 = cookie + len;
5928c057a378SRick Macklem 		if (cookie2 < cookie)
5929c057a378SRick Macklem 			nd->nd_repstat = NFSERR_BADXDR;
5930c057a378SRick Macklem 	}
5931c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5932c057a378SRick Macklem 		/* Now copy the entries out. */
5933c057a378SRick Macklem 		retlen = NFSX_HYPER + 2 * NFSX_UNSIGNED;
5934c057a378SRick Macklem 		if (len == 0 && retlen <= len2) {
5935c057a378SRick Macklem 			/* The cookie was at eof. */
5936c057a378SRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_HYPER + 2 *
5937c057a378SRick Macklem 			    NFSX_UNSIGNED);
5938c057a378SRick Macklem 			txdr_hyper(cookie2, tl); tl += 2;
5939c057a378SRick Macklem 			*tl++ = txdr_unsigned(0);
5940c057a378SRick Macklem 			*tl = newnfs_true;
5941c057a378SRick Macklem 			goto nfsmout;
5942c057a378SRick Macklem 		}
5943c057a378SRick Macklem 
5944c057a378SRick Macklem 		/* Sanity check the cookie. */
5945c057a378SRick Macklem 		for (pos = 0; pos < len; pos += (i + 1)) {
5946c057a378SRick Macklem 			if (pos == cookie)
5947c057a378SRick Macklem 				break;
5948c057a378SRick Macklem 			i = buf[pos];
5949c057a378SRick Macklem 		}
5950c057a378SRick Macklem 		if (pos != cookie) {
5951c057a378SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
5952c057a378SRick Macklem 			goto nfsmout;
5953c057a378SRick Macklem 		}
5954c057a378SRick Macklem 
5955c057a378SRick Macklem 		/* Loop around copying the entrie(s) out. */
5956c057a378SRick Macklem 		cnt = 0;
5957c057a378SRick Macklem 		len -= cookie;
5958c057a378SRick Macklem 		i = buf[pos];
5959c057a378SRick Macklem 		while (i < len && len2 >= retlen + NFSM_RNDUP(i) +
5960c057a378SRick Macklem 		    NFSX_UNSIGNED) {
5961c057a378SRick Macklem 			if (cnt == 0) {
5962c057a378SRick Macklem 				NFSM_BUILD(tl, uint32_t *, NFSX_HYPER +
5963c057a378SRick Macklem 				    NFSX_UNSIGNED);
5964c057a378SRick Macklem 				txdr_hyper(cookie2, tl); tl += 2;
5965c057a378SRick Macklem 			}
5966c057a378SRick Macklem 			retlen += nfsm_strtom(nd, &buf[pos + 1], i);
5967c057a378SRick Macklem 			len -= (i + 1);
5968c057a378SRick Macklem 			pos += (i + 1);
5969c057a378SRick Macklem 			i = buf[pos];
5970c057a378SRick Macklem 			cnt++;
5971c057a378SRick Macklem 		}
5972c057a378SRick Macklem 		/*
5973c057a378SRick Macklem 		 * eof is set true/false by nfsvno_listxattr(), but if we
5974c057a378SRick Macklem 		 * can't copy all entries returned by nfsvno_listxattr(),
5975c057a378SRick Macklem 		 * we are not at eof.
5976c057a378SRick Macklem 		 */
5977c057a378SRick Macklem 		if (len > 0)
5978c057a378SRick Macklem 			eof = false;
5979c057a378SRick Macklem 		if (cnt > 0) {
5980c057a378SRick Macklem 			/* *tl is set above. */
5981c057a378SRick Macklem 			*tl = txdr_unsigned(cnt);
5982c057a378SRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
5983c057a378SRick Macklem 			if (eof)
5984c057a378SRick Macklem 				*tl = newnfs_true;
5985c057a378SRick Macklem 			else
5986c057a378SRick Macklem 				*tl = newnfs_false;
5987c057a378SRick Macklem 		} else
5988c057a378SRick Macklem 			nd->nd_repstat = NFSERR_TOOSMALL;
5989c057a378SRick Macklem 	}
5990c057a378SRick Macklem 
5991c057a378SRick Macklem nfsmout:
5992c057a378SRick Macklem 	free(buf, M_TEMP);
5993c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5994c057a378SRick Macklem 		nd->nd_repstat = error;
5995c057a378SRick Macklem 	vput(vp);
5996c057a378SRick Macklem 	NFSEXITCODE2(0, nd);
5997c057a378SRick Macklem 	return (0);
5998c057a378SRick Macklem }
5999c057a378SRick Macklem 
6000c057a378SRick Macklem /*
6001c59e4cc3SRick Macklem  * nfsv4 service not supported
6002c59e4cc3SRick Macklem  */
6003b9cc3262SRyan Moeller int
6004c59e4cc3SRick Macklem nfsrvd_notsupp(struct nfsrv_descript *nd, __unused int isdgram,
6005af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
6006c59e4cc3SRick Macklem {
6007c59e4cc3SRick Macklem 
6008c59e4cc3SRick Macklem 	nd->nd_repstat = NFSERR_NOTSUPP;
6009c59e4cc3SRick Macklem 	NFSEXITCODE2(0, nd);
6010c59e4cc3SRick Macklem 	return (0);
6011c59e4cc3SRick Macklem }
6012