xref: /freebsd/sys/fs/nfsserver/nfs_nfsdserv.c (revision dcfa3ee44da2b139f51a8aedb0f55735c6dfe3f3)
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;
68ee29e6f3SRick Macklem extern uint32_t nfs_srvmaxio;
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");
85272c4a4dSAlexander Motin static char nfsrv_scope[NFSV4_OPAQUELIMIT];
86272c4a4dSAlexander Motin SYSCTL_STRING(_vfs_nfsd, OID_AUTO, scope, CTLFLAG_RWTUN,
87272c4a4dSAlexander Motin     &nfsrv_scope, NFSV4_OPAQUELIMIT, "Server scope");
88272c4a4dSAlexander Motin static char nfsrv_owner_major[NFSV4_OPAQUELIMIT];
89272c4a4dSAlexander Motin SYSCTL_STRING(_vfs_nfsd, OID_AUTO, owner_major, CTLFLAG_RWTUN,
90272c4a4dSAlexander Motin     &nfsrv_owner_major, NFSV4_OPAQUELIMIT, "Server owner major");
91272c4a4dSAlexander Motin static uint64_t nfsrv_owner_minor;
92272c4a4dSAlexander Motin SYSCTL_U64(_vfs_nfsd, OID_AUTO, owner_minor, CTLFLAG_RWTUN,
93272c4a4dSAlexander Motin     &nfsrv_owner_minor, 0, "Server owner minor");
94dfe887b7SRick Macklem /*
95dfe887b7SRick Macklem  * Only enable this if all your exported file systems
96dfe887b7SRick Macklem  * (or pNFS DSs for the pNFS case) support VOP_ALLOCATE.
97dfe887b7SRick Macklem  */
98dfe887b7SRick Macklem static bool	nfsrv_doallocate = false;
99dfe887b7SRick Macklem SYSCTL_BOOL(_vfs_nfsd, OID_AUTO, enable_v42allocate, CTLFLAG_RW,
100dfe887b7SRick Macklem     &nfsrv_doallocate, 0,
101dfe887b7SRick Macklem     "Enable NFSv4.2 Allocate operation");
102e4558aacSXin LI 
1039ec7b004SRick Macklem /*
1049ec7b004SRick Macklem  * This list defines the GSS mechanisms supported.
1059ec7b004SRick Macklem  * (Don't ask me how you get these strings from the RFC stuff like
1069ec7b004SRick Macklem  *  iso(1), org(3)... but someone did it, so I don't need to know.)
1079ec7b004SRick Macklem  */
1089ec7b004SRick Macklem static struct nfsgss_mechlist nfsgss_mechlist[] = {
1099ec7b004SRick Macklem 	{ 9, "\052\206\110\206\367\022\001\002\002", 11 },
1109ec7b004SRick Macklem 	{ 0, "", 0 },
1119ec7b004SRick Macklem };
1129ec7b004SRick Macklem 
1139ec7b004SRick Macklem /* local functions */
1149ec7b004SRick Macklem static void nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
1159ec7b004SRick Macklem     struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
1169ec7b004SRick Macklem     vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
1179ec7b004SRick Macklem     int *diraft_retp, nfsattrbit_t *attrbitp,
1189ec7b004SRick Macklem     NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
1199ec7b004SRick Macklem     int pathlen);
1209ec7b004SRick Macklem static void nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
1219ec7b004SRick Macklem     struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
1229ec7b004SRick Macklem     vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
1239ec7b004SRick Macklem     int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
1249ec7b004SRick Macklem     NFSPROC_T *p, struct nfsexstuff *exp);
1259ec7b004SRick Macklem 
1269ec7b004SRick Macklem /*
1279ec7b004SRick Macklem  * nfs access service (not a part of NFS V2)
1289ec7b004SRick Macklem  */
129b9cc3262SRyan Moeller int
1309ec7b004SRick Macklem nfsrvd_access(struct nfsrv_descript *nd, __unused int isdgram,
131af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
1329ec7b004SRick Macklem {
1339ec7b004SRick Macklem 	u_int32_t *tl;
1349ec7b004SRick Macklem 	int getret, error = 0;
1359ec7b004SRick Macklem 	struct nfsvattr nva;
1369ec7b004SRick Macklem 	u_int32_t testmode, nfsmode, supported = 0;
1378da45f2cSRick Macklem 	accmode_t deletebit;
138af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
1399ec7b004SRick Macklem 
1409ec7b004SRick Macklem 	if (nd->nd_repstat) {
1419ec7b004SRick Macklem 		nfsrv_postopattr(nd, 1, &nva);
142a9285ae5SZack Kirsch 		goto out;
1439ec7b004SRick Macklem 	}
1449ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1459ec7b004SRick Macklem 	nfsmode = fxdr_unsigned(u_int32_t, *tl);
1469ec7b004SRick Macklem 	if ((nd->nd_flag & ND_NFSV4) &&
1479ec7b004SRick Macklem 	    (nfsmode & ~(NFSACCESS_READ | NFSACCESS_LOOKUP |
1489ec7b004SRick Macklem 	     NFSACCESS_MODIFY | NFSACCESS_EXTEND | NFSACCESS_DELETE |
149c057a378SRick Macklem 	     NFSACCESS_EXECUTE | NFSACCESS_XAREAD | NFSACCESS_XAWRITE |
150c057a378SRick Macklem 	     NFSACCESS_XALIST))) {
1519ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
1529ec7b004SRick Macklem 		vput(vp);
153a9285ae5SZack Kirsch 		goto out;
1549ec7b004SRick Macklem 	}
1559ec7b004SRick Macklem 	if (nfsmode & NFSACCESS_READ) {
1569ec7b004SRick Macklem 		supported |= NFSACCESS_READ;
1578da45f2cSRick Macklem 		if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
1588da45f2cSRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
1599ec7b004SRick Macklem 			nfsmode &= ~NFSACCESS_READ;
1609ec7b004SRick Macklem 	}
1619ec7b004SRick Macklem 	if (nfsmode & NFSACCESS_MODIFY) {
1629ec7b004SRick Macklem 		supported |= NFSACCESS_MODIFY;
1638da45f2cSRick Macklem 		if (nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp, p,
1648da45f2cSRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
1659ec7b004SRick Macklem 			nfsmode &= ~NFSACCESS_MODIFY;
1669ec7b004SRick Macklem 	}
1679ec7b004SRick Macklem 	if (nfsmode & NFSACCESS_EXTEND) {
1689ec7b004SRick Macklem 		supported |= NFSACCESS_EXTEND;
1698da45f2cSRick Macklem 		if (nfsvno_accchk(vp, VWRITE | VAPPEND, nd->nd_cred, exp, p,
1708da45f2cSRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
1719ec7b004SRick Macklem 			nfsmode &= ~NFSACCESS_EXTEND;
1729ec7b004SRick Macklem 	}
173c057a378SRick Macklem 	if (nfsmode & NFSACCESS_XAREAD) {
174c057a378SRick Macklem 		supported |= NFSACCESS_XAREAD;
175c057a378SRick Macklem 		if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
176c057a378SRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
177c057a378SRick Macklem 			nfsmode &= ~NFSACCESS_XAREAD;
178c057a378SRick Macklem 	}
179c057a378SRick Macklem 	if (nfsmode & NFSACCESS_XAWRITE) {
180c057a378SRick Macklem 		supported |= NFSACCESS_XAWRITE;
181c057a378SRick Macklem 		if (nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp, p,
182c057a378SRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
183c057a378SRick Macklem 			nfsmode &= ~NFSACCESS_XAWRITE;
184c057a378SRick Macklem 	}
185c057a378SRick Macklem 	if (nfsmode & NFSACCESS_XALIST) {
186c057a378SRick Macklem 		supported |= NFSACCESS_XALIST;
187c057a378SRick Macklem 		if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
188c057a378SRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
189c057a378SRick Macklem 			nfsmode &= ~NFSACCESS_XALIST;
190c057a378SRick Macklem 	}
1919ec7b004SRick Macklem 	if (nfsmode & NFSACCESS_DELETE) {
1929ec7b004SRick Macklem 		supported |= NFSACCESS_DELETE;
1938da45f2cSRick Macklem 		if (vp->v_type == VDIR)
1948da45f2cSRick Macklem 			deletebit = VDELETE_CHILD;
1958da45f2cSRick Macklem 		else
1968da45f2cSRick Macklem 			deletebit = VDELETE;
1978da45f2cSRick Macklem 		if (nfsvno_accchk(vp, deletebit, nd->nd_cred, exp, p,
1988da45f2cSRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
1999ec7b004SRick Macklem 			nfsmode &= ~NFSACCESS_DELETE;
2009ec7b004SRick Macklem 	}
2015d3fe02cSRick Macklem 	if (vp->v_type == VDIR)
2029ec7b004SRick Macklem 		testmode = NFSACCESS_LOOKUP;
2039ec7b004SRick Macklem 	else
2049ec7b004SRick Macklem 		testmode = NFSACCESS_EXECUTE;
2059ec7b004SRick Macklem 	if (nfsmode & testmode) {
2069ec7b004SRick Macklem 		supported |= (nfsmode & testmode);
2078da45f2cSRick Macklem 		if (nfsvno_accchk(vp, VEXEC, nd->nd_cred, exp, p,
2088da45f2cSRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
2099ec7b004SRick Macklem 			nfsmode &= ~testmode;
2109ec7b004SRick Macklem 	}
2119ec7b004SRick Macklem 	nfsmode &= supported;
2129ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
21390d2dfabSRick Macklem 		getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
2149ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &nva);
2159ec7b004SRick Macklem 	}
2169ec7b004SRick Macklem 	vput(vp);
2179ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
2189ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2199ec7b004SRick Macklem 		*tl++ = txdr_unsigned(supported);
2209ec7b004SRick Macklem 	} else
2219ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2229ec7b004SRick Macklem 	*tl = txdr_unsigned(nfsmode);
223a9285ae5SZack Kirsch 
224a9285ae5SZack Kirsch out:
225a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
2269ec7b004SRick Macklem 	return (0);
2279ec7b004SRick Macklem nfsmout:
2289ec7b004SRick Macklem 	vput(vp);
229a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
2309ec7b004SRick Macklem 	return (error);
2319ec7b004SRick Macklem }
2329ec7b004SRick Macklem 
2339ec7b004SRick Macklem /*
2349ec7b004SRick Macklem  * nfs getattr service
2359ec7b004SRick Macklem  */
236b9cc3262SRyan Moeller int
2379ec7b004SRick Macklem nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
238af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
2399ec7b004SRick Macklem {
2409ec7b004SRick Macklem 	struct nfsvattr nva;
2419ec7b004SRick Macklem 	fhandle_t fh;
242a09001a8SRick Macklem 	int at_root = 0, error = 0, supports_nfsv4acls;
2439ec7b004SRick Macklem 	struct nfsreferral *refp;
24453f476caSRick Macklem 	nfsattrbit_t attrbits, tmpbits;
24507c0c166SRick Macklem 	struct mount *mp;
24607c0c166SRick Macklem 	struct vnode *tvp = NULL;
24707c0c166SRick Macklem 	struct vattr va;
24807c0c166SRick Macklem 	uint64_t mounted_on_fileno = 0;
24953f476caSRick Macklem 	accmode_t accmode;
250af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
2519ec7b004SRick Macklem 
2529ec7b004SRick Macklem 	if (nd->nd_repstat)
253a9285ae5SZack Kirsch 		goto out;
2549ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
2559ec7b004SRick Macklem 		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
2569ec7b004SRick Macklem 		if (error) {
2579ec7b004SRick Macklem 			vput(vp);
258a9285ae5SZack Kirsch 			goto out;
2599ec7b004SRick Macklem 		}
2609ec7b004SRick Macklem 
2619ec7b004SRick Macklem 		/*
2629ec7b004SRick Macklem 		 * Check for a referral.
2639ec7b004SRick Macklem 		 */
2649ec7b004SRick Macklem 		refp = nfsv4root_getreferral(vp, NULL, 0);
2659ec7b004SRick Macklem 		if (refp != NULL) {
2669ec7b004SRick Macklem 			(void) nfsrv_putreferralattr(nd, &attrbits, refp, 1,
2679ec7b004SRick Macklem 			    &nd->nd_repstat);
2689ec7b004SRick Macklem 			vput(vp);
269a9285ae5SZack Kirsch 			goto out;
2709ec7b004SRick Macklem 		}
27153f476caSRick Macklem 		if (nd->nd_repstat == 0) {
27253f476caSRick Macklem 			accmode = 0;
27353f476caSRick Macklem 			NFSSET_ATTRBIT(&tmpbits, &attrbits);
274d8a5961fSMarcelo Araujo 
275d8a5961fSMarcelo Araujo 			/*
276d8a5961fSMarcelo Araujo 			 * GETATTR with write-only attr time_access_set and time_modify_set
277d8a5961fSMarcelo Araujo 			 * should return NFS4ERR_INVAL.
278d8a5961fSMarcelo Araujo 			 */
279d8a5961fSMarcelo Araujo 			if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_TIMEACCESSSET) ||
280d8a5961fSMarcelo Araujo 					NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_TIMEMODIFYSET)){
281d8a5961fSMarcelo Araujo 				error = NFSERR_INVAL;
282d8a5961fSMarcelo Araujo 				vput(vp);
283d8a5961fSMarcelo Araujo 				goto out;
284d8a5961fSMarcelo Araujo 			}
28553f476caSRick Macklem 			if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_ACL)) {
28653f476caSRick Macklem 				NFSCLRBIT_ATTRBIT(&tmpbits, NFSATTRBIT_ACL);
28753f476caSRick Macklem 				accmode |= VREAD_ACL;
28853f476caSRick Macklem 			}
28953f476caSRick Macklem 			if (NFSNONZERO_ATTRBIT(&tmpbits))
29053f476caSRick Macklem 				accmode |= VREAD_ATTRIBUTES;
29153f476caSRick Macklem 			if (accmode != 0)
29253f476caSRick Macklem 				nd->nd_repstat = nfsvno_accchk(vp, accmode,
2938da45f2cSRick Macklem 				    nd->nd_cred, exp, p, NFSACCCHK_NOOVERRIDE,
2948da45f2cSRick Macklem 				    NFSACCCHK_VPISLOCKED, NULL);
2959ec7b004SRick Macklem 		}
29653f476caSRick Macklem 	}
2979ec7b004SRick Macklem 	if (!nd->nd_repstat)
29890d2dfabSRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, &attrbits);
2999ec7b004SRick Macklem 	if (!nd->nd_repstat) {
3009ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV4) {
3019ec7b004SRick Macklem 			if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_FILEHANDLE))
3029ec7b004SRick Macklem 				nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
3039ec7b004SRick Macklem 			if (!nd->nd_repstat)
3049ec7b004SRick Macklem 				nd->nd_repstat = nfsrv_checkgetattr(nd, vp,
30590d2dfabSRick Macklem 				    &nva, &attrbits, p);
30607c0c166SRick Macklem 			if (nd->nd_repstat == 0) {
307a09001a8SRick Macklem 				supports_nfsv4acls = nfs_supportsnfsv4acls(vp);
30807c0c166SRick Macklem 				mp = vp->v_mount;
30907c0c166SRick Macklem 				if (nfsrv_enable_crossmntpt != 0 &&
31007c0c166SRick Macklem 				    vp->v_type == VDIR &&
31107c0c166SRick Macklem 				    (vp->v_vflag & VV_ROOT) != 0 &&
31207c0c166SRick Macklem 				    vp != rootvnode) {
31307c0c166SRick Macklem 					tvp = mp->mnt_vnodecovered;
31407c0c166SRick Macklem 					VREF(tvp);
31507c0c166SRick Macklem 					at_root = 1;
31607c0c166SRick Macklem 				} else
31707c0c166SRick Macklem 					at_root = 0;
31807c0c166SRick Macklem 				vfs_ref(mp);
319b249ce48SMateusz Guzik 				NFSVOPUNLOCK(vp);
32007c0c166SRick Macklem 				if (at_root != 0) {
32107c0c166SRick Macklem 					if ((nd->nd_repstat =
32298f234f3SZack Kirsch 					     NFSVOPLOCK(tvp, LK_SHARED)) == 0) {
32307c0c166SRick Macklem 						nd->nd_repstat = VOP_GETATTR(
32407c0c166SRick Macklem 						    tvp, &va, nd->nd_cred);
32507c0c166SRick Macklem 						vput(tvp);
32607c0c166SRick Macklem 					} else
32707c0c166SRick Macklem 						vrele(tvp);
32807c0c166SRick Macklem 					if (nd->nd_repstat == 0)
32907c0c166SRick Macklem 						mounted_on_fileno = (uint64_t)
33007c0c166SRick Macklem 						    va.va_fileid;
33107c0c166SRick Macklem 					else
33207c0c166SRick Macklem 						at_root = 0;
33307c0c166SRick Macklem 				}
33407c0c166SRick Macklem 				if (nd->nd_repstat == 0)
33507c0c166SRick Macklem 					nd->nd_repstat = vfs_busy(mp, 0);
33607c0c166SRick Macklem 				vfs_rel(mp);
33707c0c166SRick Macklem 				if (nd->nd_repstat == 0) {
33807c0c166SRick Macklem 					(void)nfsvno_fillattr(nd, mp, vp, &nva,
33907c0c166SRick Macklem 					    &fh, 0, &attrbits, nd->nd_cred, p,
340a09001a8SRick Macklem 					    isdgram, 1, supports_nfsv4acls,
341a09001a8SRick Macklem 					    at_root, mounted_on_fileno);
34207c0c166SRick Macklem 					vfs_unbusy(mp);
34307c0c166SRick Macklem 				}
3449ec7b004SRick Macklem 				vrele(vp);
34507c0c166SRick Macklem 			} else
34607c0c166SRick Macklem 				vput(vp);
3479ec7b004SRick Macklem 		} else {
3489ec7b004SRick Macklem 			nfsrv_fillattr(nd, &nva);
3499ec7b004SRick Macklem 			vput(vp);
3509ec7b004SRick Macklem 		}
3519ec7b004SRick Macklem 	} else {
3529ec7b004SRick Macklem 		vput(vp);
3539ec7b004SRick Macklem 	}
354a9285ae5SZack Kirsch 
355a9285ae5SZack Kirsch out:
356a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
357a9285ae5SZack Kirsch 	return (error);
3589ec7b004SRick Macklem }
3599ec7b004SRick Macklem 
3609ec7b004SRick Macklem /*
3619ec7b004SRick Macklem  * nfs setattr service
3629ec7b004SRick Macklem  */
363b9cc3262SRyan Moeller int
3649ec7b004SRick Macklem nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
365af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
3669ec7b004SRick Macklem {
3679ec7b004SRick Macklem 	struct nfsvattr nva, nva2;
3689ec7b004SRick Macklem 	u_int32_t *tl;
3699ec7b004SRick Macklem 	int preat_ret = 1, postat_ret = 1, gcheck = 0, error = 0;
37090d2dfabSRick Macklem 	int gotproxystateid;
3719ec7b004SRick Macklem 	struct timespec guard = { 0, 0 };
3729ec7b004SRick Macklem 	nfsattrbit_t attrbits, retbits;
3739ec7b004SRick Macklem 	nfsv4stateid_t stateid;
3749ec7b004SRick Macklem 	NFSACL_T *aclp = NULL;
375af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
3769ec7b004SRick Macklem 
3779ec7b004SRick Macklem 	if (nd->nd_repstat) {
3789ec7b004SRick Macklem 		nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
379a9285ae5SZack Kirsch 		goto out;
3809ec7b004SRick Macklem 	}
3819ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
382c3e22f83SRick Macklem 	aclp = acl_alloc(M_WAITOK);
3839ec7b004SRick Macklem 	aclp->acl_cnt = 0;
3849ec7b004SRick Macklem #endif
38590d2dfabSRick Macklem 	gotproxystateid = 0;
3869ec7b004SRick Macklem 	NFSVNO_ATTRINIT(&nva);
3879ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
3889ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
3899ec7b004SRick Macklem 		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
39090d2dfabSRick Macklem 		stateid.other[0] = *tl++;
39190d2dfabSRick Macklem 		stateid.other[1] = *tl++;
39290d2dfabSRick Macklem 		stateid.other[2] = *tl;
39390d2dfabSRick Macklem 		if (stateid.other[0] == 0x55555555 &&
39490d2dfabSRick Macklem 		    stateid.other[1] == 0x55555555 &&
39590d2dfabSRick Macklem 		    stateid.other[2] == 0x55555555 &&
39690d2dfabSRick Macklem 		    stateid.seqid == 0xffffffff)
39790d2dfabSRick Macklem 			gotproxystateid = 1;
3989ec7b004SRick Macklem 	}
399d8a5961fSMarcelo Araujo 	error = nfsrv_sattr(nd, vp, &nva, &attrbits, aclp, p);
4009ec7b004SRick Macklem 	if (error)
4019ec7b004SRick Macklem 		goto nfsmout;
40290d2dfabSRick Macklem 
40390d2dfabSRick Macklem 	/* For NFSv4, only va_uid is used from nva2. */
40490d2dfabSRick Macklem 	NFSZERO_ATTRBIT(&retbits);
40590d2dfabSRick Macklem 	NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
40690d2dfabSRick Macklem 	preat_ret = nfsvno_getattr(vp, &nva2, nd, p, 1, &retbits);
4079ec7b004SRick Macklem 	if (!nd->nd_repstat)
4089ec7b004SRick Macklem 		nd->nd_repstat = preat_ret;
40990d2dfabSRick Macklem 
41090d2dfabSRick Macklem 	NFSZERO_ATTRBIT(&retbits);
4119ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
4129ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
4139ec7b004SRick Macklem 		gcheck = fxdr_unsigned(int, *tl);
4149ec7b004SRick Macklem 		if (gcheck) {
4159ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
4169ec7b004SRick Macklem 			fxdr_nfsv3time(tl, &guard);
4179ec7b004SRick Macklem 		}
4189ec7b004SRick Macklem 		if (!nd->nd_repstat && gcheck &&
4199ec7b004SRick Macklem 		    (nva2.na_ctime.tv_sec != guard.tv_sec ||
4209ec7b004SRick Macklem 		     nva2.na_ctime.tv_nsec != guard.tv_nsec))
4219ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_NOT_SYNC;
4229ec7b004SRick Macklem 		if (nd->nd_repstat) {
4239ec7b004SRick Macklem 			vput(vp);
4249ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
4259ec7b004SRick Macklem 			acl_free(aclp);
4269ec7b004SRick Macklem #endif
4279ec7b004SRick Macklem 			nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
428a9285ae5SZack Kirsch 			goto out;
4299ec7b004SRick Macklem 		}
4309ec7b004SRick Macklem 	} else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
4319ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
4329ec7b004SRick Macklem 
4339ec7b004SRick Macklem 	/*
4349ec7b004SRick Macklem 	 * Now that we have all the fields, lets do it.
4359ec7b004SRick Macklem 	 * If the size is being changed write access is required, otherwise
4369ec7b004SRick Macklem 	 * just check for a read only file system.
4379ec7b004SRick Macklem 	 */
4389ec7b004SRick Macklem 	if (!nd->nd_repstat) {
4399ec7b004SRick Macklem 		if (NFSVNO_NOTSETSIZE(&nva)) {
4409ec7b004SRick Macklem 			if (NFSVNO_EXRDONLY(exp) ||
4410586a129SRick Macklem 			    (vp->v_mount->mnt_flag & MNT_RDONLY))
4429ec7b004SRick Macklem 				nd->nd_repstat = EROFS;
4439ec7b004SRick Macklem 		} else {
4445d3fe02cSRick Macklem 			if (vp->v_type != VREG)
4459ec7b004SRick Macklem 				nd->nd_repstat = EINVAL;
4469ec7b004SRick Macklem 			else if (nva2.na_uid != nd->nd_cred->cr_uid ||
4479ec7b004SRick Macklem 			    NFSVNO_EXSTRICTACCESS(exp))
4489ec7b004SRick Macklem 				nd->nd_repstat = nfsvno_accchk(vp,
4498da45f2cSRick Macklem 				    VWRITE, nd->nd_cred, exp, p,
4508da45f2cSRick Macklem 				    NFSACCCHK_NOOVERRIDE,
4518da45f2cSRick Macklem 				    NFSACCCHK_VPISLOCKED, NULL);
4529ec7b004SRick Macklem 		}
4539ec7b004SRick Macklem 	}
45490d2dfabSRick Macklem 	/*
45590d2dfabSRick Macklem 	 * Proxy operations from the MDS are allowed via the all 0s special
45690d2dfabSRick Macklem 	 * stateid.
45790d2dfabSRick Macklem 	 */
45890d2dfabSRick Macklem 	if (nd->nd_repstat == 0 && (nd->nd_flag & ND_NFSV4) != 0 &&
45990d2dfabSRick Macklem 	    gotproxystateid == 0)
4609ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_checksetattr(vp, nd, &stateid,
4619ec7b004SRick Macklem 		    &nva, &attrbits, exp, p);
4629ec7b004SRick Macklem 
4639ec7b004SRick Macklem 	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
4649ec7b004SRick Macklem 	    /*
465bf312482SGordon Bergling 	     * For V4, try setting the attributes in sets, so that the
4669ec7b004SRick Macklem 	     * reply bitmap will be correct for an error case.
4679ec7b004SRick Macklem 	     */
4689ec7b004SRick Macklem 	    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER) ||
4699ec7b004SRick Macklem 		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP)) {
4709ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva2);
4719ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, uid, nva.na_uid);
4729ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, gid, nva.na_gid);
4739ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
4749ec7b004SRick Macklem 		    exp);
4759ec7b004SRick Macklem 		if (!nd->nd_repstat) {
4769ec7b004SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER))
4779ec7b004SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
4789ec7b004SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP))
4799ec7b004SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNERGROUP);
4809ec7b004SRick Macklem 		}
4819ec7b004SRick Macklem 	    }
4829ec7b004SRick Macklem 	    if (!nd->nd_repstat &&
4839ec7b004SRick Macklem 		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SIZE)) {
4849ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva2);
4859ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, size, nva.na_size);
4869ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
4879ec7b004SRick Macklem 		    exp);
4889ec7b004SRick Macklem 		if (!nd->nd_repstat)
4899ec7b004SRick Macklem 		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SIZE);
4909ec7b004SRick Macklem 	    }
4919ec7b004SRick Macklem 	    if (!nd->nd_repstat &&
4929ec7b004SRick Macklem 		(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET) ||
4939ec7b004SRick Macklem 		 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))) {
4949ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva2);
4959ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, atime, nva.na_atime);
4969ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, mtime, nva.na_mtime);
4979ec7b004SRick Macklem 		if (nva.na_vaflags & VA_UTIMES_NULL) {
4989ec7b004SRick Macklem 			nva2.na_vaflags |= VA_UTIMES_NULL;
4999ec7b004SRick Macklem 			NFSVNO_SETACTIVE(&nva2, vaflags);
5009ec7b004SRick Macklem 		}
5019ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
5029ec7b004SRick Macklem 		    exp);
5039ec7b004SRick Macklem 		if (!nd->nd_repstat) {
5049ec7b004SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET))
5059ec7b004SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEACCESSSET);
5069ec7b004SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))
5079ec7b004SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEMODIFYSET);
5089ec7b004SRick Macklem 		}
5099ec7b004SRick Macklem 	    }
5109ec7b004SRick Macklem 	    if (!nd->nd_repstat &&
511dd02d9d6SRick Macklem 		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMECREATE)) {
512dd02d9d6SRick Macklem 		NFSVNO_ATTRINIT(&nva2);
513dd02d9d6SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, btime, nva.na_btime);
514dd02d9d6SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
515dd02d9d6SRick Macklem 		    exp);
516dd02d9d6SRick Macklem 		if (!nd->nd_repstat)
517dd02d9d6SRick Macklem 		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMECREATE);
518dd02d9d6SRick Macklem 	    }
519dd02d9d6SRick Macklem 	    if (!nd->nd_repstat &&
520b4372164SRick Macklem 		(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE) ||
521b4372164SRick Macklem 		 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODESETMASKED))) {
5229ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva2);
5239ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, mode, nva.na_mode);
5249ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
5259ec7b004SRick Macklem 		    exp);
526b4372164SRick Macklem 		if (!nd->nd_repstat) {
527b4372164SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE))
5289ec7b004SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODE);
529b4372164SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODESETMASKED))
530b4372164SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODESETMASKED);
531b4372164SRick Macklem 		}
5329ec7b004SRick Macklem 	    }
5339ec7b004SRick Macklem 
5349ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
5359ec7b004SRick Macklem 	    if (!nd->nd_repstat && aclp->acl_cnt > 0 &&
5369ec7b004SRick Macklem 		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ACL)) {
5379ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_setacl(vp, aclp, nd->nd_cred, p);
5389ec7b004SRick Macklem 		if (!nd->nd_repstat)
5399ec7b004SRick Macklem 		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_ACL);
5409ec7b004SRick Macklem 	    }
5419ec7b004SRick Macklem #endif
5429ec7b004SRick Macklem 	} else if (!nd->nd_repstat) {
5439ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva, nd->nd_cred, p,
5449ec7b004SRick Macklem 		    exp);
5459ec7b004SRick Macklem 	}
5469ec7b004SRick Macklem 	if (nd->nd_flag & (ND_NFSV2 | ND_NFSV3)) {
54790d2dfabSRick Macklem 		postat_ret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
5489ec7b004SRick Macklem 		if (!nd->nd_repstat)
5499ec7b004SRick Macklem 			nd->nd_repstat = postat_ret;
5509ec7b004SRick Macklem 	}
5519ec7b004SRick Macklem 	vput(vp);
5529ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
5539ec7b004SRick Macklem 	acl_free(aclp);
5549ec7b004SRick Macklem #endif
5559ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
5569ec7b004SRick Macklem 		nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
5579ec7b004SRick Macklem 	else if (nd->nd_flag & ND_NFSV4)
5589ec7b004SRick Macklem 		(void) nfsrv_putattrbit(nd, &retbits);
5599ec7b004SRick Macklem 	else if (!nd->nd_repstat)
5609ec7b004SRick Macklem 		nfsrv_fillattr(nd, &nva);
561a9285ae5SZack Kirsch 
562a9285ae5SZack Kirsch out:
563a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
5649ec7b004SRick Macklem 	return (0);
5659ec7b004SRick Macklem nfsmout:
5669ec7b004SRick Macklem 	vput(vp);
5679ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
5689ec7b004SRick Macklem 	acl_free(aclp);
5699ec7b004SRick Macklem #endif
5709ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
5719ec7b004SRick Macklem 		/*
5729ec7b004SRick Macklem 		 * For all nd_repstat, the V4 reply includes a bitmap,
5739ec7b004SRick Macklem 		 * even NFSERR_BADXDR, which is what this will end up
5749ec7b004SRick Macklem 		 * returning.
5759ec7b004SRick Macklem 		 */
5769ec7b004SRick Macklem 		(void) nfsrv_putattrbit(nd, &retbits);
5779ec7b004SRick Macklem 	}
578a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
5799ec7b004SRick Macklem 	return (error);
5809ec7b004SRick Macklem }
5819ec7b004SRick Macklem 
5829ec7b004SRick Macklem /*
5839ec7b004SRick Macklem  * nfs lookup rpc
5849ec7b004SRick Macklem  * (Also performs lookup parent for v4)
5859ec7b004SRick Macklem  */
586b9cc3262SRyan Moeller int
5879ec7b004SRick Macklem nfsrvd_lookup(struct nfsrv_descript *nd, __unused int isdgram,
588af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
5899ec7b004SRick Macklem {
5909ec7b004SRick Macklem 	struct nameidata named;
5919ec7b004SRick Macklem 	vnode_t vp, dirp = NULL;
592a9285ae5SZack Kirsch 	int error = 0, dattr_ret = 1;
5939ec7b004SRick Macklem 	struct nfsvattr nva, dattr;
5949ec7b004SRick Macklem 	char *bufp;
5959ec7b004SRick Macklem 	u_long *hashp;
596af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
5979ec7b004SRick Macklem 
5989ec7b004SRick Macklem 	if (nd->nd_repstat) {
5999ec7b004SRick Macklem 		nfsrv_postopattr(nd, dattr_ret, &dattr);
600a9285ae5SZack Kirsch 		goto out;
6019ec7b004SRick Macklem 	}
6029ec7b004SRick Macklem 
6039ec7b004SRick Macklem 	/*
6049ec7b004SRick Macklem 	 * For some reason, if dp is a symlink, the error
6059ec7b004SRick Macklem 	 * returned is supposed to be NFSERR_SYMLINK and not NFSERR_NOTDIR.
6069ec7b004SRick Macklem 	 */
6079ec7b004SRick Macklem 	if (dp->v_type == VLNK && (nd->nd_flag & ND_NFSV4)) {
6089ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_SYMLINK;
6099ec7b004SRick Macklem 		vrele(dp);
610a9285ae5SZack Kirsch 		goto out;
6119ec7b004SRick Macklem 	}
6129ec7b004SRick Macklem 
6139ec7b004SRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
61465127e98SMateusz Guzik 	    LOCKLEAF);
6159ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
6169ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
6179ec7b004SRick Macklem 	if (error) {
6189ec7b004SRick Macklem 		vrele(dp);
6199ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
620a9285ae5SZack Kirsch 		goto out;
6219ec7b004SRick Macklem 	}
6229ec7b004SRick Macklem 	if (!nd->nd_repstat) {
623ef7d2c1fSMateusz Guzik 		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, &dirp);
6249ec7b004SRick Macklem 	} else {
6259ec7b004SRick Macklem 		vrele(dp);
6269ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
6279ec7b004SRick Macklem 	}
6289ec7b004SRick Macklem 	if (nd->nd_repstat) {
6299ec7b004SRick Macklem 		if (dirp) {
6309ec7b004SRick Macklem 			if (nd->nd_flag & ND_NFSV3)
63190d2dfabSRick Macklem 				dattr_ret = nfsvno_getattr(dirp, &dattr, nd, p,
63290d2dfabSRick Macklem 				    0, NULL);
6339ec7b004SRick Macklem 			vrele(dirp);
6349ec7b004SRick Macklem 		}
6359ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
6369ec7b004SRick Macklem 			nfsrv_postopattr(nd, dattr_ret, &dattr);
637a9285ae5SZack Kirsch 		goto out;
6389ec7b004SRick Macklem 	}
6399ec7b004SRick Macklem 	nfsvno_relpathbuf(&named);
6409ec7b004SRick Macklem 	vp = named.ni_vp;
64137b88c2dSRick Macklem 	if ((nd->nd_flag & ND_NFSV4) != 0 && !NFSVNO_EXPORTED(exp) &&
64237b88c2dSRick Macklem 	    vp->v_type != VDIR && vp->v_type != VLNK)
64337b88c2dSRick Macklem 		/*
64437b88c2dSRick Macklem 		 * Only allow lookup of VDIR and VLNK for traversal of
64537b88c2dSRick Macklem 		 * non-exported volumes during NFSv4 mounting.
64637b88c2dSRick Macklem 		 */
64737b88c2dSRick Macklem 		nd->nd_repstat = ENOENT;
6486fd6a0e3SRick Macklem 	if (nd->nd_repstat == 0) {
6499ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
6506fd6a0e3SRick Macklem 		/*
6516fd6a0e3SRick Macklem 		 * EOPNOTSUPP indicates the file system cannot be exported,
6526fd6a0e3SRick Macklem 		 * so just pretend the entry does not exist.
6536fd6a0e3SRick Macklem 		 */
6546fd6a0e3SRick Macklem 		if (nd->nd_repstat == EOPNOTSUPP)
6556fd6a0e3SRick Macklem 			nd->nd_repstat = ENOENT;
6566fd6a0e3SRick Macklem 	}
6579ec7b004SRick Macklem 	if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
65890d2dfabSRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
65981f78d99SRick Macklem 	if (vpp != NULL && nd->nd_repstat == 0)
6609ec7b004SRick Macklem 		*vpp = vp;
66181f78d99SRick Macklem 	else
6629ec7b004SRick Macklem 		vput(vp);
6639ec7b004SRick Macklem 	if (dirp) {
6649ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
66590d2dfabSRick Macklem 			dattr_ret = nfsvno_getattr(dirp, &dattr, nd, p, 0,
66690d2dfabSRick Macklem 			    NULL);
6679ec7b004SRick Macklem 		vrele(dirp);
6689ec7b004SRick Macklem 	}
6699ec7b004SRick Macklem 	if (nd->nd_repstat) {
6709ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
6719ec7b004SRick Macklem 			nfsrv_postopattr(nd, dattr_ret, &dattr);
672a9285ae5SZack Kirsch 		goto out;
6739ec7b004SRick Macklem 	}
6749ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
6759ec7b004SRick Macklem 		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
6769ec7b004SRick Macklem 		nfsrv_fillattr(nd, &nva);
6779ec7b004SRick Macklem 	} else if (nd->nd_flag & ND_NFSV3) {
6789ec7b004SRick Macklem 		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
6799ec7b004SRick Macklem 		nfsrv_postopattr(nd, 0, &nva);
6809ec7b004SRick Macklem 		nfsrv_postopattr(nd, dattr_ret, &dattr);
6819ec7b004SRick Macklem 	}
682a9285ae5SZack Kirsch 
683a9285ae5SZack Kirsch out:
684a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
685a9285ae5SZack Kirsch 	return (error);
6869ec7b004SRick Macklem }
6879ec7b004SRick Macklem 
6889ec7b004SRick Macklem /*
6899ec7b004SRick Macklem  * nfs readlink service
6909ec7b004SRick Macklem  */
691b9cc3262SRyan Moeller int
6929ec7b004SRick Macklem nfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram,
693af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
6949ec7b004SRick Macklem {
6959ec7b004SRick Macklem 	u_int32_t *tl;
696ae070589SRick Macklem 	struct mbuf *mp = NULL, *mpend = NULL;
6979ec7b004SRick Macklem 	int getret = 1, len;
6989ec7b004SRick Macklem 	struct nfsvattr nva;
699af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
700cb889ce6SRick Macklem 	uint16_t off;
7019ec7b004SRick Macklem 
7029ec7b004SRick Macklem 	if (nd->nd_repstat) {
7039ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &nva);
704a9285ae5SZack Kirsch 		goto out;
7059ec7b004SRick Macklem 	}
7065d3fe02cSRick Macklem 	if (vp->v_type != VLNK) {
7079ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV2)
7089ec7b004SRick Macklem 			nd->nd_repstat = ENXIO;
7099ec7b004SRick Macklem 		else
7109ec7b004SRick Macklem 			nd->nd_repstat = EINVAL;
7119ec7b004SRick Macklem 	}
712cb889ce6SRick Macklem 	if (nd->nd_repstat == 0) {
713cb889ce6SRick Macklem 		if ((nd->nd_flag & ND_EXTPG) != 0)
714cb889ce6SRick Macklem 			nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred,
715cb889ce6SRick Macklem 			    nd->nd_maxextsiz, p, &mp, &mpend, &len);
716cb889ce6SRick Macklem 		else
717cb889ce6SRick Macklem 			nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred,
718cb889ce6SRick Macklem 			    0, p, &mp, &mpend, &len);
719cb889ce6SRick Macklem 	}
7209ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
72190d2dfabSRick Macklem 		getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
7229ec7b004SRick Macklem 	vput(vp);
7239ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
7249ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &nva);
7259ec7b004SRick Macklem 	if (nd->nd_repstat)
726a9285ae5SZack Kirsch 		goto out;
7279ec7b004SRick Macklem 	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
7289ec7b004SRick Macklem 	*tl = txdr_unsigned(len);
72918a48314SRick Macklem 	if (mp != NULL) {
7309f6624d3SRick Macklem 		nd->nd_mb->m_next = mp;
7319ec7b004SRick Macklem 		nd->nd_mb = mpend;
732cb889ce6SRick Macklem 		if ((mpend->m_flags & M_EXTPG) != 0) {
733cb889ce6SRick Macklem 			nd->nd_bextpg = mpend->m_epg_npgs - 1;
734cb889ce6SRick Macklem 			nd->nd_bpos = (char *)(void *)
735cb889ce6SRick Macklem 			    PHYS_TO_DMAP(mpend->m_epg_pa[nd->nd_bextpg]);
736cb889ce6SRick Macklem 			off = (nd->nd_bextpg == 0) ? mpend->m_epg_1st_off : 0;
737cb889ce6SRick Macklem 			nd->nd_bpos += off + mpend->m_epg_last_len;
738cb889ce6SRick Macklem 			nd->nd_bextpgsiz = PAGE_SIZE - mpend->m_epg_last_len -
739cb889ce6SRick Macklem 			    off;
740cb889ce6SRick Macklem 		} else
741cb889ce6SRick Macklem 			nd->nd_bpos = mtod(mpend, char *) + mpend->m_len;
74218a48314SRick Macklem 	}
743a9285ae5SZack Kirsch 
744a9285ae5SZack Kirsch out:
745a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
7469ec7b004SRick Macklem 	return (0);
7479ec7b004SRick Macklem }
7489ec7b004SRick Macklem 
7499ec7b004SRick Macklem /*
7509ec7b004SRick Macklem  * nfs read service
7519ec7b004SRick Macklem  */
752b9cc3262SRyan Moeller int
7539ec7b004SRick Macklem nfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram,
754af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
7559ec7b004SRick Macklem {
7569ec7b004SRick Macklem 	u_int32_t *tl;
75790d2dfabSRick Macklem 	int error = 0, cnt, getret = 1, gotproxystateid, reqlen, eof = 0;
758ae070589SRick Macklem 	struct mbuf *m2, *m3;
7599ec7b004SRick Macklem 	struct nfsvattr nva;
7609ec7b004SRick Macklem 	off_t off = 0x0;
7619ec7b004SRick Macklem 	struct nfsstate st, *stp = &st;
7629ec7b004SRick Macklem 	struct nfslock lo, *lop = &lo;
7639ec7b004SRick Macklem 	nfsv4stateid_t stateid;
7649ec7b004SRick Macklem 	nfsquad_t clientid;
765af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
766cb889ce6SRick Macklem 	uint16_t poff;
7679ec7b004SRick Macklem 
7689ec7b004SRick Macklem 	if (nd->nd_repstat) {
7699ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &nva);
770a9285ae5SZack Kirsch 		goto out;
7719ec7b004SRick Macklem 	}
7729ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
7739ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
7749ec7b004SRick Macklem 		off = (off_t)fxdr_unsigned(u_int32_t, *tl++);
7759ec7b004SRick Macklem 		reqlen = fxdr_unsigned(int, *tl);
7769ec7b004SRick Macklem 	} else if (nd->nd_flag & ND_NFSV3) {
7779ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
7789ec7b004SRick Macklem 		off = fxdr_hyper(tl);
7799ec7b004SRick Macklem 		tl += 2;
7809ec7b004SRick Macklem 		reqlen = fxdr_unsigned(int, *tl);
7819ec7b004SRick Macklem 	} else {
7829ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3*NFSX_UNSIGNED);
7839ec7b004SRick Macklem 		reqlen = fxdr_unsigned(int, *(tl + 6));
7849ec7b004SRick Macklem 	}
7859ec7b004SRick Macklem 	if (reqlen > NFS_SRVMAXDATA(nd)) {
7869ec7b004SRick Macklem 		reqlen = NFS_SRVMAXDATA(nd);
7879ec7b004SRick Macklem 	} else if (reqlen < 0) {
7889ec7b004SRick Macklem 		error = EBADRPC;
7899ec7b004SRick Macklem 		goto nfsmout;
7909ec7b004SRick Macklem 	}
79190d2dfabSRick Macklem 	gotproxystateid = 0;
7929ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
7939ec7b004SRick Macklem 		stp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
7949ec7b004SRick Macklem 		lop->lo_flags = NFSLCK_READ;
7959ec7b004SRick Macklem 		stp->ls_ownerlen = 0;
7969ec7b004SRick Macklem 		stp->ls_op = NULL;
7979ec7b004SRick Macklem 		stp->ls_uid = nd->nd_cred->cr_uid;
7989ec7b004SRick Macklem 		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
7999ec7b004SRick Macklem 		clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
8009ec7b004SRick Macklem 		clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
801c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
802c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
803c59e4cc3SRick Macklem 				clientid.qval = nd->nd_clientid.qval;
804c59e4cc3SRick Macklem 			else if (nd->nd_clientid.qval != clientid.qval)
805c59e4cc3SRick Macklem 				printf("EEK1 multiple clids\n");
8069ec7b004SRick Macklem 		} else {
807c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
808c59e4cc3SRick Macklem 				printf("EEK! no clientid from session\n");
8099ec7b004SRick Macklem 			nd->nd_flag |= ND_IMPLIEDCLID;
8109ec7b004SRick Macklem 			nd->nd_clientid.qval = clientid.qval;
8119ec7b004SRick Macklem 		}
8129ec7b004SRick Macklem 		stp->ls_stateid.other[2] = *tl++;
81390d2dfabSRick Macklem 		/*
81490d2dfabSRick Macklem 		 * Don't allow the client to use a special stateid for a DS op.
81590d2dfabSRick Macklem 		 */
81690d2dfabSRick Macklem 		if ((nd->nd_flag & ND_DSSERVER) != 0 &&
81790d2dfabSRick Macklem 		    ((stp->ls_stateid.other[0] == 0x0 &&
81890d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0x0 &&
81990d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0x0) ||
82090d2dfabSRick Macklem 		    (stp->ls_stateid.other[0] == 0xffffffff &&
82190d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0xffffffff &&
82290d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0xffffffff) ||
82390d2dfabSRick Macklem 		    stp->ls_stateid.seqid != 0))
82490d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
82590d2dfabSRick Macklem 		/* However, allow the proxy stateid. */
82690d2dfabSRick Macklem 		if (stp->ls_stateid.seqid == 0xffffffff &&
82790d2dfabSRick Macklem 		    stp->ls_stateid.other[0] == 0x55555555 &&
82890d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0x55555555 &&
82990d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0x55555555)
83090d2dfabSRick Macklem 			gotproxystateid = 1;
8319ec7b004SRick Macklem 		off = fxdr_hyper(tl);
8329ec7b004SRick Macklem 		lop->lo_first = off;
8339ec7b004SRick Macklem 		tl += 2;
8349ec7b004SRick Macklem 		lop->lo_end = off + reqlen;
8359ec7b004SRick Macklem 		/*
8369ec7b004SRick Macklem 		 * Paranoia, just in case it wraps around.
8379ec7b004SRick Macklem 		 */
8389ec7b004SRick Macklem 		if (lop->lo_end < off)
8399ec7b004SRick Macklem 			lop->lo_end = NFS64BITSSET;
8409ec7b004SRick Macklem 	}
8415d3fe02cSRick Macklem 	if (vp->v_type != VREG) {
8429ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
8439ec7b004SRick Macklem 			nd->nd_repstat = EINVAL;
8449ec7b004SRick Macklem 		else
8455d3fe02cSRick Macklem 			nd->nd_repstat = (vp->v_type == VDIR) ? EISDIR :
8469ec7b004SRick Macklem 			    EINVAL;
8479ec7b004SRick Macklem 	}
84890d2dfabSRick Macklem 	getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
8499ec7b004SRick Macklem 	if (!nd->nd_repstat)
8509ec7b004SRick Macklem 		nd->nd_repstat = getret;
8519ec7b004SRick Macklem 	if (!nd->nd_repstat &&
8529ec7b004SRick Macklem 	    (nva.na_uid != nd->nd_cred->cr_uid ||
8539ec7b004SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp))) {
8548da45f2cSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
8559ec7b004SRick Macklem 		    nd->nd_cred, exp, p,
8568da45f2cSRick Macklem 		    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
8579ec7b004SRick Macklem 		if (nd->nd_repstat)
8588da45f2cSRick Macklem 			nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
8598da45f2cSRick Macklem 			    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
8608da45f2cSRick Macklem 			    NFSACCCHK_VPISLOCKED, NULL);
8619ec7b004SRick Macklem 	}
86290d2dfabSRick Macklem 	/*
86390d2dfabSRick Macklem 	 * DS reads are marked by ND_DSSERVER or use the proxy special
86490d2dfabSRick Macklem 	 * stateid.
86590d2dfabSRick Macklem 	 */
86690d2dfabSRick Macklem 	if (nd->nd_repstat == 0 && (nd->nd_flag & (ND_NFSV4 | ND_DSSERVER)) ==
86790d2dfabSRick Macklem 	    ND_NFSV4 && gotproxystateid == 0)
8689ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
8699ec7b004SRick Macklem 		    &stateid, exp, nd, p);
8709ec7b004SRick Macklem 	if (nd->nd_repstat) {
8719ec7b004SRick Macklem 		vput(vp);
8729ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
8739ec7b004SRick Macklem 			nfsrv_postopattr(nd, getret, &nva);
874a9285ae5SZack Kirsch 		goto out;
8759ec7b004SRick Macklem 	}
8769ec7b004SRick Macklem 	if (off >= nva.na_size) {
8779ec7b004SRick Macklem 		cnt = 0;
8789ec7b004SRick Macklem 		eof = 1;
8799ec7b004SRick Macklem 	} else if (reqlen == 0)
8809ec7b004SRick Macklem 		cnt = 0;
88106521fbbSZack Kirsch 	else if ((off + reqlen) >= nva.na_size) {
8829ec7b004SRick Macklem 		cnt = nva.na_size - off;
88306521fbbSZack Kirsch 		eof = 1;
88406521fbbSZack Kirsch 	} else
8859ec7b004SRick Macklem 		cnt = reqlen;
8869ec7b004SRick Macklem 	m3 = NULL;
8879ec7b004SRick Macklem 	if (cnt > 0) {
888cb889ce6SRick Macklem 		/*
889cb889ce6SRick Macklem 		 * If cnt > MCLBYTES and the reply will not be saved, use
890cb889ce6SRick Macklem 		 * ext_pgs mbufs for TLS.
891cb889ce6SRick Macklem 		 * For NFSv4.0, we do not know for sure if the reply will
892cb889ce6SRick Macklem 		 * be saved, so do not use ext_pgs mbufs for NFSv4.0.
893cb889ce6SRick Macklem 		 * Always use ext_pgs mbufs if ND_EXTPG is set.
894cb889ce6SRick Macklem 		 */
895cb889ce6SRick Macklem 		if ((nd->nd_flag & ND_EXTPG) != 0 || (cnt > MCLBYTES &&
896cb889ce6SRick Macklem 		    (nd->nd_flag & (ND_TLS | ND_SAVEREPLY)) == ND_TLS &&
897cb889ce6SRick Macklem 		    (nd->nd_flag & (ND_NFSV4 | ND_NFSV41)) != ND_NFSV4))
898cb889ce6SRick Macklem 			nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred,
899cb889ce6SRick Macklem 			    nd->nd_maxextsiz, p, &m3, &m2);
900cb889ce6SRick Macklem 		else
901cb889ce6SRick Macklem 			nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred,
902cb889ce6SRick Macklem 			    0, p, &m3, &m2);
9039ec7b004SRick Macklem 		if (!(nd->nd_flag & ND_NFSV4)) {
90490d2dfabSRick Macklem 			getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
9059ec7b004SRick Macklem 			if (!nd->nd_repstat)
9069ec7b004SRick Macklem 				nd->nd_repstat = getret;
9079ec7b004SRick Macklem 		}
9089ec7b004SRick Macklem 		if (nd->nd_repstat) {
9099ec7b004SRick Macklem 			vput(vp);
9109ec7b004SRick Macklem 			if (m3)
9119f6624d3SRick Macklem 				m_freem(m3);
9129ec7b004SRick Macklem 			if (nd->nd_flag & ND_NFSV3)
9139ec7b004SRick Macklem 				nfsrv_postopattr(nd, getret, &nva);
914a9285ae5SZack Kirsch 			goto out;
9159ec7b004SRick Macklem 		}
9169ec7b004SRick Macklem 	}
9179ec7b004SRick Macklem 	vput(vp);
9189ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
9199ec7b004SRick Macklem 		nfsrv_fillattr(nd, &nva);
9209ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
9219ec7b004SRick Macklem 	} else {
9229ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
9239ec7b004SRick Macklem 			nfsrv_postopattr(nd, getret, &nva);
9249ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
9259ec7b004SRick Macklem 			*tl++ = txdr_unsigned(cnt);
9269ec7b004SRick Macklem 		} else
9279ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
92806521fbbSZack Kirsch 		if (eof)
9299ec7b004SRick Macklem 			*tl++ = newnfs_true;
9309ec7b004SRick Macklem 		else
9319ec7b004SRick Macklem 			*tl++ = newnfs_false;
9329ec7b004SRick Macklem 	}
9339ec7b004SRick Macklem 	*tl = txdr_unsigned(cnt);
9349ec7b004SRick Macklem 	if (m3) {
9359f6624d3SRick Macklem 		nd->nd_mb->m_next = m3;
9369ec7b004SRick Macklem 		nd->nd_mb = m2;
937cb889ce6SRick Macklem 		if ((m2->m_flags & M_EXTPG) != 0) {
938cb889ce6SRick Macklem 			nd->nd_flag |= ND_EXTPG;
939cb889ce6SRick Macklem 			nd->nd_bextpg = m2->m_epg_npgs - 1;
940cb889ce6SRick Macklem 			nd->nd_bpos = (char *)(void *)
941cb889ce6SRick Macklem 			    PHYS_TO_DMAP(m2->m_epg_pa[nd->nd_bextpg]);
942cb889ce6SRick Macklem 			poff = (nd->nd_bextpg == 0) ? m2->m_epg_1st_off : 0;
943cb889ce6SRick Macklem 			nd->nd_bpos += poff + m2->m_epg_last_len;
944cb889ce6SRick Macklem 			nd->nd_bextpgsiz = PAGE_SIZE - m2->m_epg_last_len -
945cb889ce6SRick Macklem 			    poff;
946cb889ce6SRick Macklem 		} else
947cb889ce6SRick Macklem 			nd->nd_bpos = mtod(m2, char *) + m2->m_len;
9489ec7b004SRick Macklem 	}
949a9285ae5SZack Kirsch 
950a9285ae5SZack Kirsch out:
951a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
9529ec7b004SRick Macklem 	return (0);
9539ec7b004SRick Macklem nfsmout:
9549ec7b004SRick Macklem 	vput(vp);
955a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
9569ec7b004SRick Macklem 	return (error);
9579ec7b004SRick Macklem }
9589ec7b004SRick Macklem 
9599ec7b004SRick Macklem /*
9609ec7b004SRick Macklem  * nfs write service
9619ec7b004SRick Macklem  */
962b9cc3262SRyan Moeller int
9639ec7b004SRick Macklem nfsrvd_write(struct nfsrv_descript *nd, __unused int isdgram,
964af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
9659ec7b004SRick Macklem {
9669ec7b004SRick Macklem 	u_int32_t *tl;
9679ec7b004SRick Macklem 	struct nfsvattr nva, forat;
9689ec7b004SRick Macklem 	int aftat_ret = 1, retlen, len, error = 0, forat_ret = 1;
96990d2dfabSRick Macklem 	int gotproxystateid, stable = NFSWRITE_FILESYNC;
9709ec7b004SRick Macklem 	off_t off;
9719ec7b004SRick Macklem 	struct nfsstate st, *stp = &st;
9729ec7b004SRick Macklem 	struct nfslock lo, *lop = &lo;
9739ec7b004SRick Macklem 	nfsv4stateid_t stateid;
9749ec7b004SRick Macklem 	nfsquad_t clientid;
97590d2dfabSRick Macklem 	nfsattrbit_t attrbits;
976af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
9779ec7b004SRick Macklem 
9789ec7b004SRick Macklem 	if (nd->nd_repstat) {
9799ec7b004SRick Macklem 		nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
980a9285ae5SZack Kirsch 		goto out;
9819ec7b004SRick Macklem 	}
98290d2dfabSRick Macklem 	gotproxystateid = 0;
9839ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
9849ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
9859ec7b004SRick Macklem 		off = (off_t)fxdr_unsigned(u_int32_t, *++tl);
9869ec7b004SRick Macklem 		tl += 2;
9879ec7b004SRick Macklem 		retlen = len = fxdr_unsigned(int32_t, *tl);
9889ec7b004SRick Macklem 	} else if (nd->nd_flag & ND_NFSV3) {
9899ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
9909ec7b004SRick Macklem 		off = fxdr_hyper(tl);
9919ec7b004SRick Macklem 		tl += 3;
9929ec7b004SRick Macklem 		stable = fxdr_unsigned(int, *tl++);
9939ec7b004SRick Macklem 		retlen = len = fxdr_unsigned(int32_t, *tl);
9949ec7b004SRick Macklem 	} else {
9959ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 4 * NFSX_UNSIGNED);
9969ec7b004SRick Macklem 		stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
9979ec7b004SRick Macklem 		lop->lo_flags = NFSLCK_WRITE;
9989ec7b004SRick Macklem 		stp->ls_ownerlen = 0;
9999ec7b004SRick Macklem 		stp->ls_op = NULL;
10009ec7b004SRick Macklem 		stp->ls_uid = nd->nd_cred->cr_uid;
10019ec7b004SRick Macklem 		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
10029ec7b004SRick Macklem 		clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
10039ec7b004SRick Macklem 		clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
1004c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
1005c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
1006c59e4cc3SRick Macklem 				clientid.qval = nd->nd_clientid.qval;
1007c59e4cc3SRick Macklem 			else if (nd->nd_clientid.qval != clientid.qval)
1008c59e4cc3SRick Macklem 				printf("EEK2 multiple clids\n");
10099ec7b004SRick Macklem 		} else {
1010c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
1011c59e4cc3SRick Macklem 				printf("EEK! no clientid from session\n");
10129ec7b004SRick Macklem 			nd->nd_flag |= ND_IMPLIEDCLID;
10139ec7b004SRick Macklem 			nd->nd_clientid.qval = clientid.qval;
10149ec7b004SRick Macklem 		}
10159ec7b004SRick Macklem 		stp->ls_stateid.other[2] = *tl++;
101690d2dfabSRick Macklem 		/*
101790d2dfabSRick Macklem 		 * Don't allow the client to use a special stateid for a DS op.
101890d2dfabSRick Macklem 		 */
101990d2dfabSRick Macklem 		if ((nd->nd_flag & ND_DSSERVER) != 0 &&
102090d2dfabSRick Macklem 		    ((stp->ls_stateid.other[0] == 0x0 &&
102190d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0x0 &&
102290d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0x0) ||
102390d2dfabSRick Macklem 		    (stp->ls_stateid.other[0] == 0xffffffff &&
102490d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0xffffffff &&
102590d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0xffffffff) ||
102690d2dfabSRick Macklem 		    stp->ls_stateid.seqid != 0))
102790d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
102890d2dfabSRick Macklem 		/* However, allow the proxy stateid. */
102990d2dfabSRick Macklem 		if (stp->ls_stateid.seqid == 0xffffffff &&
103090d2dfabSRick Macklem 		    stp->ls_stateid.other[0] == 0x55555555 &&
103190d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0x55555555 &&
103290d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0x55555555)
103390d2dfabSRick Macklem 			gotproxystateid = 1;
10349ec7b004SRick Macklem 		off = fxdr_hyper(tl);
10359ec7b004SRick Macklem 		lop->lo_first = off;
10369ec7b004SRick Macklem 		tl += 2;
10379ec7b004SRick Macklem 		stable = fxdr_unsigned(int, *tl++);
10389ec7b004SRick Macklem 		retlen = len = fxdr_unsigned(int32_t, *tl);
10399ec7b004SRick Macklem 		lop->lo_end = off + len;
10409ec7b004SRick Macklem 		/*
10419ec7b004SRick Macklem 		 * Paranoia, just in case it wraps around, which shouldn't
10429ec7b004SRick Macklem 		 * ever happen anyhow.
10439ec7b004SRick Macklem 		 */
10449ec7b004SRick Macklem 		if (lop->lo_end < lop->lo_first)
10459ec7b004SRick Macklem 			lop->lo_end = NFS64BITSSET;
10469ec7b004SRick Macklem 	}
10479ec7b004SRick Macklem 
1048ee29e6f3SRick Macklem 	if (retlen > nfs_srvmaxio || retlen < 0)
10499ec7b004SRick Macklem 		nd->nd_repstat = EIO;
10505d3fe02cSRick Macklem 	if (vp->v_type != VREG && !nd->nd_repstat) {
10519ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
10529ec7b004SRick Macklem 			nd->nd_repstat = EINVAL;
10539ec7b004SRick Macklem 		else
10545d3fe02cSRick Macklem 			nd->nd_repstat = (vp->v_type == VDIR) ? EISDIR :
10559ec7b004SRick Macklem 			    EINVAL;
10569ec7b004SRick Macklem 	}
105790d2dfabSRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
105890d2dfabSRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
105990d2dfabSRick Macklem 	forat_ret = nfsvno_getattr(vp, &forat, nd, p, 1, &attrbits);
10609ec7b004SRick Macklem 	if (!nd->nd_repstat)
10619ec7b004SRick Macklem 		nd->nd_repstat = forat_ret;
10629ec7b004SRick Macklem 	if (!nd->nd_repstat &&
10639ec7b004SRick Macklem 	    (forat.na_uid != nd->nd_cred->cr_uid ||
10649ec7b004SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
10658da45f2cSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
10669ec7b004SRick Macklem 		    nd->nd_cred, exp, p,
10678da45f2cSRick Macklem 		    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
106890d2dfabSRick Macklem 	/*
106990d2dfabSRick Macklem 	 * DS reads are marked by ND_DSSERVER or use the proxy special
107090d2dfabSRick Macklem 	 * stateid.
107190d2dfabSRick Macklem 	 */
107290d2dfabSRick Macklem 	if (nd->nd_repstat == 0 && (nd->nd_flag & (ND_NFSV4 | ND_DSSERVER)) ==
107390d2dfabSRick Macklem 	    ND_NFSV4 && gotproxystateid == 0)
10749ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
10759ec7b004SRick Macklem 		    &stateid, exp, nd, p);
10769ec7b004SRick Macklem 	if (nd->nd_repstat) {
10779ec7b004SRick Macklem 		vput(vp);
10789ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
10799ec7b004SRick Macklem 			nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
1080a9285ae5SZack Kirsch 		goto out;
10819ec7b004SRick Macklem 	}
10829ec7b004SRick Macklem 
10839ec7b004SRick Macklem 	/*
10849ec7b004SRick Macklem 	 * For NFS Version 2, it is not obvious what a write of zero length
10859ec7b004SRick Macklem 	 * should do, but I might as well be consistent with Version 3,
10869ec7b004SRick Macklem 	 * which is to return ok so long as there are no permission problems.
10879ec7b004SRick Macklem 	 */
10889ec7b004SRick Macklem 	if (retlen > 0) {
1089c057a378SRick Macklem 		nd->nd_repstat = nfsvno_write(vp, off, retlen, &stable,
10909ec7b004SRick Macklem 		    nd->nd_md, nd->nd_dpos, nd->nd_cred, p);
10919ec7b004SRick Macklem 		error = nfsm_advance(nd, NFSM_RNDUP(retlen), -1);
10929ec7b004SRick Macklem 		if (error)
1093ce8d06feSRick Macklem 			goto nfsmout;
10949ec7b004SRick Macklem 	}
10959ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4)
10969ec7b004SRick Macklem 		aftat_ret = 0;
10979ec7b004SRick Macklem 	else
109890d2dfabSRick Macklem 		aftat_ret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
10999ec7b004SRick Macklem 	vput(vp);
11009ec7b004SRick Macklem 	if (!nd->nd_repstat)
11019ec7b004SRick Macklem 		nd->nd_repstat = aftat_ret;
11029ec7b004SRick Macklem 	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
11039ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
11049ec7b004SRick Macklem 			nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
11059ec7b004SRick Macklem 		if (nd->nd_repstat)
1106a9285ae5SZack Kirsch 			goto out;
11079ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
11089ec7b004SRick Macklem 		*tl++ = txdr_unsigned(retlen);
1109e4558aacSXin LI 		/*
1110e4558aacSXin LI 		 * If nfs_async is set, then pretend the write was FILESYNC.
1111e4558aacSXin LI 		 * Warning: Doing this violates RFC1813 and runs a risk
1112e4558aacSXin LI 		 * of data written by a client being lost when the server
1113e4558aacSXin LI 		 * crashes/reboots.
1114e4558aacSXin LI 		 */
1115e4558aacSXin LI 		if (stable == NFSWRITE_UNSTABLE && nfs_async == 0)
11169ec7b004SRick Macklem 			*tl++ = txdr_unsigned(stable);
11179ec7b004SRick Macklem 		else
11189ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSWRITE_FILESYNC);
11199ec7b004SRick Macklem 		/*
11209ec7b004SRick Macklem 		 * Actually, there is no need to txdr these fields,
11219ec7b004SRick Macklem 		 * but it may make the values more human readable,
11229ec7b004SRick Macklem 		 * for debugging purposes.
11239ec7b004SRick Macklem 		 */
11249ec7b004SRick Macklem 		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
11259ec7b004SRick Macklem 		*tl = txdr_unsigned(nfsboottime.tv_usec);
11269ec7b004SRick Macklem 	} else if (!nd->nd_repstat)
11279ec7b004SRick Macklem 		nfsrv_fillattr(nd, &nva);
1128a9285ae5SZack Kirsch 
1129a9285ae5SZack Kirsch out:
1130a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
11319ec7b004SRick Macklem 	return (0);
11329ec7b004SRick Macklem nfsmout:
11339ec7b004SRick Macklem 	vput(vp);
1134a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
11359ec7b004SRick Macklem 	return (error);
11369ec7b004SRick Macklem }
11379ec7b004SRick Macklem 
11389ec7b004SRick Macklem /*
11399ec7b004SRick Macklem  * nfs create service (creates regular files for V2 and V3. Spec. files for V2.)
11409ec7b004SRick Macklem  * now does a truncate to 0 length via. setattr if it already exists
11419ec7b004SRick Macklem  * The core creation routine has been extracted out into nfsrv_creatsub(),
11429ec7b004SRick Macklem  * so it can also be used by nfsrv_open() for V4.
11439ec7b004SRick Macklem  */
1144b9cc3262SRyan Moeller int
11459ec7b004SRick Macklem nfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram,
1146af444b18SEdward Tomasz Napierala     vnode_t dp, struct nfsexstuff *exp)
11479ec7b004SRick Macklem {
11489ec7b004SRick Macklem 	struct nfsvattr nva, dirfor, diraft;
11499ec7b004SRick Macklem 	struct nfsv2_sattr *sp;
11509ec7b004SRick Macklem 	struct nameidata named;
11519ec7b004SRick Macklem 	u_int32_t *tl;
11529ec7b004SRick Macklem 	int error = 0, tsize, dirfor_ret = 1, diraft_ret = 1;
11539ec7b004SRick Macklem 	int how = NFSCREATE_UNCHECKED, exclusive_flag = 0;
11549ec7b004SRick Macklem 	NFSDEV_T rdev = 0;
11559ec7b004SRick Macklem 	vnode_t vp = NULL, dirp = NULL;
11569ec7b004SRick Macklem 	fhandle_t fh;
11579ec7b004SRick Macklem 	char *bufp;
11589ec7b004SRick Macklem 	u_long *hashp;
11599ec7b004SRick Macklem 	enum vtype vtyp;
1160086f6e0cSRick Macklem 	int32_t cverf[2], tverf[2] = { 0, 0 };
1161af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
11629ec7b004SRick Macklem 
11639ec7b004SRick Macklem 	if (nd->nd_repstat) {
11649ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1165a9285ae5SZack Kirsch 		goto out;
11669ec7b004SRick Macklem 	}
11679ec7b004SRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
116865127e98SMateusz Guzik 	    LOCKPARENT | LOCKLEAF | NOCACHE);
11699ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
11709ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1171a9285ae5SZack Kirsch 	if (error)
1172a9285ae5SZack Kirsch 		goto nfsmout;
11739ec7b004SRick Macklem 	if (!nd->nd_repstat) {
11749ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva);
11759ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV2) {
11769ec7b004SRick Macklem 			NFSM_DISSECT(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
11779ec7b004SRick Macklem 			vtyp = IFTOVT(fxdr_unsigned(u_int32_t, sp->sa_mode));
11789ec7b004SRick Macklem 			if (vtyp == VNON)
11799ec7b004SRick Macklem 				vtyp = VREG;
11809ec7b004SRick Macklem 			NFSVNO_SETATTRVAL(&nva, type, vtyp);
11819ec7b004SRick Macklem 			NFSVNO_SETATTRVAL(&nva, mode,
11829ec7b004SRick Macklem 			    nfstov_mode(sp->sa_mode));
11839ec7b004SRick Macklem 			switch (nva.na_type) {
11849ec7b004SRick Macklem 			case VREG:
11859ec7b004SRick Macklem 				tsize = fxdr_unsigned(int32_t, sp->sa_size);
11869ec7b004SRick Macklem 				if (tsize != -1)
11879ec7b004SRick Macklem 					NFSVNO_SETATTRVAL(&nva, size,
11889ec7b004SRick Macklem 					    (u_quad_t)tsize);
11899ec7b004SRick Macklem 				break;
11909ec7b004SRick Macklem 			case VCHR:
11919ec7b004SRick Macklem 			case VBLK:
11929ec7b004SRick Macklem 			case VFIFO:
11939ec7b004SRick Macklem 				rdev = fxdr_unsigned(NFSDEV_T, sp->sa_size);
11949ec7b004SRick Macklem 				break;
11959ec7b004SRick Macklem 			default:
11969ec7b004SRick Macklem 				break;
119774b8d63dSPedro F. Giffuni 			}
11989ec7b004SRick Macklem 		} else {
11999ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
12009ec7b004SRick Macklem 			how = fxdr_unsigned(int, *tl);
12019ec7b004SRick Macklem 			switch (how) {
12029ec7b004SRick Macklem 			case NFSCREATE_GUARDED:
12039ec7b004SRick Macklem 			case NFSCREATE_UNCHECKED:
1204d8a5961fSMarcelo Araujo 				error = nfsrv_sattr(nd, NULL, &nva, NULL, NULL, p);
12059ec7b004SRick Macklem 				if (error)
12069ec7b004SRick Macklem 					goto nfsmout;
12079ec7b004SRick Macklem 				break;
12089ec7b004SRick Macklem 			case NFSCREATE_EXCLUSIVE:
1209086f6e0cSRick Macklem 				NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
1210086f6e0cSRick Macklem 				cverf[0] = *tl++;
1211086f6e0cSRick Macklem 				cverf[1] = *tl;
12129ec7b004SRick Macklem 				exclusive_flag = 1;
12139ec7b004SRick Macklem 				break;
121474b8d63dSPedro F. Giffuni 			}
12159ec7b004SRick Macklem 			NFSVNO_SETATTRVAL(&nva, type, VREG);
12169ec7b004SRick Macklem 		}
12179ec7b004SRick Macklem 	}
12189ec7b004SRick Macklem 	if (nd->nd_repstat) {
12199ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
12209ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
122190d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dp, &dirfor, nd, p, 1,
122290d2dfabSRick Macklem 			    NULL);
12239ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
12249ec7b004SRick Macklem 			    &diraft);
12259ec7b004SRick Macklem 		}
12269ec7b004SRick Macklem 		vput(dp);
1227a9285ae5SZack Kirsch 		goto out;
12289ec7b004SRick Macklem 	}
12299ec7b004SRick Macklem 
1230ef7d2c1fSMateusz Guzik 	nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, &dirp);
12319ec7b004SRick Macklem 	if (dirp) {
12329ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV2) {
12339ec7b004SRick Macklem 			vrele(dirp);
12349ec7b004SRick Macklem 			dirp = NULL;
12359ec7b004SRick Macklem 		} else {
123690d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
123790d2dfabSRick Macklem 			    NULL);
12389ec7b004SRick Macklem 		}
12399ec7b004SRick Macklem 	}
12409ec7b004SRick Macklem 	if (nd->nd_repstat) {
12419ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
12429ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
12439ec7b004SRick Macklem 			    &diraft);
12449ec7b004SRick Macklem 		if (dirp)
12459ec7b004SRick Macklem 			vrele(dirp);
1246a9285ae5SZack Kirsch 		goto out;
12479ec7b004SRick Macklem 	}
12489ec7b004SRick Macklem 
12499ec7b004SRick Macklem 	if (!(nd->nd_flag & ND_NFSV2)) {
12509ec7b004SRick Macklem 		switch (how) {
12519ec7b004SRick Macklem 		case NFSCREATE_GUARDED:
12529ec7b004SRick Macklem 			if (named.ni_vp)
12539ec7b004SRick Macklem 				nd->nd_repstat = EEXIST;
12549ec7b004SRick Macklem 			break;
12559ec7b004SRick Macklem 		case NFSCREATE_UNCHECKED:
12569ec7b004SRick Macklem 			break;
12579ec7b004SRick Macklem 		case NFSCREATE_EXCLUSIVE:
12589ec7b004SRick Macklem 			if (named.ni_vp == NULL)
12599ec7b004SRick Macklem 				NFSVNO_SETATTRVAL(&nva, mode, 0);
12609ec7b004SRick Macklem 			break;
126174b8d63dSPedro F. Giffuni 		}
12629ec7b004SRick Macklem 	}
12639ec7b004SRick Macklem 
12649ec7b004SRick Macklem 	/*
12659ec7b004SRick Macklem 	 * Iff doesn't exist, create it
12669ec7b004SRick Macklem 	 * otherwise just truncate to 0 length
12679ec7b004SRick Macklem 	 *   should I set the mode too ?
12689ec7b004SRick Macklem 	 */
12699ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_createsub(nd, &named, &vp, &nva,
1270127152feSEdward Tomasz Napierala 	    &exclusive_flag, cverf, rdev, exp);
12719ec7b004SRick Macklem 
12729ec7b004SRick Macklem 	if (!nd->nd_repstat) {
12739ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
12749ec7b004SRick Macklem 		if (!nd->nd_repstat)
127590d2dfabSRick Macklem 			nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1,
127690d2dfabSRick Macklem 			    NULL);
12779ec7b004SRick Macklem 		vput(vp);
1278086f6e0cSRick Macklem 		if (!nd->nd_repstat) {
1279086f6e0cSRick Macklem 			tverf[0] = nva.na_atime.tv_sec;
1280086f6e0cSRick Macklem 			tverf[1] = nva.na_atime.tv_nsec;
1281086f6e0cSRick Macklem 		}
12829ec7b004SRick Macklem 	}
12839ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
12849ec7b004SRick Macklem 		if (!nd->nd_repstat) {
12859ec7b004SRick Macklem 			(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
12869ec7b004SRick Macklem 			nfsrv_fillattr(nd, &nva);
12879ec7b004SRick Macklem 		}
12889ec7b004SRick Macklem 	} else {
1289086f6e0cSRick Macklem 		if (exclusive_flag && !nd->nd_repstat && (cverf[0] != tverf[0]
1290086f6e0cSRick Macklem 		    || cverf[1] != tverf[1]))
12919ec7b004SRick Macklem 			nd->nd_repstat = EEXIST;
129290d2dfabSRick Macklem 		diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
12939ec7b004SRick Macklem 		vrele(dirp);
12949ec7b004SRick Macklem 		if (!nd->nd_repstat) {
12959ec7b004SRick Macklem 			(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 1);
12969ec7b004SRick Macklem 			nfsrv_postopattr(nd, 0, &nva);
12979ec7b004SRick Macklem 		}
12989ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
12999ec7b004SRick Macklem 	}
1300a9285ae5SZack Kirsch 
1301a9285ae5SZack Kirsch out:
1302a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
13039ec7b004SRick Macklem 	return (0);
13049ec7b004SRick Macklem nfsmout:
13059ec7b004SRick Macklem 	vput(dp);
13069ec7b004SRick Macklem 	nfsvno_relpathbuf(&named);
1307a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
13089ec7b004SRick Macklem 	return (error);
13099ec7b004SRick Macklem }
13109ec7b004SRick Macklem 
13119ec7b004SRick Macklem /*
13129ec7b004SRick Macklem  * nfs v3 mknod service (and v4 create)
13139ec7b004SRick Macklem  */
1314b9cc3262SRyan Moeller int
13159ec7b004SRick Macklem nfsrvd_mknod(struct nfsrv_descript *nd, __unused int isdgram,
1316af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
13179ec7b004SRick Macklem {
13189ec7b004SRick Macklem 	struct nfsvattr nva, dirfor, diraft;
13199ec7b004SRick Macklem 	u_int32_t *tl;
13209ec7b004SRick Macklem 	struct nameidata named;
13219ec7b004SRick Macklem 	int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
13229ec7b004SRick Macklem 	u_int32_t major, minor;
13239ec7b004SRick Macklem 	enum vtype vtyp = VNON;
13249ec7b004SRick Macklem 	nfstype nfs4type = NFNON;
13259ec7b004SRick Macklem 	vnode_t vp, dirp = NULL;
13269ec7b004SRick Macklem 	nfsattrbit_t attrbits;
13279ec7b004SRick Macklem 	char *bufp = NULL, *pathcp = NULL;
13289ec7b004SRick Macklem 	u_long *hashp, cnflags;
13299ec7b004SRick Macklem 	NFSACL_T *aclp = NULL;
1330af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
13319ec7b004SRick Macklem 
13329ec7b004SRick Macklem 	NFSVNO_ATTRINIT(&nva);
133365127e98SMateusz Guzik 	cnflags = LOCKPARENT;
13349ec7b004SRick Macklem 	if (nd->nd_repstat) {
13359ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1336a9285ae5SZack Kirsch 		goto out;
13379ec7b004SRick Macklem 	}
13389ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
1339c3e22f83SRick Macklem 	aclp = acl_alloc(M_WAITOK);
13409ec7b004SRick Macklem 	aclp->acl_cnt = 0;
13419ec7b004SRick Macklem #endif
13429ec7b004SRick Macklem 
13439ec7b004SRick Macklem 	/*
13449ec7b004SRick Macklem 	 * For V4, the creation stuff is here, Yuck!
13459ec7b004SRick Macklem 	 */
13469ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
13479ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
13489ec7b004SRick Macklem 		vtyp = nfsv34tov_type(*tl);
13499ec7b004SRick Macklem 		nfs4type = fxdr_unsigned(nfstype, *tl);
13509ec7b004SRick Macklem 		switch (nfs4type) {
13519ec7b004SRick Macklem 		case NFLNK:
13529ec7b004SRick Macklem 			error = nfsvno_getsymlink(nd, &nva, p, &pathcp,
13539ec7b004SRick Macklem 			    &pathlen);
1354a9285ae5SZack Kirsch 			if (error)
1355a9285ae5SZack Kirsch 				goto nfsmout;
13569ec7b004SRick Macklem 			break;
13579ec7b004SRick Macklem 		case NFCHR:
13589ec7b004SRick Macklem 		case NFBLK:
13599ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
13609ec7b004SRick Macklem 			major = fxdr_unsigned(u_int32_t, *tl++);
13619ec7b004SRick Macklem 			minor = fxdr_unsigned(u_int32_t, *tl);
13629ec7b004SRick Macklem 			nva.na_rdev = NFSMAKEDEV(major, minor);
13639ec7b004SRick Macklem 			break;
13649ec7b004SRick Macklem 		case NFSOCK:
13659ec7b004SRick Macklem 		case NFFIFO:
13669ec7b004SRick Macklem 			break;
13679ec7b004SRick Macklem 		case NFDIR:
13685b5b7e2cSMateusz Guzik 			cnflags = LOCKPARENT;
13699ec7b004SRick Macklem 			break;
13709ec7b004SRick Macklem 		default:
13719ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_BADTYPE;
13729ec7b004SRick Macklem 			vrele(dp);
13739ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
13749ec7b004SRick Macklem 			acl_free(aclp);
13759ec7b004SRick Macklem #endif
1376a9285ae5SZack Kirsch 			goto out;
1377a9285ae5SZack Kirsch 		}
13789ec7b004SRick Macklem 	}
13796c21f6edSKonstantin Belousov 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, cnflags | NOCACHE);
13809ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
13819ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1382a9285ae5SZack Kirsch 	if (error)
1383a9285ae5SZack Kirsch 		goto nfsmout;
13849ec7b004SRick Macklem 	if (!nd->nd_repstat) {
13859ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
13869ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
13879ec7b004SRick Macklem 			vtyp = nfsv34tov_type(*tl);
13889ec7b004SRick Macklem 		}
1389d8a5961fSMarcelo Araujo 		error = nfsrv_sattr(nd, NULL, &nva, &attrbits, aclp, p);
1390a9285ae5SZack Kirsch 		if (error)
1391a9285ae5SZack Kirsch 			goto nfsmout;
13929ec7b004SRick Macklem 		nva.na_type = vtyp;
13939ec7b004SRick Macklem 		if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV3) &&
13949ec7b004SRick Macklem 		    (vtyp == VCHR || vtyp == VBLK)) {
13959ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
13969ec7b004SRick Macklem 			major = fxdr_unsigned(u_int32_t, *tl++);
13979ec7b004SRick Macklem 			minor = fxdr_unsigned(u_int32_t, *tl);
13989ec7b004SRick Macklem 			nva.na_rdev = NFSMAKEDEV(major, minor);
13999ec7b004SRick Macklem 		}
14009ec7b004SRick Macklem 	}
14019ec7b004SRick Macklem 
140290d2dfabSRick Macklem 	dirfor_ret = nfsvno_getattr(dp, &dirfor, nd, p, 0, NULL);
14039ec7b004SRick Macklem 	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
14049ec7b004SRick Macklem 		if (!dirfor_ret && NFSVNO_ISSETGID(&nva) &&
14059ec7b004SRick Macklem 		    dirfor.na_gid == nva.na_gid)
14069ec7b004SRick Macklem 			NFSVNO_UNSET(&nva, gid);
14079ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
14089ec7b004SRick Macklem 	}
14099ec7b004SRick Macklem 	if (nd->nd_repstat) {
14109ec7b004SRick Macklem 		vrele(dp);
14119ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
14129ec7b004SRick Macklem 		acl_free(aclp);
14139ec7b004SRick Macklem #endif
14149ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
14159ec7b004SRick Macklem 		if (pathcp)
1416222daa42SConrad Meyer 			free(pathcp, M_TEMP);
14179ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
14189ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
14199ec7b004SRick Macklem 			    &diraft);
1420a9285ae5SZack Kirsch 		goto out;
14219ec7b004SRick Macklem 	}
14229ec7b004SRick Macklem 
14239ec7b004SRick Macklem 	/*
14249ec7b004SRick Macklem 	 * Yuck! For V4, mkdir and link are here and some V4 clients don't fill
14259ec7b004SRick Macklem 	 * in va_mode, so we'll have to set a default here.
14269ec7b004SRick Macklem 	 */
14279ec7b004SRick Macklem 	if (NFSVNO_NOTSETMODE(&nva)) {
14289ec7b004SRick Macklem 		if (vtyp == VLNK)
14299ec7b004SRick Macklem 			nva.na_mode = 0755;
14309ec7b004SRick Macklem 		else
14319ec7b004SRick Macklem 			nva.na_mode = 0400;
14329ec7b004SRick Macklem 	}
14339ec7b004SRick Macklem 
14349ec7b004SRick Macklem 	if (vtyp == VDIR)
14359ec7b004SRick Macklem 		named.ni_cnd.cn_flags |= WILLBEDIR;
1436ef7d2c1fSMateusz Guzik 	nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, &dirp);
14379ec7b004SRick Macklem 	if (nd->nd_repstat) {
14389ec7b004SRick Macklem 		if (dirp) {
14399ec7b004SRick Macklem 			if (nd->nd_flag & ND_NFSV3)
144090d2dfabSRick Macklem 				dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd,
144190d2dfabSRick Macklem 				    p, 0, NULL);
14429ec7b004SRick Macklem 			vrele(dirp);
14439ec7b004SRick Macklem 		}
14449ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
14459ec7b004SRick Macklem 		acl_free(aclp);
14469ec7b004SRick Macklem #endif
14479ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
14489ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
14499ec7b004SRick Macklem 			    &diraft);
1450a9285ae5SZack Kirsch 		goto out;
14519ec7b004SRick Macklem 	}
14529ec7b004SRick Macklem 	if (dirp)
145390d2dfabSRick Macklem 		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL);
14549ec7b004SRick Macklem 
14559ec7b004SRick Macklem 	if ((nd->nd_flag & ND_NFSV4) && (vtyp == VDIR || vtyp == VLNK)) {
14569ec7b004SRick Macklem 		if (vtyp == VDIR) {
14579ec7b004SRick Macklem 			nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp,
14589ec7b004SRick Macklem 			    &dirfor, &diraft, &diraft_ret, &attrbits, aclp, p,
14599ec7b004SRick Macklem 			    exp);
14609ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
14619ec7b004SRick Macklem 			acl_free(aclp);
14629ec7b004SRick Macklem #endif
1463a9285ae5SZack Kirsch 			goto out;
14649ec7b004SRick Macklem 		} else if (vtyp == VLNK) {
14659ec7b004SRick Macklem 			nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
14669ec7b004SRick Macklem 			    &dirfor, &diraft, &diraft_ret, &attrbits,
14679ec7b004SRick Macklem 			    aclp, p, exp, pathcp, pathlen);
14689ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
14699ec7b004SRick Macklem 			acl_free(aclp);
14709ec7b004SRick Macklem #endif
1471222daa42SConrad Meyer 			free(pathcp, M_TEMP);
1472a9285ae5SZack Kirsch 			goto out;
14739ec7b004SRick Macklem 		}
14749ec7b004SRick Macklem 	}
14759ec7b004SRick Macklem 
14769ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_mknod(&named, &nva, nd->nd_cred, p);
14779ec7b004SRick Macklem 	if (!nd->nd_repstat) {
14789ec7b004SRick Macklem 		vp = named.ni_vp;
14799ec7b004SRick Macklem 		nfsrv_fixattr(nd, vp, &nva, aclp, p, &attrbits, exp);
14809ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
14819ec7b004SRick Macklem 		if ((nd->nd_flag & ND_NFSV3) && !nd->nd_repstat)
148290d2dfabSRick Macklem 			nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1,
148390d2dfabSRick Macklem 			    NULL);
148481f78d99SRick Macklem 		if (vpp != NULL && nd->nd_repstat == 0) {
1485b249ce48SMateusz Guzik 			NFSVOPUNLOCK(vp);
14869ec7b004SRick Macklem 			*vpp = vp;
148781f78d99SRick Macklem 		} else
14889ec7b004SRick Macklem 			vput(vp);
14899ec7b004SRick Macklem 	}
14909ec7b004SRick Macklem 
149190d2dfabSRick Macklem 	diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
14929ec7b004SRick Macklem 	vrele(dirp);
14939ec7b004SRick Macklem 	if (!nd->nd_repstat) {
14949ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
14959ec7b004SRick Macklem 			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
14969ec7b004SRick Macklem 			nfsrv_postopattr(nd, 0, &nva);
14979ec7b004SRick Macklem 		} else {
14989ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
14999ec7b004SRick Macklem 			*tl++ = newnfs_false;
15009ec7b004SRick Macklem 			txdr_hyper(dirfor.na_filerev, tl);
15019ec7b004SRick Macklem 			tl += 2;
15029ec7b004SRick Macklem 			txdr_hyper(diraft.na_filerev, tl);
15039ec7b004SRick Macklem 			(void) nfsrv_putattrbit(nd, &attrbits);
15049ec7b004SRick Macklem 		}
15059ec7b004SRick Macklem 	}
15069ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
15079ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
15089ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
15099ec7b004SRick Macklem 	acl_free(aclp);
15109ec7b004SRick Macklem #endif
1511a9285ae5SZack Kirsch 
1512a9285ae5SZack Kirsch out:
1513a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
15149ec7b004SRick Macklem 	return (0);
15159ec7b004SRick Macklem nfsmout:
15169ec7b004SRick Macklem 	vrele(dp);
15179ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
15189ec7b004SRick Macklem 	acl_free(aclp);
15199ec7b004SRick Macklem #endif
15209ec7b004SRick Macklem 	if (bufp)
15219ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
15229ec7b004SRick Macklem 	if (pathcp)
1523222daa42SConrad Meyer 		free(pathcp, M_TEMP);
1524a9285ae5SZack Kirsch 
1525a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
15269ec7b004SRick Macklem 	return (error);
15279ec7b004SRick Macklem }
15289ec7b004SRick Macklem 
15299ec7b004SRick Macklem /*
15309ec7b004SRick Macklem  * nfs remove service
15319ec7b004SRick Macklem  */
1532b9cc3262SRyan Moeller int
15339ec7b004SRick Macklem nfsrvd_remove(struct nfsrv_descript *nd, __unused int isdgram,
1534af444b18SEdward Tomasz Napierala     vnode_t dp, struct nfsexstuff *exp)
15359ec7b004SRick Macklem {
15369ec7b004SRick Macklem 	struct nameidata named;
15379ec7b004SRick Macklem 	u_int32_t *tl;
1538a9285ae5SZack Kirsch 	int error = 0, dirfor_ret = 1, diraft_ret = 1;
15399ec7b004SRick Macklem 	vnode_t dirp = NULL;
15409ec7b004SRick Macklem 	struct nfsvattr dirfor, diraft;
15419ec7b004SRick Macklem 	char *bufp;
15429ec7b004SRick Macklem 	u_long *hashp;
1543af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
15449ec7b004SRick Macklem 
15459ec7b004SRick Macklem 	if (nd->nd_repstat) {
15469ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1547a9285ae5SZack Kirsch 		goto out;
15489ec7b004SRick Macklem 	}
15499ec7b004SRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, DELETE,
15509ec7b004SRick Macklem 	    LOCKPARENT | LOCKLEAF);
15519ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
15529ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
15539ec7b004SRick Macklem 	if (error) {
15549ec7b004SRick Macklem 		vput(dp);
15559ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
1556a9285ae5SZack Kirsch 		goto out;
15579ec7b004SRick Macklem 	}
15589ec7b004SRick Macklem 	if (!nd->nd_repstat) {
1559ef7d2c1fSMateusz Guzik 		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, &dirp);
15609ec7b004SRick Macklem 	} else {
15619ec7b004SRick Macklem 		vput(dp);
15629ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
15639ec7b004SRick Macklem 	}
15649ec7b004SRick Macklem 	if (dirp) {
15659ec7b004SRick Macklem 		if (!(nd->nd_flag & ND_NFSV2)) {
156690d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
156790d2dfabSRick Macklem 			    NULL);
15689ec7b004SRick Macklem 		} else {
15699ec7b004SRick Macklem 			vrele(dirp);
15709ec7b004SRick Macklem 			dirp = NULL;
15719ec7b004SRick Macklem 		}
15729ec7b004SRick Macklem 	}
15739ec7b004SRick Macklem 	if (!nd->nd_repstat) {
15749ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV4) {
15755d3fe02cSRick Macklem 			if (named.ni_vp->v_type == VDIR)
15769ec7b004SRick Macklem 				nd->nd_repstat = nfsvno_rmdirsub(&named, 1,
15779ec7b004SRick Macklem 				    nd->nd_cred, p, exp);
15789ec7b004SRick Macklem 			else
15799ec7b004SRick Macklem 				nd->nd_repstat = nfsvno_removesub(&named, 1,
15809ec7b004SRick Macklem 				    nd->nd_cred, p, exp);
15819ec7b004SRick Macklem 		} else if (nd->nd_procnum == NFSPROC_RMDIR) {
15829ec7b004SRick Macklem 			nd->nd_repstat = nfsvno_rmdirsub(&named, 0,
15839ec7b004SRick Macklem 			    nd->nd_cred, p, exp);
15849ec7b004SRick Macklem 		} else {
15859ec7b004SRick Macklem 			nd->nd_repstat = nfsvno_removesub(&named, 0,
15869ec7b004SRick Macklem 			    nd->nd_cred, p, exp);
15879ec7b004SRick Macklem 		}
15889ec7b004SRick Macklem 	}
15899ec7b004SRick Macklem 	if (!(nd->nd_flag & ND_NFSV2)) {
15909ec7b004SRick Macklem 		if (dirp) {
159190d2dfabSRick Macklem 			diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0,
159290d2dfabSRick Macklem 			    NULL);
15939ec7b004SRick Macklem 			vrele(dirp);
15949ec7b004SRick Macklem 		}
15959ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
15969ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
15979ec7b004SRick Macklem 			    &diraft);
15989ec7b004SRick Macklem 		} else if (!nd->nd_repstat) {
15999ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
16009ec7b004SRick Macklem 			*tl++ = newnfs_false;
16019ec7b004SRick Macklem 			txdr_hyper(dirfor.na_filerev, tl);
16029ec7b004SRick Macklem 			tl += 2;
16039ec7b004SRick Macklem 			txdr_hyper(diraft.na_filerev, tl);
16049ec7b004SRick Macklem 		}
16059ec7b004SRick Macklem 	}
1606a9285ae5SZack Kirsch 
1607a9285ae5SZack Kirsch out:
1608a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
1609a9285ae5SZack Kirsch 	return (error);
16109ec7b004SRick Macklem }
16119ec7b004SRick Macklem 
16129ec7b004SRick Macklem /*
16139ec7b004SRick Macklem  * nfs rename service
16149ec7b004SRick Macklem  */
1615b9cc3262SRyan Moeller int
16169ec7b004SRick Macklem nfsrvd_rename(struct nfsrv_descript *nd, int isdgram,
1617af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t todp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
16189ec7b004SRick Macklem {
16199ec7b004SRick Macklem 	u_int32_t *tl;
1620a9285ae5SZack Kirsch 	int error = 0, fdirfor_ret = 1, fdiraft_ret = 1;
16219ec7b004SRick Macklem 	int tdirfor_ret = 1, tdiraft_ret = 1;
16229ec7b004SRick Macklem 	struct nameidata fromnd, tond;
16239ec7b004SRick Macklem 	vnode_t fdirp = NULL, tdirp = NULL, tdp = NULL;
16249ec7b004SRick Macklem 	struct nfsvattr fdirfor, fdiraft, tdirfor, tdiraft;
16259ec7b004SRick Macklem 	struct nfsexstuff tnes;
16269ec7b004SRick Macklem 	struct nfsrvfh tfh;
16279ec7b004SRick Macklem 	char *bufp, *tbufp = NULL;
16289ec7b004SRick Macklem 	u_long *hashp;
16296b3dfc6aSRick Macklem 	fhandle_t fh;
1630af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
16319ec7b004SRick Macklem 
16329ec7b004SRick Macklem 	if (nd->nd_repstat) {
16339ec7b004SRick Macklem 		nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
16349ec7b004SRick Macklem 		nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
1635a9285ae5SZack Kirsch 		goto out;
16369ec7b004SRick Macklem 	}
16379ec7b004SRick Macklem 	if (!(nd->nd_flag & ND_NFSV2))
163890d2dfabSRick Macklem 		fdirfor_ret = nfsvno_getattr(dp, &fdirfor, nd, p, 1, NULL);
16399ec7b004SRick Macklem 	tond.ni_cnd.cn_nameiop = 0;
16409ec7b004SRick Macklem 	tond.ni_startdir = NULL;
164165127e98SMateusz Guzik 	NFSNAMEICNDSET(&fromnd.ni_cnd, nd->nd_cred, DELETE, WANTPARENT);
16429ec7b004SRick Macklem 	nfsvno_setpathbuf(&fromnd, &bufp, &hashp);
16439ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &fromnd.ni_pathlen);
16449ec7b004SRick Macklem 	if (error) {
16459ec7b004SRick Macklem 		vput(dp);
16469ec7b004SRick Macklem 		if (todp)
16479ec7b004SRick Macklem 			vrele(todp);
16489ec7b004SRick Macklem 		nfsvno_relpathbuf(&fromnd);
1649a9285ae5SZack Kirsch 		goto out;
16509ec7b004SRick Macklem 	}
165125bfde79SXin LI 	/*
165225bfde79SXin LI 	 * Unlock dp in this code section, so it is unlocked before
165325bfde79SXin LI 	 * tdp gets locked. This avoids a potential LOR if tdp is the
165425bfde79SXin LI 	 * parent directory of dp.
165525bfde79SXin LI 	 */
16569ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
16579ec7b004SRick Macklem 		tdp = todp;
16589ec7b004SRick Macklem 		tnes = *toexp;
165925bfde79SXin LI 		if (dp != tdp) {
1660b249ce48SMateusz Guzik 			NFSVOPUNLOCK(dp);
166190d2dfabSRick Macklem 			/* Might lock tdp. */
166290d2dfabSRick Macklem 			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd, p, 0,
166390d2dfabSRick Macklem 			    NULL);
166425bfde79SXin LI 		} else {
166590d2dfabSRick Macklem 			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd, p, 1,
166690d2dfabSRick Macklem 			    NULL);
1667b249ce48SMateusz Guzik 			NFSVOPUNLOCK(dp);
166825bfde79SXin LI 		}
16699ec7b004SRick Macklem 	} else {
16706b3dfc6aSRick Macklem 		tfh.nfsrvfh_len = 0;
16719ec7b004SRick Macklem 		error = nfsrv_mtofh(nd, &tfh);
16726b3dfc6aSRick Macklem 		if (error == 0)
16736b3dfc6aSRick Macklem 			error = nfsvno_getfh(dp, &fh, p);
16749ec7b004SRick Macklem 		if (error) {
16759ec7b004SRick Macklem 			vput(dp);
16769ec7b004SRick Macklem 			/* todp is always NULL except NFSv4 */
16779ec7b004SRick Macklem 			nfsvno_relpathbuf(&fromnd);
1678a9285ae5SZack Kirsch 			goto out;
16799ec7b004SRick Macklem 		}
16806b3dfc6aSRick Macklem 
16816b3dfc6aSRick Macklem 		/* If this is the same file handle, just VREF() the vnode. */
16826b3dfc6aSRick Macklem 		if (tfh.nfsrvfh_len == NFSX_MYFH &&
16836b3dfc6aSRick Macklem 		    !NFSBCMP(tfh.nfsrvfh_data, &fh, NFSX_MYFH)) {
16846b3dfc6aSRick Macklem 			VREF(dp);
16856b3dfc6aSRick Macklem 			tdp = dp;
16866b3dfc6aSRick Macklem 			tnes = *exp;
168790d2dfabSRick Macklem 			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd, p, 1,
168890d2dfabSRick Macklem 			    NULL);
1689b249ce48SMateusz Guzik 			NFSVOPUNLOCK(dp);
16906b3dfc6aSRick Macklem 		} else {
1691b249ce48SMateusz Guzik 			NFSVOPUNLOCK(dp);
16926b3dfc6aSRick Macklem 			nd->nd_cred->cr_uid = nd->nd_saveduid;
16936b3dfc6aSRick Macklem 			nfsd_fhtovp(nd, &tfh, LK_EXCLUSIVE, &tdp, &tnes, NULL,
1694a5df139eSRick Macklem 			    0, -1);	/* Locks tdp. */
16956b3dfc6aSRick Macklem 			if (tdp) {
169690d2dfabSRick Macklem 				tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd,
169790d2dfabSRick Macklem 				    p, 1, NULL);
1698b249ce48SMateusz Guzik 				NFSVOPUNLOCK(tdp);
16999ec7b004SRick Macklem 			}
17009ec7b004SRick Macklem 		}
17016b3dfc6aSRick Macklem 	}
170265127e98SMateusz Guzik 	NFSNAMEICNDSET(&tond.ni_cnd, nd->nd_cred, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE);
17039ec7b004SRick Macklem 	nfsvno_setpathbuf(&tond, &tbufp, &hashp);
17049ec7b004SRick Macklem 	if (!nd->nd_repstat) {
17059ec7b004SRick Macklem 		error = nfsrv_parsename(nd, tbufp, hashp, &tond.ni_pathlen);
17069ec7b004SRick Macklem 		if (error) {
17078974bc2fSRick Macklem 			if (tdp)
17089ec7b004SRick Macklem 				vrele(tdp);
170925bfde79SXin LI 			vrele(dp);
17109ec7b004SRick Macklem 			nfsvno_relpathbuf(&fromnd);
17119ec7b004SRick Macklem 			nfsvno_relpathbuf(&tond);
1712a9285ae5SZack Kirsch 			goto out;
17139ec7b004SRick Macklem 		}
17149ec7b004SRick Macklem 	}
17159ec7b004SRick Macklem 	if (nd->nd_repstat) {
17169ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
17179ec7b004SRick Macklem 			nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
17189ec7b004SRick Macklem 			    &fdiraft);
17199ec7b004SRick Macklem 			nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
17209ec7b004SRick Macklem 			    &tdiraft);
17219ec7b004SRick Macklem 		}
17228974bc2fSRick Macklem 		if (tdp)
17239ec7b004SRick Macklem 			vrele(tdp);
172425bfde79SXin LI 		vrele(dp);
17259ec7b004SRick Macklem 		nfsvno_relpathbuf(&fromnd);
17269ec7b004SRick Macklem 		nfsvno_relpathbuf(&tond);
1727a9285ae5SZack Kirsch 		goto out;
17289ec7b004SRick Macklem 	}
17299ec7b004SRick Macklem 
17309ec7b004SRick Macklem 	/*
17319ec7b004SRick Macklem 	 * Done parsing, now down to business.
17329ec7b004SRick Macklem 	 */
1733ef7d2c1fSMateusz Guzik 	nd->nd_repstat = nfsvno_namei(nd, &fromnd, dp, 0, exp, &fdirp);
17349ec7b004SRick Macklem 	if (nd->nd_repstat) {
17359ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
17369ec7b004SRick Macklem 			nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
17379ec7b004SRick Macklem 			    &fdiraft);
17389ec7b004SRick Macklem 			nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
17399ec7b004SRick Macklem 			    &tdiraft);
17409ec7b004SRick Macklem 		}
17419ec7b004SRick Macklem 		if (fdirp)
17429ec7b004SRick Macklem 			vrele(fdirp);
17438974bc2fSRick Macklem 		if (tdp)
17449ec7b004SRick Macklem 			vrele(tdp);
17459ec7b004SRick Macklem 		nfsvno_relpathbuf(&tond);
1746a9285ae5SZack Kirsch 		goto out;
17479ec7b004SRick Macklem 	}
17485d3fe02cSRick Macklem 	if (fromnd.ni_vp->v_type == VDIR)
17499ec7b004SRick Macklem 		tond.ni_cnd.cn_flags |= WILLBEDIR;
1750ef7d2c1fSMateusz Guzik 	nd->nd_repstat = nfsvno_namei(nd, &tond, tdp, 0, &tnes, &tdirp);
17519ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_rename(&fromnd, &tond, nd->nd_repstat,
17529ec7b004SRick Macklem 	    nd->nd_flag, nd->nd_cred, p);
17539ec7b004SRick Macklem 	if (fdirp)
175490d2dfabSRick Macklem 		fdiraft_ret = nfsvno_getattr(fdirp, &fdiraft, nd, p, 0, NULL);
17559ec7b004SRick Macklem 	if (tdirp)
175690d2dfabSRick Macklem 		tdiraft_ret = nfsvno_getattr(tdirp, &tdiraft, nd, p, 0, NULL);
17579ec7b004SRick Macklem 	if (fdirp)
17589ec7b004SRick Macklem 		vrele(fdirp);
17599ec7b004SRick Macklem 	if (tdirp)
17609ec7b004SRick Macklem 		vrele(tdirp);
17619ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
17629ec7b004SRick Macklem 		nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
17639ec7b004SRick Macklem 		nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
17649ec7b004SRick Macklem 	} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
17659ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 10 * NFSX_UNSIGNED);
17669ec7b004SRick Macklem 		*tl++ = newnfs_false;
17679ec7b004SRick Macklem 		txdr_hyper(fdirfor.na_filerev, tl);
17689ec7b004SRick Macklem 		tl += 2;
17699ec7b004SRick Macklem 		txdr_hyper(fdiraft.na_filerev, tl);
17709ec7b004SRick Macklem 		tl += 2;
17719ec7b004SRick Macklem 		*tl++ = newnfs_false;
17729ec7b004SRick Macklem 		txdr_hyper(tdirfor.na_filerev, tl);
17739ec7b004SRick Macklem 		tl += 2;
17749ec7b004SRick Macklem 		txdr_hyper(tdiraft.na_filerev, tl);
17759ec7b004SRick Macklem 	}
1776a9285ae5SZack Kirsch 
1777a9285ae5SZack Kirsch out:
1778a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
1779a9285ae5SZack Kirsch 	return (error);
17809ec7b004SRick Macklem }
17819ec7b004SRick Macklem 
17829ec7b004SRick Macklem /*
17839ec7b004SRick Macklem  * nfs link service
17849ec7b004SRick Macklem  */
1785b9cc3262SRyan Moeller int
17869ec7b004SRick Macklem nfsrvd_link(struct nfsrv_descript *nd, int isdgram,
1787af444b18SEdward Tomasz Napierala     vnode_t vp, vnode_t tovp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
17889ec7b004SRick Macklem {
17899ec7b004SRick Macklem 	struct nameidata named;
17909ec7b004SRick Macklem 	u_int32_t *tl;
17919ec7b004SRick Macklem 	int error = 0, dirfor_ret = 1, diraft_ret = 1, getret = 1;
17929ec7b004SRick Macklem 	vnode_t dirp = NULL, dp = NULL;
17939ec7b004SRick Macklem 	struct nfsvattr dirfor, diraft, at;
17949ec7b004SRick Macklem 	struct nfsexstuff tnes;
17959ec7b004SRick Macklem 	struct nfsrvfh dfh;
17969ec7b004SRick Macklem 	char *bufp;
17979ec7b004SRick Macklem 	u_long *hashp;
1798af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
17999ec7b004SRick Macklem 
18009ec7b004SRick Macklem 	if (nd->nd_repstat) {
18019ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
18029ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1803a9285ae5SZack Kirsch 		goto out;
18049ec7b004SRick Macklem 	}
1805b249ce48SMateusz Guzik 	NFSVOPUNLOCK(vp);
18065d3fe02cSRick Macklem 	if (vp->v_type == VDIR) {
18079ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV4)
18089ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_ISDIR;
18099ec7b004SRick Macklem 		else
18109ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
18119ec7b004SRick Macklem 		if (tovp)
18129ec7b004SRick Macklem 			vrele(tovp);
18139ec7b004SRick Macklem 	}
18149ec7b004SRick Macklem 	if (!nd->nd_repstat) {
18159ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV4) {
18169ec7b004SRick Macklem 			dp = tovp;
18179ec7b004SRick Macklem 			tnes = *toexp;
18189ec7b004SRick Macklem 		} else {
18199ec7b004SRick Macklem 			error = nfsrv_mtofh(nd, &dfh);
18209ec7b004SRick Macklem 			if (error) {
18219ec7b004SRick Macklem 				vrele(vp);
18229ec7b004SRick Macklem 				/* tovp is always NULL unless NFSv4 */
1823a9285ae5SZack Kirsch 				goto out;
18249ec7b004SRick Macklem 			}
1825a5df139eSRick Macklem 			nfsd_fhtovp(nd, &dfh, LK_EXCLUSIVE, &dp, &tnes, NULL,
1826a5df139eSRick Macklem 			    0, -1);
18279ec7b004SRick Macklem 			if (dp)
1828b249ce48SMateusz Guzik 				NFSVOPUNLOCK(dp);
18299ec7b004SRick Macklem 		}
18309ec7b004SRick Macklem 	}
18315b5b7e2cSMateusz Guzik 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, LOCKPARENT | NOCACHE);
18329ec7b004SRick Macklem 	if (!nd->nd_repstat) {
18339ec7b004SRick Macklem 		nfsvno_setpathbuf(&named, &bufp, &hashp);
18349ec7b004SRick Macklem 		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
18359ec7b004SRick Macklem 		if (error) {
18369ec7b004SRick Macklem 			vrele(vp);
18378974bc2fSRick Macklem 			if (dp)
18389ec7b004SRick Macklem 				vrele(dp);
18399ec7b004SRick Macklem 			nfsvno_relpathbuf(&named);
1840a9285ae5SZack Kirsch 			goto out;
18419ec7b004SRick Macklem 		}
18429ec7b004SRick Macklem 		if (!nd->nd_repstat) {
18439ec7b004SRick Macklem 			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, &tnes,
1844ef7d2c1fSMateusz Guzik 			    &dirp);
18459ec7b004SRick Macklem 		} else {
18469ec7b004SRick Macklem 			if (dp)
18479ec7b004SRick Macklem 				vrele(dp);
18489ec7b004SRick Macklem 			nfsvno_relpathbuf(&named);
18499ec7b004SRick Macklem 		}
18509ec7b004SRick Macklem 	}
18519ec7b004SRick Macklem 	if (dirp) {
18529ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV2) {
18539ec7b004SRick Macklem 			vrele(dirp);
18549ec7b004SRick Macklem 			dirp = NULL;
18559ec7b004SRick Macklem 		} else {
185690d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
185790d2dfabSRick Macklem 			    NULL);
18589ec7b004SRick Macklem 		}
18599ec7b004SRick Macklem 	}
18609ec7b004SRick Macklem 	if (!nd->nd_repstat)
18619ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_link(&named, vp, nd->nd_cred, p, exp);
18629ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
186390d2dfabSRick Macklem 		getret = nfsvno_getattr(vp, &at, nd, p, 0, NULL);
18649ec7b004SRick Macklem 	if (dirp) {
186590d2dfabSRick Macklem 		diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
18669ec7b004SRick Macklem 		vrele(dirp);
18679ec7b004SRick Macklem 	}
18689ec7b004SRick Macklem 	vrele(vp);
18699ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
18709ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
18719ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
18729ec7b004SRick Macklem 	} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
18739ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
18749ec7b004SRick Macklem 		*tl++ = newnfs_false;
18759ec7b004SRick Macklem 		txdr_hyper(dirfor.na_filerev, tl);
18769ec7b004SRick Macklem 		tl += 2;
18779ec7b004SRick Macklem 		txdr_hyper(diraft.na_filerev, tl);
18789ec7b004SRick Macklem 	}
1879a9285ae5SZack Kirsch 
1880a9285ae5SZack Kirsch out:
1881a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
1882a9285ae5SZack Kirsch 	return (error);
18839ec7b004SRick Macklem }
18849ec7b004SRick Macklem 
18859ec7b004SRick Macklem /*
18869ec7b004SRick Macklem  * nfs symbolic link service
18879ec7b004SRick Macklem  */
1888b9cc3262SRyan Moeller int
18899ec7b004SRick Macklem nfsrvd_symlink(struct nfsrv_descript *nd, __unused int isdgram,
1890af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
18919ec7b004SRick Macklem {
18929ec7b004SRick Macklem 	struct nfsvattr nva, dirfor, diraft;
18939ec7b004SRick Macklem 	struct nameidata named;
1894a9285ae5SZack Kirsch 	int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
18959ec7b004SRick Macklem 	vnode_t dirp = NULL;
18969ec7b004SRick Macklem 	char *bufp, *pathcp = NULL;
18979ec7b004SRick Macklem 	u_long *hashp;
1898af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
18999ec7b004SRick Macklem 
19009ec7b004SRick Macklem 	if (nd->nd_repstat) {
19019ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1902a9285ae5SZack Kirsch 		goto out;
19039ec7b004SRick Macklem 	}
19049ec7b004SRick Macklem 	if (vpp)
19059ec7b004SRick Macklem 		*vpp = NULL;
19069ec7b004SRick Macklem 	NFSVNO_ATTRINIT(&nva);
19079ec7b004SRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
190865127e98SMateusz Guzik 	    LOCKPARENT | NOCACHE);
19099ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
19109ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
19119ec7b004SRick Macklem 	if (!error && !nd->nd_repstat)
19129ec7b004SRick Macklem 		error = nfsvno_getsymlink(nd, &nva, p, &pathcp, &pathlen);
19139ec7b004SRick Macklem 	if (error) {
19149ec7b004SRick Macklem 		vrele(dp);
19159ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
1916a9285ae5SZack Kirsch 		goto out;
19179ec7b004SRick Macklem 	}
19189ec7b004SRick Macklem 	if (!nd->nd_repstat) {
1919ef7d2c1fSMateusz Guzik 		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, &dirp);
19209ec7b004SRick Macklem 	} else {
19219ec7b004SRick Macklem 		vrele(dp);
19229ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
19239ec7b004SRick Macklem 	}
19249ec7b004SRick Macklem 	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
19259ec7b004SRick Macklem 		vrele(dirp);
19269ec7b004SRick Macklem 		dirp = NULL;
19279ec7b004SRick Macklem 	}
19289ec7b004SRick Macklem 
19299ec7b004SRick Macklem 	/*
19309ec7b004SRick Macklem 	 * And call nfsrvd_symlinksub() to do the common code. It will
19319ec7b004SRick Macklem 	 * return EBADRPC upon a parsing error, 0 otherwise.
19329ec7b004SRick Macklem 	 */
19339ec7b004SRick Macklem 	if (!nd->nd_repstat) {
19349ec7b004SRick Macklem 		if (dirp != NULL)
193590d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
193690d2dfabSRick Macklem 			    NULL);
19379ec7b004SRick Macklem 		nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
19389ec7b004SRick Macklem 		    &dirfor, &diraft, &diraft_ret, NULL, NULL, p, exp,
19399ec7b004SRick Macklem 		    pathcp, pathlen);
19409ec7b004SRick Macklem 	} else if (dirp != NULL) {
194190d2dfabSRick Macklem 		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL);
19429ec7b004SRick Macklem 		vrele(dirp);
19439ec7b004SRick Macklem 	}
19449ec7b004SRick Macklem 	if (pathcp)
1945222daa42SConrad Meyer 		free(pathcp, M_TEMP);
19469ec7b004SRick Macklem 
19479ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
19489ec7b004SRick Macklem 		if (!nd->nd_repstat) {
19499ec7b004SRick Macklem 			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
19509ec7b004SRick Macklem 			nfsrv_postopattr(nd, 0, &nva);
19519ec7b004SRick Macklem 		}
19529ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
19539ec7b004SRick Macklem 	}
1954a9285ae5SZack Kirsch 
1955a9285ae5SZack Kirsch out:
1956a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
1957a9285ae5SZack Kirsch 	return (error);
19589ec7b004SRick Macklem }
19599ec7b004SRick Macklem 
19609ec7b004SRick Macklem /*
19619ec7b004SRick Macklem  * Common code for creating a symbolic link.
19629ec7b004SRick Macklem  */
19639ec7b004SRick Macklem static void
19649ec7b004SRick Macklem nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
19659ec7b004SRick Macklem     struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
19669ec7b004SRick Macklem     vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
19679ec7b004SRick Macklem     int *diraft_retp, nfsattrbit_t *attrbitp,
19689ec7b004SRick Macklem     NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
19699ec7b004SRick Macklem     int pathlen)
19709ec7b004SRick Macklem {
19719ec7b004SRick Macklem 	u_int32_t *tl;
19729ec7b004SRick Macklem 
19739ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_symlink(ndp, nvap, pathcp, pathlen,
19749ec7b004SRick Macklem 	    !(nd->nd_flag & ND_NFSV2), nd->nd_saveduid, nd->nd_cred, p, exp);
19759ec7b004SRick Macklem 	if (!nd->nd_repstat && !(nd->nd_flag & ND_NFSV2)) {
19769ec7b004SRick Macklem 		nfsrv_fixattr(nd, ndp->ni_vp, nvap, aclp, p, attrbitp, exp);
19779ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
19789ec7b004SRick Macklem 			nd->nd_repstat = nfsvno_getfh(ndp->ni_vp, fhp, p);
19799ec7b004SRick Macklem 			if (!nd->nd_repstat)
19809ec7b004SRick Macklem 				nd->nd_repstat = nfsvno_getattr(ndp->ni_vp,
198190d2dfabSRick Macklem 				    nvap, nd, p, 1, NULL);
19829ec7b004SRick Macklem 		}
198381f78d99SRick Macklem 		if (vpp != NULL && nd->nd_repstat == 0) {
1984b249ce48SMateusz Guzik 			NFSVOPUNLOCK(ndp->ni_vp);
19859ec7b004SRick Macklem 			*vpp = ndp->ni_vp;
198681f78d99SRick Macklem 		} else
19879ec7b004SRick Macklem 			vput(ndp->ni_vp);
19889ec7b004SRick Macklem 	}
19899ec7b004SRick Macklem 	if (dirp) {
199090d2dfabSRick Macklem 		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd, p, 0, NULL);
19919ec7b004SRick Macklem 		vrele(dirp);
19929ec7b004SRick Macklem 	}
19939ec7b004SRick Macklem 	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
19949ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
19959ec7b004SRick Macklem 		*tl++ = newnfs_false;
19969ec7b004SRick Macklem 		txdr_hyper(dirforp->na_filerev, tl);
19979ec7b004SRick Macklem 		tl += 2;
19989ec7b004SRick Macklem 		txdr_hyper(diraftp->na_filerev, tl);
19999ec7b004SRick Macklem 		(void) nfsrv_putattrbit(nd, attrbitp);
20009ec7b004SRick Macklem 	}
2001a9285ae5SZack Kirsch 
2002a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
20039ec7b004SRick Macklem }
20049ec7b004SRick Macklem 
20059ec7b004SRick Macklem /*
20069ec7b004SRick Macklem  * nfs mkdir service
20079ec7b004SRick Macklem  */
2008b9cc3262SRyan Moeller int
20099ec7b004SRick Macklem nfsrvd_mkdir(struct nfsrv_descript *nd, __unused int isdgram,
2010af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
20119ec7b004SRick Macklem {
20129ec7b004SRick Macklem 	struct nfsvattr nva, dirfor, diraft;
20139ec7b004SRick Macklem 	struct nameidata named;
20149ec7b004SRick Macklem 	u_int32_t *tl;
2015a9285ae5SZack Kirsch 	int error = 0, dirfor_ret = 1, diraft_ret = 1;
20169ec7b004SRick Macklem 	vnode_t dirp = NULL;
20179ec7b004SRick Macklem 	char *bufp;
20189ec7b004SRick Macklem 	u_long *hashp;
2019af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
20209ec7b004SRick Macklem 
20219ec7b004SRick Macklem 	if (nd->nd_repstat) {
20229ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
2023a9285ae5SZack Kirsch 		goto out;
20249ec7b004SRick Macklem 	}
20255b5b7e2cSMateusz Guzik 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, LOCKPARENT | NOCACHE);
20269ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
20279ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
2028a9285ae5SZack Kirsch 	if (error)
2029a9285ae5SZack Kirsch 		goto nfsmout;
20309ec7b004SRick Macklem 	if (!nd->nd_repstat) {
20319ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva);
20329ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
2033d8a5961fSMarcelo Araujo 			error = nfsrv_sattr(nd, NULL, &nva, NULL, NULL, p);
2034a9285ae5SZack Kirsch 			if (error)
2035a9285ae5SZack Kirsch 				goto nfsmout;
20369ec7b004SRick Macklem 		} else {
20379ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
20389ec7b004SRick Macklem 			nva.na_mode = nfstov_mode(*tl++);
20399ec7b004SRick Macklem 		}
20409ec7b004SRick Macklem 	}
20419ec7b004SRick Macklem 	if (!nd->nd_repstat) {
2042ef7d2c1fSMateusz Guzik 		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, &dirp);
20439ec7b004SRick Macklem 	} else {
20449ec7b004SRick Macklem 		vrele(dp);
20459ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
20469ec7b004SRick Macklem 	}
20479ec7b004SRick Macklem 	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
20489ec7b004SRick Macklem 		vrele(dirp);
20499ec7b004SRick Macklem 		dirp = NULL;
20509ec7b004SRick Macklem 	}
20519ec7b004SRick Macklem 	if (nd->nd_repstat) {
20529ec7b004SRick Macklem 		if (dirp != NULL) {
205390d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
205490d2dfabSRick Macklem 			    NULL);
20559ec7b004SRick Macklem 			vrele(dirp);
20569ec7b004SRick Macklem 		}
20579ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
20589ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
20599ec7b004SRick Macklem 			    &diraft);
2060a9285ae5SZack Kirsch 		goto out;
20619ec7b004SRick Macklem 	}
20629ec7b004SRick Macklem 	if (dirp != NULL)
206390d2dfabSRick Macklem 		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL);
20649ec7b004SRick Macklem 
20659ec7b004SRick Macklem 	/*
20669ec7b004SRick Macklem 	 * Call nfsrvd_mkdirsub() for the code common to V4 as well.
20679ec7b004SRick Macklem 	 */
20689ec7b004SRick Macklem 	nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp, &dirfor, &diraft,
20699ec7b004SRick Macklem 	    &diraft_ret, NULL, NULL, p, exp);
20709ec7b004SRick Macklem 
20719ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
20729ec7b004SRick Macklem 		if (!nd->nd_repstat) {
20739ec7b004SRick Macklem 			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
20749ec7b004SRick Macklem 			nfsrv_postopattr(nd, 0, &nva);
20759ec7b004SRick Macklem 		}
20769ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
20779ec7b004SRick Macklem 	} else if (!nd->nd_repstat) {
20789ec7b004SRick Macklem 		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
20799ec7b004SRick Macklem 		nfsrv_fillattr(nd, &nva);
20809ec7b004SRick Macklem 	}
2081a9285ae5SZack Kirsch 
2082a9285ae5SZack Kirsch out:
2083a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
20849ec7b004SRick Macklem 	return (0);
20859ec7b004SRick Macklem nfsmout:
20869ec7b004SRick Macklem 	vrele(dp);
20879ec7b004SRick Macklem 	nfsvno_relpathbuf(&named);
2088a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
20899ec7b004SRick Macklem 	return (error);
20909ec7b004SRick Macklem }
20919ec7b004SRick Macklem 
20929ec7b004SRick Macklem /*
20939ec7b004SRick Macklem  * Code common to mkdir for V2,3 and 4.
20949ec7b004SRick Macklem  */
20959ec7b004SRick Macklem static void
20969ec7b004SRick Macklem nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
20979ec7b004SRick Macklem     struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
20989ec7b004SRick Macklem     vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
20999ec7b004SRick Macklem     int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
21009ec7b004SRick Macklem     NFSPROC_T *p, struct nfsexstuff *exp)
21019ec7b004SRick Macklem {
21029ec7b004SRick Macklem 	vnode_t vp;
21039ec7b004SRick Macklem 	u_int32_t *tl;
21049ec7b004SRick Macklem 
21059ec7b004SRick Macklem 	NFSVNO_SETATTRVAL(nvap, type, VDIR);
21069ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_mkdir(ndp, nvap, nd->nd_saveduid,
21079ec7b004SRick Macklem 	    nd->nd_cred, p, exp);
21089ec7b004SRick Macklem 	if (!nd->nd_repstat) {
21099ec7b004SRick Macklem 		vp = ndp->ni_vp;
21109ec7b004SRick Macklem 		nfsrv_fixattr(nd, vp, nvap, aclp, p, attrbitp, exp);
21119ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
21129ec7b004SRick Macklem 		if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
211390d2dfabSRick Macklem 			nd->nd_repstat = nfsvno_getattr(vp, nvap, nd, p, 1,
211490d2dfabSRick Macklem 			    NULL);
21159ec7b004SRick Macklem 		if (vpp && !nd->nd_repstat) {
2116b249ce48SMateusz Guzik 			NFSVOPUNLOCK(vp);
21179ec7b004SRick Macklem 			*vpp = vp;
21189ec7b004SRick Macklem 		} else {
21199ec7b004SRick Macklem 			vput(vp);
21209ec7b004SRick Macklem 		}
21219ec7b004SRick Macklem 	}
21229ec7b004SRick Macklem 	if (dirp) {
212390d2dfabSRick Macklem 		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd, p, 0, NULL);
21249ec7b004SRick Macklem 		vrele(dirp);
21259ec7b004SRick Macklem 	}
21269ec7b004SRick Macklem 	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
21279ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
21289ec7b004SRick Macklem 		*tl++ = newnfs_false;
21299ec7b004SRick Macklem 		txdr_hyper(dirforp->na_filerev, tl);
21309ec7b004SRick Macklem 		tl += 2;
21319ec7b004SRick Macklem 		txdr_hyper(diraftp->na_filerev, tl);
21329ec7b004SRick Macklem 		(void) nfsrv_putattrbit(nd, attrbitp);
21339ec7b004SRick Macklem 	}
2134a9285ae5SZack Kirsch 
2135a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
21369ec7b004SRick Macklem }
21379ec7b004SRick Macklem 
21389ec7b004SRick Macklem /*
21399ec7b004SRick Macklem  * nfs commit service
21409ec7b004SRick Macklem  */
2141b9cc3262SRyan Moeller int
21429ec7b004SRick Macklem nfsrvd_commit(struct nfsrv_descript *nd, __unused int isdgram,
2143af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
21449ec7b004SRick Macklem {
21459ec7b004SRick Macklem 	struct nfsvattr bfor, aft;
21469ec7b004SRick Macklem 	u_int32_t *tl;
21479ec7b004SRick Macklem 	int error = 0, for_ret = 1, aft_ret = 1, cnt;
21489ec7b004SRick Macklem 	u_int64_t off;
2149af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
21509ec7b004SRick Macklem 
21519ec7b004SRick Macklem        if (nd->nd_repstat) {
21529ec7b004SRick Macklem 		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
2153a9285ae5SZack Kirsch 		goto out;
21549ec7b004SRick Macklem 	}
2155d8a5961fSMarcelo Araujo 
2156d8a5961fSMarcelo Araujo 	/* Return NFSERR_ISDIR in NFSv4 when commit on a directory. */
2157d8a5961fSMarcelo Araujo 	if (vp->v_type != VREG) {
2158d8a5961fSMarcelo Araujo 		if (nd->nd_flag & ND_NFSV3)
2159d8a5961fSMarcelo Araujo 			error = NFSERR_NOTSUPP;
2160d8a5961fSMarcelo Araujo 		else
2161d8a5961fSMarcelo Araujo 			error = (vp->v_type == VDIR) ? NFSERR_ISDIR : NFSERR_INVAL;
2162d8a5961fSMarcelo Araujo 		goto nfsmout;
2163d8a5961fSMarcelo Araujo 	}
21649ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
2165d8a5961fSMarcelo Araujo 
21669ec7b004SRick Macklem 	/*
21679ec7b004SRick Macklem 	 * XXX At this time VOP_FSYNC() does not accept offset and byte
21689ec7b004SRick Macklem 	 * count parameters, so these arguments are useless (someday maybe).
21699ec7b004SRick Macklem 	 */
21709ec7b004SRick Macklem 	off = fxdr_hyper(tl);
21719ec7b004SRick Macklem 	tl += 2;
21729ec7b004SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
21739ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
217490d2dfabSRick Macklem 		for_ret = nfsvno_getattr(vp, &bfor, nd, p, 1, NULL);
21759ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_fsync(vp, off, cnt, nd->nd_cred, p);
21769ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
217790d2dfabSRick Macklem 		aft_ret = nfsvno_getattr(vp, &aft, nd, p, 1, NULL);
21789ec7b004SRick Macklem 		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
21799ec7b004SRick Macklem 	}
21809ec7b004SRick Macklem 	vput(vp);
21819ec7b004SRick Macklem 	if (!nd->nd_repstat) {
21829ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
21839ec7b004SRick Macklem 		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
21849ec7b004SRick Macklem 		*tl = txdr_unsigned(nfsboottime.tv_usec);
21859ec7b004SRick Macklem 	}
2186a9285ae5SZack Kirsch 
2187a9285ae5SZack Kirsch out:
2188a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
21899ec7b004SRick Macklem 	return (0);
21909ec7b004SRick Macklem nfsmout:
21919ec7b004SRick Macklem 	vput(vp);
2192a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
21939ec7b004SRick Macklem 	return (error);
21949ec7b004SRick Macklem }
21959ec7b004SRick Macklem 
21969ec7b004SRick Macklem /*
21979ec7b004SRick Macklem  * nfs statfs service
21989ec7b004SRick Macklem  */
2199b9cc3262SRyan Moeller int
22009ec7b004SRick Macklem nfsrvd_statfs(struct nfsrv_descript *nd, __unused int isdgram,
2201af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
22029ec7b004SRick Macklem {
22039ec7b004SRick Macklem 	struct statfs *sf;
22049ec7b004SRick Macklem 	u_int32_t *tl;
22059ec7b004SRick Macklem 	int getret = 1;
22069ec7b004SRick Macklem 	struct nfsvattr at;
22079ec7b004SRick Macklem 	u_quad_t tval;
2208af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
22099ec7b004SRick Macklem 
22102f304845SKonstantin Belousov 	sf = NULL;
22119ec7b004SRick Macklem 	if (nd->nd_repstat) {
22129ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
2213a9285ae5SZack Kirsch 		goto out;
22149ec7b004SRick Macklem 	}
22152f304845SKonstantin Belousov 	sf = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
2216dfd233edSAttilio Rao 	nd->nd_repstat = nfsvno_statfs(vp, sf);
221790d2dfabSRick Macklem 	getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL);
22189ec7b004SRick Macklem 	vput(vp);
22199ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
22209ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
22219ec7b004SRick Macklem 	if (nd->nd_repstat)
2222a9285ae5SZack Kirsch 		goto out;
22239ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
22249ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_V2STATFS);
22259ec7b004SRick Macklem 		*tl++ = txdr_unsigned(NFS_V2MAXDATA);
22269ec7b004SRick Macklem 		*tl++ = txdr_unsigned(sf->f_bsize);
22279ec7b004SRick Macklem 		*tl++ = txdr_unsigned(sf->f_blocks);
22289ec7b004SRick Macklem 		*tl++ = txdr_unsigned(sf->f_bfree);
22299ec7b004SRick Macklem 		*tl = txdr_unsigned(sf->f_bavail);
22309ec7b004SRick Macklem 	} else {
22319ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_V3STATFS);
22329ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_blocks;
22339ec7b004SRick Macklem 		tval *= (u_quad_t)sf->f_bsize;
22349ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
22359ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_bfree;
22369ec7b004SRick Macklem 		tval *= (u_quad_t)sf->f_bsize;
22379ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
22389ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_bavail;
22399ec7b004SRick Macklem 		tval *= (u_quad_t)sf->f_bsize;
22409ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
22419ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_files;
22429ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
22439ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_ffree;
22449ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
22459ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_ffree;
22469ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
22479ec7b004SRick Macklem 		*tl = 0;
22489ec7b004SRick Macklem 	}
2249a9285ae5SZack Kirsch 
2250a9285ae5SZack Kirsch out:
22512f304845SKonstantin Belousov 	free(sf, M_STATFS);
2252a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
22539ec7b004SRick Macklem 	return (0);
22549ec7b004SRick Macklem }
22559ec7b004SRick Macklem 
22569ec7b004SRick Macklem /*
22579ec7b004SRick Macklem  * nfs fsinfo service
22589ec7b004SRick Macklem  */
2259b9cc3262SRyan Moeller int
22609ec7b004SRick Macklem nfsrvd_fsinfo(struct nfsrv_descript *nd, int isdgram,
2261af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
22629ec7b004SRick Macklem {
22639ec7b004SRick Macklem 	u_int32_t *tl;
22649ec7b004SRick Macklem 	struct nfsfsinfo fs;
22659ec7b004SRick Macklem 	int getret = 1;
22669ec7b004SRick Macklem 	struct nfsvattr at;
2267af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
22689ec7b004SRick Macklem 
22699ec7b004SRick Macklem 	if (nd->nd_repstat) {
22709ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
2271a9285ae5SZack Kirsch 		goto out;
22729ec7b004SRick Macklem 	}
227390d2dfabSRick Macklem 	getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL);
22749ec7b004SRick Macklem 	nfsvno_getfs(&fs, isdgram);
22759ec7b004SRick Macklem 	vput(vp);
22769ec7b004SRick Macklem 	nfsrv_postopattr(nd, getret, &at);
22779ec7b004SRick Macklem 	NFSM_BUILD(tl, u_int32_t *, NFSX_V3FSINFO);
22789ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_rtmax);
22799ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_rtpref);
22809ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_rtmult);
22819ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_wtmax);
22829ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_wtpref);
22839ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_wtmult);
22849ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_dtpref);
22859ec7b004SRick Macklem 	txdr_hyper(fs.fs_maxfilesize, tl);
22869ec7b004SRick Macklem 	tl += 2;
22879ec7b004SRick Macklem 	txdr_nfsv3time(&fs.fs_timedelta, tl);
22889ec7b004SRick Macklem 	tl += 2;
22899ec7b004SRick Macklem 	*tl = txdr_unsigned(fs.fs_properties);
2290a9285ae5SZack Kirsch 
2291a9285ae5SZack Kirsch out:
2292a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
22939ec7b004SRick Macklem 	return (0);
22949ec7b004SRick Macklem }
22959ec7b004SRick Macklem 
22969ec7b004SRick Macklem /*
22979ec7b004SRick Macklem  * nfs pathconf service
22989ec7b004SRick Macklem  */
2299b9cc3262SRyan Moeller int
23009ec7b004SRick Macklem nfsrvd_pathconf(struct nfsrv_descript *nd, __unused int isdgram,
2301af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
23029ec7b004SRick Macklem {
23039ec7b004SRick Macklem 	struct nfsv3_pathconf *pc;
23049ec7b004SRick Macklem 	int getret = 1;
2305b1288166SJohn Baldwin 	long linkmax, namemax, chownres, notrunc;
23069ec7b004SRick Macklem 	struct nfsvattr at;
2307af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
23089ec7b004SRick Macklem 
23099ec7b004SRick Macklem 	if (nd->nd_repstat) {
23109ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
2311a9285ae5SZack Kirsch 		goto out;
23129ec7b004SRick Macklem 	}
23139ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_pathconf(vp, _PC_LINK_MAX, &linkmax,
23149ec7b004SRick Macklem 	    nd->nd_cred, p);
23159ec7b004SRick Macklem 	if (!nd->nd_repstat)
23169ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NAME_MAX, &namemax,
23179ec7b004SRick Macklem 		    nd->nd_cred, p);
23189ec7b004SRick Macklem 	if (!nd->nd_repstat)
23199ec7b004SRick Macklem 		nd->nd_repstat=nfsvno_pathconf(vp, _PC_CHOWN_RESTRICTED,
23209ec7b004SRick Macklem 		    &chownres, nd->nd_cred, p);
23219ec7b004SRick Macklem 	if (!nd->nd_repstat)
23229ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NO_TRUNC, &notrunc,
23239ec7b004SRick Macklem 		    nd->nd_cred, p);
232490d2dfabSRick Macklem 	getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL);
23259ec7b004SRick Macklem 	vput(vp);
23269ec7b004SRick Macklem 	nfsrv_postopattr(nd, getret, &at);
23279ec7b004SRick Macklem 	if (!nd->nd_repstat) {
23289ec7b004SRick Macklem 		NFSM_BUILD(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
23299ec7b004SRick Macklem 		pc->pc_linkmax = txdr_unsigned(linkmax);
23309ec7b004SRick Macklem 		pc->pc_namemax = txdr_unsigned(namemax);
23319ec7b004SRick Macklem 		pc->pc_notrunc = txdr_unsigned(notrunc);
23329ec7b004SRick Macklem 		pc->pc_chownrestricted = txdr_unsigned(chownres);
23339ec7b004SRick Macklem 
23349ec7b004SRick Macklem 		/*
23359ec7b004SRick Macklem 		 * These should probably be supported by VOP_PATHCONF(), but
23369ec7b004SRick Macklem 		 * until msdosfs is exportable (why would you want to?), the
23379ec7b004SRick Macklem 		 * Unix defaults should be ok.
23389ec7b004SRick Macklem 		 */
23399ec7b004SRick Macklem 		pc->pc_caseinsensitive = newnfs_false;
23409ec7b004SRick Macklem 		pc->pc_casepreserving = newnfs_true;
23419ec7b004SRick Macklem 	}
2342a9285ae5SZack Kirsch 
2343a9285ae5SZack Kirsch out:
2344a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
23459ec7b004SRick Macklem 	return (0);
23469ec7b004SRick Macklem }
23479ec7b004SRick Macklem 
23489ec7b004SRick Macklem /*
23499ec7b004SRick Macklem  * nfsv4 lock service
23509ec7b004SRick Macklem  */
2351b9cc3262SRyan Moeller int
23529ec7b004SRick Macklem nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram,
2353af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
23549ec7b004SRick Macklem {
23559ec7b004SRick Macklem 	u_int32_t *tl;
23569ec7b004SRick Macklem 	int i;
23579ec7b004SRick Macklem 	struct nfsstate *stp = NULL;
23589ec7b004SRick Macklem 	struct nfslock *lop;
23599ec7b004SRick Macklem 	struct nfslockconflict cf;
23609ec7b004SRick Macklem 	int error = 0;
23619ec7b004SRick Macklem 	u_short flags = NFSLCK_LOCK, lflags;
23629ec7b004SRick Macklem 	u_int64_t offset, len;
23639ec7b004SRick Macklem 	nfsv4stateid_t stateid;
23649ec7b004SRick Macklem 	nfsquad_t clientid;
2365af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
23669ec7b004SRick Macklem 
23679ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
23689ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
23699ec7b004SRick Macklem 	switch (i) {
23709ec7b004SRick Macklem 	case NFSV4LOCKT_READW:
23719ec7b004SRick Macklem 		flags |= NFSLCK_BLOCKING;
23729ec7b004SRick Macklem 	case NFSV4LOCKT_READ:
23739ec7b004SRick Macklem 		lflags = NFSLCK_READ;
23749ec7b004SRick Macklem 		break;
23759ec7b004SRick Macklem 	case NFSV4LOCKT_WRITEW:
23769ec7b004SRick Macklem 		flags |= NFSLCK_BLOCKING;
23779ec7b004SRick Macklem 	case NFSV4LOCKT_WRITE:
23789ec7b004SRick Macklem 		lflags = NFSLCK_WRITE;
23799ec7b004SRick Macklem 		break;
23809ec7b004SRick Macklem 	default:
23819ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
23829ec7b004SRick Macklem 		goto nfsmout;
238374b8d63dSPedro F. Giffuni 	}
23849ec7b004SRick Macklem 	if (*tl++ == newnfs_true)
23859ec7b004SRick Macklem 		flags |= NFSLCK_RECLAIM;
23869ec7b004SRick Macklem 	offset = fxdr_hyper(tl);
23879ec7b004SRick Macklem 	tl += 2;
23889ec7b004SRick Macklem 	len = fxdr_hyper(tl);
23899ec7b004SRick Macklem 	tl += 2;
23909ec7b004SRick Macklem 	if (*tl == newnfs_true)
23919ec7b004SRick Macklem 		flags |= NFSLCK_OPENTOLOCK;
23929ec7b004SRick Macklem 	if (flags & NFSLCK_OPENTOLOCK) {
23939ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID);
23949ec7b004SRick Macklem 		i = fxdr_unsigned(int, *(tl+4+(NFSX_STATEID / NFSX_UNSIGNED)));
23952a45247cSRick Macklem 		if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
23962a45247cSRick Macklem 			nd->nd_repstat = NFSERR_BADXDR;
23972a45247cSRick Macklem 			goto nfsmout;
23982a45247cSRick Macklem 		}
2399222daa42SConrad Meyer 		stp = malloc(sizeof (struct nfsstate) + i,
24009ec7b004SRick Macklem 			M_NFSDSTATE, M_WAITOK);
24019ec7b004SRick Macklem 		stp->ls_ownerlen = i;
24029ec7b004SRick Macklem 		stp->ls_op = nd->nd_rp;
24039ec7b004SRick Macklem 		stp->ls_seq = fxdr_unsigned(int, *tl++);
24049ec7b004SRick Macklem 		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
24059ec7b004SRick Macklem 		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
24069ec7b004SRick Macklem 			NFSX_STATEIDOTHER);
24079ec7b004SRick Macklem 		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
240890d2dfabSRick Macklem 
240990d2dfabSRick Macklem 		/*
241090d2dfabSRick Macklem 		 * For the special stateid of other all 0s and seqid == 1, set
241190d2dfabSRick Macklem 		 * the stateid to the current stateid, if it is set.
241290d2dfabSRick Macklem 		 */
241390d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0 &&
241490d2dfabSRick Macklem 		    stp->ls_stateid.seqid == 1 &&
241590d2dfabSRick Macklem 		    stp->ls_stateid.other[0] == 0 &&
241690d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0 &&
241790d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0) {
241890d2dfabSRick Macklem 			if ((nd->nd_flag & ND_CURSTATEID) != 0) {
241990d2dfabSRick Macklem 				stp->ls_stateid = nd->nd_curstateid;
242090d2dfabSRick Macklem 				stp->ls_stateid.seqid = 0;
242190d2dfabSRick Macklem 			} else {
242290d2dfabSRick Macklem 				nd->nd_repstat = NFSERR_BADSTATEID;
242390d2dfabSRick Macklem 				goto nfsmout;
242490d2dfabSRick Macklem 			}
242590d2dfabSRick Macklem 		}
242690d2dfabSRick Macklem 
24279ec7b004SRick Macklem 		stp->ls_opentolockseq = fxdr_unsigned(int, *tl++);
24289ec7b004SRick Macklem 		clientid.lval[0] = *tl++;
24299ec7b004SRick Macklem 		clientid.lval[1] = *tl++;
2430c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2431c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
2432c59e4cc3SRick Macklem 				clientid.qval = nd->nd_clientid.qval;
2433c59e4cc3SRick Macklem 			else if (nd->nd_clientid.qval != clientid.qval)
2434c59e4cc3SRick Macklem 				printf("EEK3 multiple clids\n");
24359ec7b004SRick Macklem 		} else {
2436c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
2437c59e4cc3SRick Macklem 				printf("EEK! no clientid from session\n");
24389ec7b004SRick Macklem 			nd->nd_flag |= ND_IMPLIEDCLID;
24399ec7b004SRick Macklem 			nd->nd_clientid.qval = clientid.qval;
24409ec7b004SRick Macklem 		}
24419ec7b004SRick Macklem 		error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
24429ec7b004SRick Macklem 		if (error)
24439ec7b004SRick Macklem 			goto nfsmout;
24449ec7b004SRick Macklem 	} else {
24459ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
2446222daa42SConrad Meyer 		stp = malloc(sizeof (struct nfsstate),
24479ec7b004SRick Macklem 			M_NFSDSTATE, M_WAITOK);
24489ec7b004SRick Macklem 		stp->ls_ownerlen = 0;
24499ec7b004SRick Macklem 		stp->ls_op = nd->nd_rp;
24509ec7b004SRick Macklem 		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
24519ec7b004SRick Macklem 		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
24529ec7b004SRick Macklem 			NFSX_STATEIDOTHER);
24539ec7b004SRick Macklem 		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
245490d2dfabSRick Macklem 
245590d2dfabSRick Macklem 		/*
245690d2dfabSRick Macklem 		 * For the special stateid of other all 0s and seqid == 1, set
245790d2dfabSRick Macklem 		 * the stateid to the current stateid, if it is set.
245890d2dfabSRick Macklem 		 */
245990d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0 &&
246090d2dfabSRick Macklem 		    stp->ls_stateid.seqid == 1 &&
246190d2dfabSRick Macklem 		    stp->ls_stateid.other[0] == 0 &&
246290d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0 &&
246390d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0) {
246490d2dfabSRick Macklem 			if ((nd->nd_flag & ND_CURSTATEID) != 0) {
246590d2dfabSRick Macklem 				stp->ls_stateid = nd->nd_curstateid;
246690d2dfabSRick Macklem 				stp->ls_stateid.seqid = 0;
246790d2dfabSRick Macklem 			} else {
246890d2dfabSRick Macklem 				nd->nd_repstat = NFSERR_BADSTATEID;
246990d2dfabSRick Macklem 				goto nfsmout;
247090d2dfabSRick Macklem 			}
247190d2dfabSRick Macklem 		}
247290d2dfabSRick Macklem 
24739ec7b004SRick Macklem 		stp->ls_seq = fxdr_unsigned(int, *tl);
24749ec7b004SRick Macklem 		clientid.lval[0] = stp->ls_stateid.other[0];
24759ec7b004SRick Macklem 		clientid.lval[1] = stp->ls_stateid.other[1];
2476c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2477c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
2478c59e4cc3SRick Macklem 				clientid.qval = nd->nd_clientid.qval;
2479c59e4cc3SRick Macklem 			else if (nd->nd_clientid.qval != clientid.qval)
2480c59e4cc3SRick Macklem 				printf("EEK4 multiple clids\n");
24819ec7b004SRick Macklem 		} else {
2482c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
2483c59e4cc3SRick Macklem 				printf("EEK! no clientid from session\n");
24849ec7b004SRick Macklem 			nd->nd_flag |= ND_IMPLIEDCLID;
24859ec7b004SRick Macklem 			nd->nd_clientid.qval = clientid.qval;
24869ec7b004SRick Macklem 		}
24879ec7b004SRick Macklem 	}
2488222daa42SConrad Meyer 	lop = malloc(sizeof (struct nfslock),
24899ec7b004SRick Macklem 		M_NFSDLOCK, M_WAITOK);
24909ec7b004SRick Macklem 	lop->lo_first = offset;
24919ec7b004SRick Macklem 	if (len == NFS64BITSSET) {
24929ec7b004SRick Macklem 		lop->lo_end = NFS64BITSSET;
24939ec7b004SRick Macklem 	} else {
24949ec7b004SRick Macklem 		lop->lo_end = offset + len;
24959ec7b004SRick Macklem 		if (lop->lo_end <= lop->lo_first)
24969ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
24979ec7b004SRick Macklem 	}
24989ec7b004SRick Macklem 	lop->lo_flags = lflags;
24999ec7b004SRick Macklem 	stp->ls_flags = flags;
25009ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
25019ec7b004SRick Macklem 
25029ec7b004SRick Macklem 	/*
25039ec7b004SRick Macklem 	 * Do basic access checking.
25049ec7b004SRick Macklem 	 */
25055d3fe02cSRick Macklem 	if (!nd->nd_repstat && vp->v_type != VREG) {
25065d3fe02cSRick Macklem 	    if (vp->v_type == VDIR)
25079ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_ISDIR;
25089ec7b004SRick Macklem 	    else
25099ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
25109ec7b004SRick Macklem 	}
25119ec7b004SRick Macklem 	if (!nd->nd_repstat) {
25129ec7b004SRick Macklem 	    if (lflags & NFSLCK_WRITE) {
25138da45f2cSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
25149ec7b004SRick Macklem 		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
25158da45f2cSRick Macklem 		    NFSACCCHK_VPISLOCKED, NULL);
25169ec7b004SRick Macklem 	    } else {
25178da45f2cSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
25189ec7b004SRick Macklem 		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
25198da45f2cSRick Macklem 		    NFSACCCHK_VPISLOCKED, NULL);
25209ec7b004SRick Macklem 		if (nd->nd_repstat)
25218da45f2cSRick Macklem 		    nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
25229ec7b004SRick Macklem 			nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
25238da45f2cSRick Macklem 			NFSACCCHK_VPISLOCKED, NULL);
25249ec7b004SRick Macklem 	    }
25259ec7b004SRick Macklem 	}
25269ec7b004SRick Macklem 
25279ec7b004SRick Macklem 	/*
25289ec7b004SRick Macklem 	 * We call nfsrv_lockctrl() even if nd_repstat set, so that the
25299ec7b004SRick Macklem 	 * seqid# gets updated. nfsrv_lockctrl() will return the value
25309ec7b004SRick Macklem 	 * of nd_repstat, if it gets that far.
25319ec7b004SRick Macklem 	 */
25329ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
25339ec7b004SRick Macklem 		&stateid, exp, nd, p);
25349ec7b004SRick Macklem 	if (lop)
2535222daa42SConrad Meyer 		free(lop, M_NFSDLOCK);
25369ec7b004SRick Macklem 	if (stp)
2537222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
25389ec7b004SRick Macklem 	if (!nd->nd_repstat) {
253990d2dfabSRick Macklem 		/* For NFSv4.1, set the Current StateID. */
254090d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0) {
254190d2dfabSRick Macklem 			nd->nd_curstateid = stateid;
254290d2dfabSRick Macklem 			nd->nd_flag |= ND_CURSTATEID;
254390d2dfabSRick Macklem 		}
25449ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
25459ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
25469ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
25479ec7b004SRick Macklem 	} else if (nd->nd_repstat == NFSERR_DENIED) {
25489ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
25499ec7b004SRick Macklem 		txdr_hyper(cf.cl_first, tl);
25509ec7b004SRick Macklem 		tl += 2;
25519ec7b004SRick Macklem 		if (cf.cl_end == NFS64BITSSET)
25529ec7b004SRick Macklem 			len = NFS64BITSSET;
25539ec7b004SRick Macklem 		else
25549ec7b004SRick Macklem 			len = cf.cl_end - cf.cl_first;
25559ec7b004SRick Macklem 		txdr_hyper(len, tl);
25569ec7b004SRick Macklem 		tl += 2;
25579ec7b004SRick Macklem 		if (cf.cl_flags == NFSLCK_WRITE)
25589ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
25599ec7b004SRick Macklem 		else
25609ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
25619ec7b004SRick Macklem 		*tl++ = stateid.other[0];
25629ec7b004SRick Macklem 		*tl = stateid.other[1];
25639ec7b004SRick Macklem 		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
25649ec7b004SRick Macklem 	}
25659ec7b004SRick Macklem 	vput(vp);
2566a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
25679ec7b004SRick Macklem 	return (0);
25689ec7b004SRick Macklem nfsmout:
25699ec7b004SRick Macklem 	vput(vp);
25709ec7b004SRick Macklem 	if (stp)
2571222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
2572a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
25739ec7b004SRick Macklem 	return (error);
25749ec7b004SRick Macklem }
25759ec7b004SRick Macklem 
25769ec7b004SRick Macklem /*
25779ec7b004SRick Macklem  * nfsv4 lock test service
25789ec7b004SRick Macklem  */
2579b9cc3262SRyan Moeller int
25809ec7b004SRick Macklem nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram,
2581af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
25829ec7b004SRick Macklem {
25839ec7b004SRick Macklem 	u_int32_t *tl;
25849ec7b004SRick Macklem 	int i;
25859ec7b004SRick Macklem 	struct nfsstate *stp = NULL;
25869ec7b004SRick Macklem 	struct nfslock lo, *lop = &lo;
25879ec7b004SRick Macklem 	struct nfslockconflict cf;
25889ec7b004SRick Macklem 	int error = 0;
25899ec7b004SRick Macklem 	nfsv4stateid_t stateid;
25909ec7b004SRick Macklem 	nfsquad_t clientid;
25919ec7b004SRick Macklem 	u_int64_t len;
2592af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
25939ec7b004SRick Macklem 
25949ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
25959ec7b004SRick Macklem 	i = fxdr_unsigned(int, *(tl + 7));
25962a45247cSRick Macklem 	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
25972a45247cSRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
25982a45247cSRick Macklem 		goto nfsmout;
25992a45247cSRick Macklem 	}
2600222daa42SConrad Meyer 	stp = malloc(sizeof (struct nfsstate) + i,
26019ec7b004SRick Macklem 	    M_NFSDSTATE, M_WAITOK);
26029ec7b004SRick Macklem 	stp->ls_ownerlen = i;
26039ec7b004SRick Macklem 	stp->ls_op = NULL;
26049ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_TEST;
26059ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
26069ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
26079ec7b004SRick Macklem 	switch (i) {
26089ec7b004SRick Macklem 	case NFSV4LOCKT_READW:
26099ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_BLOCKING;
26109ec7b004SRick Macklem 	case NFSV4LOCKT_READ:
26119ec7b004SRick Macklem 		lo.lo_flags = NFSLCK_READ;
26129ec7b004SRick Macklem 		break;
26139ec7b004SRick Macklem 	case NFSV4LOCKT_WRITEW:
26149ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_BLOCKING;
26159ec7b004SRick Macklem 	case NFSV4LOCKT_WRITE:
26169ec7b004SRick Macklem 		lo.lo_flags = NFSLCK_WRITE;
26179ec7b004SRick Macklem 		break;
26189ec7b004SRick Macklem 	default:
26199ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
26209ec7b004SRick Macklem 		goto nfsmout;
262174b8d63dSPedro F. Giffuni 	}
26229ec7b004SRick Macklem 	lo.lo_first = fxdr_hyper(tl);
26239ec7b004SRick Macklem 	tl += 2;
26249ec7b004SRick Macklem 	len = fxdr_hyper(tl);
26259ec7b004SRick Macklem 	if (len == NFS64BITSSET) {
26269ec7b004SRick Macklem 		lo.lo_end = NFS64BITSSET;
26279ec7b004SRick Macklem 	} else {
26289ec7b004SRick Macklem 		lo.lo_end = lo.lo_first + len;
26299ec7b004SRick Macklem 		if (lo.lo_end <= lo.lo_first)
26309ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
26319ec7b004SRick Macklem 	}
26329ec7b004SRick Macklem 	tl += 2;
26339ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
26349ec7b004SRick Macklem 	clientid.lval[1] = *tl;
2635c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2636c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2637c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
2638c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
2639c59e4cc3SRick Macklem 			printf("EEK5 multiple clids\n");
26409ec7b004SRick Macklem 	} else {
2641c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2642c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
26439ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
26449ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
26459ec7b004SRick Macklem 	}
26469ec7b004SRick Macklem 	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
26479ec7b004SRick Macklem 	if (error)
26489ec7b004SRick Macklem 		goto nfsmout;
26495d3fe02cSRick Macklem 	if (!nd->nd_repstat && vp->v_type != VREG) {
26505d3fe02cSRick Macklem 	    if (vp->v_type == VDIR)
26519ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_ISDIR;
26529ec7b004SRick Macklem 	    else
26539ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
26549ec7b004SRick Macklem 	}
26559ec7b004SRick Macklem 	if (!nd->nd_repstat)
26569ec7b004SRick Macklem 	  nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
26579ec7b004SRick Macklem 	    &stateid, exp, nd, p);
26589ec7b004SRick Macklem 	if (nd->nd_repstat) {
26599ec7b004SRick Macklem 	    if (nd->nd_repstat == NFSERR_DENIED) {
26609ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
26619ec7b004SRick Macklem 		txdr_hyper(cf.cl_first, tl);
26629ec7b004SRick Macklem 		tl += 2;
26639ec7b004SRick Macklem 		if (cf.cl_end == NFS64BITSSET)
26649ec7b004SRick Macklem 			len = NFS64BITSSET;
26659ec7b004SRick Macklem 		else
26669ec7b004SRick Macklem 			len = cf.cl_end - cf.cl_first;
26679ec7b004SRick Macklem 		txdr_hyper(len, tl);
26689ec7b004SRick Macklem 		tl += 2;
26699ec7b004SRick Macklem 		if (cf.cl_flags == NFSLCK_WRITE)
26709ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
26719ec7b004SRick Macklem 		else
26729ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
26739ec7b004SRick Macklem 		*tl++ = stp->ls_stateid.other[0];
26749ec7b004SRick Macklem 		*tl = stp->ls_stateid.other[1];
26759ec7b004SRick Macklem 		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
26769ec7b004SRick Macklem 	    }
26779ec7b004SRick Macklem 	}
26789ec7b004SRick Macklem 	vput(vp);
26795ecc225fSConrad Meyer 	if (stp)
2680222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
2681a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
26829ec7b004SRick Macklem 	return (0);
26839ec7b004SRick Macklem nfsmout:
26849ec7b004SRick Macklem 	vput(vp);
26859ec7b004SRick Macklem 	if (stp)
2686222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
2687a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
26889ec7b004SRick Macklem 	return (error);
26899ec7b004SRick Macklem }
26909ec7b004SRick Macklem 
26919ec7b004SRick Macklem /*
26929ec7b004SRick Macklem  * nfsv4 unlock service
26939ec7b004SRick Macklem  */
2694b9cc3262SRyan Moeller int
26959ec7b004SRick Macklem nfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram,
2696af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
26979ec7b004SRick Macklem {
26989ec7b004SRick Macklem 	u_int32_t *tl;
26999ec7b004SRick Macklem 	int i;
27009ec7b004SRick Macklem 	struct nfsstate *stp;
27019ec7b004SRick Macklem 	struct nfslock *lop;
27029ec7b004SRick Macklem 	int error = 0;
27039ec7b004SRick Macklem 	nfsv4stateid_t stateid;
27049ec7b004SRick Macklem 	nfsquad_t clientid;
27059ec7b004SRick Macklem 	u_int64_t len;
2706af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
27079ec7b004SRick Macklem 
27089ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED + NFSX_STATEID);
2709222daa42SConrad Meyer 	stp = malloc(sizeof (struct nfsstate),
27109ec7b004SRick Macklem 	    M_NFSDSTATE, M_WAITOK);
2711222daa42SConrad Meyer 	lop = malloc(sizeof (struct nfslock),
27129ec7b004SRick Macklem 	    M_NFSDLOCK, M_WAITOK);
27139ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_UNLOCK;
27149ec7b004SRick Macklem 	lop->lo_flags = NFSLCK_UNLOCK;
27159ec7b004SRick Macklem 	stp->ls_op = nd->nd_rp;
27169ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
27179ec7b004SRick Macklem 	switch (i) {
27189ec7b004SRick Macklem 	case NFSV4LOCKT_READW:
27199ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_BLOCKING;
27209ec7b004SRick Macklem 	case NFSV4LOCKT_READ:
27219ec7b004SRick Macklem 		break;
27229ec7b004SRick Macklem 	case NFSV4LOCKT_WRITEW:
27239ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_BLOCKING;
27249ec7b004SRick Macklem 	case NFSV4LOCKT_WRITE:
27259ec7b004SRick Macklem 		break;
27269ec7b004SRick Macklem 	default:
27279ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
27282a45247cSRick Macklem 		free(stp, M_NFSDSTATE);
27292a45247cSRick Macklem 		free(lop, M_NFSDLOCK);
27309ec7b004SRick Macklem 		goto nfsmout;
273174b8d63dSPedro F. Giffuni 	}
27329ec7b004SRick Macklem 	stp->ls_ownerlen = 0;
27339ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
27349ec7b004SRick Macklem 	stp->ls_seq = fxdr_unsigned(int, *tl++);
27359ec7b004SRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
27369ec7b004SRick Macklem 	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
27379ec7b004SRick Macklem 	    NFSX_STATEIDOTHER);
27389ec7b004SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
273990d2dfabSRick Macklem 
274090d2dfabSRick Macklem 	/*
274190d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
274290d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
274390d2dfabSRick Macklem 	 */
274490d2dfabSRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 1 &&
274590d2dfabSRick Macklem 	    stp->ls_stateid.other[0] == 0 && stp->ls_stateid.other[1] == 0 &&
274690d2dfabSRick Macklem 	    stp->ls_stateid.other[2] == 0) {
274790d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
274890d2dfabSRick Macklem 			stp->ls_stateid = nd->nd_curstateid;
274990d2dfabSRick Macklem 			stp->ls_stateid.seqid = 0;
275090d2dfabSRick Macklem 		} else {
275190d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
27520bb42627SEric van Gyzen 			free(stp, M_NFSDSTATE);
27530bb42627SEric van Gyzen 			free(lop, M_NFSDLOCK);
275490d2dfabSRick Macklem 			goto nfsmout;
275590d2dfabSRick Macklem 		}
275690d2dfabSRick Macklem 	}
275790d2dfabSRick Macklem 
27589ec7b004SRick Macklem 	lop->lo_first = fxdr_hyper(tl);
27599ec7b004SRick Macklem 	tl += 2;
27609ec7b004SRick Macklem 	len = fxdr_hyper(tl);
27619ec7b004SRick Macklem 	if (len == NFS64BITSSET) {
27629ec7b004SRick Macklem 		lop->lo_end = NFS64BITSSET;
27639ec7b004SRick Macklem 	} else {
27649ec7b004SRick Macklem 		lop->lo_end = lop->lo_first + len;
27659ec7b004SRick Macklem 		if (lop->lo_end <= lop->lo_first)
27669ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
27679ec7b004SRick Macklem 	}
27689ec7b004SRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0];
27699ec7b004SRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1];
2770c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2771c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2772c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
2773c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
2774c59e4cc3SRick Macklem 			printf("EEK6 multiple clids\n");
27759ec7b004SRick Macklem 	} else {
2776c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2777c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
27789ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
27799ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
27809ec7b004SRick Macklem 	}
27815d3fe02cSRick Macklem 	if (!nd->nd_repstat && vp->v_type != VREG) {
27825d3fe02cSRick Macklem 	    if (vp->v_type == VDIR)
27839ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_ISDIR;
27849ec7b004SRick Macklem 	    else
27859ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
27869ec7b004SRick Macklem 	}
27879ec7b004SRick Macklem 	/*
27889ec7b004SRick Macklem 	 * Call nfsrv_lockctrl() even if nd_repstat is set, so that the
27899ec7b004SRick Macklem 	 * seqid# gets incremented. nfsrv_lockctrl() will return the
27909ec7b004SRick Macklem 	 * value of nd_repstat, if it gets that far.
27919ec7b004SRick Macklem 	 */
27929ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
27939ec7b004SRick Macklem 	    &stateid, exp, nd, p);
27949ec7b004SRick Macklem 	if (stp)
2795222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
27969ec7b004SRick Macklem 	if (lop)
2797222daa42SConrad Meyer 		free(lop, M_NFSDLOCK);
27989ec7b004SRick Macklem 	if (!nd->nd_repstat) {
27999ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
28009ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
28019ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
28029ec7b004SRick Macklem 	}
28039ec7b004SRick Macklem nfsmout:
28049ec7b004SRick Macklem 	vput(vp);
2805a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
28069ec7b004SRick Macklem 	return (error);
28079ec7b004SRick Macklem }
28089ec7b004SRick Macklem 
28099ec7b004SRick Macklem /*
28109ec7b004SRick Macklem  * nfsv4 open service
28119ec7b004SRick Macklem  */
2812b9cc3262SRyan Moeller int
28139ec7b004SRick Macklem nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
2814af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t *vpp, __unused fhandle_t *fhp, struct nfsexstuff *exp)
28159ec7b004SRick Macklem {
28169ec7b004SRick Macklem 	u_int32_t *tl;
2817c59e4cc3SRick Macklem 	int i, retext;
28189ec7b004SRick Macklem 	struct nfsstate *stp = NULL;
2819b0b7d978SRick Macklem 	int error = 0, create, claim, exclusive_flag = 0, override;
28209ec7b004SRick Macklem 	u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask;
28219ec7b004SRick Macklem 	int how = NFSCREATE_UNCHECKED;
2822086f6e0cSRick Macklem 	int32_t cverf[2], tverf[2] = { 0, 0 };
28239ec7b004SRick Macklem 	vnode_t vp = NULL, dirp = NULL;
28249ec7b004SRick Macklem 	struct nfsvattr nva, dirfor, diraft;
28259ec7b004SRick Macklem 	struct nameidata named;
28269ec7b004SRick Macklem 	nfsv4stateid_t stateid, delegstateid;
28279ec7b004SRick Macklem 	nfsattrbit_t attrbits;
28289ec7b004SRick Macklem 	nfsquad_t clientid;
28299ec7b004SRick Macklem 	char *bufp = NULL;
28309ec7b004SRick Macklem 	u_long *hashp;
28319ec7b004SRick Macklem 	NFSACL_T *aclp = NULL;
2832af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
2833*dcfa3ee4SRick Macklem 	bool done_namei;
28349ec7b004SRick Macklem 
28359ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
2836c3e22f83SRick Macklem 	aclp = acl_alloc(M_WAITOK);
28379ec7b004SRick Macklem 	aclp->acl_cnt = 0;
28389ec7b004SRick Macklem #endif
28399ec7b004SRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
2840*dcfa3ee4SRick Macklem 	done_namei = false;
28419ec7b004SRick Macklem 	named.ni_cnd.cn_nameiop = 0;
28429ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
28439ec7b004SRick Macklem 	i = fxdr_unsigned(int, *(tl + 5));
28442a45247cSRick Macklem 	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
28452a45247cSRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
2846a9285ae5SZack Kirsch 		goto nfsmout;
28472a45247cSRick Macklem 	}
2848222daa42SConrad Meyer 	stp = malloc(sizeof (struct nfsstate) + i,
28499ec7b004SRick Macklem 	    M_NFSDSTATE, M_WAITOK);
28509ec7b004SRick Macklem 	stp->ls_ownerlen = i;
28519ec7b004SRick Macklem 	stp->ls_op = nd->nd_rp;
28529ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_OPEN;
28539ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
28549ec7b004SRick Macklem 	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
28559ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
2856c59e4cc3SRick Macklem 	retext = 0;
2857c59e4cc3SRick Macklem 	if ((i & (NFSV4OPEN_WANTDELEGMASK | NFSV4OPEN_WANTSIGNALDELEG |
2858c59e4cc3SRick Macklem 	    NFSV4OPEN_WANTPUSHDELEG)) != 0 && (nd->nd_flag & ND_NFSV41) != 0) {
2859c59e4cc3SRick Macklem 		retext = 1;
2860c59e4cc3SRick Macklem 		/* For now, ignore these. */
2861c59e4cc3SRick Macklem 		i &= ~(NFSV4OPEN_WANTPUSHDELEG | NFSV4OPEN_WANTSIGNALDELEG);
2862c59e4cc3SRick Macklem 		switch (i & NFSV4OPEN_WANTDELEGMASK) {
2863c59e4cc3SRick Macklem 		case NFSV4OPEN_WANTANYDELEG:
2864c59e4cc3SRick Macklem 			stp->ls_flags |= (NFSLCK_WANTRDELEG |
2865c59e4cc3SRick Macklem 			    NFSLCK_WANTWDELEG);
2866c59e4cc3SRick Macklem 			i &= ~NFSV4OPEN_WANTDELEGMASK;
2867c59e4cc3SRick Macklem 			break;
2868c59e4cc3SRick Macklem 		case NFSV4OPEN_WANTREADDELEG:
2869c59e4cc3SRick Macklem 			stp->ls_flags |= NFSLCK_WANTRDELEG;
2870c59e4cc3SRick Macklem 			i &= ~NFSV4OPEN_WANTDELEGMASK;
2871c59e4cc3SRick Macklem 			break;
2872c59e4cc3SRick Macklem 		case NFSV4OPEN_WANTWRITEDELEG:
2873c59e4cc3SRick Macklem 			stp->ls_flags |= NFSLCK_WANTWDELEG;
2874c59e4cc3SRick Macklem 			i &= ~NFSV4OPEN_WANTDELEGMASK;
2875c59e4cc3SRick Macklem 			break;
2876c59e4cc3SRick Macklem 		case NFSV4OPEN_WANTNODELEG:
2877c59e4cc3SRick Macklem 			stp->ls_flags |= NFSLCK_WANTNODELEG;
2878c59e4cc3SRick Macklem 			i &= ~NFSV4OPEN_WANTDELEGMASK;
2879c59e4cc3SRick Macklem 			break;
2880c59e4cc3SRick Macklem 		case NFSV4OPEN_WANTCANCEL:
2881c59e4cc3SRick Macklem 			printf("NFSv4: ignore Open WantCancel\n");
2882c59e4cc3SRick Macklem 			i &= ~NFSV4OPEN_WANTDELEGMASK;
2883c59e4cc3SRick Macklem 			break;
2884c59e4cc3SRick Macklem 		default:
2885c59e4cc3SRick Macklem 			/* nd_repstat will be set to NFSERR_INVAL below. */
2886c59e4cc3SRick Macklem 			break;
288774b8d63dSPedro F. Giffuni 		}
2888c59e4cc3SRick Macklem 	}
28899ec7b004SRick Macklem 	switch (i) {
28909ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSREAD:
28919ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_READACCESS;
28929ec7b004SRick Macklem 		break;
28939ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSWRITE:
28949ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_WRITEACCESS;
28959ec7b004SRick Macklem 		break;
28969ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSBOTH:
28979ec7b004SRick Macklem 		stp->ls_flags |= (NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
28989ec7b004SRick Macklem 		break;
28999ec7b004SRick Macklem 	default:
29009ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
290174b8d63dSPedro F. Giffuni 	}
29029ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
29039ec7b004SRick Macklem 	switch (i) {
29049ec7b004SRick Macklem 	case NFSV4OPEN_DENYNONE:
29059ec7b004SRick Macklem 		break;
29069ec7b004SRick Macklem 	case NFSV4OPEN_DENYREAD:
29079ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_READDENY;
29089ec7b004SRick Macklem 		break;
29099ec7b004SRick Macklem 	case NFSV4OPEN_DENYWRITE:
29109ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_WRITEDENY;
29119ec7b004SRick Macklem 		break;
29129ec7b004SRick Macklem 	case NFSV4OPEN_DENYBOTH:
29139ec7b004SRick Macklem 		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
29149ec7b004SRick Macklem 		break;
29159ec7b004SRick Macklem 	default:
29169ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
291774b8d63dSPedro F. Giffuni 	}
29189ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
29199ec7b004SRick Macklem 	clientid.lval[1] = *tl;
2920c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2921c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2922c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
2923c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
2924c59e4cc3SRick Macklem 			printf("EEK7 multiple clids\n");
29259ec7b004SRick Macklem 	} else {
2926c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2927c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
29289ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
29299ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
29309ec7b004SRick Macklem 	}
29319ec7b004SRick Macklem 	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2932a9285ae5SZack Kirsch 	if (error)
2933a9285ae5SZack Kirsch 		goto nfsmout;
29349ec7b004SRick Macklem 	NFSVNO_ATTRINIT(&nva);
29359ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
29369ec7b004SRick Macklem 	create = fxdr_unsigned(int, *tl);
29379ec7b004SRick Macklem 	if (!nd->nd_repstat)
293890d2dfabSRick Macklem 		nd->nd_repstat = nfsvno_getattr(dp, &dirfor, nd, p, 0, NULL);
29399ec7b004SRick Macklem 	if (create == NFSV4OPEN_CREATE) {
29409ec7b004SRick Macklem 		nva.na_type = VREG;
29419ec7b004SRick Macklem 		nva.na_mode = 0;
29429ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
29439ec7b004SRick Macklem 		how = fxdr_unsigned(int, *tl);
29449ec7b004SRick Macklem 		switch (how) {
29459ec7b004SRick Macklem 		case NFSCREATE_UNCHECKED:
29469ec7b004SRick Macklem 		case NFSCREATE_GUARDED:
2947d8a5961fSMarcelo Araujo 			error = nfsv4_sattr(nd, NULL, &nva, &attrbits, aclp, p);
2948a9285ae5SZack Kirsch 			if (error)
2949a9285ae5SZack Kirsch 				goto nfsmout;
29509ec7b004SRick Macklem 			/*
29519ec7b004SRick Macklem 			 * If the na_gid being set is the same as that of
29529ec7b004SRick Macklem 			 * the directory it is going in, clear it, since
29539ec7b004SRick Macklem 			 * that is what will be set by default. This allows
29549ec7b004SRick Macklem 			 * a user that isn't in that group to do the create.
29559ec7b004SRick Macklem 			 */
29569ec7b004SRick Macklem 			if (!nd->nd_repstat && NFSVNO_ISSETGID(&nva) &&
29579ec7b004SRick Macklem 			    nva.na_gid == dirfor.na_gid)
29589ec7b004SRick Macklem 				NFSVNO_UNSET(&nva, gid);
29599ec7b004SRick Macklem 			if (!nd->nd_repstat)
29609ec7b004SRick Macklem 				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
29619ec7b004SRick Macklem 			break;
29629ec7b004SRick Macklem 		case NFSCREATE_EXCLUSIVE:
29639ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2964086f6e0cSRick Macklem 			cverf[0] = *tl++;
2965086f6e0cSRick Macklem 			cverf[1] = *tl;
29669ec7b004SRick Macklem 			break;
2967c59e4cc3SRick Macklem 		case NFSCREATE_EXCLUSIVE41:
2968c59e4cc3SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2969c59e4cc3SRick Macklem 			cverf[0] = *tl++;
2970c59e4cc3SRick Macklem 			cverf[1] = *tl;
2971b4645807SRick Macklem 			error = nfsv4_sattr(nd, NULL, &nva, &attrbits, aclp, p);
2972c59e4cc3SRick Macklem 			if (error != 0)
2973c59e4cc3SRick Macklem 				goto nfsmout;
2974c59e4cc3SRick Macklem 			if (NFSISSET_ATTRBIT(&attrbits,
2975c59e4cc3SRick Macklem 			    NFSATTRBIT_TIMEACCESSSET))
2976c59e4cc3SRick Macklem 				nd->nd_repstat = NFSERR_INVAL;
2977c59e4cc3SRick Macklem 			/*
2978c59e4cc3SRick Macklem 			 * If the na_gid being set is the same as that of
2979c59e4cc3SRick Macklem 			 * the directory it is going in, clear it, since
2980c59e4cc3SRick Macklem 			 * that is what will be set by default. This allows
2981c59e4cc3SRick Macklem 			 * a user that isn't in that group to do the create.
2982c59e4cc3SRick Macklem 			 */
2983c59e4cc3SRick Macklem 			if (nd->nd_repstat == 0 && NFSVNO_ISSETGID(&nva) &&
2984c59e4cc3SRick Macklem 			    nva.na_gid == dirfor.na_gid)
2985c59e4cc3SRick Macklem 				NFSVNO_UNSET(&nva, gid);
2986c59e4cc3SRick Macklem 			if (nd->nd_repstat == 0)
2987c59e4cc3SRick Macklem 				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
2988c59e4cc3SRick Macklem 			break;
29899ec7b004SRick Macklem 		default:
29909ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_BADXDR;
2991a9285ae5SZack Kirsch 			goto nfsmout;
299274b8d63dSPedro F. Giffuni 		}
29939ec7b004SRick Macklem 	} else if (create != NFSV4OPEN_NOCREATE) {
29949ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
2995a9285ae5SZack Kirsch 		goto nfsmout;
29969ec7b004SRick Macklem 	}
29979ec7b004SRick Macklem 
29989ec7b004SRick Macklem 	/*
29999ec7b004SRick Macklem 	 * Now, handle the claim, which usually includes looking up a
30009ec7b004SRick Macklem 	 * name in the directory referenced by dp. The exception is
30019ec7b004SRick Macklem 	 * NFSV4OPEN_CLAIMPREVIOUS.
30029ec7b004SRick Macklem 	 */
30039ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
30049ec7b004SRick Macklem 	claim = fxdr_unsigned(int, *tl);
3005b3d4c70dSRick Macklem 	if (claim == NFSV4OPEN_CLAIMDELEGATECUR || claim ==
3006b3d4c70dSRick Macklem 	    NFSV4OPEN_CLAIMDELEGATECURFH) {
30079ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
30089ec7b004SRick Macklem 		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
30099ec7b004SRick Macklem 		NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
30109ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_DELEGCUR;
3011d80a903aSRick Macklem 	} else if (claim == NFSV4OPEN_CLAIMDELEGATEPREV || claim ==
3012d80a903aSRick Macklem 	    NFSV4OPEN_CLAIMDELEGATEPREVFH) {
30139ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_DELEGPREV;
30149ec7b004SRick Macklem 	}
30159ec7b004SRick Macklem 	if (claim == NFSV4OPEN_CLAIMNULL || claim == NFSV4OPEN_CLAIMDELEGATECUR
30169ec7b004SRick Macklem 	    || claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
30179ec7b004SRick Macklem 		if (!nd->nd_repstat && create == NFSV4OPEN_CREATE &&
30189ec7b004SRick Macklem 		    claim != NFSV4OPEN_CLAIMNULL)
30199ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
30209ec7b004SRick Macklem 		if (nd->nd_repstat) {
30219ec7b004SRick Macklem 			nd->nd_repstat = nfsrv_opencheck(clientid,
30229ec7b004SRick Macklem 			    &stateid, stp, NULL, nd, p, nd->nd_repstat);
3023a9285ae5SZack Kirsch 			goto nfsmout;
30249ec7b004SRick Macklem 		}
30259ec7b004SRick Macklem 		if (create == NFSV4OPEN_CREATE)
30269ec7b004SRick Macklem 		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
302765127e98SMateusz Guzik 			LOCKPARENT | LOCKLEAF | NOCACHE);
30289ec7b004SRick Macklem 		else
30299ec7b004SRick Macklem 		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
303065127e98SMateusz Guzik 			LOCKLEAF);
30319ec7b004SRick Macklem 		nfsvno_setpathbuf(&named, &bufp, &hashp);
30329ec7b004SRick Macklem 		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
30339ec7b004SRick Macklem 		if (error) {
30349ec7b004SRick Macklem 			vrele(dp);
30359ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
30369ec7b004SRick Macklem 			acl_free(aclp);
30379ec7b004SRick Macklem #endif
3038222daa42SConrad Meyer 			free(stp, M_NFSDSTATE);
30399ec7b004SRick Macklem 			nfsvno_relpathbuf(&named);
3040a9285ae5SZack Kirsch 			NFSEXITCODE2(error, nd);
30419ec7b004SRick Macklem 			return (error);
30429ec7b004SRick Macklem 		}
30439ec7b004SRick Macklem 		if (!nd->nd_repstat) {
30449ec7b004SRick Macklem 			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp,
3045ef7d2c1fSMateusz Guzik 			    &dirp);
3046*dcfa3ee4SRick Macklem 			done_namei = true;
30479ec7b004SRick Macklem 		} else {
30489ec7b004SRick Macklem 			vrele(dp);
30499ec7b004SRick Macklem 			nfsvno_relpathbuf(&named);
30509ec7b004SRick Macklem 		}
30519ec7b004SRick Macklem 		if (create == NFSV4OPEN_CREATE) {
30529ec7b004SRick Macklem 		    switch (how) {
30539ec7b004SRick Macklem 		    case NFSCREATE_UNCHECKED:
3054*dcfa3ee4SRick Macklem 			if (done_namei && named.ni_vp != NULL) {
30559ec7b004SRick Macklem 				/*
30569ec7b004SRick Macklem 				 * Clear the setable attribute bits, except
30579ec7b004SRick Macklem 				 * for Size, if it is being truncated.
30589ec7b004SRick Macklem 				 */
30599ec7b004SRick Macklem 				NFSZERO_ATTRBIT(&attrbits);
30609ec7b004SRick Macklem 				if (NFSVNO_ISSETSIZE(&nva))
30619ec7b004SRick Macklem 					NFSSETBIT_ATTRBIT(&attrbits,
30629ec7b004SRick Macklem 					    NFSATTRBIT_SIZE);
30639ec7b004SRick Macklem 			}
30649ec7b004SRick Macklem 			break;
30659ec7b004SRick Macklem 		    case NFSCREATE_GUARDED:
3066*dcfa3ee4SRick Macklem 			if (done_namei && named.ni_vp != NULL &&
3067*dcfa3ee4SRick Macklem 			    nd->nd_repstat == 0)
30689ec7b004SRick Macklem 				nd->nd_repstat = EEXIST;
30699ec7b004SRick Macklem 			break;
30709ec7b004SRick Macklem 		    case NFSCREATE_EXCLUSIVE:
30719ec7b004SRick Macklem 			exclusive_flag = 1;
3072*dcfa3ee4SRick Macklem 			if (done_namei && named.ni_vp == NULL)
30739ec7b004SRick Macklem 				nva.na_mode = 0;
3074c59e4cc3SRick Macklem 			break;
3075c59e4cc3SRick Macklem 		    case NFSCREATE_EXCLUSIVE41:
3076c59e4cc3SRick Macklem 			exclusive_flag = 1;
3077c59e4cc3SRick Macklem 			break;
307874b8d63dSPedro F. Giffuni 		    }
30799ec7b004SRick Macklem 		}
30809ec7b004SRick Macklem 		nfsvno_open(nd, &named, clientid, &stateid, stp,
30819ec7b004SRick Macklem 		    &exclusive_flag, &nva, cverf, create, aclp, &attrbits,
3082*dcfa3ee4SRick Macklem 		    nd->nd_cred, done_namei, exp, &vp);
3083c59e4cc3SRick Macklem 	} else if (claim == NFSV4OPEN_CLAIMPREVIOUS || claim ==
3084d80a903aSRick Macklem 	    NFSV4OPEN_CLAIMFH || claim == NFSV4OPEN_CLAIMDELEGATECURFH ||
3085d80a903aSRick Macklem 	    claim == NFSV4OPEN_CLAIMDELEGATEPREVFH) {
3086c59e4cc3SRick Macklem 		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
30879ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
30889ec7b004SRick Macklem 			i = fxdr_unsigned(int, *tl);
30899ec7b004SRick Macklem 			switch (i) {
30909ec7b004SRick Macklem 			case NFSV4OPEN_DELEGATEREAD:
30919ec7b004SRick Macklem 				stp->ls_flags |= NFSLCK_DELEGREAD;
30929ec7b004SRick Macklem 				break;
30939ec7b004SRick Macklem 			case NFSV4OPEN_DELEGATEWRITE:
30949ec7b004SRick Macklem 				stp->ls_flags |= NFSLCK_DELEGWRITE;
30959ec7b004SRick Macklem 			case NFSV4OPEN_DELEGATENONE:
30969ec7b004SRick Macklem 				break;
30979ec7b004SRick Macklem 			default:
30989ec7b004SRick Macklem 				nd->nd_repstat = NFSERR_BADXDR;
3099a9285ae5SZack Kirsch 				goto nfsmout;
310074b8d63dSPedro F. Giffuni 			}
31019ec7b004SRick Macklem 			stp->ls_flags |= NFSLCK_RECLAIM;
3102c59e4cc3SRick Macklem 		} else {
3103c59e4cc3SRick Macklem 			if (nd->nd_repstat == 0 && create == NFSV4OPEN_CREATE)
3104c59e4cc3SRick Macklem 				nd->nd_repstat = NFSERR_INVAL;
3105c59e4cc3SRick Macklem 		}
31069ec7b004SRick Macklem 		vp = dp;
310798f234f3SZack Kirsch 		NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
3108abd80ddbSMateusz Guzik 		if (!VN_IS_DOOMED(vp))
3109629fa50eSRick Macklem 			nd->nd_repstat = nfsrv_opencheck(clientid, &stateid,
3110629fa50eSRick Macklem 			    stp, vp, nd, p, nd->nd_repstat);
3111629fa50eSRick Macklem 		else
3112629fa50eSRick Macklem 			nd->nd_repstat = NFSERR_PERM;
31139ec7b004SRick Macklem 	} else {
31149ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
3115a9285ae5SZack Kirsch 		goto nfsmout;
31169ec7b004SRick Macklem 	}
31179ec7b004SRick Macklem 
31189ec7b004SRick Macklem 	/*
31199ec7b004SRick Macklem 	 * Do basic access checking.
31209ec7b004SRick Macklem 	 */
31215d3fe02cSRick Macklem 	if (!nd->nd_repstat && vp->v_type != VREG) {
3122de67b496SRick Macklem 		/*
3123de67b496SRick Macklem 		 * The IETF working group decided that this is the correct
3124de67b496SRick Macklem 		 * error return for all non-regular files.
3125de67b496SRick Macklem 		 */
3126d8a5961fSMarcelo Araujo 		nd->nd_repstat = (vp->v_type == VDIR) ? NFSERR_ISDIR : NFSERR_SYMLINK;
31279ec7b004SRick Macklem 	}
3128b0b7d978SRick Macklem 
3129b0b7d978SRick Macklem 	/*
3130b0b7d978SRick Macklem 	 * If the Open is being done for a file that already exists, apply
3131b0b7d978SRick Macklem 	 * normal permission checking including for the file owner, if
3132b0b7d978SRick Macklem 	 * vfs.nfsd.v4openaccess is set.
3133b0b7d978SRick Macklem 	 * Previously, the owner was always allowed to open the file to
3134b0b7d978SRick Macklem 	 * be consistent with the NFS tradition of always allowing the
3135b0b7d978SRick Macklem 	 * owner of the file to write to the file regardless of permissions.
3136b0b7d978SRick Macklem 	 * It now appears that the Linux client expects the owner
3137b0b7d978SRick Macklem 	 * permissions to be checked for opens that are not creating the
3138b0b7d978SRick Macklem 	 * file.  I believe the correct approach is to use the Access
3139b0b7d978SRick Macklem 	 * operation's results to be consistent with NFSv3, but that is
3140b0b7d978SRick Macklem 	 * not what the current Linux client appears to be doing.
3141b0b7d978SRick Macklem 	 * Since both the Linux and OpenSolaris NFSv4 servers do this check,
3142e2fe58d6SRick Macklem 	 * I have enabled it by default.  Since Linux does not apply this
3143e2fe58d6SRick Macklem 	 * check for claim_delegate_cur, this code does the same.
3144b0b7d978SRick Macklem 	 * If this semantic change causes a problem, it can be disabled by
3145b0b7d978SRick Macklem 	 * setting the sysctl vfs.nfsd.v4openaccess to 0 to re-enable the
3146b0b7d978SRick Macklem 	 * previous semantics.
3147b0b7d978SRick Macklem 	 */
3148e2fe58d6SRick Macklem 	if (nfsrv_openaccess && create == NFSV4OPEN_NOCREATE &&
3149e2fe58d6SRick Macklem 	    (stp->ls_flags & NFSLCK_DELEGCUR) == 0)
3150b0b7d978SRick Macklem 		override = NFSACCCHK_NOOVERRIDE;
3151b0b7d978SRick Macklem 	else
3152b0b7d978SRick Macklem 		override = NFSACCCHK_ALLOWOWNER;
31539ec7b004SRick Macklem 	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_WRITEACCESS))
31548da45f2cSRick Macklem 	    nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred,
3155b0b7d978SRick Macklem 	        exp, p, override, NFSACCCHK_VPISLOCKED, NULL);
31569ec7b004SRick Macklem 	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_READACCESS)) {
31578da45f2cSRick Macklem 	    nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred,
3158b0b7d978SRick Macklem 	        exp, p, override, NFSACCCHK_VPISLOCKED, NULL);
31599ec7b004SRick Macklem 	    if (nd->nd_repstat)
31608da45f2cSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
3161b0b7d978SRick Macklem 		    nd->nd_cred, exp, p, override,
31628da45f2cSRick Macklem 		    NFSACCCHK_VPISLOCKED, NULL);
31639ec7b004SRick Macklem 	}
31649ec7b004SRick Macklem 
3165086f6e0cSRick Macklem 	if (!nd->nd_repstat) {
316690d2dfabSRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
3167086f6e0cSRick Macklem 		if (!nd->nd_repstat) {
3168086f6e0cSRick Macklem 			tverf[0] = nva.na_atime.tv_sec;
3169086f6e0cSRick Macklem 			tverf[1] = nva.na_atime.tv_nsec;
3170086f6e0cSRick Macklem 		}
3171086f6e0cSRick Macklem 	}
3172086f6e0cSRick Macklem 	if (!nd->nd_repstat && exclusive_flag && (cverf[0] != tverf[0] ||
3173086f6e0cSRick Macklem 	    cverf[1] != tverf[1]))
31749ec7b004SRick Macklem 		nd->nd_repstat = EEXIST;
31759ec7b004SRick Macklem 	/*
31769ec7b004SRick Macklem 	 * Do the open locking/delegation stuff.
31779ec7b004SRick Macklem 	 */
31789ec7b004SRick Macklem 	if (!nd->nd_repstat)
31799ec7b004SRick Macklem 	    nd->nd_repstat = nfsrv_openctrl(nd, vp, &stp, clientid, &stateid,
31809ec7b004SRick Macklem 		&delegstateid, &rflags, exp, p, nva.na_filerev);
31819ec7b004SRick Macklem 
31829ec7b004SRick Macklem 	/*
31839ec7b004SRick Macklem 	 * vp must be unlocked before the call to nfsvno_getattr(dirp,...)
31849ec7b004SRick Macklem 	 * below, to avoid a deadlock with the lookup in nfsvno_namei() above.
31859ec7b004SRick Macklem 	 * (ie: Leave the NFSVOPUNLOCK() about here.)
31869ec7b004SRick Macklem 	 */
31879ec7b004SRick Macklem 	if (vp)
3188b249ce48SMateusz Guzik 		NFSVOPUNLOCK(vp);
31899ec7b004SRick Macklem 	if (stp)
3190222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
31919ec7b004SRick Macklem 	if (!nd->nd_repstat && dirp)
319290d2dfabSRick Macklem 		nd->nd_repstat = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
31939ec7b004SRick Macklem 	if (!nd->nd_repstat) {
319490d2dfabSRick Macklem 		/* For NFSv4.1, set the Current StateID. */
319590d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0) {
319690d2dfabSRick Macklem 			nd->nd_curstateid = stateid;
319790d2dfabSRick Macklem 			nd->nd_flag |= ND_CURSTATEID;
319890d2dfabSRick Macklem 		}
31999ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
32009ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
32019ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
32029ec7b004SRick Macklem 		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
32039ec7b004SRick Macklem 		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
32049ec7b004SRick Macklem 			*tl++ = newnfs_true;
32059ec7b004SRick Macklem 			*tl++ = 0;
32069ec7b004SRick Macklem 			*tl++ = 0;
32079ec7b004SRick Macklem 			*tl++ = 0;
32089ec7b004SRick Macklem 			*tl++ = 0;
32099ec7b004SRick Macklem 		} else {
32109ec7b004SRick Macklem 			*tl++ = newnfs_false;	/* Since dirp is not locked */
32119ec7b004SRick Macklem 			txdr_hyper(dirfor.na_filerev, tl);
32129ec7b004SRick Macklem 			tl += 2;
32139ec7b004SRick Macklem 			txdr_hyper(diraft.na_filerev, tl);
32149ec7b004SRick Macklem 			tl += 2;
32159ec7b004SRick Macklem 		}
32169ec7b004SRick Macklem 		*tl = txdr_unsigned(rflags & NFSV4OPEN_RFLAGS);
32179ec7b004SRick Macklem 		(void) nfsrv_putattrbit(nd, &attrbits);
32189ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
32199ec7b004SRick Macklem 		if (rflags & NFSV4OPEN_READDELEGATE)
32209ec7b004SRick Macklem 			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEREAD);
32219ec7b004SRick Macklem 		else if (rflags & NFSV4OPEN_WRITEDELEGATE)
32229ec7b004SRick Macklem 			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEWRITE);
3223c59e4cc3SRick Macklem 		else if (retext != 0) {
3224c59e4cc3SRick Macklem 			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONEEXT);
32255d54f186SRick Macklem 			if ((rflags & NFSV4OPEN_WDNOTWANTED) != 0) {
32265d54f186SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
32275d54f186SRick Macklem 				*tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
32285d54f186SRick Macklem 			} else if ((rflags & NFSV4OPEN_WDSUPPFTYPE) != 0) {
32295d54f186SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
32305d54f186SRick Macklem 				*tl = txdr_unsigned(NFSV4OPEN_NOTSUPPFTYPE);
32315d54f186SRick Macklem 			} else if ((rflags & NFSV4OPEN_WDCONTENTION) != 0) {
3232c59e4cc3SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3233c59e4cc3SRick Macklem 				*tl++ = txdr_unsigned(NFSV4OPEN_CONTENTION);
3234c59e4cc3SRick Macklem 				*tl = newnfs_false;
3235c59e4cc3SRick Macklem 			} else if ((rflags & NFSV4OPEN_WDRESOURCE) != 0) {
3236c59e4cc3SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3237c59e4cc3SRick Macklem 				*tl++ = txdr_unsigned(NFSV4OPEN_RESOURCE);
3238c59e4cc3SRick Macklem 				*tl = newnfs_false;
3239c59e4cc3SRick Macklem 			} else {
3240c59e4cc3SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3241c59e4cc3SRick Macklem 				*tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
3242c59e4cc3SRick Macklem 			}
3243c59e4cc3SRick Macklem 		} else
32449ec7b004SRick Macklem 			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONE);
32459ec7b004SRick Macklem 		if (rflags & (NFSV4OPEN_READDELEGATE|NFSV4OPEN_WRITEDELEGATE)) {
32469ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID+NFSX_UNSIGNED);
32479ec7b004SRick Macklem 			*tl++ = txdr_unsigned(delegstateid.seqid);
32489ec7b004SRick Macklem 			NFSBCOPY((caddr_t)delegstateid.other, (caddr_t)tl,
32499ec7b004SRick Macklem 			    NFSX_STATEIDOTHER);
32509ec7b004SRick Macklem 			tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
32519ec7b004SRick Macklem 			if (rflags & NFSV4OPEN_RECALL)
32529ec7b004SRick Macklem 				*tl = newnfs_true;
32539ec7b004SRick Macklem 			else
32549ec7b004SRick Macklem 				*tl = newnfs_false;
32559ec7b004SRick Macklem 			if (rflags & NFSV4OPEN_WRITEDELEGATE) {
32569ec7b004SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
32579ec7b004SRick Macklem 				*tl++ = txdr_unsigned(NFSV4OPEN_LIMITSIZE);
32589ec7b004SRick Macklem 				txdr_hyper(nva.na_size, tl);
32599ec7b004SRick Macklem 			}
32609ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
32619ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSV4ACE_ALLOWEDTYPE);
32629ec7b004SRick Macklem 			*tl++ = txdr_unsigned(0x0);
32639ec7b004SRick Macklem 			acemask = NFSV4ACE_ALLFILESMASK;
32649ec7b004SRick Macklem 			if (nva.na_mode & S_IRUSR)
32659ec7b004SRick Macklem 			    acemask |= NFSV4ACE_READMASK;
32669ec7b004SRick Macklem 			if (nva.na_mode & S_IWUSR)
32679ec7b004SRick Macklem 			    acemask |= NFSV4ACE_WRITEMASK;
32689ec7b004SRick Macklem 			if (nva.na_mode & S_IXUSR)
32699ec7b004SRick Macklem 			    acemask |= NFSV4ACE_EXECUTEMASK;
32709ec7b004SRick Macklem 			*tl = txdr_unsigned(acemask);
32719ec7b004SRick Macklem 			(void) nfsm_strtom(nd, "OWNER@", 6);
32729ec7b004SRick Macklem 		}
32739ec7b004SRick Macklem 		*vpp = vp;
32749ec7b004SRick Macklem 	} else if (vp) {
32759ec7b004SRick Macklem 		vrele(vp);
32769ec7b004SRick Macklem 	}
32779ec7b004SRick Macklem 	if (dirp)
32789ec7b004SRick Macklem 		vrele(dirp);
32799ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
32809ec7b004SRick Macklem 	acl_free(aclp);
32819ec7b004SRick Macklem #endif
3282a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
32839ec7b004SRick Macklem 	return (0);
32849ec7b004SRick Macklem nfsmout:
32859ec7b004SRick Macklem 	vrele(dp);
32869ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
32879ec7b004SRick Macklem 	acl_free(aclp);
32889ec7b004SRick Macklem #endif
32899ec7b004SRick Macklem 	if (stp)
3290222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
3291a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
32929ec7b004SRick Macklem 	return (error);
32939ec7b004SRick Macklem }
32949ec7b004SRick Macklem 
32959ec7b004SRick Macklem /*
32969ec7b004SRick Macklem  * nfsv4 close service
32979ec7b004SRick Macklem  */
3298b9cc3262SRyan Moeller int
32999ec7b004SRick Macklem nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram,
3300af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
33019ec7b004SRick Macklem {
33029ec7b004SRick Macklem 	u_int32_t *tl;
33039ec7b004SRick Macklem 	struct nfsstate st, *stp = &st;
330490d2dfabSRick Macklem 	int error = 0, writeacc;
33059ec7b004SRick Macklem 	nfsv4stateid_t stateid;
33069ec7b004SRick Macklem 	nfsquad_t clientid;
330790d2dfabSRick Macklem 	struct nfsvattr na;
3308af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
33099ec7b004SRick Macklem 
33109ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
33119ec7b004SRick Macklem 	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
33129ec7b004SRick Macklem 	stp->ls_ownerlen = 0;
33139ec7b004SRick Macklem 	stp->ls_op = nd->nd_rp;
33149ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
33159ec7b004SRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
33169ec7b004SRick Macklem 	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
33179ec7b004SRick Macklem 	    NFSX_STATEIDOTHER);
331890d2dfabSRick Macklem 
331990d2dfabSRick Macklem 	/*
332090d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
332190d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
332290d2dfabSRick Macklem 	 */
332390d2dfabSRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 1 &&
332490d2dfabSRick Macklem 	    stp->ls_stateid.other[0] == 0 && stp->ls_stateid.other[1] == 0 &&
332590d2dfabSRick Macklem 	    stp->ls_stateid.other[2] == 0) {
332690d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0)
332790d2dfabSRick Macklem 			stp->ls_stateid = nd->nd_curstateid;
332890d2dfabSRick Macklem 		else {
332990d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
333090d2dfabSRick Macklem 			goto nfsmout;
333190d2dfabSRick Macklem 		}
333290d2dfabSRick Macklem 	}
333390d2dfabSRick Macklem 
33349ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_CLOSE;
33359ec7b004SRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0];
33369ec7b004SRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1];
3337c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3338c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3339c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3340c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3341c59e4cc3SRick Macklem 			printf("EEK8 multiple clids\n");
33429ec7b004SRick Macklem 	} else {
3343c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3344c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
33459ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
33469ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
33479ec7b004SRick Macklem 	}
334890d2dfabSRick Macklem 	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p,
334990d2dfabSRick Macklem 	    &writeacc);
335090d2dfabSRick Macklem 	/* For pNFS, update the attributes. */
335190d2dfabSRick Macklem 	if (writeacc != 0 || nfsrv_pnfsatime != 0)
335290d2dfabSRick Macklem 		nfsrv_updatemdsattr(vp, &na, p);
33539ec7b004SRick Macklem 	vput(vp);
33549ec7b004SRick Macklem 	if (!nd->nd_repstat) {
335590d2dfabSRick Macklem 		/*
335690d2dfabSRick Macklem 		 * If the stateid that has been closed is the current stateid,
335790d2dfabSRick Macklem 		 * unset it.
335890d2dfabSRick Macklem 		 */
335990d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0 &&
336090d2dfabSRick Macklem 		    stateid.other[0] == nd->nd_curstateid.other[0] &&
336190d2dfabSRick Macklem 		    stateid.other[1] == nd->nd_curstateid.other[1] &&
336290d2dfabSRick Macklem 		    stateid.other[2] == nd->nd_curstateid.other[2])
336390d2dfabSRick Macklem 			nd->nd_flag &= ~ND_CURSTATEID;
33649ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
33659ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
33669ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
33679ec7b004SRick Macklem 	}
3368a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
33699ec7b004SRick Macklem 	return (0);
33709ec7b004SRick Macklem nfsmout:
33719ec7b004SRick Macklem 	vput(vp);
3372a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
33739ec7b004SRick Macklem 	return (error);
33749ec7b004SRick Macklem }
33759ec7b004SRick Macklem 
33769ec7b004SRick Macklem /*
33779ec7b004SRick Macklem  * nfsv4 delegpurge service
33789ec7b004SRick Macklem  */
3379b9cc3262SRyan Moeller int
33809ec7b004SRick Macklem nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram,
3381af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
33829ec7b004SRick Macklem {
33839ec7b004SRick Macklem 	u_int32_t *tl;
33849ec7b004SRick Macklem 	int error = 0;
33859ec7b004SRick Macklem 	nfsquad_t clientid;
3386af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
33879ec7b004SRick Macklem 
3388984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
3389a9285ae5SZack Kirsch 		goto nfsmout;
33909ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
33919ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
33929ec7b004SRick Macklem 	clientid.lval[1] = *tl;
3393c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3394c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3395c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3396c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3397c59e4cc3SRick Macklem 			printf("EEK9 multiple clids\n");
33989ec7b004SRick Macklem 	} else {
3399c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3400c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
34019ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
34029ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
34039ec7b004SRick Macklem 	}
3404c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_delegupdate(nd, clientid, NULL, NULL,
340590d2dfabSRick Macklem 	    NFSV4OP_DELEGPURGE, nd->nd_cred, p, NULL);
34069ec7b004SRick Macklem nfsmout:
3407a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
34089ec7b004SRick Macklem 	return (error);
34099ec7b004SRick Macklem }
34109ec7b004SRick Macklem 
34119ec7b004SRick Macklem /*
34129ec7b004SRick Macklem  * nfsv4 delegreturn service
34139ec7b004SRick Macklem  */
3414b9cc3262SRyan Moeller int
34159ec7b004SRick Macklem nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram,
3416af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
34179ec7b004SRick Macklem {
34189ec7b004SRick Macklem 	u_int32_t *tl;
341990d2dfabSRick Macklem 	int error = 0, writeacc;
34209ec7b004SRick Macklem 	nfsv4stateid_t stateid;
34219ec7b004SRick Macklem 	nfsquad_t clientid;
342290d2dfabSRick Macklem 	struct nfsvattr na;
3423af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
34249ec7b004SRick Macklem 
34259ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
34269ec7b004SRick Macklem 	stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
34279ec7b004SRick Macklem 	NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, NFSX_STATEIDOTHER);
34289ec7b004SRick Macklem 	clientid.lval[0] = stateid.other[0];
34299ec7b004SRick Macklem 	clientid.lval[1] = stateid.other[1];
3430c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3431c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3432c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3433c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3434c59e4cc3SRick Macklem 			printf("EEK10 multiple clids\n");
34359ec7b004SRick Macklem 	} else {
3436c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3437c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
34389ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
34399ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
34409ec7b004SRick Macklem 	}
3441c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_delegupdate(nd, clientid, &stateid, vp,
344290d2dfabSRick Macklem 	    NFSV4OP_DELEGRETURN, nd->nd_cred, p, &writeacc);
344390d2dfabSRick Macklem 	/* For pNFS, update the attributes. */
344490d2dfabSRick Macklem 	if (writeacc != 0 || nfsrv_pnfsatime != 0)
344590d2dfabSRick Macklem 		nfsrv_updatemdsattr(vp, &na, p);
34469ec7b004SRick Macklem nfsmout:
34479ec7b004SRick Macklem 	vput(vp);
3448a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
34499ec7b004SRick Macklem 	return (error);
34509ec7b004SRick Macklem }
34519ec7b004SRick Macklem 
34529ec7b004SRick Macklem /*
34539ec7b004SRick Macklem  * nfsv4 get file handle service
34549ec7b004SRick Macklem  */
3455b9cc3262SRyan Moeller int
34569ec7b004SRick Macklem nfsrvd_getfh(struct nfsrv_descript *nd, __unused int isdgram,
3457af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
34589ec7b004SRick Macklem {
34599ec7b004SRick Macklem 	fhandle_t fh;
3460af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
34619ec7b004SRick Macklem 
34629ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
34639ec7b004SRick Macklem 	vput(vp);
34649ec7b004SRick Macklem 	if (!nd->nd_repstat)
34659ec7b004SRick Macklem 		(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
3466a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
34679ec7b004SRick Macklem 	return (0);
34689ec7b004SRick Macklem }
34699ec7b004SRick Macklem 
34709ec7b004SRick Macklem /*
34719ec7b004SRick Macklem  * nfsv4 open confirm service
34729ec7b004SRick Macklem  */
3473b9cc3262SRyan Moeller int
34749ec7b004SRick Macklem nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram,
3475af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
34769ec7b004SRick Macklem {
34779ec7b004SRick Macklem 	u_int32_t *tl;
34789ec7b004SRick Macklem 	struct nfsstate st, *stp = &st;
34799ec7b004SRick Macklem 	int error = 0;
34809ec7b004SRick Macklem 	nfsv4stateid_t stateid;
34819ec7b004SRick Macklem 	nfsquad_t clientid;
3482af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
34839ec7b004SRick Macklem 
3484c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0) {
3485c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
3486c59e4cc3SRick Macklem 		goto nfsmout;
3487c59e4cc3SRick Macklem 	}
34889ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
34899ec7b004SRick Macklem 	stp->ls_ownerlen = 0;
34909ec7b004SRick Macklem 	stp->ls_op = nd->nd_rp;
34919ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
34929ec7b004SRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
34939ec7b004SRick Macklem 	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
34949ec7b004SRick Macklem 	    NFSX_STATEIDOTHER);
34959ec7b004SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
34969ec7b004SRick Macklem 	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl);
34979ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_CONFIRM;
34989ec7b004SRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0];
34999ec7b004SRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1];
3500c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3501c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3502c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3503c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3504c59e4cc3SRick Macklem 			printf("EEK11 multiple clids\n");
35059ec7b004SRick Macklem 	} else {
3506c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3507c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
35089ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
35099ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
35109ec7b004SRick Macklem 	}
351190d2dfabSRick Macklem 	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p,
351290d2dfabSRick Macklem 	    NULL);
35139ec7b004SRick Macklem 	if (!nd->nd_repstat) {
35149ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
35159ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
35169ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
35179ec7b004SRick Macklem 	}
35189ec7b004SRick Macklem nfsmout:
35199ec7b004SRick Macklem 	vput(vp);
3520a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
35219ec7b004SRick Macklem 	return (error);
35229ec7b004SRick Macklem }
35239ec7b004SRick Macklem 
35249ec7b004SRick Macklem /*
35259ec7b004SRick Macklem  * nfsv4 open downgrade service
35269ec7b004SRick Macklem  */
3527b9cc3262SRyan Moeller int
35289ec7b004SRick Macklem nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram,
3529af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
35309ec7b004SRick Macklem {
35319ec7b004SRick Macklem 	u_int32_t *tl;
35329ec7b004SRick Macklem 	int i;
35339ec7b004SRick Macklem 	struct nfsstate st, *stp = &st;
35349ec7b004SRick Macklem 	int error = 0;
35359ec7b004SRick Macklem 	nfsv4stateid_t stateid;
35369ec7b004SRick Macklem 	nfsquad_t clientid;
3537af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
35389ec7b004SRick Macklem 
3539d8a5961fSMarcelo Araujo 	/* opendowngrade can only work on a file object.*/
3540d8a5961fSMarcelo Araujo 	if (vp->v_type != VREG) {
3541d8a5961fSMarcelo Araujo 		error = NFSERR_INVAL;
3542d8a5961fSMarcelo Araujo 		goto nfsmout;
3543d8a5961fSMarcelo Araujo 	}
35449ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
35459ec7b004SRick Macklem 	stp->ls_ownerlen = 0;
35469ec7b004SRick Macklem 	stp->ls_op = nd->nd_rp;
35479ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
35489ec7b004SRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
35499ec7b004SRick Macklem 	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
35509ec7b004SRick Macklem 	    NFSX_STATEIDOTHER);
35519ec7b004SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
355290d2dfabSRick Macklem 
355390d2dfabSRick Macklem 	/*
355490d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
355590d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
355690d2dfabSRick Macklem 	 */
355790d2dfabSRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 1 &&
355890d2dfabSRick Macklem 	    stp->ls_stateid.other[0] == 0 && stp->ls_stateid.other[1] == 0 &&
355990d2dfabSRick Macklem 	    stp->ls_stateid.other[2] == 0) {
356090d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0)
356190d2dfabSRick Macklem 			stp->ls_stateid = nd->nd_curstateid;
356290d2dfabSRick Macklem 		else {
356390d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
356490d2dfabSRick Macklem 			goto nfsmout;
356590d2dfabSRick Macklem 		}
356690d2dfabSRick Macklem 	}
356790d2dfabSRick Macklem 
35689ec7b004SRick Macklem 	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
35699ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
35706269d663SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0)
35716269d663SRick Macklem 		i &= ~NFSV4OPEN_WANTDELEGMASK;
35729ec7b004SRick Macklem 	switch (i) {
35739ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSREAD:
35749ec7b004SRick Macklem 		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_DOWNGRADE);
35759ec7b004SRick Macklem 		break;
35769ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSWRITE:
35779ec7b004SRick Macklem 		stp->ls_flags = (NFSLCK_WRITEACCESS | NFSLCK_DOWNGRADE);
35789ec7b004SRick Macklem 		break;
35799ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSBOTH:
35809ec7b004SRick Macklem 		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_WRITEACCESS |
35819ec7b004SRick Macklem 		    NFSLCK_DOWNGRADE);
35829ec7b004SRick Macklem 		break;
35839ec7b004SRick Macklem 	default:
35846269d663SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
358574b8d63dSPedro F. Giffuni 	}
35869ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl);
35879ec7b004SRick Macklem 	switch (i) {
35889ec7b004SRick Macklem 	case NFSV4OPEN_DENYNONE:
35899ec7b004SRick Macklem 		break;
35909ec7b004SRick Macklem 	case NFSV4OPEN_DENYREAD:
35919ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_READDENY;
35929ec7b004SRick Macklem 		break;
35939ec7b004SRick Macklem 	case NFSV4OPEN_DENYWRITE:
35949ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_WRITEDENY;
35959ec7b004SRick Macklem 		break;
35969ec7b004SRick Macklem 	case NFSV4OPEN_DENYBOTH:
35979ec7b004SRick Macklem 		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
35989ec7b004SRick Macklem 		break;
35999ec7b004SRick Macklem 	default:
36006269d663SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
360174b8d63dSPedro F. Giffuni 	}
36029ec7b004SRick Macklem 
36039ec7b004SRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0];
36049ec7b004SRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1];
3605c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3606c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3607c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3608c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3609c59e4cc3SRick Macklem 			printf("EEK12 multiple clids\n");
36109ec7b004SRick Macklem 	} else {
3611c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3612c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
36139ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
36149ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
36159ec7b004SRick Macklem 	}
36169ec7b004SRick Macklem 	if (!nd->nd_repstat)
36179ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid,
361890d2dfabSRick Macklem 		    nd, p, NULL);
36199ec7b004SRick Macklem 	if (!nd->nd_repstat) {
362090d2dfabSRick Macklem 		/* For NFSv4.1, set the Current StateID. */
362190d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0) {
362290d2dfabSRick Macklem 			nd->nd_curstateid = stateid;
362390d2dfabSRick Macklem 			nd->nd_flag |= ND_CURSTATEID;
362490d2dfabSRick Macklem 		}
36259ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
36269ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
36279ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
36289ec7b004SRick Macklem 	}
36299ec7b004SRick Macklem nfsmout:
36309ec7b004SRick Macklem 	vput(vp);
3631a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
36329ec7b004SRick Macklem 	return (error);
36339ec7b004SRick Macklem }
36349ec7b004SRick Macklem 
36359ec7b004SRick Macklem /*
36369ec7b004SRick Macklem  * nfsv4 renew lease service
36379ec7b004SRick Macklem  */
3638b9cc3262SRyan Moeller int
36399ec7b004SRick Macklem nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram,
3640af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
36419ec7b004SRick Macklem {
36429ec7b004SRick Macklem 	u_int32_t *tl;
36439ec7b004SRick Macklem 	int error = 0;
36449ec7b004SRick Macklem 	nfsquad_t clientid;
3645af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
36469ec7b004SRick Macklem 
3647c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0) {
3648c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
3649c59e4cc3SRick Macklem 		goto nfsmout;
3650c59e4cc3SRick Macklem 	}
3651984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
3652a9285ae5SZack Kirsch 		goto nfsmout;
36539ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
36549ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
36559ec7b004SRick Macklem 	clientid.lval[1] = *tl;
3656c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3657c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3658c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3659c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3660c59e4cc3SRick Macklem 			printf("EEK13 multiple clids\n");
36619ec7b004SRick Macklem 	} else {
3662c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3663c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
36649ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
36659ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
36669ec7b004SRick Macklem 	}
36679ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW),
3668c59e4cc3SRick Macklem 	    NULL, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p);
36699ec7b004SRick Macklem nfsmout:
3670a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
36719ec7b004SRick Macklem 	return (error);
36729ec7b004SRick Macklem }
36739ec7b004SRick Macklem 
36749ec7b004SRick Macklem /*
36759ec7b004SRick Macklem  * nfsv4 security info service
36769ec7b004SRick Macklem  */
3677b9cc3262SRyan Moeller int
36789ec7b004SRick Macklem nfsrvd_secinfo(struct nfsrv_descript *nd, int isdgram,
3679af444b18SEdward Tomasz Napierala     vnode_t dp, struct nfsexstuff *exp)
36809ec7b004SRick Macklem {
36819ec7b004SRick Macklem 	u_int32_t *tl;
36829ec7b004SRick Macklem 	int len;
36839ec7b004SRick Macklem 	struct nameidata named;
36849ec7b004SRick Macklem 	vnode_t dirp = NULL, vp;
36859ec7b004SRick Macklem 	struct nfsrvfh fh;
36869ec7b004SRick Macklem 	struct nfsexstuff retnes;
36879ec7b004SRick Macklem 	u_int32_t *sizp;
3688947bd247SRick Macklem 	int error = 0, i;
3689947bd247SRick Macklem 	uint64_t savflag;
36909ec7b004SRick Macklem 	char *bufp;
36919ec7b004SRick Macklem 	u_long *hashp;
3692af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
36939ec7b004SRick Macklem 
36949ec7b004SRick Macklem 	/*
36959ec7b004SRick Macklem 	 * All this just to get the export flags for the name.
36969ec7b004SRick Macklem 	 */
36979ec7b004SRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
369865127e98SMateusz Guzik 	    LOCKLEAF);
36999ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
37009ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
37019ec7b004SRick Macklem 	if (error) {
37029ec7b004SRick Macklem 		vput(dp);
37039ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
3704a9285ae5SZack Kirsch 		goto out;
37059ec7b004SRick Macklem 	}
37069ec7b004SRick Macklem 	if (!nd->nd_repstat) {
3707ef7d2c1fSMateusz Guzik 		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, &dirp);
37089ec7b004SRick Macklem 	} else {
37099ec7b004SRick Macklem 		vput(dp);
37109ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
37119ec7b004SRick Macklem 	}
37129ec7b004SRick Macklem 	if (dirp)
37139ec7b004SRick Macklem 		vrele(dirp);
37149ec7b004SRick Macklem 	if (nd->nd_repstat)
3715a9285ae5SZack Kirsch 		goto out;
37169ec7b004SRick Macklem 	nfsvno_relpathbuf(&named);
37179ec7b004SRick Macklem 	fh.nfsrvfh_len = NFSX_MYFH;
37189ec7b004SRick Macklem 	vp = named.ni_vp;
37199ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
37209ec7b004SRick Macklem 	vput(vp);
37219ec7b004SRick Macklem 	savflag = nd->nd_flag;
37229ec7b004SRick Macklem 	if (!nd->nd_repstat) {
3723a5df139eSRick Macklem 		/*
3724a5df139eSRick Macklem 		 * Pretend the next op is Secinfo, so that no wrongsec
3725a5df139eSRick Macklem 		 * test will be done.
3726a5df139eSRick Macklem 		 */
3727a5df139eSRick Macklem 		nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0,
3728a5df139eSRick Macklem 		    NFSV4OP_SECINFO);
37299ec7b004SRick Macklem 		if (vp)
37309ec7b004SRick Macklem 			vput(vp);
37319ec7b004SRick Macklem 	}
37329ec7b004SRick Macklem 	nd->nd_flag = savflag;
37339ec7b004SRick Macklem 	if (nd->nd_repstat)
3734a9285ae5SZack Kirsch 		goto out;
37359ec7b004SRick Macklem 
37369ec7b004SRick Macklem 	/*
37379ec7b004SRick Macklem 	 * Finally have the export flags for name, so we can create
37389ec7b004SRick Macklem 	 * the security info.
37399ec7b004SRick Macklem 	 */
37409ec7b004SRick Macklem 	len = 0;
37419ec7b004SRick Macklem 	NFSM_BUILD(sizp, u_int32_t *, NFSX_UNSIGNED);
374256e9d8e3SRick Macklem 
374356e9d8e3SRick Macklem 	/* If nes_numsecflavor == 0, all are allowed. */
374456e9d8e3SRick Macklem 	if (retnes.nes_numsecflavor == 0) {
374556e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
374656e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(RPCAUTH_UNIX);
374756e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTH_GSS);
374856e9d8e3SRick Macklem 		nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
374956e9d8e3SRick Macklem 		    nfsgss_mechlist[KERBV_MECH].len);
375056e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
375156e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(GSS_KERBV_QOP);
375256e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(RPCAUTHGSS_SVCNONE);
375356e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTH_GSS);
375456e9d8e3SRick Macklem 		nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
375556e9d8e3SRick Macklem 		    nfsgss_mechlist[KERBV_MECH].len);
375656e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
375756e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(GSS_KERBV_QOP);
375856e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
375956e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTH_GSS);
376056e9d8e3SRick Macklem 		nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
376156e9d8e3SRick Macklem 		    nfsgss_mechlist[KERBV_MECH].len);
376256e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
376356e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(GSS_KERBV_QOP);
376456e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
376556e9d8e3SRick Macklem 		len = 4;
376656e9d8e3SRick Macklem 	}
376798ad4453SRick Macklem 	for (i = 0; i < retnes.nes_numsecflavor; i++) {
376898ad4453SRick Macklem 		if (retnes.nes_secflavors[i] == AUTH_SYS) {
37699ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
37709ec7b004SRick Macklem 			*tl = txdr_unsigned(RPCAUTH_UNIX);
37719ec7b004SRick Macklem 			len++;
377298ad4453SRick Macklem 		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) {
37739ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
37749ec7b004SRick Macklem 			*tl++ = txdr_unsigned(RPCAUTH_GSS);
37759ec7b004SRick Macklem 			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
37769ec7b004SRick Macklem 			    nfsgss_mechlist[KERBV_MECH].len);
37779ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
37789ec7b004SRick Macklem 			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
377998ad4453SRick Macklem 			*tl = txdr_unsigned(RPCAUTHGSS_SVCNONE);
37809ec7b004SRick Macklem 			len++;
378198ad4453SRick Macklem 		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) {
378298ad4453SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
378398ad4453SRick Macklem 			*tl++ = txdr_unsigned(RPCAUTH_GSS);
378498ad4453SRick Macklem 			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
378598ad4453SRick Macklem 			    nfsgss_mechlist[KERBV_MECH].len);
378698ad4453SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
378798ad4453SRick Macklem 			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
378898ad4453SRick Macklem 			*tl = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
378998ad4453SRick Macklem 			len++;
379098ad4453SRick Macklem 		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) {
379198ad4453SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
379298ad4453SRick Macklem 			*tl++ = txdr_unsigned(RPCAUTH_GSS);
379398ad4453SRick Macklem 			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
379498ad4453SRick Macklem 			    nfsgss_mechlist[KERBV_MECH].len);
379598ad4453SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
379698ad4453SRick Macklem 			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
379798ad4453SRick Macklem 			*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
379898ad4453SRick Macklem 			len++;
379998ad4453SRick Macklem 		}
38009ec7b004SRick Macklem 	}
38019ec7b004SRick Macklem 	*sizp = txdr_unsigned(len);
3802a9285ae5SZack Kirsch 
3803a9285ae5SZack Kirsch out:
3804a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
3805a9285ae5SZack Kirsch 	return (error);
38069ec7b004SRick Macklem }
38079ec7b004SRick Macklem 
38089ec7b004SRick Macklem /*
3809947bd247SRick Macklem  * nfsv4 security info no name service
3810947bd247SRick Macklem  */
3811947bd247SRick Macklem int
3812947bd247SRick Macklem nfsrvd_secinfononame(struct nfsrv_descript *nd, int isdgram,
3813947bd247SRick Macklem     vnode_t dp, struct nfsexstuff *exp)
3814947bd247SRick Macklem {
3815947bd247SRick Macklem 	uint32_t *tl, *sizp;
3816947bd247SRick Macklem 	struct nameidata named;
3817947bd247SRick Macklem 	vnode_t dirp = NULL, vp;
3818947bd247SRick Macklem 	struct nfsrvfh fh;
3819947bd247SRick Macklem 	struct nfsexstuff retnes;
3820947bd247SRick Macklem 	int error = 0, fhstyle, i, len;
3821947bd247SRick Macklem 	uint64_t savflag;
3822947bd247SRick Macklem 	char *bufp;
3823947bd247SRick Macklem 	u_long *hashp;
3824947bd247SRick Macklem 	struct thread *p = curthread;
3825947bd247SRick Macklem 
3826947bd247SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
3827947bd247SRick Macklem 	fhstyle = fxdr_unsigned(int, *tl);
3828947bd247SRick Macklem 	switch (fhstyle) {
3829947bd247SRick Macklem 	case NFSSECINFONONAME_PARENT:
383047d75c29SRick Macklem 		if (dp->v_type != VDIR) {
383147d75c29SRick Macklem 			vput(dp);
383247d75c29SRick Macklem 			nd->nd_repstat = NFSERR_NOTDIR;
383347d75c29SRick Macklem 			goto nfsmout;
383447d75c29SRick Macklem 		}
3835947bd247SRick Macklem 		NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
383665127e98SMateusz Guzik 		    LOCKLEAF);
3837947bd247SRick Macklem 		nfsvno_setpathbuf(&named, &bufp, &hashp);
3838947bd247SRick Macklem 		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
3839947bd247SRick Macklem 		if (error != 0) {
3840947bd247SRick Macklem 			vput(dp);
3841947bd247SRick Macklem 			nfsvno_relpathbuf(&named);
3842947bd247SRick Macklem 			goto nfsmout;
3843947bd247SRick Macklem 		}
3844947bd247SRick Macklem 		if (nd->nd_repstat == 0)
3845ef7d2c1fSMateusz Guzik 			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, &dirp);
3846947bd247SRick Macklem 		else
3847947bd247SRick Macklem 			vput(dp);
3848947bd247SRick Macklem 		if (dirp != NULL)
3849947bd247SRick Macklem 			vrele(dirp);
3850947bd247SRick Macklem 		nfsvno_relpathbuf(&named);
3851947bd247SRick Macklem 		vp = named.ni_vp;
3852947bd247SRick Macklem 		break;
3853947bd247SRick Macklem 	case NFSSECINFONONAME_CURFH:
3854947bd247SRick Macklem 		vp = dp;
3855947bd247SRick Macklem 		break;
3856947bd247SRick Macklem 	default:
3857947bd247SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
3858947bd247SRick Macklem 		vput(dp);
3859947bd247SRick Macklem 	}
3860947bd247SRick Macklem 	if (nd->nd_repstat != 0)
3861947bd247SRick Macklem 		goto nfsmout;
3862947bd247SRick Macklem 	fh.nfsrvfh_len = NFSX_MYFH;
3863947bd247SRick Macklem 	nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
3864947bd247SRick Macklem 	vput(vp);
3865947bd247SRick Macklem 	savflag = nd->nd_flag;
3866947bd247SRick Macklem 	if (nd->nd_repstat == 0) {
3867a5df139eSRick Macklem 		/*
3868a5df139eSRick Macklem 		 * Pretend the next op is Secinfo, so that no wrongsec
3869a5df139eSRick Macklem 		 * test will be done.
3870a5df139eSRick Macklem 		 */
3871a5df139eSRick Macklem 		nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0,
3872a5df139eSRick Macklem 		    NFSV4OP_SECINFO);
3873947bd247SRick Macklem 		if (vp != NULL)
3874947bd247SRick Macklem 			vput(vp);
3875947bd247SRick Macklem 	}
3876947bd247SRick Macklem 	nd->nd_flag = savflag;
3877947bd247SRick Macklem 	if (nd->nd_repstat != 0)
3878947bd247SRick Macklem 		goto nfsmout;
3879947bd247SRick Macklem 
3880947bd247SRick Macklem 	/*
3881947bd247SRick Macklem 	 * Finally have the export flags for fh/parent, so we can create
3882947bd247SRick Macklem 	 * the security info.
3883947bd247SRick Macklem 	 */
3884947bd247SRick Macklem 	len = 0;
3885947bd247SRick Macklem 	NFSM_BUILD(sizp, uint32_t *, NFSX_UNSIGNED);
388656e9d8e3SRick Macklem 
388756e9d8e3SRick Macklem 	/* If nes_numsecflavor == 0, all are allowed. */
388856e9d8e3SRick Macklem 	if (retnes.nes_numsecflavor == 0) {
388956e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
389056e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(RPCAUTH_UNIX);
389156e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTH_GSS);
389256e9d8e3SRick Macklem 		nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
389356e9d8e3SRick Macklem 		    nfsgss_mechlist[KERBV_MECH].len);
389456e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
389556e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(GSS_KERBV_QOP);
389656e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(RPCAUTHGSS_SVCNONE);
389756e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTH_GSS);
389856e9d8e3SRick Macklem 		nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
389956e9d8e3SRick Macklem 		    nfsgss_mechlist[KERBV_MECH].len);
390056e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
390156e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(GSS_KERBV_QOP);
390256e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
390356e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTH_GSS);
390456e9d8e3SRick Macklem 		nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
390556e9d8e3SRick Macklem 		    nfsgss_mechlist[KERBV_MECH].len);
390656e9d8e3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
390756e9d8e3SRick Macklem 		*tl++ = txdr_unsigned(GSS_KERBV_QOP);
390856e9d8e3SRick Macklem 		*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
390956e9d8e3SRick Macklem 		len = 4;
391056e9d8e3SRick Macklem 	}
3911947bd247SRick Macklem 	for (i = 0; i < retnes.nes_numsecflavor; i++) {
3912947bd247SRick Macklem 		if (retnes.nes_secflavors[i] == AUTH_SYS) {
3913947bd247SRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
3914947bd247SRick Macklem 			*tl = txdr_unsigned(RPCAUTH_UNIX);
3915947bd247SRick Macklem 			len++;
3916947bd247SRick Macklem 		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) {
3917947bd247SRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
3918947bd247SRick Macklem 			*tl = txdr_unsigned(RPCAUTH_GSS);
3919947bd247SRick Macklem 			nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3920947bd247SRick Macklem 			    nfsgss_mechlist[KERBV_MECH].len);
3921947bd247SRick Macklem 			NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
3922947bd247SRick Macklem 			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3923947bd247SRick Macklem 			*tl = txdr_unsigned(RPCAUTHGSS_SVCNONE);
3924947bd247SRick Macklem 			len++;
3925947bd247SRick Macklem 		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) {
3926947bd247SRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
3927947bd247SRick Macklem 			*tl = txdr_unsigned(RPCAUTH_GSS);
3928947bd247SRick Macklem 			nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3929947bd247SRick Macklem 			    nfsgss_mechlist[KERBV_MECH].len);
3930947bd247SRick Macklem 			NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
3931947bd247SRick Macklem 			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3932947bd247SRick Macklem 			*tl = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
3933947bd247SRick Macklem 			len++;
3934947bd247SRick Macklem 		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) {
3935947bd247SRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
3936947bd247SRick Macklem 			*tl = txdr_unsigned(RPCAUTH_GSS);
3937947bd247SRick Macklem 			nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3938947bd247SRick Macklem 			    nfsgss_mechlist[KERBV_MECH].len);
3939947bd247SRick Macklem 			NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
3940947bd247SRick Macklem 			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3941947bd247SRick Macklem 			*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
3942947bd247SRick Macklem 			len++;
3943947bd247SRick Macklem 		}
3944947bd247SRick Macklem 	}
3945947bd247SRick Macklem 	*sizp = txdr_unsigned(len);
3946947bd247SRick Macklem 
3947947bd247SRick Macklem nfsmout:
3948947bd247SRick Macklem 	NFSEXITCODE2(error, nd);
3949947bd247SRick Macklem 	return (error);
3950947bd247SRick Macklem }
3951947bd247SRick Macklem 
3952947bd247SRick Macklem /*
39539ec7b004SRick Macklem  * nfsv4 set client id service
39549ec7b004SRick Macklem  */
3955b9cc3262SRyan Moeller int
39569ec7b004SRick Macklem nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram,
3957af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
39589ec7b004SRick Macklem {
39599ec7b004SRick Macklem 	u_int32_t *tl;
39609ec7b004SRick Macklem 	int i;
39619ec7b004SRick Macklem 	int error = 0, idlen;
39629ec7b004SRick Macklem 	struct nfsclient *clp = NULL;
3963ed2f1001SRick Macklem #ifdef INET
3964ed2f1001SRick Macklem 	struct sockaddr_in *rin;
3965ed2f1001SRick Macklem #endif
3966ed2f1001SRick Macklem #ifdef INET6
3967ed2f1001SRick Macklem 	struct sockaddr_in6 *rin6;
3968ed2f1001SRick Macklem #endif
3969ed2f1001SRick Macklem #if defined(INET) || defined(INET6)
3970ed2f1001SRick Macklem 	u_char *ucp, *ucp2;
3971ed2f1001SRick Macklem #endif
3972ed2f1001SRick Macklem 	u_char *verf, *addrbuf;
39739ec7b004SRick Macklem 	nfsquad_t clientid, confirm;
3974af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
39759ec7b004SRick Macklem 
3976c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0) {
3977c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
3978c59e4cc3SRick Macklem 		goto nfsmout;
3979c59e4cc3SRick Macklem 	}
3980984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
3981a9285ae5SZack Kirsch 		goto out;
39829ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
39839ec7b004SRick Macklem 	verf = (u_char *)tl;
39849ec7b004SRick Macklem 	tl += (NFSX_VERF / NFSX_UNSIGNED);
39859ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl);
39869ec7b004SRick Macklem 	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
39879ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
3988a9285ae5SZack Kirsch 		goto nfsmout;
39899ec7b004SRick Macklem 	}
39909ec7b004SRick Macklem 	idlen = i;
39919ec7b004SRick Macklem 	if (nd->nd_flag & ND_GSS)
39929ec7b004SRick Macklem 		i += nd->nd_princlen;
39931f54e596SRick Macklem 	clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
39941f54e596SRick Macklem 	    M_ZERO);
39951f54e596SRick Macklem 	clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
39961f54e596SRick Macklem 	    nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
39979ec7b004SRick Macklem 	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
3998ed2f1001SRick Macklem 	/* Allocated large enough for an AF_INET or AF_INET6 socket. */
3999ed2f1001SRick Macklem 	clp->lc_req.nr_nam = malloc(sizeof(struct sockaddr_in6), M_SONAME,
4000b97b91b5SConrad Meyer 	    M_WAITOK | M_ZERO);
40019ec7b004SRick Macklem 	clp->lc_req.nr_cred = NULL;
40029ec7b004SRick Macklem 	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
40039ec7b004SRick Macklem 	clp->lc_idlen = idlen;
40049ec7b004SRick Macklem 	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
40059ec7b004SRick Macklem 	if (error)
40069ec7b004SRick Macklem 		goto nfsmout;
40079ec7b004SRick Macklem 	if (nd->nd_flag & ND_GSS) {
40089ec7b004SRick Macklem 		clp->lc_flags = LCL_GSS;
40099ec7b004SRick Macklem 		if (nd->nd_flag & ND_GSSINTEGRITY)
40109ec7b004SRick Macklem 			clp->lc_flags |= LCL_GSSINTEGRITY;
40119ec7b004SRick Macklem 		else if (nd->nd_flag & ND_GSSPRIVACY)
40129ec7b004SRick Macklem 			clp->lc_flags |= LCL_GSSPRIVACY;
40139ec7b004SRick Macklem 	} else {
40149ec7b004SRick Macklem 		clp->lc_flags = 0;
40159ec7b004SRick Macklem 	}
40169ec7b004SRick Macklem 	if ((nd->nd_flag & ND_GSS) && nd->nd_princlen > 0) {
40179ec7b004SRick Macklem 		clp->lc_flags |= LCL_NAME;
40189ec7b004SRick Macklem 		clp->lc_namelen = nd->nd_princlen;
40199ec7b004SRick Macklem 		clp->lc_name = &clp->lc_id[idlen];
40209ec7b004SRick Macklem 		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
40219ec7b004SRick Macklem 	} else {
40229ec7b004SRick Macklem 		clp->lc_uid = nd->nd_cred->cr_uid;
40239ec7b004SRick Macklem 		clp->lc_gid = nd->nd_cred->cr_gid;
40249ec7b004SRick Macklem 	}
40256e4b6ff8SRick Macklem 
40266e4b6ff8SRick Macklem 	/* If the client is using TLS, do so for the callback connection. */
40276e4b6ff8SRick Macklem 	if (nd->nd_flag & ND_TLS)
40286e4b6ff8SRick Macklem 		clp->lc_flags |= LCL_TLSCB;
40296e4b6ff8SRick Macklem 
40309ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
40319ec7b004SRick Macklem 	clp->lc_program = fxdr_unsigned(u_int32_t, *tl);
40329ec7b004SRick Macklem 	error = nfsrv_getclientipaddr(nd, clp);
40339ec7b004SRick Macklem 	if (error)
40349ec7b004SRick Macklem 		goto nfsmout;
40359ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
40369ec7b004SRick Macklem 	clp->lc_callback = fxdr_unsigned(u_int32_t, *tl);
40379ec7b004SRick Macklem 
40389ec7b004SRick Macklem 	/*
40399ec7b004SRick Macklem 	 * nfsrv_setclient() does the actual work of adding it to the
40409ec7b004SRick Macklem 	 * client list. If there is no error, the structure has been
40419ec7b004SRick Macklem 	 * linked into the client list and clp should no longer be used
40429ec7b004SRick Macklem 	 * here. When an error is returned, it has not been linked in,
40439ec7b004SRick Macklem 	 * so it should be free'd.
40449ec7b004SRick Macklem 	 */
40459ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
40469ec7b004SRick Macklem 	if (nd->nd_repstat == NFSERR_CLIDINUSE) {
4047ed2f1001SRick Macklem 		/*
4048ed2f1001SRick Macklem 		 * 8 is the maximum length of the port# string.
4049ed2f1001SRick Macklem 		 */
4050ed2f1001SRick Macklem 		addrbuf = malloc(INET6_ADDRSTRLEN + 8, M_TEMP, M_WAITOK);
4051ed2f1001SRick Macklem 		switch (clp->lc_req.nr_nam->sa_family) {
4052ed2f1001SRick Macklem #ifdef INET
4053ed2f1001SRick Macklem 		case AF_INET:
40549ec7b004SRick Macklem 			if (clp->lc_flags & LCL_TCPCALLBACK)
40559ec7b004SRick Macklem 				(void) nfsm_strtom(nd, "tcp", 3);
40569ec7b004SRick Macklem 			else
40579ec7b004SRick Macklem 				(void) nfsm_strtom(nd, "udp", 3);
4058ed2f1001SRick Macklem 			rin = (struct sockaddr_in *)clp->lc_req.nr_nam;
4059ed2f1001SRick Macklem 			ucp = (u_char *)&rin->sin_addr.s_addr;
4060ed2f1001SRick Macklem 			ucp2 = (u_char *)&rin->sin_port;
40619ec7b004SRick Macklem 			sprintf(addrbuf, "%d.%d.%d.%d.%d.%d", ucp[0] & 0xff,
40629ec7b004SRick Macklem 			    ucp[1] & 0xff, ucp[2] & 0xff, ucp[3] & 0xff,
40639ec7b004SRick Macklem 			    ucp2[0] & 0xff, ucp2[1] & 0xff);
4064ed2f1001SRick Macklem 			break;
4065ed2f1001SRick Macklem #endif
4066ed2f1001SRick Macklem #ifdef INET6
4067ed2f1001SRick Macklem 		case AF_INET6:
4068ed2f1001SRick Macklem 			if (clp->lc_flags & LCL_TCPCALLBACK)
4069ed2f1001SRick Macklem 				(void) nfsm_strtom(nd, "tcp6", 4);
4070ed2f1001SRick Macklem 			else
4071ed2f1001SRick Macklem 				(void) nfsm_strtom(nd, "udp6", 4);
4072ed2f1001SRick Macklem 			rin6 = (struct sockaddr_in6 *)clp->lc_req.nr_nam;
4073ed2f1001SRick Macklem 			ucp = inet_ntop(AF_INET6, &rin6->sin6_addr, addrbuf,
4074ed2f1001SRick Macklem 			    INET6_ADDRSTRLEN);
4075ed2f1001SRick Macklem 			if (ucp != NULL)
4076ed2f1001SRick Macklem 				i = strlen(ucp);
4077ed2f1001SRick Macklem 			else
4078ed2f1001SRick Macklem 				i = 0;
4079ed2f1001SRick Macklem 			ucp2 = (u_char *)&rin6->sin6_port;
4080ed2f1001SRick Macklem 			sprintf(&addrbuf[i], ".%d.%d", ucp2[0] & 0xff,
4081ed2f1001SRick Macklem 			    ucp2[1] & 0xff);
4082ed2f1001SRick Macklem 			break;
4083ed2f1001SRick Macklem #endif
4084ed2f1001SRick Macklem 		}
40859ec7b004SRick Macklem 		(void) nfsm_strtom(nd, addrbuf, strlen(addrbuf));
4086ed2f1001SRick Macklem 		free(addrbuf, M_TEMP);
40879ec7b004SRick Macklem 	}
40889ec7b004SRick Macklem 	if (clp) {
4089b97b91b5SConrad Meyer 		free(clp->lc_req.nr_nam, M_SONAME);
40909ec7b004SRick Macklem 		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
40911f54e596SRick Macklem 		free(clp->lc_stateid, M_NFSDCLIENT);
40921f54e596SRick Macklem 		free(clp, M_NFSDCLIENT);
40939ec7b004SRick Macklem 	}
40949ec7b004SRick Macklem 	if (!nd->nd_repstat) {
40959ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_HYPER);
40969ec7b004SRick Macklem 		*tl++ = clientid.lval[0];
40979ec7b004SRick Macklem 		*tl++ = clientid.lval[1];
40989ec7b004SRick Macklem 		*tl++ = confirm.lval[0];
40999ec7b004SRick Macklem 		*tl = confirm.lval[1];
41009ec7b004SRick Macklem 	}
4101a9285ae5SZack Kirsch 
4102a9285ae5SZack Kirsch out:
4103a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
41049ec7b004SRick Macklem 	return (0);
41059ec7b004SRick Macklem nfsmout:
41069ec7b004SRick Macklem 	if (clp) {
4107b97b91b5SConrad Meyer 		free(clp->lc_req.nr_nam, M_SONAME);
41089ec7b004SRick Macklem 		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
41091f54e596SRick Macklem 		free(clp->lc_stateid, M_NFSDCLIENT);
41101f54e596SRick Macklem 		free(clp, M_NFSDCLIENT);
41119ec7b004SRick Macklem 	}
4112a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
41139ec7b004SRick Macklem 	return (error);
41149ec7b004SRick Macklem }
41159ec7b004SRick Macklem 
41169ec7b004SRick Macklem /*
41179ec7b004SRick Macklem  * nfsv4 set client id confirm service
41189ec7b004SRick Macklem  */
4119b9cc3262SRyan Moeller int
41209ec7b004SRick Macklem nfsrvd_setclientidcfrm(struct nfsrv_descript *nd,
4121af444b18SEdward Tomasz Napierala     __unused int isdgram, __unused vnode_t vp,
41229ec7b004SRick Macklem     __unused struct nfsexstuff *exp)
41239ec7b004SRick Macklem {
41249ec7b004SRick Macklem 	u_int32_t *tl;
41259ec7b004SRick Macklem 	int error = 0;
41269ec7b004SRick Macklem 	nfsquad_t clientid, confirm;
4127af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
41289ec7b004SRick Macklem 
4129c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0) {
4130c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
4131c59e4cc3SRick Macklem 		goto nfsmout;
4132c59e4cc3SRick Macklem 	}
4133984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
4134a9285ae5SZack Kirsch 		goto nfsmout;
41359ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER);
41369ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
41379ec7b004SRick Macklem 	clientid.lval[1] = *tl++;
41389ec7b004SRick Macklem 	confirm.lval[0] = *tl++;
41399ec7b004SRick Macklem 	confirm.lval[1] = *tl;
41409ec7b004SRick Macklem 
41419ec7b004SRick Macklem 	/*
41429ec7b004SRick Macklem 	 * nfsrv_getclient() searches the client list for a match and
41439ec7b004SRick Macklem 	 * returns the appropriate NFSERR status.
41449ec7b004SRick Macklem 	 */
41459ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_CONFIRM|CLOPS_RENEW),
4146c59e4cc3SRick Macklem 	    NULL, NULL, confirm, 0, nd, p);
41479ec7b004SRick Macklem nfsmout:
4148a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
41499ec7b004SRick Macklem 	return (error);
41509ec7b004SRick Macklem }
41519ec7b004SRick Macklem 
41529ec7b004SRick Macklem /*
41539ec7b004SRick Macklem  * nfsv4 verify service
41549ec7b004SRick Macklem  */
4155b9cc3262SRyan Moeller int
41569ec7b004SRick Macklem nfsrvd_verify(struct nfsrv_descript *nd, int isdgram,
4157af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
41589ec7b004SRick Macklem {
41599ec7b004SRick Macklem 	int error = 0, ret, fhsize = NFSX_MYFH;
41609ec7b004SRick Macklem 	struct nfsvattr nva;
41612f304845SKonstantin Belousov 	struct statfs *sf;
41629ec7b004SRick Macklem 	struct nfsfsinfo fs;
41639ec7b004SRick Macklem 	fhandle_t fh;
4164af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
41659ec7b004SRick Macklem 
41662f304845SKonstantin Belousov 	sf = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
416790d2dfabSRick Macklem 	nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
41689ec7b004SRick Macklem 	if (!nd->nd_repstat)
41692f304845SKonstantin Belousov 		nd->nd_repstat = nfsvno_statfs(vp, sf);
41709ec7b004SRick Macklem 	if (!nd->nd_repstat)
41719ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
41729ec7b004SRick Macklem 	if (!nd->nd_repstat) {
41739ec7b004SRick Macklem 		nfsvno_getfs(&fs, isdgram);
41749ec7b004SRick Macklem 		error = nfsv4_loadattr(nd, vp, &nva, NULL, &fh, fhsize, NULL,
41752f304845SKonstantin Belousov 		    sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, p, nd->nd_cred);
41769ec7b004SRick Macklem 		if (!error) {
41779ec7b004SRick Macklem 			if (nd->nd_procnum == NFSV4OP_NVERIFY) {
41789ec7b004SRick Macklem 				if (ret == 0)
41799ec7b004SRick Macklem 					nd->nd_repstat = NFSERR_SAME;
41809ec7b004SRick Macklem 				else if (ret != NFSERR_NOTSAME)
41819ec7b004SRick Macklem 					nd->nd_repstat = ret;
41829ec7b004SRick Macklem 			} else if (ret)
41839ec7b004SRick Macklem 				nd->nd_repstat = ret;
41849ec7b004SRick Macklem 		}
41859ec7b004SRick Macklem 	}
41869ec7b004SRick Macklem 	vput(vp);
41872f304845SKonstantin Belousov 	free(sf, M_STATFS);
4188a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
41899ec7b004SRick Macklem 	return (error);
41909ec7b004SRick Macklem }
41919ec7b004SRick Macklem 
41929ec7b004SRick Macklem /*
41939ec7b004SRick Macklem  * nfs openattr rpc
41949ec7b004SRick Macklem  */
4195b9cc3262SRyan Moeller int
41969ec7b004SRick Macklem nfsrvd_openattr(struct nfsrv_descript *nd, __unused int isdgram,
41979ec7b004SRick Macklem     vnode_t dp, __unused vnode_t *vpp, __unused fhandle_t *fhp,
4198af444b18SEdward Tomasz Napierala     __unused struct nfsexstuff *exp)
41999ec7b004SRick Macklem {
42009ec7b004SRick Macklem 	u_int32_t *tl;
42018014c971SRick Macklem 	int error = 0, createdir __unused;
42029ec7b004SRick Macklem 
42039ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
42049ec7b004SRick Macklem 	createdir = fxdr_unsigned(int, *tl);
42059ec7b004SRick Macklem 	nd->nd_repstat = NFSERR_NOTSUPP;
42069ec7b004SRick Macklem nfsmout:
42079ec7b004SRick Macklem 	vrele(dp);
4208a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
42099ec7b004SRick Macklem 	return (error);
42109ec7b004SRick Macklem }
42119ec7b004SRick Macklem 
42129ec7b004SRick Macklem /*
42139ec7b004SRick Macklem  * nfsv4 release lock owner service
42149ec7b004SRick Macklem  */
4215b9cc3262SRyan Moeller int
42169ec7b004SRick Macklem nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram,
4217af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
42189ec7b004SRick Macklem {
42199ec7b004SRick Macklem 	u_int32_t *tl;
42209ec7b004SRick Macklem 	struct nfsstate *stp = NULL;
42219ec7b004SRick Macklem 	int error = 0, len;
42229ec7b004SRick Macklem 	nfsquad_t clientid;
4223af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
42249ec7b004SRick Macklem 
4225c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0) {
4226c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
4227c59e4cc3SRick Macklem 		goto nfsmout;
4228c59e4cc3SRick Macklem 	}
4229984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
4230a9285ae5SZack Kirsch 		goto nfsmout;
42319ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
42329ec7b004SRick Macklem 	len = fxdr_unsigned(int, *(tl + 2));
42332a45247cSRick Macklem 	if (len <= 0 || len > NFSV4_OPAQUELIMIT) {
42342a45247cSRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
4235a9285ae5SZack Kirsch 		goto nfsmout;
42362a45247cSRick Macklem 	}
4237222daa42SConrad Meyer 	stp = malloc(sizeof (struct nfsstate) + len,
42389ec7b004SRick Macklem 	    M_NFSDSTATE, M_WAITOK);
42399ec7b004SRick Macklem 	stp->ls_ownerlen = len;
42409ec7b004SRick Macklem 	stp->ls_op = NULL;
42419ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_RELEASE;
42429ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
42439ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
42449ec7b004SRick Macklem 	clientid.lval[1] = *tl;
4245c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
4246c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
4247c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
4248c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
4249c59e4cc3SRick Macklem 			printf("EEK14 multiple clids\n");
42509ec7b004SRick Macklem 	} else {
4251c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
4252c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
42539ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
42549ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
42559ec7b004SRick Macklem 	}
42569ec7b004SRick Macklem 	error = nfsrv_mtostr(nd, stp->ls_owner, len);
42579ec7b004SRick Macklem 	if (error)
42589ec7b004SRick Macklem 		goto nfsmout;
42599ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_releaselckown(stp, clientid, p);
4260222daa42SConrad Meyer 	free(stp, M_NFSDSTATE);
4261a9285ae5SZack Kirsch 
4262a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
42639ec7b004SRick Macklem 	return (0);
42649ec7b004SRick Macklem nfsmout:
42659ec7b004SRick Macklem 	if (stp)
4266222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
4267a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
42689ec7b004SRick Macklem 	return (error);
42699ec7b004SRick Macklem }
4270c59e4cc3SRick Macklem 
4271c59e4cc3SRick Macklem /*
4272c59e4cc3SRick Macklem  * nfsv4 exchange_id service
4273c59e4cc3SRick Macklem  */
4274b9cc3262SRyan Moeller int
4275c59e4cc3SRick Macklem nfsrvd_exchangeid(struct nfsrv_descript *nd, __unused int isdgram,
4276af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4277c59e4cc3SRick Macklem {
4278c59e4cc3SRick Macklem 	uint32_t *tl;
4279c59e4cc3SRick Macklem 	int error = 0, i, idlen;
4280c59e4cc3SRick Macklem 	struct nfsclient *clp = NULL;
4281c59e4cc3SRick Macklem 	nfsquad_t clientid, confirm;
4282c59e4cc3SRick Macklem 	uint8_t *verf;
4283c59e4cc3SRick Macklem 	uint32_t sp4type, v41flags;
4284c59e4cc3SRick Macklem 	struct timespec verstime;
4285ed2f1001SRick Macklem #ifdef INET
4286ed2f1001SRick Macklem 	struct sockaddr_in *sin, *rin;
4287ed2f1001SRick Macklem #endif
4288ed2f1001SRick Macklem #ifdef INET6
4289ed2f1001SRick Macklem 	struct sockaddr_in6 *sin6, *rin6;
4290ed2f1001SRick Macklem #endif
4291af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
4292272c4a4dSAlexander Motin 	char *s;
4293c59e4cc3SRick Macklem 
4294984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
4295c59e4cc3SRick Macklem 		goto nfsmout;
4296c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
4297c59e4cc3SRick Macklem 	verf = (uint8_t *)tl;
4298c59e4cc3SRick Macklem 	tl += (NFSX_VERF / NFSX_UNSIGNED);
4299c59e4cc3SRick Macklem 	i = fxdr_unsigned(int, *tl);
4300c59e4cc3SRick Macklem 	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
4301c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
4302c59e4cc3SRick Macklem 		goto nfsmout;
4303c59e4cc3SRick Macklem 	}
4304c59e4cc3SRick Macklem 	idlen = i;
4305c59e4cc3SRick Macklem 	if (nd->nd_flag & ND_GSS)
4306c59e4cc3SRick Macklem 		i += nd->nd_princlen;
43071f54e596SRick Macklem 	clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
43081f54e596SRick Macklem 	    M_ZERO);
43091f54e596SRick Macklem 	clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
43101f54e596SRick Macklem 	    nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
4311c59e4cc3SRick Macklem 	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
4312ed2f1001SRick Macklem 	/* Allocated large enough for an AF_INET or AF_INET6 socket. */
4313ed2f1001SRick Macklem 	clp->lc_req.nr_nam = malloc(sizeof(struct sockaddr_in6), M_SONAME,
4314b97b91b5SConrad Meyer 	    M_WAITOK | M_ZERO);
4315ed2f1001SRick Macklem 	switch (nd->nd_nam->sa_family) {
4316ed2f1001SRick Macklem #ifdef INET
4317ed2f1001SRick Macklem 	case AF_INET:
4318ed2f1001SRick Macklem 		rin = (struct sockaddr_in *)clp->lc_req.nr_nam;
4319ed2f1001SRick Macklem 		sin = (struct sockaddr_in *)nd->nd_nam;
4320ed2f1001SRick Macklem 		rin->sin_family = AF_INET;
4321ed2f1001SRick Macklem 		rin->sin_len = sizeof(struct sockaddr_in);
4322ed2f1001SRick Macklem 		rin->sin_port = 0;
4323ed2f1001SRick Macklem 		rin->sin_addr.s_addr = sin->sin_addr.s_addr;
4324ed2f1001SRick Macklem 		break;
4325ed2f1001SRick Macklem #endif
4326ed2f1001SRick Macklem #ifdef INET6
4327ed2f1001SRick Macklem 	case AF_INET6:
4328ed2f1001SRick Macklem 		rin6 = (struct sockaddr_in6 *)clp->lc_req.nr_nam;
4329ed2f1001SRick Macklem 		sin6 = (struct sockaddr_in6 *)nd->nd_nam;
4330ed2f1001SRick Macklem 		rin6->sin6_family = AF_INET6;
4331ed2f1001SRick Macklem 		rin6->sin6_len = sizeof(struct sockaddr_in6);
4332ed2f1001SRick Macklem 		rin6->sin6_port = 0;
4333ed2f1001SRick Macklem 		rin6->sin6_addr = sin6->sin6_addr;
4334ed2f1001SRick Macklem 		break;
4335ed2f1001SRick Macklem #endif
4336ed2f1001SRick Macklem 	}
4337c59e4cc3SRick Macklem 	clp->lc_req.nr_cred = NULL;
4338c59e4cc3SRick Macklem 	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
4339c59e4cc3SRick Macklem 	clp->lc_idlen = idlen;
4340c59e4cc3SRick Macklem 	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
4341c59e4cc3SRick Macklem 	if (error != 0)
4342c59e4cc3SRick Macklem 		goto nfsmout;
4343c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_GSS) != 0) {
4344c59e4cc3SRick Macklem 		clp->lc_flags = LCL_GSS | LCL_NFSV41;
4345c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_GSSINTEGRITY) != 0)
4346c59e4cc3SRick Macklem 			clp->lc_flags |= LCL_GSSINTEGRITY;
4347c59e4cc3SRick Macklem 		else if ((nd->nd_flag & ND_GSSPRIVACY) != 0)
4348c59e4cc3SRick Macklem 			clp->lc_flags |= LCL_GSSPRIVACY;
4349c59e4cc3SRick Macklem 	} else
4350c59e4cc3SRick Macklem 		clp->lc_flags = LCL_NFSV41;
4351c057a378SRick Macklem 	if ((nd->nd_flag & ND_NFSV42) != 0)
4352c057a378SRick Macklem 		clp->lc_flags |= LCL_NFSV42;
4353c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_GSS) != 0 && nd->nd_princlen > 0) {
4354c59e4cc3SRick Macklem 		clp->lc_flags |= LCL_NAME;
4355c59e4cc3SRick Macklem 		clp->lc_namelen = nd->nd_princlen;
4356c59e4cc3SRick Macklem 		clp->lc_name = &clp->lc_id[idlen];
4357c59e4cc3SRick Macklem 		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
4358c59e4cc3SRick Macklem 	} else {
4359c59e4cc3SRick Macklem 		clp->lc_uid = nd->nd_cred->cr_uid;
4360c59e4cc3SRick Macklem 		clp->lc_gid = nd->nd_cred->cr_gid;
4361c59e4cc3SRick Macklem 	}
4362c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
4363c59e4cc3SRick Macklem 	v41flags = fxdr_unsigned(uint32_t, *tl++);
4364c59e4cc3SRick Macklem 	if ((v41flags & ~(NFSV4EXCH_SUPPMOVEDREFER | NFSV4EXCH_SUPPMOVEDMIGR |
4365c59e4cc3SRick Macklem 	    NFSV4EXCH_BINDPRINCSTATEID | NFSV4EXCH_MASKPNFS |
4366c59e4cc3SRick Macklem 	    NFSV4EXCH_UPDCONFIRMEDRECA)) != 0) {
4367c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
4368c59e4cc3SRick Macklem 		goto nfsmout;
4369c59e4cc3SRick Macklem 	}
4370c59e4cc3SRick Macklem 	if ((v41flags & NFSV4EXCH_UPDCONFIRMEDRECA) != 0)
4371c59e4cc3SRick Macklem 		confirm.lval[1] = 1;
4372c59e4cc3SRick Macklem 	else
4373c59e4cc3SRick Macklem 		confirm.lval[1] = 0;
437490d2dfabSRick Macklem 	if (nfsrv_devidcnt == 0)
437590d2dfabSRick Macklem 		v41flags = NFSV4EXCH_USENONPNFS | NFSV4EXCH_USEPNFSDS;
437690d2dfabSRick Macklem  	else
437790d2dfabSRick Macklem  		v41flags = NFSV4EXCH_USEPNFSMDS;
4378c59e4cc3SRick Macklem 	sp4type = fxdr_unsigned(uint32_t, *tl);
4379c59e4cc3SRick Macklem 	if (sp4type != NFSV4EXCH_SP4NONE) {
4380c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
4381c59e4cc3SRick Macklem 		goto nfsmout;
4382c59e4cc3SRick Macklem 	}
4383c59e4cc3SRick Macklem 
4384c59e4cc3SRick Macklem 	/*
4385c59e4cc3SRick Macklem 	 * nfsrv_setclient() does the actual work of adding it to the
4386c59e4cc3SRick Macklem 	 * client list. If there is no error, the structure has been
4387c59e4cc3SRick Macklem 	 * linked into the client list and clp should no longer be used
4388c59e4cc3SRick Macklem 	 * here. When an error is returned, it has not been linked in,
4389c59e4cc3SRick Macklem 	 * so it should be free'd.
4390c59e4cc3SRick Macklem 	 */
4391c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
4392c59e4cc3SRick Macklem 	if (clp != NULL) {
4393b97b91b5SConrad Meyer 		free(clp->lc_req.nr_nam, M_SONAME);
4394c59e4cc3SRick Macklem 		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
43951f54e596SRick Macklem 		free(clp->lc_stateid, M_NFSDCLIENT);
4396c59e4cc3SRick Macklem 		free(clp, M_NFSDCLIENT);
4397c59e4cc3SRick Macklem 	}
4398c59e4cc3SRick Macklem 	if (nd->nd_repstat == 0) {
4399c59e4cc3SRick Macklem 		if (confirm.lval[1] != 0)
4400c59e4cc3SRick Macklem 			v41flags |= NFSV4EXCH_CONFIRMEDR;
4401c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + 3 * NFSX_UNSIGNED);
4402c59e4cc3SRick Macklem 		*tl++ = clientid.lval[0];			/* ClientID */
4403c59e4cc3SRick Macklem 		*tl++ = clientid.lval[1];
4404c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(confirm.lval[0]);		/* SequenceID */
4405c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(v41flags);		/* Exch flags */
4406c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(NFSV4EXCH_SP4NONE);	/* No SSV */
4407272c4a4dSAlexander Motin 		txdr_hyper(nfsrv_owner_minor, tl);	/* Owner Minor */
4408272c4a4dSAlexander Motin 		if (nfsrv_owner_major[0] != 0)
4409272c4a4dSAlexander Motin 			s = nfsrv_owner_major;
4410272c4a4dSAlexander Motin 		else
4411272c4a4dSAlexander Motin 			s = nd->nd_cred->cr_prison->pr_hostuuid;
4412272c4a4dSAlexander Motin 		nfsm_strtom(nd, s, strlen(s));		/* Owner Major */
4413272c4a4dSAlexander Motin 		if (nfsrv_scope[0] != 0)
4414272c4a4dSAlexander Motin 			s = nfsrv_scope;
4415272c4a4dSAlexander Motin 		else
4416272c4a4dSAlexander Motin 			s = nd->nd_cred->cr_prison->pr_hostuuid;
4417272c4a4dSAlexander Motin 		nfsm_strtom(nd, s, strlen(s)	);		/* Scope */
44188932a483SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
4419c59e4cc3SRick Macklem 		*tl = txdr_unsigned(1);
4420c59e4cc3SRick Macklem 		(void)nfsm_strtom(nd, "freebsd.org", strlen("freebsd.org"));
4421c59e4cc3SRick Macklem 		(void)nfsm_strtom(nd, version, strlen(version));
4422c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_V4TIME);
4423c59e4cc3SRick Macklem 		verstime.tv_sec = 1293840000;		/* Jan 1, 2011 */
4424c59e4cc3SRick Macklem 		verstime.tv_nsec = 0;
4425c59e4cc3SRick Macklem 		txdr_nfsv4time(&verstime, tl);
4426c59e4cc3SRick Macklem 	}
4427c59e4cc3SRick Macklem 	NFSEXITCODE2(0, nd);
4428c59e4cc3SRick Macklem 	return (0);
4429c59e4cc3SRick Macklem nfsmout:
4430c59e4cc3SRick Macklem 	if (clp != NULL) {
4431b97b91b5SConrad Meyer 		free(clp->lc_req.nr_nam, M_SONAME);
4432c59e4cc3SRick Macklem 		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
44331f54e596SRick Macklem 		free(clp->lc_stateid, M_NFSDCLIENT);
4434c59e4cc3SRick Macklem 		free(clp, M_NFSDCLIENT);
4435c59e4cc3SRick Macklem 	}
4436c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4437c59e4cc3SRick Macklem 	return (error);
4438c59e4cc3SRick Macklem }
4439c59e4cc3SRick Macklem 
4440c59e4cc3SRick Macklem /*
4441c59e4cc3SRick Macklem  * nfsv4 create session service
4442c59e4cc3SRick Macklem  */
4443b9cc3262SRyan Moeller int
4444c59e4cc3SRick Macklem nfsrvd_createsession(struct nfsrv_descript *nd, __unused int isdgram,
4445af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4446c59e4cc3SRick Macklem {
4447c59e4cc3SRick Macklem 	uint32_t *tl;
4448c59e4cc3SRick Macklem 	int error = 0;
4449c59e4cc3SRick Macklem 	nfsquad_t clientid, confirm;
4450c59e4cc3SRick Macklem 	struct nfsdsession *sep = NULL;
4451c59e4cc3SRick Macklem 	uint32_t rdmacnt;
4452af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
4453ee29e6f3SRick Macklem 	static bool do_printf = true;
4454c59e4cc3SRick Macklem 
4455984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
4456c59e4cc3SRick Macklem 		goto nfsmout;
4457c59e4cc3SRick Macklem 	sep = (struct nfsdsession *)malloc(sizeof(struct nfsdsession),
4458c59e4cc3SRick Macklem 	    M_NFSDSESSION, M_WAITOK | M_ZERO);
4459c59e4cc3SRick Macklem 	sep->sess_refcnt = 1;
4460c59e4cc3SRick Macklem 	mtx_init(&sep->sess_cbsess.nfsess_mtx, "nfscbsession", NULL, MTX_DEF);
4461c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + 2 * NFSX_UNSIGNED);
4462c59e4cc3SRick Macklem 	clientid.lval[0] = *tl++;
4463c59e4cc3SRick Macklem 	clientid.lval[1] = *tl++;
4464c59e4cc3SRick Macklem 	confirm.lval[0] = fxdr_unsigned(uint32_t, *tl++);
4465c59e4cc3SRick Macklem 	sep->sess_crflags = fxdr_unsigned(uint32_t, *tl);
4466c59e4cc3SRick Macklem 	/* Persistent sessions and RDMA are not supported. */
4467c59e4cc3SRick Macklem 	sep->sess_crflags &= NFSV4CRSESS_CONNBACKCHAN;
4468c59e4cc3SRick Macklem 
4469c59e4cc3SRick Macklem 	/* Fore channel attributes. */
4470c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
4471c59e4cc3SRick Macklem 	tl++;					/* Header pad always 0. */
4472c59e4cc3SRick Macklem 	sep->sess_maxreq = fxdr_unsigned(uint32_t, *tl++);
447390d2dfabSRick Macklem 	if (sep->sess_maxreq > sb_max_adj - NFS_MAXXDR) {
447490d2dfabSRick Macklem 		sep->sess_maxreq = sb_max_adj - NFS_MAXXDR;
4475ee29e6f3SRick Macklem 		if (do_printf)
447690d2dfabSRick Macklem 			printf("Consider increasing kern.ipc.maxsockbuf\n");
4477ee29e6f3SRick Macklem 		do_printf = false;
447890d2dfabSRick Macklem 	}
4479c59e4cc3SRick Macklem 	sep->sess_maxresp = fxdr_unsigned(uint32_t, *tl++);
448090d2dfabSRick Macklem 	if (sep->sess_maxresp > sb_max_adj - NFS_MAXXDR) {
448190d2dfabSRick Macklem 		sep->sess_maxresp = sb_max_adj - NFS_MAXXDR;
4482ee29e6f3SRick Macklem 		if (do_printf)
448390d2dfabSRick Macklem 			printf("Consider increasing kern.ipc.maxsockbuf\n");
4484ee29e6f3SRick Macklem 		do_printf = false;
448590d2dfabSRick Macklem 	}
4486c59e4cc3SRick Macklem 	sep->sess_maxrespcached = fxdr_unsigned(uint32_t, *tl++);
4487c59e4cc3SRick Macklem 	sep->sess_maxops = fxdr_unsigned(uint32_t, *tl++);
4488c59e4cc3SRick Macklem 	sep->sess_maxslots = fxdr_unsigned(uint32_t, *tl++);
4489c59e4cc3SRick Macklem 	if (sep->sess_maxslots > NFSV4_SLOTS)
4490c59e4cc3SRick Macklem 		sep->sess_maxslots = NFSV4_SLOTS;
4491c59e4cc3SRick Macklem 	rdmacnt = fxdr_unsigned(uint32_t, *tl);
4492c59e4cc3SRick Macklem 	if (rdmacnt > 1) {
4493c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
4494c59e4cc3SRick Macklem 		goto nfsmout;
4495c59e4cc3SRick Macklem 	} else if (rdmacnt == 1)
4496c59e4cc3SRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4497c59e4cc3SRick Macklem 
4498c59e4cc3SRick Macklem 	/* Back channel attributes. */
4499c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
4500c59e4cc3SRick Macklem 	tl++;					/* Header pad always 0. */
4501c59e4cc3SRick Macklem 	sep->sess_cbmaxreq = fxdr_unsigned(uint32_t, *tl++);
4502c59e4cc3SRick Macklem 	sep->sess_cbmaxresp = fxdr_unsigned(uint32_t, *tl++);
4503c59e4cc3SRick Macklem 	sep->sess_cbmaxrespcached = fxdr_unsigned(uint32_t, *tl++);
4504c59e4cc3SRick Macklem 	sep->sess_cbmaxops = fxdr_unsigned(uint32_t, *tl++);
4505c59e4cc3SRick Macklem 	sep->sess_cbsess.nfsess_foreslots = fxdr_unsigned(uint32_t, *tl++);
4506c59e4cc3SRick Macklem 	rdmacnt = fxdr_unsigned(uint32_t, *tl);
4507c59e4cc3SRick Macklem 	if (rdmacnt > 1) {
4508c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
4509c59e4cc3SRick Macklem 		goto nfsmout;
4510c59e4cc3SRick Macklem 	} else if (rdmacnt == 1)
4511c59e4cc3SRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4512c59e4cc3SRick Macklem 
4513c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4514c59e4cc3SRick Macklem 	sep->sess_cbprogram = fxdr_unsigned(uint32_t, *tl);
4515c59e4cc3SRick Macklem 
4516c59e4cc3SRick Macklem 	/*
4517c59e4cc3SRick Macklem 	 * nfsrv_getclient() searches the client list for a match and
4518c59e4cc3SRick Macklem 	 * returns the appropriate NFSERR status.
4519c59e4cc3SRick Macklem 	 */
4520c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_getclient(clientid, CLOPS_CONFIRM | CLOPS_RENEW,
4521c59e4cc3SRick Macklem 	    NULL, sep, confirm, sep->sess_cbprogram, nd, p);
4522c59e4cc3SRick Macklem 	if (nd->nd_repstat == 0) {
4523c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
4524c59e4cc3SRick Macklem 		NFSBCOPY(sep->sess_sessionid, tl, NFSX_V4SESSIONID);
4525c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 18 * NFSX_UNSIGNED);
4526c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(confirm.lval[0]);	/* sequenceid */
4527c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_crflags);
4528c59e4cc3SRick Macklem 
4529c59e4cc3SRick Macklem 		/* Fore channel attributes. */
4530c59e4cc3SRick Macklem 		*tl++ = 0;
4531c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_maxreq);
4532c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_maxresp);
4533c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_maxrespcached);
4534c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_maxops);
4535c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_maxslots);
4536c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(1);
4537c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(0);			/* No RDMA. */
4538c59e4cc3SRick Macklem 
4539c59e4cc3SRick Macklem 		/* Back channel attributes. */
4540c59e4cc3SRick Macklem 		*tl++ = 0;
4541c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_cbmaxreq);
4542c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_cbmaxresp);
4543c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_cbmaxrespcached);
4544c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_cbmaxops);
4545c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_cbsess.nfsess_foreslots);
4546c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(1);
4547c59e4cc3SRick Macklem 		*tl = txdr_unsigned(0);			/* No RDMA. */
4548c59e4cc3SRick Macklem 	}
4549c59e4cc3SRick Macklem nfsmout:
4550c59e4cc3SRick Macklem 	if (nd->nd_repstat != 0 && sep != NULL)
4551c59e4cc3SRick Macklem 		free(sep, M_NFSDSESSION);
4552c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4553c59e4cc3SRick Macklem 	return (error);
4554c59e4cc3SRick Macklem }
4555c59e4cc3SRick Macklem 
4556c59e4cc3SRick Macklem /*
4557c59e4cc3SRick Macklem  * nfsv4 sequence service
4558c59e4cc3SRick Macklem  */
4559b9cc3262SRyan Moeller int
4560c59e4cc3SRick Macklem nfsrvd_sequence(struct nfsrv_descript *nd, __unused int isdgram,
4561af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4562c59e4cc3SRick Macklem {
4563c59e4cc3SRick Macklem 	uint32_t *tl;
4564c59e4cc3SRick Macklem 	uint32_t highest_slotid, sequenceid, sflags, target_highest_slotid;
4565c59e4cc3SRick Macklem 	int cache_this, error = 0;
4566af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
4567c59e4cc3SRick Macklem 
4568984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
4569c59e4cc3SRick Macklem 		goto nfsmout;
4570c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID);
4571c59e4cc3SRick Macklem 	NFSBCOPY(tl, nd->nd_sessionid, NFSX_V4SESSIONID);
4572c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
4573c59e4cc3SRick Macklem 	sequenceid = fxdr_unsigned(uint32_t, *tl++);
4574c59e4cc3SRick Macklem 	nd->nd_slotid = fxdr_unsigned(uint32_t, *tl++);
4575c59e4cc3SRick Macklem 	highest_slotid = fxdr_unsigned(uint32_t, *tl++);
4576c59e4cc3SRick Macklem 	if (*tl == newnfs_true)
4577c59e4cc3SRick Macklem 		cache_this = 1;
4578c59e4cc3SRick Macklem 	else
4579c59e4cc3SRick Macklem 		cache_this = 0;
4580c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_checksequence(nd, sequenceid, &highest_slotid,
4581c59e4cc3SRick Macklem 	    &target_highest_slotid, cache_this, &sflags, p);
4582db0ac6deSCy Schubert 	if (nd->nd_repstat != NFSERR_BADSLOT)
4583db0ac6deSCy Schubert 		nd->nd_flag |= ND_HASSEQUENCE;
4584c59e4cc3SRick Macklem 	if (nd->nd_repstat == 0) {
4585c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
4586c59e4cc3SRick Macklem 		NFSBCOPY(nd->nd_sessionid, tl, NFSX_V4SESSIONID);
4587c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 5 * NFSX_UNSIGNED);
4588c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sequenceid);
4589c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(nd->nd_slotid);
4590c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(highest_slotid);
4591c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(target_highest_slotid);
4592c59e4cc3SRick Macklem 		*tl = txdr_unsigned(sflags);
4593c59e4cc3SRick Macklem 	}
4594c59e4cc3SRick Macklem nfsmout:
4595c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4596c59e4cc3SRick Macklem 	return (error);
4597c59e4cc3SRick Macklem }
4598c59e4cc3SRick Macklem 
4599c59e4cc3SRick Macklem /*
4600c59e4cc3SRick Macklem  * nfsv4 reclaim complete service
4601c59e4cc3SRick Macklem  */
4602b9cc3262SRyan Moeller int
4603c59e4cc3SRick Macklem nfsrvd_reclaimcomplete(struct nfsrv_descript *nd, __unused int isdgram,
4604af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4605c59e4cc3SRick Macklem {
4606c59e4cc3SRick Macklem 	uint32_t *tl;
4607a3e709cdSRick Macklem 	int error = 0, onefs;
4608c59e4cc3SRick Macklem 
4609c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4610a3e709cdSRick Macklem 	/*
4611a3e709cdSRick Macklem 	 * I believe that a ReclaimComplete with rca_one_fs == TRUE is only
4612a3e709cdSRick Macklem 	 * to be used after a file system has been transferred to a different
4613a3e709cdSRick Macklem 	 * file server.  However, RFC5661 is somewhat vague w.r.t. this and
4614a3e709cdSRick Macklem 	 * the ESXi 6.7 client does both a ReclaimComplete with rca_one_fs
4615a3e709cdSRick Macklem 	 * == TRUE and one with ReclaimComplete with rca_one_fs == FALSE.
4616a3e709cdSRick Macklem 	 * Therefore, just ignore the rca_one_fs == TRUE operation and return
4617a3e709cdSRick Macklem 	 * NFS_OK without doing anything.
4618a3e709cdSRick Macklem 	 */
4619a3e709cdSRick Macklem 	onefs = 0;
4620c59e4cc3SRick Macklem 	if (*tl == newnfs_true)
4621a3e709cdSRick Macklem 		onefs = 1;
4622a3e709cdSRick Macklem 	nd->nd_repstat = nfsrv_checkreclaimcomplete(nd, onefs);
4623c59e4cc3SRick Macklem nfsmout:
4624c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4625c59e4cc3SRick Macklem 	return (error);
4626c59e4cc3SRick Macklem }
4627c59e4cc3SRick Macklem 
4628c59e4cc3SRick Macklem /*
4629c59e4cc3SRick Macklem  * nfsv4 destroy clientid service
4630c59e4cc3SRick Macklem  */
4631b9cc3262SRyan Moeller int
4632c59e4cc3SRick Macklem nfsrvd_destroyclientid(struct nfsrv_descript *nd, __unused int isdgram,
4633af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4634c59e4cc3SRick Macklem {
4635c59e4cc3SRick Macklem 	uint32_t *tl;
4636c59e4cc3SRick Macklem 	nfsquad_t clientid;
4637c59e4cc3SRick Macklem 	int error = 0;
4638af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
4639c59e4cc3SRick Macklem 
4640984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
4641c59e4cc3SRick Macklem 		goto nfsmout;
4642c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
4643c59e4cc3SRick Macklem 	clientid.lval[0] = *tl++;
4644c59e4cc3SRick Macklem 	clientid.lval[1] = *tl;
4645c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_destroyclient(clientid, p);
4646c59e4cc3SRick Macklem nfsmout:
4647c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4648c59e4cc3SRick Macklem 	return (error);
4649c59e4cc3SRick Macklem }
4650c59e4cc3SRick Macklem 
4651c59e4cc3SRick Macklem /*
46529442a64eSRick Macklem  * nfsv4 bind connection to session service
46539442a64eSRick Macklem  */
4654b9cc3262SRyan Moeller int
46559442a64eSRick Macklem nfsrvd_bindconnsess(struct nfsrv_descript *nd, __unused int isdgram,
4656af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
46579442a64eSRick Macklem {
46589442a64eSRick Macklem 	uint32_t *tl;
46599442a64eSRick Macklem 	uint8_t sessid[NFSX_V4SESSIONID];
46609442a64eSRick Macklem 	int error = 0, foreaft;
46619442a64eSRick Macklem 
4662984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
46639442a64eSRick Macklem 		goto nfsmout;
46649442a64eSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID + 2 * NFSX_UNSIGNED);
46659442a64eSRick Macklem 	NFSBCOPY(tl, sessid, NFSX_V4SESSIONID);
46669442a64eSRick Macklem 	tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
46679442a64eSRick Macklem 	foreaft = fxdr_unsigned(int, *tl++);
46689442a64eSRick Macklem 	if (*tl == newnfs_true) {
46699442a64eSRick Macklem 		/* RDMA is not supported. */
46709442a64eSRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
46719442a64eSRick Macklem 		goto nfsmout;
46729442a64eSRick Macklem 	}
46739442a64eSRick Macklem 
46749442a64eSRick Macklem 	nd->nd_repstat = nfsrv_bindconnsess(nd, sessid, &foreaft);
46759442a64eSRick Macklem 	if (nd->nd_repstat == 0) {
46769442a64eSRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 2 *
46779442a64eSRick Macklem 		    NFSX_UNSIGNED);
46789442a64eSRick Macklem 		NFSBCOPY(sessid, tl, NFSX_V4SESSIONID);
46799442a64eSRick Macklem 		tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
46809442a64eSRick Macklem 		*tl++ = txdr_unsigned(foreaft);
46819442a64eSRick Macklem 		*tl = newnfs_false;
46829442a64eSRick Macklem 	}
46839442a64eSRick Macklem nfsmout:
46849442a64eSRick Macklem 	NFSEXITCODE2(error, nd);
46859442a64eSRick Macklem 	return (error);
46869442a64eSRick Macklem }
46879442a64eSRick Macklem 
46889442a64eSRick Macklem /*
4689c59e4cc3SRick Macklem  * nfsv4 destroy session service
4690c59e4cc3SRick Macklem  */
4691b9cc3262SRyan Moeller int
4692c59e4cc3SRick Macklem nfsrvd_destroysession(struct nfsrv_descript *nd, __unused int isdgram,
4693af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4694c59e4cc3SRick Macklem {
4695c59e4cc3SRick Macklem 	uint8_t *cp, sessid[NFSX_V4SESSIONID];
4696c59e4cc3SRick Macklem 	int error = 0;
4697c59e4cc3SRick Macklem 
4698984c71f9SRick Macklem 	if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
4699c59e4cc3SRick Macklem 		goto nfsmout;
4700c59e4cc3SRick Macklem 	NFSM_DISSECT(cp, uint8_t *, NFSX_V4SESSIONID);
4701c59e4cc3SRick Macklem 	NFSBCOPY(cp, sessid, NFSX_V4SESSIONID);
4702c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_destroysession(nd, sessid);
4703c59e4cc3SRick Macklem nfsmout:
4704c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4705c59e4cc3SRick Macklem 	return (error);
4706c59e4cc3SRick Macklem }
4707c59e4cc3SRick Macklem 
4708c59e4cc3SRick Macklem /*
4709c59e4cc3SRick Macklem  * nfsv4 free stateid service
4710c59e4cc3SRick Macklem  */
4711b9cc3262SRyan Moeller int
4712c59e4cc3SRick Macklem nfsrvd_freestateid(struct nfsrv_descript *nd, __unused int isdgram,
4713af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4714c59e4cc3SRick Macklem {
4715c59e4cc3SRick Macklem 	uint32_t *tl;
4716c59e4cc3SRick Macklem 	nfsv4stateid_t stateid;
4717c59e4cc3SRick Macklem 	int error = 0;
4718af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
4719c59e4cc3SRick Macklem 
4720c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
4721c59e4cc3SRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
4722c59e4cc3SRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
472390d2dfabSRick Macklem 
472490d2dfabSRick Macklem 	/*
472590d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
472690d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
472790d2dfabSRick Macklem 	 */
472890d2dfabSRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
472990d2dfabSRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
473090d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
473190d2dfabSRick Macklem 			stateid = nd->nd_curstateid;
473290d2dfabSRick Macklem 			stateid.seqid = 0;
473390d2dfabSRick Macklem 		} else {
473490d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
473590d2dfabSRick Macklem 			goto nfsmout;
473690d2dfabSRick Macklem 		}
473790d2dfabSRick Macklem 	}
473890d2dfabSRick Macklem 
4739c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_freestateid(nd, &stateid, p);
474090d2dfabSRick Macklem 
474190d2dfabSRick Macklem 	/* If the current stateid has been free'd, unset it. */
474290d2dfabSRick Macklem 	if (nd->nd_repstat == 0 && (nd->nd_flag & ND_CURSTATEID) != 0 &&
474390d2dfabSRick Macklem 	    stateid.other[0] == nd->nd_curstateid.other[0] &&
474490d2dfabSRick Macklem 	    stateid.other[1] == nd->nd_curstateid.other[1] &&
474590d2dfabSRick Macklem 	    stateid.other[2] == nd->nd_curstateid.other[2])
474690d2dfabSRick Macklem 		nd->nd_flag &= ~ND_CURSTATEID;
474790d2dfabSRick Macklem nfsmout:
474890d2dfabSRick Macklem 	NFSEXITCODE2(error, nd);
474990d2dfabSRick Macklem 	return (error);
475090d2dfabSRick Macklem }
475190d2dfabSRick Macklem 
475290d2dfabSRick Macklem /*
475390d2dfabSRick Macklem  * nfsv4 layoutget service
475490d2dfabSRick Macklem  */
4755b9cc3262SRyan Moeller int
475690d2dfabSRick Macklem nfsrvd_layoutget(struct nfsrv_descript *nd, __unused int isdgram,
4757af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
475890d2dfabSRick Macklem {
475990d2dfabSRick Macklem 	uint32_t *tl;
476090d2dfabSRick Macklem 	nfsv4stateid_t stateid;
476190d2dfabSRick Macklem 	int error = 0, layoutlen, layouttype, iomode, maxcnt, retonclose;
476290d2dfabSRick Macklem 	uint64_t offset, len, minlen;
476390d2dfabSRick Macklem 	char *layp;
4764af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
476590d2dfabSRick Macklem 
476690d2dfabSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED + 3 * NFSX_HYPER +
476790d2dfabSRick Macklem 	    NFSX_STATEID);
476890d2dfabSRick Macklem 	tl++;		/* Signal layout available. Ignore for now. */
476990d2dfabSRick Macklem 	layouttype = fxdr_unsigned(int, *tl++);
477090d2dfabSRick Macklem 	iomode = fxdr_unsigned(int, *tl++);
477190d2dfabSRick Macklem 	offset = fxdr_hyper(tl); tl += 2;
477290d2dfabSRick Macklem 	len = fxdr_hyper(tl); tl += 2;
477390d2dfabSRick Macklem 	minlen = fxdr_hyper(tl); tl += 2;
477490d2dfabSRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
477590d2dfabSRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
477690d2dfabSRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
477790d2dfabSRick Macklem 	maxcnt = fxdr_unsigned(int, *tl);
477890d2dfabSRick Macklem 	NFSD_DEBUG(4, "layoutget ltyp=%d iom=%d off=%ju len=%ju mlen=%ju\n",
477990d2dfabSRick Macklem 	    layouttype, iomode, (uintmax_t)offset, (uintmax_t)len,
478090d2dfabSRick Macklem 	    (uintmax_t)minlen);
478190d2dfabSRick Macklem 	if (len < minlen ||
478290d2dfabSRick Macklem 	    (minlen != UINT64_MAX && offset + minlen < offset) ||
478390d2dfabSRick Macklem 	    (len != UINT64_MAX && offset + len < offset)) {
478490d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
478590d2dfabSRick Macklem 		goto nfsmout;
478690d2dfabSRick Macklem 	}
478790d2dfabSRick Macklem 
478890d2dfabSRick Macklem 	/*
478990d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
479090d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
479190d2dfabSRick Macklem 	 */
479290d2dfabSRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
479390d2dfabSRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
479490d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
479590d2dfabSRick Macklem 			stateid = nd->nd_curstateid;
479690d2dfabSRick Macklem 			stateid.seqid = 0;
479790d2dfabSRick Macklem 		} else {
479890d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
479990d2dfabSRick Macklem 			goto nfsmout;
480090d2dfabSRick Macklem 		}
480190d2dfabSRick Macklem 	}
480290d2dfabSRick Macklem 
480390d2dfabSRick Macklem 	layp = NULL;
480490d2dfabSRick Macklem 	if (layouttype == NFSLAYOUT_NFSV4_1_FILES && nfsrv_maxpnfsmirror == 1)
480590d2dfabSRick Macklem 		layp = malloc(NFSX_V4FILELAYOUT, M_TEMP, M_WAITOK);
480690d2dfabSRick Macklem 	else if (layouttype == NFSLAYOUT_FLEXFILE)
480790d2dfabSRick Macklem 		layp = malloc(NFSX_V4FLEXLAYOUT(nfsrv_maxpnfsmirror), M_TEMP,
480890d2dfabSRick Macklem 		    M_WAITOK);
480990d2dfabSRick Macklem 	else
481090d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_UNKNLAYOUTTYPE;
481190d2dfabSRick Macklem 	if (layp != NULL)
481290d2dfabSRick Macklem 		nd->nd_repstat = nfsrv_layoutget(nd, vp, exp, layouttype,
481390d2dfabSRick Macklem 		    &iomode, &offset, &len, minlen, &stateid, maxcnt,
481490d2dfabSRick Macklem 		    &retonclose, &layoutlen, layp, nd->nd_cred, p);
481590d2dfabSRick Macklem 	NFSD_DEBUG(4, "nfsrv_layoutget stat=%u layoutlen=%d\n", nd->nd_repstat,
481690d2dfabSRick Macklem 	    layoutlen);
481790d2dfabSRick Macklem 	if (nd->nd_repstat == 0) {
481890d2dfabSRick Macklem 		/* For NFSv4.1, set the Current StateID. */
481990d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0) {
482090d2dfabSRick Macklem 			nd->nd_curstateid = stateid;
482190d2dfabSRick Macklem 			nd->nd_flag |= ND_CURSTATEID;
482290d2dfabSRick Macklem 		}
482390d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED + NFSX_STATEID +
482490d2dfabSRick Macklem 		    2 * NFSX_HYPER);
482590d2dfabSRick Macklem 		*tl++ = txdr_unsigned(retonclose);
482690d2dfabSRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
482790d2dfabSRick Macklem 		NFSBCOPY(stateid.other, tl, NFSX_STATEIDOTHER);
482890d2dfabSRick Macklem 		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
482990d2dfabSRick Macklem 		*tl++ = txdr_unsigned(1);	/* Only returns one layout. */
483090d2dfabSRick Macklem 		txdr_hyper(offset, tl); tl += 2;
483190d2dfabSRick Macklem 		txdr_hyper(len, tl); tl += 2;
483290d2dfabSRick Macklem 		*tl++ = txdr_unsigned(iomode);
483390d2dfabSRick Macklem 		*tl = txdr_unsigned(layouttype);
483490d2dfabSRick Macklem 		nfsm_strtom(nd, layp, layoutlen);
483590d2dfabSRick Macklem 	} else if (nd->nd_repstat == NFSERR_LAYOUTTRYLATER) {
483690d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
483790d2dfabSRick Macklem 		*tl = newnfs_false;
483890d2dfabSRick Macklem 	}
483990d2dfabSRick Macklem 	free(layp, M_TEMP);
484090d2dfabSRick Macklem nfsmout:
484190d2dfabSRick Macklem 	vput(vp);
484290d2dfabSRick Macklem 	NFSEXITCODE2(error, nd);
484390d2dfabSRick Macklem 	return (error);
484490d2dfabSRick Macklem }
484590d2dfabSRick Macklem 
484690d2dfabSRick Macklem /*
484790d2dfabSRick Macklem  * nfsv4 layoutcommit service
484890d2dfabSRick Macklem  */
4849b9cc3262SRyan Moeller int
485090d2dfabSRick Macklem nfsrvd_layoutcommit(struct nfsrv_descript *nd, __unused int isdgram,
4851af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
485290d2dfabSRick Macklem {
485390d2dfabSRick Macklem 	uint32_t *tl;
485490d2dfabSRick Macklem 	nfsv4stateid_t stateid;
485590d2dfabSRick Macklem 	int error = 0, hasnewoff, hasnewmtime, layouttype, maxcnt, reclaim;
485690d2dfabSRick Macklem 	int hasnewsize;
4857f808cf72SRick Macklem 	uint64_t offset, len, newoff = 0, newsize;
485890d2dfabSRick Macklem 	struct timespec newmtime;
485990d2dfabSRick Macklem 	char *layp;
4860af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
486190d2dfabSRick Macklem 
486290d2dfabSRick Macklem 	layp = NULL;
486390d2dfabSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED + 2 * NFSX_HYPER +
486490d2dfabSRick Macklem 	    NFSX_STATEID);
486590d2dfabSRick Macklem 	offset = fxdr_hyper(tl); tl += 2;
486690d2dfabSRick Macklem 	len = fxdr_hyper(tl); tl += 2;
486790d2dfabSRick Macklem 	reclaim = fxdr_unsigned(int, *tl++);
486890d2dfabSRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
486990d2dfabSRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
487090d2dfabSRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
487190d2dfabSRick Macklem 	/*
487290d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
487390d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
487490d2dfabSRick Macklem 	 */
487590d2dfabSRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
487690d2dfabSRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
487790d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
487890d2dfabSRick Macklem 			stateid = nd->nd_curstateid;
487990d2dfabSRick Macklem 			stateid.seqid = 0;
488090d2dfabSRick Macklem 		} else {
488190d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
488290d2dfabSRick Macklem 			goto nfsmout;
488390d2dfabSRick Macklem 		}
488490d2dfabSRick Macklem 	}
488590d2dfabSRick Macklem 
488690d2dfabSRick Macklem 	hasnewoff = fxdr_unsigned(int, *tl);
488790d2dfabSRick Macklem 	if (hasnewoff != 0) {
488890d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + NFSX_UNSIGNED);
488990d2dfabSRick Macklem 		newoff = fxdr_hyper(tl); tl += 2;
489090d2dfabSRick Macklem 	} else
489190d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
489290d2dfabSRick Macklem 	hasnewmtime = fxdr_unsigned(int, *tl);
489390d2dfabSRick Macklem 	if (hasnewmtime != 0) {
489490d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_V4TIME + 2 * NFSX_UNSIGNED);
489590d2dfabSRick Macklem 		fxdr_nfsv4time(tl, &newmtime);
489690d2dfabSRick Macklem 		tl += (NFSX_V4TIME / NFSX_UNSIGNED);
489790d2dfabSRick Macklem 	} else
489890d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
489990d2dfabSRick Macklem 	layouttype = fxdr_unsigned(int, *tl++);
490090d2dfabSRick Macklem 	maxcnt = fxdr_unsigned(int, *tl);
490190d2dfabSRick Macklem 	if (maxcnt > 0) {
490290d2dfabSRick Macklem 		layp = malloc(maxcnt + 1, M_TEMP, M_WAITOK);
490390d2dfabSRick Macklem 		error = nfsrv_mtostr(nd, layp, maxcnt);
490490d2dfabSRick Macklem 		if (error != 0)
490590d2dfabSRick Macklem 			goto nfsmout;
490690d2dfabSRick Macklem 	}
490790d2dfabSRick Macklem 	nd->nd_repstat = nfsrv_layoutcommit(nd, vp, layouttype, hasnewoff,
490890d2dfabSRick Macklem 	    newoff, offset, len, hasnewmtime, &newmtime, reclaim, &stateid,
490990d2dfabSRick Macklem 	    maxcnt, layp, &hasnewsize, &newsize, nd->nd_cred, p);
491090d2dfabSRick Macklem 	NFSD_DEBUG(4, "nfsrv_layoutcommit stat=%u\n", nd->nd_repstat);
491190d2dfabSRick Macklem 	if (nd->nd_repstat == 0) {
491290d2dfabSRick Macklem 		if (hasnewsize != 0) {
491390d2dfabSRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED + NFSX_HYPER);
491490d2dfabSRick Macklem 			*tl++ = newnfs_true;
491590d2dfabSRick Macklem 			txdr_hyper(newsize, tl);
491690d2dfabSRick Macklem 		} else {
491790d2dfabSRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
491890d2dfabSRick Macklem 			*tl = newnfs_false;
491990d2dfabSRick Macklem 		}
492090d2dfabSRick Macklem 	}
492190d2dfabSRick Macklem nfsmout:
492290d2dfabSRick Macklem 	free(layp, M_TEMP);
492390d2dfabSRick Macklem 	vput(vp);
492490d2dfabSRick Macklem 	NFSEXITCODE2(error, nd);
492590d2dfabSRick Macklem 	return (error);
492690d2dfabSRick Macklem }
492790d2dfabSRick Macklem 
492890d2dfabSRick Macklem /*
492990d2dfabSRick Macklem  * nfsv4 layoutreturn service
493090d2dfabSRick Macklem  */
4931b9cc3262SRyan Moeller int
493290d2dfabSRick Macklem nfsrvd_layoutreturn(struct nfsrv_descript *nd, __unused int isdgram,
4933af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
493490d2dfabSRick Macklem {
493590d2dfabSRick Macklem 	uint32_t *tl, *layp;
493690d2dfabSRick Macklem 	nfsv4stateid_t stateid;
493790d2dfabSRick Macklem 	int error = 0, fnd, kind, layouttype, iomode, maxcnt, reclaim;
493890d2dfabSRick Macklem 	uint64_t offset, len;
4939af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
494090d2dfabSRick Macklem 
494190d2dfabSRick Macklem 	layp = NULL;
494290d2dfabSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
494390d2dfabSRick Macklem 	reclaim = *tl++;
494490d2dfabSRick Macklem 	layouttype = fxdr_unsigned(int, *tl++);
494590d2dfabSRick Macklem 	iomode = fxdr_unsigned(int, *tl++);
494690d2dfabSRick Macklem 	kind = fxdr_unsigned(int, *tl);
494790d2dfabSRick Macklem 	NFSD_DEBUG(4, "layoutreturn recl=%d ltyp=%d iom=%d kind=%d\n", reclaim,
494890d2dfabSRick Macklem 	    layouttype, iomode, kind);
494990d2dfabSRick Macklem 	if (kind == NFSV4LAYOUTRET_FILE) {
495090d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_STATEID +
495190d2dfabSRick Macklem 		    NFSX_UNSIGNED);
495290d2dfabSRick Macklem 		offset = fxdr_hyper(tl); tl += 2;
495390d2dfabSRick Macklem 		len = fxdr_hyper(tl); tl += 2;
495490d2dfabSRick Macklem 		stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
495590d2dfabSRick Macklem 		NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
495690d2dfabSRick Macklem 		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
495790d2dfabSRick Macklem 
495890d2dfabSRick Macklem 		/*
495990d2dfabSRick Macklem 		 * For the special stateid of other all 0s and seqid == 1, set
496090d2dfabSRick Macklem 		 * the stateid to the current stateid, if it is set.
496190d2dfabSRick Macklem 		 */
496290d2dfabSRick Macklem 		if (stateid.seqid == 1 && stateid.other[0] == 0 &&
496390d2dfabSRick Macklem 		    stateid.other[1] == 0 && stateid.other[2] == 0) {
496490d2dfabSRick Macklem 			if ((nd->nd_flag & ND_CURSTATEID) != 0) {
496590d2dfabSRick Macklem 				stateid = nd->nd_curstateid;
496690d2dfabSRick Macklem 				stateid.seqid = 0;
496790d2dfabSRick Macklem 			} else {
496890d2dfabSRick Macklem 				nd->nd_repstat = NFSERR_BADSTATEID;
496990d2dfabSRick Macklem 				goto nfsmout;
497090d2dfabSRick Macklem 			}
497190d2dfabSRick Macklem 		}
497290d2dfabSRick Macklem 
497390d2dfabSRick Macklem 		maxcnt = fxdr_unsigned(int, *tl);
4974bdd57cbbSRick Macklem 		/*
4975bdd57cbbSRick Macklem 		 * There is no fixed upper bound defined in the RFCs,
4976bdd57cbbSRick Macklem 		 * but 128Kbytes should be more than sufficient.
4977bdd57cbbSRick Macklem 		 */
4978bdd57cbbSRick Macklem 		if (maxcnt < 0 || maxcnt > 131072)
4979bdd57cbbSRick Macklem 			maxcnt = 0;
498090d2dfabSRick Macklem 		if (maxcnt > 0) {
498190d2dfabSRick Macklem 			layp = malloc(maxcnt + 1, M_TEMP, M_WAITOK);
498290d2dfabSRick Macklem 			error = nfsrv_mtostr(nd, (char *)layp, maxcnt);
498390d2dfabSRick Macklem 			if (error != 0)
498490d2dfabSRick Macklem 				goto nfsmout;
498590d2dfabSRick Macklem 		}
498690d2dfabSRick Macklem 	} else {
498790d2dfabSRick Macklem 		if (reclaim == newnfs_true) {
498890d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
498990d2dfabSRick Macklem 			goto nfsmout;
499090d2dfabSRick Macklem 		}
499190d2dfabSRick Macklem 		offset = len = 0;
499290d2dfabSRick Macklem 		maxcnt = 0;
499390d2dfabSRick Macklem 	}
499490d2dfabSRick Macklem 	nd->nd_repstat = nfsrv_layoutreturn(nd, vp, layouttype, iomode,
499590d2dfabSRick Macklem 	    offset, len, reclaim, kind, &stateid, maxcnt, layp, &fnd,
499690d2dfabSRick Macklem 	    nd->nd_cred, p);
499790d2dfabSRick Macklem 	NFSD_DEBUG(4, "nfsrv_layoutreturn stat=%u fnd=%d\n", nd->nd_repstat,
499890d2dfabSRick Macklem 	    fnd);
499990d2dfabSRick Macklem 	if (nd->nd_repstat == 0) {
500090d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
500190d2dfabSRick Macklem 		if (fnd != 0) {
500290d2dfabSRick Macklem 			*tl = newnfs_true;
500390d2dfabSRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_STATEID);
500490d2dfabSRick Macklem 			*tl++ = txdr_unsigned(stateid.seqid);
500590d2dfabSRick Macklem 			NFSBCOPY(stateid.other, tl, NFSX_STATEIDOTHER);
500690d2dfabSRick Macklem 		} else
500790d2dfabSRick Macklem 			*tl = newnfs_false;
500890d2dfabSRick Macklem 	}
500990d2dfabSRick Macklem nfsmout:
501090d2dfabSRick Macklem 	free(layp, M_TEMP);
501190d2dfabSRick Macklem 	vput(vp);
501290d2dfabSRick Macklem 	NFSEXITCODE2(error, nd);
501390d2dfabSRick Macklem 	return (error);
501490d2dfabSRick Macklem }
501590d2dfabSRick Macklem 
501690d2dfabSRick Macklem /*
5017c057a378SRick Macklem  * nfsv4 layout error service
5018c057a378SRick Macklem  */
5019b9cc3262SRyan Moeller int
5020c057a378SRick Macklem nfsrvd_layouterror(struct nfsrv_descript *nd, __unused int isdgram,
5021c057a378SRick Macklem     vnode_t vp, struct nfsexstuff *exp)
5022c057a378SRick Macklem {
5023c057a378SRick Macklem 	uint32_t *tl;
5024c057a378SRick Macklem 	nfsv4stateid_t stateid;
5025c057a378SRick Macklem 	int cnt, error = 0, i, stat;
5026c057a378SRick Macklem 	int opnum __unused;
5027c057a378SRick Macklem 	char devid[NFSX_V4DEVICEID];
5028c057a378SRick Macklem 	uint64_t offset, len;
5029c057a378SRick Macklem 
5030c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_STATEID +
5031c057a378SRick Macklem 	    NFSX_UNSIGNED);
5032c057a378SRick Macklem 	offset = fxdr_hyper(tl); tl += 2;
5033c057a378SRick Macklem 	len = fxdr_hyper(tl); tl += 2;
5034c057a378SRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
5035c057a378SRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
5036c057a378SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
5037c057a378SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
5038c057a378SRick Macklem 	NFSD_DEBUG(4, "layouterror off=%ju len=%ju cnt=%d\n", (uintmax_t)offset,
5039c057a378SRick Macklem 	    (uintmax_t)len, cnt);
5040c057a378SRick Macklem 	/*
5041c057a378SRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set
5042c057a378SRick Macklem 	 * the stateid to the current stateid, if it is set.
5043c057a378SRick Macklem 	 */
5044c057a378SRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
5045c057a378SRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
5046c057a378SRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
5047c057a378SRick Macklem 			stateid = nd->nd_curstateid;
5048c057a378SRick Macklem 			stateid.seqid = 0;
5049c057a378SRick Macklem 		} else {
5050c057a378SRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
5051c057a378SRick Macklem 			goto nfsmout;
5052c057a378SRick Macklem 		}
5053c057a378SRick Macklem 	}
5054c057a378SRick Macklem 
5055c057a378SRick Macklem 	/*
5056c057a378SRick Macklem 	 * Ignore offset, len and stateid for now.
5057c057a378SRick Macklem 	 */
5058c057a378SRick Macklem 	for (i = 0; i < cnt; i++) {
5059c057a378SRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_V4DEVICEID + 2 *
5060c057a378SRick Macklem 		    NFSX_UNSIGNED);
5061c057a378SRick Macklem 		NFSBCOPY(tl, devid, NFSX_V4DEVICEID);
5062c057a378SRick Macklem 		tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
5063c057a378SRick Macklem 		stat = fxdr_unsigned(int, *tl++);
5064c057a378SRick Macklem 		opnum = fxdr_unsigned(int, *tl);
5065c057a378SRick Macklem 		NFSD_DEBUG(4, "nfsrvd_layouterr op=%d stat=%d\n", opnum, stat);
5066c057a378SRick Macklem 		/*
5067a7e014eeSRick Macklem 		 * Except for NFSERR_ACCES, NFSERR_STALE and NFSERR_NOSPC
5068a7e014eeSRick Macklem 		 * errors, disable the mirror.
5069c057a378SRick Macklem 		 */
5070a7e014eeSRick Macklem 		if (stat != NFSERR_ACCES && stat != NFSERR_STALE &&
5071a7e014eeSRick Macklem 		    stat != NFSERR_NOSPC)
5072c057a378SRick Macklem 			nfsrv_delds(devid, curthread);
5073f8dc0630SRick Macklem 
5074f8dc0630SRick Macklem 		/* For NFSERR_NOSPC, mark all deviceids and layouts. */
5075f8dc0630SRick Macklem 		if (stat == NFSERR_NOSPC)
5076f8dc0630SRick Macklem 			nfsrv_marknospc(devid, true);
5077c057a378SRick Macklem 	}
5078c057a378SRick Macklem nfsmout:
5079c057a378SRick Macklem 	vput(vp);
5080c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5081c057a378SRick Macklem 	return (error);
5082c057a378SRick Macklem }
5083c057a378SRick Macklem 
5084c057a378SRick Macklem /*
5085c057a378SRick Macklem  * nfsv4 layout stats service
5086c057a378SRick Macklem  */
5087b9cc3262SRyan Moeller int
5088c057a378SRick Macklem nfsrvd_layoutstats(struct nfsrv_descript *nd, __unused int isdgram,
5089c057a378SRick Macklem     vnode_t vp, struct nfsexstuff *exp)
5090c057a378SRick Macklem {
5091c057a378SRick Macklem 	uint32_t *tl;
5092c057a378SRick Macklem 	nfsv4stateid_t stateid;
5093c057a378SRick Macklem 	int cnt, error = 0;
5094c057a378SRick Macklem 	int layouttype __unused;
5095c057a378SRick Macklem 	char devid[NFSX_V4DEVICEID] __unused;
5096638b90a1SRick Macklem 	uint64_t offset __unused, len __unused, readcount __unused;
5097638b90a1SRick Macklem 	uint64_t readbytes __unused, writecount __unused, writebytes __unused;
5098c057a378SRick Macklem 
5099c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 6 * NFSX_HYPER + NFSX_STATEID +
5100c057a378SRick Macklem 	    NFSX_V4DEVICEID + 2 * NFSX_UNSIGNED);
5101c057a378SRick Macklem 	offset = fxdr_hyper(tl); tl += 2;
5102c057a378SRick Macklem 	len = fxdr_hyper(tl); tl += 2;
5103c057a378SRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
5104c057a378SRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
5105c057a378SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
5106c057a378SRick Macklem 	readcount = fxdr_hyper(tl); tl += 2;
5107c057a378SRick Macklem 	readbytes = fxdr_hyper(tl); tl += 2;
5108c057a378SRick Macklem 	writecount = fxdr_hyper(tl); tl += 2;
5109c057a378SRick Macklem 	writebytes = fxdr_hyper(tl); tl += 2;
5110c057a378SRick Macklem 	NFSBCOPY(tl, devid, NFSX_V4DEVICEID);
5111c057a378SRick Macklem 	tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
5112c057a378SRick Macklem 	layouttype = fxdr_unsigned(int, *tl++);
5113c057a378SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
5114c057a378SRick Macklem 	error = nfsm_advance(nd, NFSM_RNDUP(cnt), -1);
5115c057a378SRick Macklem 	if (error != 0)
5116c057a378SRick Macklem 		goto nfsmout;
5117c057a378SRick Macklem 	NFSD_DEBUG(4, "layoutstats cnt=%d\n", cnt);
5118c057a378SRick Macklem 	/*
5119c057a378SRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set
5120c057a378SRick Macklem 	 * the stateid to the current stateid, if it is set.
5121c057a378SRick Macklem 	 */
5122c057a378SRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
5123c057a378SRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
5124c057a378SRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
5125c057a378SRick Macklem 			stateid = nd->nd_curstateid;
5126c057a378SRick Macklem 			stateid.seqid = 0;
5127c057a378SRick Macklem 		} else {
5128c057a378SRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
5129c057a378SRick Macklem 			goto nfsmout;
5130c057a378SRick Macklem 		}
5131c057a378SRick Macklem 	}
5132c057a378SRick Macklem 
5133c057a378SRick Macklem 	/*
5134c057a378SRick Macklem 	 * No use for the stats for now.
5135c057a378SRick Macklem 	 */
5136c057a378SRick Macklem nfsmout:
5137c057a378SRick Macklem 	vput(vp);
5138c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5139c057a378SRick Macklem 	return (error);
5140c057a378SRick Macklem }
5141c057a378SRick Macklem 
5142c057a378SRick Macklem /*
5143c057a378SRick Macklem  * nfsv4 io_advise service
5144c057a378SRick Macklem  */
5145b9cc3262SRyan Moeller int
5146c057a378SRick Macklem nfsrvd_ioadvise(struct nfsrv_descript *nd, __unused int isdgram,
5147c057a378SRick Macklem     vnode_t vp, struct nfsexstuff *exp)
5148c057a378SRick Macklem {
5149c057a378SRick Macklem 	uint32_t *tl;
5150c057a378SRick Macklem 	nfsv4stateid_t stateid;
5151c057a378SRick Macklem 	nfsattrbit_t hints;
5152c057a378SRick Macklem 	int error = 0, ret;
5153c057a378SRick Macklem 	off_t offset, len;
5154c057a378SRick Macklem 
5155c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + 2 * NFSX_HYPER);
5156c057a378SRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
5157c057a378SRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
5158c057a378SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
5159c057a378SRick Macklem 	offset = fxdr_hyper(tl); tl += 2;
5160c057a378SRick Macklem 	len = fxdr_hyper(tl);
5161c057a378SRick Macklem 	error = nfsrv_getattrbits(nd, &hints, NULL, NULL);
5162c057a378SRick Macklem 	if (error != 0)
5163c057a378SRick Macklem 		goto nfsmout;
5164c057a378SRick Macklem 	/*
5165c057a378SRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set
5166c057a378SRick Macklem 	 * the stateid to the current stateid, if it is set.
5167c057a378SRick Macklem 	 */
5168c057a378SRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
5169c057a378SRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
5170c057a378SRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
5171c057a378SRick Macklem 			stateid = nd->nd_curstateid;
5172c057a378SRick Macklem 			stateid.seqid = 0;
5173c057a378SRick Macklem 		} else {
5174c057a378SRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
5175c057a378SRick Macklem 			goto nfsmout;
5176c057a378SRick Macklem 		}
5177c057a378SRick Macklem 	}
5178c057a378SRick Macklem 
5179c057a378SRick Macklem 	if (offset < 0) {
5180c057a378SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
5181c057a378SRick Macklem 		goto nfsmout;
5182c057a378SRick Macklem 	}
5183c057a378SRick Macklem 	if (len < 0)
5184c057a378SRick Macklem 		len = 0;
5185c057a378SRick Macklem 	if (vp->v_type != VREG) {
5186c057a378SRick Macklem 		if (vp->v_type == VDIR)
5187c057a378SRick Macklem 			nd->nd_repstat = NFSERR_ISDIR;
5188c057a378SRick Macklem 		else
5189c057a378SRick Macklem 			nd->nd_repstat = NFSERR_WRONGTYPE;
5190c057a378SRick Macklem 		goto nfsmout;
5191c057a378SRick Macklem 	}
5192c057a378SRick Macklem 
5193c057a378SRick Macklem 	/*
5194c057a378SRick Macklem 	 * For now, we can only handle WILLNEED and DONTNEED and don't use
5195c057a378SRick Macklem 	 * the stateid.
5196c057a378SRick Macklem 	 */
5197c057a378SRick Macklem 	if ((NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED) &&
5198c057a378SRick Macklem 	    !NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_DONTNEED)) ||
5199c057a378SRick Macklem 	    (NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_DONTNEED) &&
5200c057a378SRick Macklem 	    !NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED))) {
5201b249ce48SMateusz Guzik 		NFSVOPUNLOCK(vp);
5202c057a378SRick Macklem 		if (NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED)) {
5203c057a378SRick Macklem 			ret = VOP_ADVISE(vp, offset, len, POSIX_FADV_WILLNEED);
5204c057a378SRick Macklem 			NFSZERO_ATTRBIT(&hints);
5205c057a378SRick Macklem 			if (ret == 0)
5206c057a378SRick Macklem 				NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED);
5207c057a378SRick Macklem 			else
5208c057a378SRick Macklem 				NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_NORMAL);
5209c057a378SRick Macklem 		} else {
5210c057a378SRick Macklem 			ret = VOP_ADVISE(vp, offset, len, POSIX_FADV_DONTNEED);
5211c057a378SRick Macklem 			NFSZERO_ATTRBIT(&hints);
5212c057a378SRick Macklem 			if (ret == 0)
5213c057a378SRick Macklem 				NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_DONTNEED);
5214c057a378SRick Macklem 			else
5215c057a378SRick Macklem 				NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_NORMAL);
5216c057a378SRick Macklem 		}
5217c057a378SRick Macklem 		vrele(vp);
5218c057a378SRick Macklem 	} else {
5219c057a378SRick Macklem 		NFSZERO_ATTRBIT(&hints);
5220c057a378SRick Macklem 		NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_NORMAL);
5221c057a378SRick Macklem 		vput(vp);
5222c057a378SRick Macklem 	}
5223c057a378SRick Macklem 	nfsrv_putattrbit(nd, &hints);
5224c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5225c057a378SRick Macklem 	return (error);
5226c057a378SRick Macklem nfsmout:
5227c057a378SRick Macklem 	vput(vp);
5228c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5229c057a378SRick Macklem 	return (error);
5230c057a378SRick Macklem }
5231c057a378SRick Macklem 
5232c057a378SRick Macklem /*
523390d2dfabSRick Macklem  * nfsv4 getdeviceinfo service
523490d2dfabSRick Macklem  */
5235b9cc3262SRyan Moeller int
523690d2dfabSRick Macklem nfsrvd_getdevinfo(struct nfsrv_descript *nd, __unused int isdgram,
5237af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
523890d2dfabSRick Macklem {
523990d2dfabSRick Macklem 	uint32_t *tl, maxcnt, notify[NFSV4_NOTIFYBITMAP];
524090d2dfabSRick Macklem 	int cnt, devaddrlen, error = 0, i, layouttype;
524190d2dfabSRick Macklem 	char devid[NFSX_V4DEVICEID], *devaddr;
524290d2dfabSRick Macklem 	time_t dev_time;
524390d2dfabSRick Macklem 
524490d2dfabSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 3 * NFSX_UNSIGNED + NFSX_V4DEVICEID);
524590d2dfabSRick Macklem 	NFSBCOPY(tl, devid, NFSX_V4DEVICEID);
524690d2dfabSRick Macklem 	tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
524790d2dfabSRick Macklem 	layouttype = fxdr_unsigned(int, *tl++);
524890d2dfabSRick Macklem 	maxcnt = fxdr_unsigned(uint32_t, *tl++);
524990d2dfabSRick Macklem 	cnt = fxdr_unsigned(int, *tl);
525090d2dfabSRick Macklem 	NFSD_DEBUG(4, "getdevinfo ltyp=%d maxcnt=%u bitcnt=%d\n", layouttype,
525190d2dfabSRick Macklem 	    maxcnt, cnt);
525290d2dfabSRick Macklem 	if (cnt > NFSV4_NOTIFYBITMAP || cnt < 0) {
525390d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
525490d2dfabSRick Macklem 		goto nfsmout;
525590d2dfabSRick Macklem 	}
525690d2dfabSRick Macklem 	if (cnt > 0) {
525790d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, cnt * NFSX_UNSIGNED);
525890d2dfabSRick Macklem 		for (i = 0; i < cnt; i++)
525990d2dfabSRick Macklem 			notify[i] = fxdr_unsigned(uint32_t, *tl++);
526090d2dfabSRick Macklem 	}
526190d2dfabSRick Macklem 	for (i = cnt; i < NFSV4_NOTIFYBITMAP; i++)
526290d2dfabSRick Macklem 		notify[i] = 0;
526390d2dfabSRick Macklem 
526490d2dfabSRick Macklem 	/*
526590d2dfabSRick Macklem 	 * Check that the device id is not stale.  Device ids are recreated
526690d2dfabSRick Macklem 	 * each time the nfsd threads are restarted.
526790d2dfabSRick Macklem 	 */
526890d2dfabSRick Macklem 	NFSBCOPY(devid, &dev_time, sizeof(dev_time));
526990d2dfabSRick Macklem 	if (dev_time != nfsdev_time) {
527090d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_NOENT;
527190d2dfabSRick Macklem 		goto nfsmout;
527290d2dfabSRick Macklem 	}
527390d2dfabSRick Macklem 
527490d2dfabSRick Macklem 	/* Look for the device id. */
527590d2dfabSRick Macklem 	nd->nd_repstat = nfsrv_getdevinfo(devid, layouttype, &maxcnt,
527690d2dfabSRick Macklem 	    notify, &devaddrlen, &devaddr);
527790d2dfabSRick Macklem 	NFSD_DEBUG(4, "nfsrv_getdevinfo stat=%u\n", nd->nd_repstat);
527890d2dfabSRick Macklem 	if (nd->nd_repstat == 0) {
527990d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
528090d2dfabSRick Macklem 		*tl = txdr_unsigned(layouttype);
528190d2dfabSRick Macklem 		nfsm_strtom(nd, devaddr, devaddrlen);
528290d2dfabSRick Macklem 		cnt = 0;
528390d2dfabSRick Macklem 		for (i = 0; i < NFSV4_NOTIFYBITMAP; i++) {
528490d2dfabSRick Macklem 			if (notify[i] != 0)
528590d2dfabSRick Macklem 				cnt = i + 1;
528690d2dfabSRick Macklem 		}
528790d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, (cnt + 1) * NFSX_UNSIGNED);
528890d2dfabSRick Macklem 		*tl++ = txdr_unsigned(cnt);
528990d2dfabSRick Macklem 		for (i = 0; i < cnt; i++)
529090d2dfabSRick Macklem 			*tl++ = txdr_unsigned(notify[i]);
529190d2dfabSRick Macklem 	} else if (nd->nd_repstat == NFSERR_TOOSMALL) {
529290d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
529390d2dfabSRick Macklem 		*tl = txdr_unsigned(maxcnt);
529490d2dfabSRick Macklem 	}
5295c59e4cc3SRick Macklem nfsmout:
5296c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
5297c59e4cc3SRick Macklem 	return (error);
5298c59e4cc3SRick Macklem }
5299c59e4cc3SRick Macklem 
5300c59e4cc3SRick Macklem /*
53015d4835e4SRick Macklem  * nfsv4 test stateid service
53025d4835e4SRick Macklem  */
5303b9cc3262SRyan Moeller int
53045d4835e4SRick Macklem nfsrvd_teststateid(struct nfsrv_descript *nd, __unused int isdgram,
5305af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
53065d4835e4SRick Macklem {
53075d4835e4SRick Macklem 	uint32_t *tl;
53085d4835e4SRick Macklem 	nfsv4stateid_t *stateidp = NULL, *tstateidp;
53095d4835e4SRick Macklem 	int cnt, error = 0, i, ret;
5310af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
53115d4835e4SRick Macklem 
53125d4835e4SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
53135d4835e4SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
53145d4835e4SRick Macklem 	if (cnt <= 0 || cnt > 1024) {
53155d4835e4SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
53165d4835e4SRick Macklem 		goto nfsmout;
53175d4835e4SRick Macklem 	}
53185d4835e4SRick Macklem 	stateidp = mallocarray(cnt, sizeof(nfsv4stateid_t), M_TEMP, M_WAITOK);
53195d4835e4SRick Macklem 	tstateidp = stateidp;
53205d4835e4SRick Macklem 	for (i = 0; i < cnt; i++) {
53215d4835e4SRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
53225d4835e4SRick Macklem 		tstateidp->seqid = fxdr_unsigned(uint32_t, *tl++);
53235d4835e4SRick Macklem 		NFSBCOPY(tl, tstateidp->other, NFSX_STATEIDOTHER);
53245d4835e4SRick Macklem 		tstateidp++;
53255d4835e4SRick Macklem 	}
53265d4835e4SRick Macklem 	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
53275d4835e4SRick Macklem 	*tl = txdr_unsigned(cnt);
53285d4835e4SRick Macklem 	tstateidp = stateidp;
53295d4835e4SRick Macklem 	for (i = 0; i < cnt; i++) {
53305d4835e4SRick Macklem 		ret = nfsrv_teststateid(nd, tstateidp, p);
53315d4835e4SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
53325d4835e4SRick Macklem 		*tl = txdr_unsigned(ret);
53335d4835e4SRick Macklem 		tstateidp++;
53345d4835e4SRick Macklem 	}
53355d4835e4SRick Macklem nfsmout:
53365d4835e4SRick Macklem 	free(stateidp, M_TEMP);
53375d4835e4SRick Macklem 	NFSEXITCODE2(error, nd);
53385d4835e4SRick Macklem 	return (error);
53395d4835e4SRick Macklem }
53405d4835e4SRick Macklem 
53415d4835e4SRick Macklem /*
5342c057a378SRick Macklem  * nfs allocate service
5343c057a378SRick Macklem  */
5344b9cc3262SRyan Moeller int
5345c057a378SRick Macklem nfsrvd_allocate(struct nfsrv_descript *nd, __unused int isdgram,
5346c057a378SRick Macklem     vnode_t vp, struct nfsexstuff *exp)
5347c057a378SRick Macklem {
5348c057a378SRick Macklem 	uint32_t *tl;
5349c057a378SRick Macklem 	struct nfsvattr forat;
5350c057a378SRick Macklem 	int error = 0, forat_ret = 1, gotproxystateid;
5351c057a378SRick Macklem 	off_t off, len;
5352c057a378SRick Macklem 	struct nfsstate st, *stp = &st;
5353c057a378SRick Macklem 	struct nfslock lo, *lop = &lo;
5354c057a378SRick Macklem 	nfsv4stateid_t stateid;
5355c057a378SRick Macklem 	nfsquad_t clientid;
5356c057a378SRick Macklem 	nfsattrbit_t attrbits;
5357c057a378SRick Macklem 
5358dfe887b7SRick Macklem 	if (!nfsrv_doallocate) {
5359dfe887b7SRick Macklem 		/*
5360dfe887b7SRick Macklem 		 * If any exported file system, such as a ZFS one, cannot
5361dfe887b7SRick Macklem 		 * do VOP_ALLOCATE(), this operation cannot be supported
5362dfe887b7SRick Macklem 		 * for NFSv4.2.  This cannot be done 'per filesystem', but
5363dfe887b7SRick Macklem 		 * must be for the entire nfsd NFSv4.2 service.
5364dfe887b7SRick Macklem 		 */
5365dfe887b7SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5366dfe887b7SRick Macklem 		goto nfsmout;
5367dfe887b7SRick Macklem 	}
5368c057a378SRick Macklem 	gotproxystateid = 0;
5369c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + 2 * NFSX_HYPER);
5370c057a378SRick Macklem 	stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
5371c057a378SRick Macklem 	lop->lo_flags = NFSLCK_WRITE;
5372c057a378SRick Macklem 	stp->ls_ownerlen = 0;
5373c057a378SRick Macklem 	stp->ls_op = NULL;
5374c057a378SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
5375c057a378SRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
5376c057a378SRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
5377c057a378SRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
5378c057a378SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
5379c057a378SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
5380c057a378SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
5381c057a378SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
5382c057a378SRick Macklem 			printf("EEK2 multiple clids\n");
5383c057a378SRick Macklem 	} else {
5384c057a378SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
5385c057a378SRick Macklem 			printf("EEK! no clientid from session\n");
5386c057a378SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
5387c057a378SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
5388c057a378SRick Macklem 	}
5389c057a378SRick Macklem 	stp->ls_stateid.other[2] = *tl++;
5390c057a378SRick Macklem 	/*
5391c057a378SRick Macklem 	 * Don't allow this to be done for a DS.
5392c057a378SRick Macklem 	 */
5393c057a378SRick Macklem 	if ((nd->nd_flag & ND_DSSERVER) != 0)
5394c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5395c057a378SRick Macklem 	/* However, allow the proxy stateid. */
5396c057a378SRick Macklem 	if (stp->ls_stateid.seqid == 0xffffffff &&
5397c057a378SRick Macklem 	    stp->ls_stateid.other[0] == 0x55555555 &&
5398c057a378SRick Macklem 	    stp->ls_stateid.other[1] == 0x55555555 &&
5399c057a378SRick Macklem 	    stp->ls_stateid.other[2] == 0x55555555)
5400c057a378SRick Macklem 		gotproxystateid = 1;
5401c057a378SRick Macklem 	off = fxdr_hyper(tl); tl += 2;
5402c057a378SRick Macklem 	lop->lo_first = off;
5403c057a378SRick Macklem 	len = fxdr_hyper(tl);
540406afb53bSRick Macklem 	lop->lo_end = lop->lo_first + len;
5405c057a378SRick Macklem 	/*
540606afb53bSRick Macklem 	 * Sanity check the offset and length.
540706afb53bSRick Macklem 	 * off and len are off_t (signed int64_t) whereas
540806afb53bSRick Macklem 	 * lo_first and lo_end are uint64_t and, as such,
540906afb53bSRick Macklem 	 * if off >= 0 && len > 0, lo_end cannot overflow
541006afb53bSRick Macklem 	 * unless off_t is changed to something other than
541106afb53bSRick Macklem 	 * int64_t.  Check lo_end < lo_first in case that
541206afb53bSRick Macklem 	 * is someday the case.
5413c057a378SRick Macklem 	 */
541406afb53bSRick Macklem 	if (nd->nd_repstat == 0 && (len <= 0 || off < 0 || lop->lo_end >
541506afb53bSRick Macklem 	    OFF_MAX || lop->lo_end < lop->lo_first))
5416c057a378SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
5417c057a378SRick Macklem 
54185d3fe02cSRick Macklem 	if (nd->nd_repstat == 0 && vp->v_type != VREG)
5419c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGTYPE;
5420c057a378SRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
5421c057a378SRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
5422c057a378SRick Macklem 	forat_ret = nfsvno_getattr(vp, &forat, nd, curthread, 1, &attrbits);
5423c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5424c057a378SRick Macklem 		nd->nd_repstat = forat_ret;
5425c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (forat.na_uid != nd->nd_cred->cr_uid ||
5426c057a378SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
5427c057a378SRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp,
5428c057a378SRick Macklem 		    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
5429c057a378SRick Macklem 		    NULL);
5430c057a378SRick Macklem 	if (nd->nd_repstat == 0 && gotproxystateid == 0)
5431c057a378SRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
5432c057a378SRick Macklem 		    &stateid, exp, nd, curthread);
5433c057a378SRick Macklem 
5434dfe887b7SRick Macklem 	NFSD_DEBUG(4, "nfsrvd_allocate: off=%jd len=%jd stat=%d\n",
5435dfe887b7SRick Macklem 	    (intmax_t)off, (intmax_t)len, nd->nd_repstat);
5436c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5437c057a378SRick Macklem 		nd->nd_repstat = nfsvno_allocate(vp, off, len, nd->nd_cred,
5438c057a378SRick Macklem 		    curthread);
5439dfe887b7SRick Macklem 	NFSD_DEBUG(4, "nfsrvd_allocate: aft nfsvno_allocate=%d\n",
5440dfe887b7SRick Macklem 	    nd->nd_repstat);
5441c057a378SRick Macklem 	vput(vp);
5442c057a378SRick Macklem 	NFSEXITCODE2(0, nd);
5443c057a378SRick Macklem 	return (0);
5444c057a378SRick Macklem nfsmout:
5445c057a378SRick Macklem 	vput(vp);
5446c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5447c057a378SRick Macklem 	return (error);
5448c057a378SRick Macklem }
5449c057a378SRick Macklem 
5450c057a378SRick Macklem /*
5451bb958dcfSRick Macklem  * nfs deallocate service
5452bb958dcfSRick Macklem  */
5453bb958dcfSRick Macklem int
5454bb958dcfSRick Macklem nfsrvd_deallocate(struct nfsrv_descript *nd, __unused int isdgram,
5455bb958dcfSRick Macklem     vnode_t vp, struct nfsexstuff *exp)
5456bb958dcfSRick Macklem {
5457bb958dcfSRick Macklem 	uint32_t *tl;
5458bb958dcfSRick Macklem 	struct nfsvattr forat;
5459bb958dcfSRick Macklem 	int error = 0, forat_ret = 1, gotproxystateid;
5460bb958dcfSRick Macklem 	off_t off, len;
5461bb958dcfSRick Macklem 	struct nfsstate st, *stp = &st;
5462bb958dcfSRick Macklem 	struct nfslock lo, *lop = &lo;
5463bb958dcfSRick Macklem 	nfsv4stateid_t stateid;
5464bb958dcfSRick Macklem 	nfsquad_t clientid;
5465bb958dcfSRick Macklem 	nfsattrbit_t attrbits;
5466bb958dcfSRick Macklem 
5467bb958dcfSRick Macklem 	gotproxystateid = 0;
5468bb958dcfSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + 2 * NFSX_HYPER);
5469bb958dcfSRick Macklem 	stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
5470bb958dcfSRick Macklem 	lop->lo_flags = NFSLCK_WRITE;
5471bb958dcfSRick Macklem 	stp->ls_ownerlen = 0;
5472bb958dcfSRick Macklem 	stp->ls_op = NULL;
5473bb958dcfSRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
5474bb958dcfSRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
5475bb958dcfSRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
5476bb958dcfSRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
5477bb958dcfSRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
5478bb958dcfSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
5479bb958dcfSRick Macklem 			clientid.qval = nd->nd_clientid.qval;
5480bb958dcfSRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
5481bb958dcfSRick Macklem 			printf("EEK2 multiple clids\n");
5482bb958dcfSRick Macklem 	} else {
5483bb958dcfSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
5484bb958dcfSRick Macklem 			printf("EEK! no clientid from session\n");
5485bb958dcfSRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
5486bb958dcfSRick Macklem 		nd->nd_clientid.qval = clientid.qval;
5487bb958dcfSRick Macklem 	}
5488bb958dcfSRick Macklem 	stp->ls_stateid.other[2] = *tl++;
5489bb958dcfSRick Macklem 	/*
5490bb958dcfSRick Macklem 	 * Don't allow this to be done for a DS.
5491bb958dcfSRick Macklem 	 */
5492bb958dcfSRick Macklem 	if ((nd->nd_flag & ND_DSSERVER) != 0)
5493bb958dcfSRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5494bb958dcfSRick Macklem 	/* However, allow the proxy stateid. */
5495bb958dcfSRick Macklem 	if (stp->ls_stateid.seqid == 0xffffffff &&
5496bb958dcfSRick Macklem 	    stp->ls_stateid.other[0] == 0x55555555 &&
5497bb958dcfSRick Macklem 	    stp->ls_stateid.other[1] == 0x55555555 &&
5498bb958dcfSRick Macklem 	    stp->ls_stateid.other[2] == 0x55555555)
5499bb958dcfSRick Macklem 		gotproxystateid = 1;
5500bb958dcfSRick Macklem 	off = fxdr_hyper(tl); tl += 2;
5501bb958dcfSRick Macklem 	lop->lo_first = off;
5502bb958dcfSRick Macklem 	len = fxdr_hyper(tl);
5503bb958dcfSRick Macklem 	if (len < 0)
5504bb958dcfSRick Macklem 		len = OFF_MAX;
5505bb958dcfSRick Macklem 	NFSD_DEBUG(4, "dealloc: off=%jd len=%jd\n", (intmax_t)off,
5506bb958dcfSRick Macklem 	    (intmax_t)len);
5507bb958dcfSRick Macklem 	lop->lo_end = lop->lo_first + len;
5508bb958dcfSRick Macklem 	/*
5509bb958dcfSRick Macklem 	 * Sanity check the offset and length.
5510bb958dcfSRick Macklem 	 * off and len are off_t (signed int64_t) whereas
5511bb958dcfSRick Macklem 	 * lo_first and lo_end are uint64_t and, as such,
5512bb958dcfSRick Macklem 	 * if off >= 0 && len > 0, lo_end cannot overflow
5513bb958dcfSRick Macklem 	 * unless off_t is changed to something other than
5514bb958dcfSRick Macklem 	 * int64_t.  Check lo_end < lo_first in case that
5515bb958dcfSRick Macklem 	 * is someday the case.
5516bb958dcfSRick Macklem 	 * The error to return is not specified by RFC 7862 so I
5517bb958dcfSRick Macklem 	 * made this compatible with the Linux knfsd.
5518bb958dcfSRick Macklem 	 */
5519bb958dcfSRick Macklem 	if (nd->nd_repstat == 0) {
5520bb958dcfSRick Macklem 		if (off < 0 || lop->lo_end > NFSRV_MAXFILESIZE)
5521bb958dcfSRick Macklem 			nd->nd_repstat = NFSERR_FBIG;
5522bb958dcfSRick Macklem 		else if (len == 0 || lop->lo_end < lop->lo_first)
5523bb958dcfSRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
5524bb958dcfSRick Macklem 	}
5525bb958dcfSRick Macklem 
55265d3fe02cSRick Macklem 	if (nd->nd_repstat == 0 && vp->v_type != VREG)
5527bb958dcfSRick Macklem 		nd->nd_repstat = NFSERR_WRONGTYPE;
5528bb958dcfSRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
5529bb958dcfSRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
5530bb958dcfSRick Macklem 	forat_ret = nfsvno_getattr(vp, &forat, nd, curthread, 1, &attrbits);
5531bb958dcfSRick Macklem 	if (nd->nd_repstat == 0)
5532bb958dcfSRick Macklem 		nd->nd_repstat = forat_ret;
5533bb958dcfSRick Macklem 	if (nd->nd_repstat == 0 && (forat.na_uid != nd->nd_cred->cr_uid ||
5534bb958dcfSRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
5535bb958dcfSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp,
5536bb958dcfSRick Macklem 		    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
5537bb958dcfSRick Macklem 		    NULL);
5538bb958dcfSRick Macklem 	if (nd->nd_repstat == 0 && gotproxystateid == 0)
5539bb958dcfSRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
5540bb958dcfSRick Macklem 		    &stateid, exp, nd, curthread);
5541bb958dcfSRick Macklem 
5542bb958dcfSRick Macklem 	if (nd->nd_repstat == 0)
5543bb958dcfSRick Macklem 		nd->nd_repstat = nfsvno_deallocate(vp, off, len, nd->nd_cred,
5544bb958dcfSRick Macklem 		    curthread);
5545bb958dcfSRick Macklem 	vput(vp);
5546bb958dcfSRick Macklem 	NFSD_DEBUG(4, "eo deallocate=%d\n", nd->nd_repstat);
5547bb958dcfSRick Macklem 	NFSEXITCODE2(0, nd);
5548bb958dcfSRick Macklem 	return (0);
5549bb958dcfSRick Macklem nfsmout:
5550bb958dcfSRick Macklem 	vput(vp);
5551bb958dcfSRick Macklem 	NFSEXITCODE2(error, nd);
5552bb958dcfSRick Macklem 	return (error);
5553bb958dcfSRick Macklem }
5554bb958dcfSRick Macklem 
5555bb958dcfSRick Macklem /*
5556c057a378SRick Macklem  * nfs copy service
5557c057a378SRick Macklem  */
5558b9cc3262SRyan Moeller int
5559c057a378SRick Macklem nfsrvd_copy_file_range(struct nfsrv_descript *nd, __unused int isdgram,
5560c057a378SRick Macklem     vnode_t vp, vnode_t tovp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
5561c057a378SRick Macklem {
5562c057a378SRick Macklem 	uint32_t *tl;
5563c057a378SRick Macklem 	struct nfsvattr at;
5564c057a378SRick Macklem 	int cnt, error = 0, ret;
5565c057a378SRick Macklem 	off_t inoff, outoff;
5566c057a378SRick Macklem 	uint64_t len;
5567f1c8811dSRick Macklem 	size_t xfer;
5568c057a378SRick Macklem 	struct nfsstate inst, outst, *instp = &inst, *outstp = &outst;
5569c057a378SRick Macklem 	struct nfslock inlo, outlo, *inlop = &inlo, *outlop = &outlo;
5570c057a378SRick Macklem 	nfsquad_t clientid;
5571c057a378SRick Macklem 	nfsv4stateid_t stateid;
5572c057a378SRick Macklem 	nfsattrbit_t attrbits;
5573c057a378SRick Macklem 	void *rl_rcookie, *rl_wcookie;
5574c057a378SRick Macklem 
5575c057a378SRick Macklem 	rl_rcookie = rl_wcookie = NULL;
5576c057a378SRick Macklem 	if (nfsrv_devidcnt > 0) {
5577c057a378SRick Macklem 		/*
5578c057a378SRick Macklem 		 * For a pNFS server, reply NFSERR_NOTSUPP so that the client
5579c057a378SRick Macklem 		 * will do the copy via I/O on the DS(s).
5580c057a378SRick Macklem 		 */
5581c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5582c057a378SRick Macklem 		goto nfsmout;
5583c057a378SRick Macklem 	}
5584c057a378SRick Macklem 	if (vp == tovp) {
5585c057a378SRick Macklem 		/* Copying a byte range within the same file is not allowed. */
5586c057a378SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
5587c057a378SRick Macklem 		goto nfsmout;
5588c057a378SRick Macklem 	}
5589c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_STATEID + 3 * NFSX_HYPER +
5590c057a378SRick Macklem 	    3 * NFSX_UNSIGNED);
5591c057a378SRick Macklem 	instp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
5592c057a378SRick Macklem 	inlop->lo_flags = NFSLCK_READ;
5593c057a378SRick Macklem 	instp->ls_ownerlen = 0;
5594c057a378SRick Macklem 	instp->ls_op = NULL;
5595c057a378SRick Macklem 	instp->ls_uid = nd->nd_cred->cr_uid;
5596c057a378SRick Macklem 	instp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
5597c057a378SRick Macklem 	clientid.lval[0] = instp->ls_stateid.other[0] = *tl++;
5598c057a378SRick Macklem 	clientid.lval[1] = instp->ls_stateid.other[1] = *tl++;
5599c057a378SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0)
5600c057a378SRick Macklem 		clientid.qval = nd->nd_clientid.qval;
5601c057a378SRick Macklem 	instp->ls_stateid.other[2] = *tl++;
5602c057a378SRick Macklem 	outstp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
5603c057a378SRick Macklem 	outlop->lo_flags = NFSLCK_WRITE;
5604c057a378SRick Macklem 	outstp->ls_ownerlen = 0;
5605c057a378SRick Macklem 	outstp->ls_op = NULL;
5606c057a378SRick Macklem 	outstp->ls_uid = nd->nd_cred->cr_uid;
5607c057a378SRick Macklem 	outstp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
5608c057a378SRick Macklem 	outstp->ls_stateid.other[0] = *tl++;
5609c057a378SRick Macklem 	outstp->ls_stateid.other[1] = *tl++;
5610c057a378SRick Macklem 	outstp->ls_stateid.other[2] = *tl++;
5611c057a378SRick Macklem 	inoff = fxdr_hyper(tl); tl += 2;
5612c057a378SRick Macklem 	inlop->lo_first = inoff;
5613c057a378SRick Macklem 	outoff = fxdr_hyper(tl); tl += 2;
5614c057a378SRick Macklem 	outlop->lo_first = outoff;
5615c057a378SRick Macklem 	len = fxdr_hyper(tl); tl += 2;
5616c057a378SRick Macklem 	if (len == 0) {
5617c057a378SRick Macklem 		/* len == 0 means to EOF. */
5618c057a378SRick Macklem 		inlop->lo_end = OFF_MAX;
5619c057a378SRick Macklem 		outlop->lo_end = OFF_MAX;
5620c057a378SRick Macklem 	} else {
5621c057a378SRick Macklem 		inlop->lo_end = inlop->lo_first + len;
5622c057a378SRick Macklem 		outlop->lo_end = outlop->lo_first + len;
5623c057a378SRick Macklem 	}
5624c057a378SRick Macklem 
5625c057a378SRick Macklem 	/*
5626c057a378SRick Macklem 	 * At this time only consecutive, synchronous copy is supported,
5627c057a378SRick Macklem 	 * so ca_consecutive and ca_synchronous can be ignored.
5628c057a378SRick Macklem 	 */
5629c057a378SRick Macklem 	tl += 2;
5630c057a378SRick Macklem 
5631c057a378SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
5632c057a378SRick Macklem 	if ((nd->nd_flag & ND_DSSERVER) != 0 || cnt != 0)
5633c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5634c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (inoff > OFF_MAX || outoff > OFF_MAX ||
5635c057a378SRick Macklem 	    inlop->lo_end > OFF_MAX || outlop->lo_end > OFF_MAX ||
5636c057a378SRick Macklem 	    inlop->lo_end < inlop->lo_first || outlop->lo_end <
5637c057a378SRick Macklem 	    outlop->lo_first))
5638c057a378SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
5639c057a378SRick Macklem 
56405d3fe02cSRick Macklem 	if (nd->nd_repstat == 0 && vp->v_type != VREG)
5641c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGTYPE;
5642c057a378SRick Macklem 
5643c057a378SRick Macklem 	/* Check permissions for the input file. */
5644c057a378SRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
5645c057a378SRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
5646c057a378SRick Macklem 	ret = nfsvno_getattr(vp, &at, nd, curthread, 1, &attrbits);
5647c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5648c057a378SRick Macklem 		nd->nd_repstat = ret;
5649c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
5650c057a378SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
5651c057a378SRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred, exp,
5652c057a378SRick Macklem 		    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
5653c057a378SRick Macklem 		    NULL);
5654c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5655c057a378SRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(vp, &instp, &inlop, NULL,
5656c057a378SRick Macklem 		    clientid, &stateid, exp, nd, curthread);
5657b249ce48SMateusz Guzik 	NFSVOPUNLOCK(vp);
5658c057a378SRick Macklem 	if (nd->nd_repstat != 0)
5659c057a378SRick Macklem 		goto out;
5660c057a378SRick Macklem 
5661c057a378SRick Macklem 	error = NFSVOPLOCK(tovp, LK_SHARED);
5662c057a378SRick Macklem 	if (error != 0)
5663c057a378SRick Macklem 		goto out;
56645d3fe02cSRick Macklem 	if (tovp->v_type != VREG)
5665c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGTYPE;
5666c057a378SRick Macklem 
5667c057a378SRick Macklem 	/* For the output file, we only need the Owner attribute. */
5668c057a378SRick Macklem 	ret = nfsvno_getattr(tovp, &at, nd, curthread, 1, &attrbits);
5669c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5670c057a378SRick Macklem 		nd->nd_repstat = ret;
5671c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
5672c057a378SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
5673c057a378SRick Macklem 		nd->nd_repstat = nfsvno_accchk(tovp, VWRITE, nd->nd_cred, toexp,
5674c057a378SRick Macklem 		    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
5675c057a378SRick Macklem 		    NULL);
5676c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5677c057a378SRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(tovp, &outstp, &outlop, NULL,
5678c057a378SRick Macklem 		    clientid, &stateid, toexp, nd, curthread);
5679b249ce48SMateusz Guzik 	NFSVOPUNLOCK(tovp);
5680c057a378SRick Macklem 
5681c057a378SRick Macklem 	/* Range lock the byte ranges for both invp and outvp. */
5682c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5683c057a378SRick Macklem 		for (;;) {
5684c057a378SRick Macklem 			if (len == 0) {
5685c057a378SRick Macklem 				rl_wcookie = vn_rangelock_wlock(tovp, outoff,
5686c057a378SRick Macklem 				    OFF_MAX);
5687c057a378SRick Macklem 				rl_rcookie = vn_rangelock_tryrlock(vp, inoff,
5688c057a378SRick Macklem 				    OFF_MAX);
5689c057a378SRick Macklem 			} else {
5690c057a378SRick Macklem 				rl_wcookie = vn_rangelock_wlock(tovp, outoff,
5691c057a378SRick Macklem 				    outoff + len);
5692c057a378SRick Macklem 				rl_rcookie = vn_rangelock_tryrlock(vp, inoff,
5693c057a378SRick Macklem 				    inoff + len);
5694c057a378SRick Macklem 			}
5695c057a378SRick Macklem 			if (rl_rcookie != NULL)
5696c057a378SRick Macklem 				break;
5697c057a378SRick Macklem 			vn_rangelock_unlock(tovp, rl_wcookie);
5698c057a378SRick Macklem 			if (len == 0)
5699c057a378SRick Macklem 				rl_rcookie = vn_rangelock_rlock(vp, inoff,
5700c057a378SRick Macklem 				    OFF_MAX);
5701c057a378SRick Macklem 			else
5702c057a378SRick Macklem 				rl_rcookie = vn_rangelock_rlock(vp, inoff,
5703c057a378SRick Macklem 				    inoff + len);
5704c057a378SRick Macklem 			vn_rangelock_unlock(vp, rl_rcookie);
5705c057a378SRick Macklem 		}
5706c057a378SRick Macklem 
5707c057a378SRick Macklem 		error = NFSVOPLOCK(vp, LK_SHARED);
5708c057a378SRick Macklem 		if (error == 0) {
5709c057a378SRick Macklem 			ret = nfsvno_getattr(vp, &at, nd, curthread, 1, NULL);
5710c057a378SRick Macklem 			if (ret == 0) {
5711c057a378SRick Macklem 				/*
5712c057a378SRick Macklem 				 * Since invp is range locked, na_size should
5713c057a378SRick Macklem 				 * not change.
5714c057a378SRick Macklem 				 */
5715c057a378SRick Macklem 				if (len == 0 && at.na_size > inoff) {
5716c057a378SRick Macklem 					/*
5717c057a378SRick Macklem 					 * If len == 0, set it based on invp's
5718c057a378SRick Macklem 					 * size. If offset is past EOF, just
5719c057a378SRick Macklem 					 * leave len == 0.
5720c057a378SRick Macklem 					 */
5721c057a378SRick Macklem 					len = at.na_size - inoff;
5722c057a378SRick Macklem 				} else if (nfsrv_linux42server == 0 &&
5723c057a378SRick Macklem 				    inoff + len > at.na_size) {
5724c057a378SRick Macklem 					/*
5725c057a378SRick Macklem 					 * RFC-7862 says that NFSERR_INVAL must
5726c057a378SRick Macklem 					 * be returned when inoff + len exceeds
5727c057a378SRick Macklem 					 * the file size, however the NFSv4.2
5728c057a378SRick Macklem 					 * Linux client likes to do this, so
5729c057a378SRick Macklem 					 * only check if nfsrv_linux42server
5730c057a378SRick Macklem 					 * is not set.
5731c057a378SRick Macklem 					 */
5732c057a378SRick Macklem 					nd->nd_repstat = NFSERR_INVAL;
5733c057a378SRick Macklem 				}
5734c057a378SRick Macklem 			}
5735b249ce48SMateusz Guzik 			NFSVOPUNLOCK(vp);
5736c057a378SRick Macklem 			if (ret != 0 && nd->nd_repstat == 0)
5737c057a378SRick Macklem 				nd->nd_repstat = ret;
5738c057a378SRick Macklem 		} else if (nd->nd_repstat == 0)
5739c057a378SRick Macklem 			nd->nd_repstat = error;
5740c057a378SRick Macklem 	}
5741c057a378SRick Macklem 
5742f1c8811dSRick Macklem 	xfer = len;
5743f1c8811dSRick Macklem 	if (nd->nd_repstat == 0) {
5744103b2075SRick Macklem 		nd->nd_repstat = vn_copy_file_range(vp, &inoff, tovp, &outoff,
5745f1c8811dSRick Macklem 		    &xfer, COPY_FILE_RANGE_TIMEO1SEC, nd->nd_cred, nd->nd_cred,
5746103b2075SRick Macklem 		    NULL);
5747f1c8811dSRick Macklem 		if (nd->nd_repstat == 0)
5748f1c8811dSRick Macklem 			len = xfer;
5749f1c8811dSRick Macklem 	}
5750c057a378SRick Macklem 
5751c057a378SRick Macklem 	/* Unlock the ranges. */
5752c057a378SRick Macklem 	if (rl_rcookie != NULL)
5753c057a378SRick Macklem 		vn_rangelock_unlock(vp, rl_rcookie);
5754c057a378SRick Macklem 	if (rl_wcookie != NULL)
5755c057a378SRick Macklem 		vn_rangelock_unlock(tovp, rl_wcookie);
5756c057a378SRick Macklem 
5757c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5758c057a378SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED + NFSX_HYPER +
5759c057a378SRick Macklem 		    NFSX_VERF);
5760c057a378SRick Macklem 		*tl++ = txdr_unsigned(0);	/* No callback ids. */
5761c057a378SRick Macklem 		txdr_hyper(len, tl); tl += 2;
5762c057a378SRick Macklem 		*tl++ = txdr_unsigned(NFSWRITE_UNSTABLE);
5763c057a378SRick Macklem 		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
5764c057a378SRick Macklem 		*tl++ = txdr_unsigned(nfsboottime.tv_usec);
5765c057a378SRick Macklem 		*tl++ = newnfs_true;
5766c057a378SRick Macklem 		*tl = newnfs_true;
5767c057a378SRick Macklem 	}
5768c057a378SRick Macklem out:
5769c057a378SRick Macklem 	vrele(vp);
5770c057a378SRick Macklem 	vrele(tovp);
5771c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5772c057a378SRick Macklem 	return (error);
5773c057a378SRick Macklem nfsmout:
5774c057a378SRick Macklem 	vput(vp);
5775c057a378SRick Macklem 	vrele(tovp);
5776c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5777c057a378SRick Macklem 	return (error);
5778c057a378SRick Macklem }
5779c057a378SRick Macklem 
5780c057a378SRick Macklem /*
5781c057a378SRick Macklem  * nfs seek service
5782c057a378SRick Macklem  */
5783b9cc3262SRyan Moeller int
5784c057a378SRick Macklem nfsrvd_seek(struct nfsrv_descript *nd, __unused int isdgram,
5785c057a378SRick Macklem     vnode_t vp, struct nfsexstuff *exp)
5786c057a378SRick Macklem {
5787c057a378SRick Macklem 	uint32_t *tl;
5788c057a378SRick Macklem 	struct nfsvattr at;
5789c057a378SRick Macklem 	int content, error = 0;
5790c057a378SRick Macklem 	off_t off;
5791c057a378SRick Macklem 	u_long cmd;
5792c057a378SRick Macklem 	nfsattrbit_t attrbits;
5793c057a378SRick Macklem 	bool eof;
5794c057a378SRick Macklem 
5795c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + NFSX_HYPER + NFSX_UNSIGNED);
5796c057a378SRick Macklem 	/* Ignore the stateid for now. */
5797c057a378SRick Macklem 	tl += (NFSX_STATEID / NFSX_UNSIGNED);
5798c057a378SRick Macklem 	off = fxdr_hyper(tl); tl += 2;
5799c057a378SRick Macklem 	content = fxdr_unsigned(int, *tl);
5800c057a378SRick Macklem 	if (content == NFSV4CONTENT_DATA)
5801c057a378SRick Macklem 		cmd = FIOSEEKDATA;
5802c057a378SRick Macklem 	else if (content == NFSV4CONTENT_HOLE)
5803c057a378SRick Macklem 		cmd = FIOSEEKHOLE;
5804c057a378SRick Macklem 	else
5805c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
58065d3fe02cSRick Macklem 	if (nd->nd_repstat == 0 && vp->v_type == VDIR)
5807c057a378SRick Macklem 		nd->nd_repstat = NFSERR_ISDIR;
58085d3fe02cSRick Macklem 	if (nd->nd_repstat == 0 && vp->v_type != VREG)
5809c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGTYPE;
5810c057a378SRick Macklem 	if (nd->nd_repstat == 0 && off < 0)
5811c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NXIO;
5812c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5813c057a378SRick Macklem 		/* Check permissions for the input file. */
5814c057a378SRick Macklem 		NFSZERO_ATTRBIT(&attrbits);
5815c057a378SRick Macklem 		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
5816c057a378SRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &at, nd, curthread, 1,
5817c057a378SRick Macklem 		    &attrbits);
5818c057a378SRick Macklem 	}
5819c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
5820c057a378SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
5821c057a378SRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred, exp,
5822c057a378SRick Macklem 		    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
5823c057a378SRick Macklem 		    NULL);
5824c057a378SRick Macklem 	if (nd->nd_repstat != 0)
5825c057a378SRick Macklem 		goto nfsmout;
5826c057a378SRick Macklem 
5827c057a378SRick Macklem 	/* nfsvno_seek() unlocks and vrele()s the vp. */
5828c057a378SRick Macklem 	nd->nd_repstat = nfsvno_seek(nd, vp, cmd, &off, content, &eof,
5829c057a378SRick Macklem 	    nd->nd_cred, curthread);
5830c057a378SRick Macklem 	if (nd->nd_repstat == 0 && eof && content == NFSV4CONTENT_DATA &&
5831c057a378SRick Macklem 	    nfsrv_linux42server != 0)
5832c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NXIO;
5833c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5834c057a378SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED + NFSX_HYPER);
5835c057a378SRick Macklem 		if (eof)
5836c057a378SRick Macklem 			*tl++ = newnfs_true;
5837c057a378SRick Macklem 		else
5838c057a378SRick Macklem 			*tl++ = newnfs_false;
5839c057a378SRick Macklem 		txdr_hyper(off, tl);
5840c057a378SRick Macklem 	}
5841c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5842c057a378SRick Macklem 	return (error);
5843c057a378SRick Macklem nfsmout:
5844c057a378SRick Macklem 	vput(vp);
5845c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5846c057a378SRick Macklem 	return (error);
5847c057a378SRick Macklem }
5848c057a378SRick Macklem 
5849c057a378SRick Macklem /*
5850c057a378SRick Macklem  * nfs get extended attribute service
5851c057a378SRick Macklem  */
5852b9cc3262SRyan Moeller int
5853c057a378SRick Macklem nfsrvd_getxattr(struct nfsrv_descript *nd, __unused int isdgram,
5854c057a378SRick Macklem     vnode_t vp, __unused struct nfsexstuff *exp)
5855c057a378SRick Macklem {
5856c057a378SRick Macklem 	uint32_t *tl;
5857ae070589SRick Macklem 	struct mbuf *mp = NULL, *mpend = NULL;
5858c057a378SRick Macklem 	int error, len;
5859c057a378SRick Macklem 	char *name;
5860c057a378SRick Macklem 	struct thread *p = curthread;
5861cb889ce6SRick Macklem 	uint16_t off;
5862c057a378SRick Macklem 
5863c057a378SRick Macklem 	error = 0;
5864c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
5865c057a378SRick Macklem 	len = fxdr_unsigned(int, *tl);
5866c057a378SRick Macklem 	if (len <= 0) {
5867c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
5868c057a378SRick Macklem 		goto nfsmout;
5869c057a378SRick Macklem 	}
5870c057a378SRick Macklem 	if (len > EXTATTR_MAXNAMELEN) {
5871c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOXATTR;
5872c057a378SRick Macklem 		goto nfsmout;
5873c057a378SRick Macklem 	}
5874c057a378SRick Macklem 	name = malloc(len + 1, M_TEMP, M_WAITOK);
5875c057a378SRick Macklem 	nd->nd_repstat = nfsrv_mtostr(nd, name, len);
5876c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5877cb889ce6SRick Macklem 		nd->nd_repstat = nfsvno_getxattr(vp, name,
5878cb889ce6SRick Macklem 		    nd->nd_maxresp, nd->nd_cred, nd->nd_flag,
5879cb889ce6SRick Macklem 		    nd->nd_maxextsiz, p, &mp, &mpend, &len);
5880c057a378SRick Macklem 	if (nd->nd_repstat == ENOATTR)
5881c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOXATTR;
5882c057a378SRick Macklem 	else if (nd->nd_repstat == EOPNOTSUPP)
5883c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5884c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5885c057a378SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
5886c057a378SRick Macklem 		*tl = txdr_unsigned(len);
5887fb8ed4c5SRick Macklem 		if (len > 0) {
58889f6624d3SRick Macklem 			nd->nd_mb->m_next = mp;
5889c057a378SRick Macklem 			nd->nd_mb = mpend;
5890cb889ce6SRick Macklem 			if ((mpend->m_flags & M_EXTPG) != 0) {
5891cb889ce6SRick Macklem 				nd->nd_flag |= ND_EXTPG;
5892cb889ce6SRick Macklem 				nd->nd_bextpg = mpend->m_epg_npgs - 1;
5893cb889ce6SRick Macklem 				nd->nd_bpos = (char *)(void *)
5894cb889ce6SRick Macklem 				   PHYS_TO_DMAP(mpend->m_epg_pa[nd->nd_bextpg]);
5895cb889ce6SRick Macklem 				off = (nd->nd_bextpg == 0) ?
5896cb889ce6SRick Macklem 				    mpend->m_epg_1st_off : 0;
5897cb889ce6SRick Macklem 				nd->nd_bpos += off + mpend->m_epg_last_len;
5898cb889ce6SRick Macklem 				nd->nd_bextpgsiz = PAGE_SIZE -
5899cb889ce6SRick Macklem 				    mpend->m_epg_last_len - off;
5900cb889ce6SRick Macklem 			} else
5901cb889ce6SRick Macklem 				nd->nd_bpos = mtod(mpend, char *) +
5902cb889ce6SRick Macklem 				    mpend->m_len;
5903c057a378SRick Macklem 		}
5904fb8ed4c5SRick Macklem 	}
5905c057a378SRick Macklem 	free(name, M_TEMP);
5906c057a378SRick Macklem 
5907c057a378SRick Macklem nfsmout:
5908c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5909c057a378SRick Macklem 		nd->nd_repstat = error;
5910c057a378SRick Macklem 	vput(vp);
5911c057a378SRick Macklem 	NFSEXITCODE2(0, nd);
5912c057a378SRick Macklem 	return (0);
5913c057a378SRick Macklem }
5914c057a378SRick Macklem 
5915c057a378SRick Macklem /*
5916c057a378SRick Macklem  * nfs set extended attribute service
5917c057a378SRick Macklem  */
5918b9cc3262SRyan Moeller int
5919c057a378SRick Macklem nfsrvd_setxattr(struct nfsrv_descript *nd, __unused int isdgram,
5920c057a378SRick Macklem     vnode_t vp, __unused struct nfsexstuff *exp)
5921c057a378SRick Macklem {
5922c057a378SRick Macklem 	uint32_t *tl;
5923c057a378SRick Macklem 	struct nfsvattr ova, nva;
5924c057a378SRick Macklem 	nfsattrbit_t attrbits;
5925c057a378SRick Macklem 	int error, len, opt;
5926c057a378SRick Macklem 	char *name;
5927c057a378SRick Macklem 	size_t siz;
5928c057a378SRick Macklem 	struct thread *p = curthread;
5929c057a378SRick Macklem 
5930c057a378SRick Macklem 	error = 0;
5931c057a378SRick Macklem 	name = NULL;
5932c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
5933c057a378SRick Macklem 	opt = fxdr_unsigned(int, *tl++);
5934c057a378SRick Macklem 	len = fxdr_unsigned(int, *tl);
5935c057a378SRick Macklem 	if (len <= 0) {
5936c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
5937c057a378SRick Macklem 		goto nfsmout;
5938c057a378SRick Macklem 	}
5939c057a378SRick Macklem 	if (len > EXTATTR_MAXNAMELEN) {
5940c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOXATTR;
5941c057a378SRick Macklem 		goto nfsmout;
5942c057a378SRick Macklem 	}
5943c057a378SRick Macklem 	name = malloc(len + 1, M_TEMP, M_WAITOK);
5944c057a378SRick Macklem 	error = nfsrv_mtostr(nd, name, len);
5945c057a378SRick Macklem 	if (error != 0)
5946c057a378SRick Macklem 		goto nfsmout;
5947c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
5948c057a378SRick Macklem 	len = fxdr_unsigned(int, *tl);
5949fb8ed4c5SRick Macklem 	if (len < 0 || len > IOSIZE_MAX) {
5950c057a378SRick Macklem 		nd->nd_repstat = NFSERR_XATTR2BIG;
5951c057a378SRick Macklem 		goto nfsmout;
5952c057a378SRick Macklem 	}
5953c057a378SRick Macklem 	switch (opt) {
5954c057a378SRick Macklem 	case NFSV4SXATTR_CREATE:
5955c057a378SRick Macklem 		error = VOP_GETEXTATTR(vp, EXTATTR_NAMESPACE_USER, name, NULL,
5956c057a378SRick Macklem 		    &siz, nd->nd_cred, p);
5957c057a378SRick Macklem 		if (error != ENOATTR)
5958c057a378SRick Macklem 			nd->nd_repstat = NFSERR_EXIST;
5959c057a378SRick Macklem 		error = 0;
5960c057a378SRick Macklem 		break;
5961c057a378SRick Macklem 	case NFSV4SXATTR_REPLACE:
5962c057a378SRick Macklem 		error = VOP_GETEXTATTR(vp, EXTATTR_NAMESPACE_USER, name, NULL,
5963c057a378SRick Macklem 		    &siz, nd->nd_cred, p);
5964c057a378SRick Macklem 		if (error != 0)
5965c057a378SRick Macklem 			nd->nd_repstat = NFSERR_NOXATTR;
5966c057a378SRick Macklem 		break;
5967c057a378SRick Macklem 	case NFSV4SXATTR_EITHER:
5968c057a378SRick Macklem 		break;
5969c057a378SRick Macklem 	default:
5970c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
5971c057a378SRick Macklem 	}
5972c057a378SRick Macklem 	if (nd->nd_repstat != 0)
5973c057a378SRick Macklem 		goto nfsmout;
5974c057a378SRick Macklem 
5975c057a378SRick Macklem 	/* Now, do the Set Extended attribute, with Change before and after. */
5976c057a378SRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
5977c057a378SRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
5978c057a378SRick Macklem 	nd->nd_repstat = nfsvno_getattr(vp, &ova, nd, p, 1, &attrbits);
5979c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5980c057a378SRick Macklem 		nd->nd_repstat = nfsvno_setxattr(vp, name, len, nd->nd_md,
5981c057a378SRick Macklem 		    nd->nd_dpos, nd->nd_cred, p);
5982c057a378SRick Macklem 		if (nd->nd_repstat == ENXIO)
5983c057a378SRick Macklem 			nd->nd_repstat = NFSERR_XATTR2BIG;
5984c057a378SRick Macklem 	}
5985fb8ed4c5SRick Macklem 	if (nd->nd_repstat == 0 && len > 0)
5986c057a378SRick Macklem 		nd->nd_repstat = nfsm_advance(nd, NFSM_RNDUP(len), -1);
5987c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5988c057a378SRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, &attrbits);
5989c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5990c057a378SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_UNSIGNED);
5991c057a378SRick Macklem 		*tl++ = newnfs_true;
5992c057a378SRick Macklem 		txdr_hyper(ova.na_filerev, tl); tl += 2;
5993c057a378SRick Macklem 		txdr_hyper(nva.na_filerev, tl);
5994c057a378SRick Macklem 	}
5995c057a378SRick Macklem 
5996c057a378SRick Macklem nfsmout:
5997c057a378SRick Macklem 	free(name, M_TEMP);
5998c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5999c057a378SRick Macklem 		nd->nd_repstat = error;
6000c057a378SRick Macklem 	vput(vp);
6001c057a378SRick Macklem 	NFSEXITCODE2(0, nd);
6002c057a378SRick Macklem 	return (0);
6003c057a378SRick Macklem }
6004c057a378SRick Macklem 
6005c057a378SRick Macklem /*
6006c057a378SRick Macklem  * nfs remove extended attribute service
6007c057a378SRick Macklem  */
6008b9cc3262SRyan Moeller int
6009c057a378SRick Macklem nfsrvd_rmxattr(struct nfsrv_descript *nd, __unused int isdgram,
6010c057a378SRick Macklem     vnode_t vp, __unused struct nfsexstuff *exp)
6011c057a378SRick Macklem {
6012c057a378SRick Macklem 	uint32_t *tl;
6013c057a378SRick Macklem 	struct nfsvattr ova, nva;
6014c057a378SRick Macklem 	nfsattrbit_t attrbits;
6015c057a378SRick Macklem 	int error, len;
6016c057a378SRick Macklem 	char *name;
6017c057a378SRick Macklem 	struct thread *p = curthread;
6018c057a378SRick Macklem 
6019c057a378SRick Macklem 	error = 0;
6020c057a378SRick Macklem 	name = NULL;
6021c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
6022c057a378SRick Macklem 	len = fxdr_unsigned(int, *tl);
6023c057a378SRick Macklem 	if (len <= 0) {
6024c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
6025c057a378SRick Macklem 		goto nfsmout;
6026c057a378SRick Macklem 	}
6027c057a378SRick Macklem 	if (len > EXTATTR_MAXNAMELEN) {
6028c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOXATTR;
6029c057a378SRick Macklem 		goto nfsmout;
6030c057a378SRick Macklem 	}
6031c057a378SRick Macklem 	name = malloc(len + 1, M_TEMP, M_WAITOK);
6032c057a378SRick Macklem 	error = nfsrv_mtostr(nd, name, len);
6033c057a378SRick Macklem 	if (error != 0)
6034c057a378SRick Macklem 		goto nfsmout;
6035c057a378SRick Macklem 
6036c057a378SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) == 0) {
6037c057a378SRick Macklem 		printf("EEK! nfsrvd_rmxattr: no implied clientid\n");
6038c057a378SRick Macklem 		error = NFSERR_NOXATTR;
6039c057a378SRick Macklem 		goto nfsmout;
6040c057a378SRick Macklem 	}
6041c057a378SRick Macklem 	/*
6042c057a378SRick Macklem 	 * Now, do the Remove Extended attribute, with Change before and
6043c057a378SRick Macklem 	 * after.
6044c057a378SRick Macklem 	*/
6045c057a378SRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
6046c057a378SRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
6047c057a378SRick Macklem 	nd->nd_repstat = nfsvno_getattr(vp, &ova, nd, p, 1, &attrbits);
6048c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
6049c057a378SRick Macklem 		nd->nd_repstat = nfsvno_rmxattr(nd, vp, name, nd->nd_cred, p);
6050c057a378SRick Macklem 		if (nd->nd_repstat == ENOATTR)
6051c057a378SRick Macklem 			nd->nd_repstat = NFSERR_NOXATTR;
6052c057a378SRick Macklem 	}
6053c057a378SRick Macklem 	if (nd->nd_repstat == 0)
6054c057a378SRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, &attrbits);
6055c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
60560bda1dddSRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_UNSIGNED);
60570bda1dddSRick Macklem 		*tl++ = newnfs_true;
6058c057a378SRick Macklem 		txdr_hyper(ova.na_filerev, tl); tl += 2;
6059c057a378SRick Macklem 		txdr_hyper(nva.na_filerev, tl);
6060c057a378SRick Macklem 	}
6061c057a378SRick Macklem 
6062c057a378SRick Macklem nfsmout:
6063c057a378SRick Macklem 	free(name, M_TEMP);
6064c057a378SRick Macklem 	if (nd->nd_repstat == 0)
6065c057a378SRick Macklem 		nd->nd_repstat = error;
6066c057a378SRick Macklem 	vput(vp);
6067c057a378SRick Macklem 	NFSEXITCODE2(0, nd);
6068c057a378SRick Macklem 	return (0);
6069c057a378SRick Macklem }
6070c057a378SRick Macklem 
6071c057a378SRick Macklem /*
6072c057a378SRick Macklem  * nfs list extended attribute service
6073c057a378SRick Macklem  */
6074b9cc3262SRyan Moeller int
6075c057a378SRick Macklem nfsrvd_listxattr(struct nfsrv_descript *nd, __unused int isdgram,
6076c057a378SRick Macklem     vnode_t vp, __unused struct nfsexstuff *exp)
6077c057a378SRick Macklem {
6078c057a378SRick Macklem 	uint32_t cnt, *tl, len, len2, i, pos, retlen;
6079c057a378SRick Macklem 	int error;
6080c057a378SRick Macklem 	uint64_t cookie, cookie2;
6081c057a378SRick Macklem 	u_char *buf;
6082c057a378SRick Macklem 	bool eof;
6083c057a378SRick Macklem 	struct thread *p = curthread;
6084c057a378SRick Macklem 
6085c057a378SRick Macklem 	error = 0;
6086c057a378SRick Macklem 	buf = NULL;
6087c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + NFSX_UNSIGNED);
6088c057a378SRick Macklem 	/*
6089c057a378SRick Macklem 	 * The cookie doesn't need to be in net byte order, but FreeBSD
6090c057a378SRick Macklem 	 * does so to make it more readable in packet traces.
6091c057a378SRick Macklem 	 */
6092c057a378SRick Macklem 	cookie = fxdr_hyper(tl); tl += 2;
6093c057a378SRick Macklem 	len = fxdr_unsigned(uint32_t, *tl);
6094c057a378SRick Macklem 	if (len == 0 || cookie >= IOSIZE_MAX) {
6095c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
6096c057a378SRick Macklem 		goto nfsmout;
6097c057a378SRick Macklem 	}
6098c057a378SRick Macklem 	if (len > nd->nd_maxresp - NFS_MAXXDR)
6099c057a378SRick Macklem 		len = nd->nd_maxresp - NFS_MAXXDR;
6100c057a378SRick Macklem 	len2 = len;
6101c057a378SRick Macklem 	nd->nd_repstat = nfsvno_listxattr(vp, cookie, nd->nd_cred, p, &buf,
6102c057a378SRick Macklem 	    &len, &eof);
6103c057a378SRick Macklem 	if (nd->nd_repstat == EOPNOTSUPP)
6104c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
6105c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
6106c057a378SRick Macklem 		cookie2 = cookie + len;
6107c057a378SRick Macklem 		if (cookie2 < cookie)
6108c057a378SRick Macklem 			nd->nd_repstat = NFSERR_BADXDR;
6109c057a378SRick Macklem 	}
61105b430a13SRick Macklem 	retlen = NFSX_HYPER + 2 * NFSX_UNSIGNED;
61115b430a13SRick Macklem 	if (nd->nd_repstat == 0 && len2 < retlen)
61125b430a13SRick Macklem 		nd->nd_repstat = NFSERR_TOOSMALL;
6113c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
6114c057a378SRick Macklem 		/* Now copy the entries out. */
61155b430a13SRick Macklem 		if (len == 0) {
6116c057a378SRick Macklem 			/* The cookie was at eof. */
6117c057a378SRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_HYPER + 2 *
6118c057a378SRick Macklem 			    NFSX_UNSIGNED);
6119c057a378SRick Macklem 			txdr_hyper(cookie2, tl); tl += 2;
6120c057a378SRick Macklem 			*tl++ = txdr_unsigned(0);
6121c057a378SRick Macklem 			*tl = newnfs_true;
6122c057a378SRick Macklem 			goto nfsmout;
6123c057a378SRick Macklem 		}
6124c057a378SRick Macklem 
6125c057a378SRick Macklem 		/* Sanity check the cookie. */
6126c057a378SRick Macklem 		for (pos = 0; pos < len; pos += (i + 1)) {
6127c057a378SRick Macklem 			if (pos == cookie)
6128c057a378SRick Macklem 				break;
6129c057a378SRick Macklem 			i = buf[pos];
6130c057a378SRick Macklem 		}
6131c057a378SRick Macklem 		if (pos != cookie) {
6132c057a378SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
6133c057a378SRick Macklem 			goto nfsmout;
6134c057a378SRick Macklem 		}
6135c057a378SRick Macklem 
6136c057a378SRick Macklem 		/* Loop around copying the entrie(s) out. */
6137c057a378SRick Macklem 		cnt = 0;
6138c057a378SRick Macklem 		len -= cookie;
6139c057a378SRick Macklem 		i = buf[pos];
6140c057a378SRick Macklem 		while (i < len && len2 >= retlen + NFSM_RNDUP(i) +
6141c057a378SRick Macklem 		    NFSX_UNSIGNED) {
6142c057a378SRick Macklem 			if (cnt == 0) {
6143c057a378SRick Macklem 				NFSM_BUILD(tl, uint32_t *, NFSX_HYPER +
6144c057a378SRick Macklem 				    NFSX_UNSIGNED);
6145c057a378SRick Macklem 				txdr_hyper(cookie2, tl); tl += 2;
6146c057a378SRick Macklem 			}
6147c057a378SRick Macklem 			retlen += nfsm_strtom(nd, &buf[pos + 1], i);
6148c057a378SRick Macklem 			len -= (i + 1);
6149c057a378SRick Macklem 			pos += (i + 1);
6150c057a378SRick Macklem 			i = buf[pos];
6151c057a378SRick Macklem 			cnt++;
6152c057a378SRick Macklem 		}
6153c057a378SRick Macklem 		/*
6154c057a378SRick Macklem 		 * eof is set true/false by nfsvno_listxattr(), but if we
6155c057a378SRick Macklem 		 * can't copy all entries returned by nfsvno_listxattr(),
6156c057a378SRick Macklem 		 * we are not at eof.
6157c057a378SRick Macklem 		 */
6158c057a378SRick Macklem 		if (len > 0)
6159c057a378SRick Macklem 			eof = false;
6160c057a378SRick Macklem 		if (cnt > 0) {
6161c057a378SRick Macklem 			/* *tl is set above. */
6162c057a378SRick Macklem 			*tl = txdr_unsigned(cnt);
6163c057a378SRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
6164c057a378SRick Macklem 			if (eof)
6165c057a378SRick Macklem 				*tl = newnfs_true;
6166c057a378SRick Macklem 			else
6167c057a378SRick Macklem 				*tl = newnfs_false;
6168c057a378SRick Macklem 		} else
6169c057a378SRick Macklem 			nd->nd_repstat = NFSERR_TOOSMALL;
6170c057a378SRick Macklem 	}
6171c057a378SRick Macklem 
6172c057a378SRick Macklem nfsmout:
6173c057a378SRick Macklem 	free(buf, M_TEMP);
6174c057a378SRick Macklem 	if (nd->nd_repstat == 0)
6175c057a378SRick Macklem 		nd->nd_repstat = error;
6176c057a378SRick Macklem 	vput(vp);
6177c057a378SRick Macklem 	NFSEXITCODE2(0, nd);
6178c057a378SRick Macklem 	return (0);
6179c057a378SRick Macklem }
6180c057a378SRick Macklem 
6181c057a378SRick Macklem /*
6182c59e4cc3SRick Macklem  * nfsv4 service not supported
6183c59e4cc3SRick Macklem  */
6184b9cc3262SRyan Moeller int
6185c59e4cc3SRick Macklem nfsrvd_notsupp(struct nfsrv_descript *nd, __unused int isdgram,
6186af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
6187c59e4cc3SRick Macklem {
6188c59e4cc3SRick Macklem 
6189c59e4cc3SRick Macklem 	nd->nd_repstat = NFSERR_NOTSUPP;
6190c59e4cc3SRick Macklem 	NFSEXITCODE2(0, nd);
6191c59e4cc3SRick Macklem 	return (0);
6192c59e4cc3SRick Macklem }
6193