xref: /freebsd/sys/fs/nfsserver/nfs_nfsdserv.c (revision 18a48314bae9d51ba2a18f20df3d8ed6cd2fd33d)
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;
59c9aad40fSRick Macklem extern int nfs_rootfhset;
6007c0c166SRick Macklem extern int nfsrv_enable_crossmntpt;
611f54e596SRick Macklem extern int nfsrv_statehashsize;
6290d2dfabSRick Macklem extern int nfsrv_layouthashsize;
6390d2dfabSRick Macklem extern time_t nfsdev_time;
6490d2dfabSRick Macklem extern volatile int nfsrv_devidcnt;
6590d2dfabSRick Macklem extern int nfsd_debuglevel;
6690d2dfabSRick Macklem extern u_long sb_max_adj;
6790d2dfabSRick Macklem extern int nfsrv_pnfsatime;
6890d2dfabSRick Macklem extern int nfsrv_maxpnfsmirror;
69c057a378SRick Macklem extern int nfs_maxcopyrange;
709ec7b004SRick Macklem 
71e4558aacSXin LI static int	nfs_async = 0;
72e4558aacSXin LI SYSCTL_DECL(_vfs_nfsd);
73e4558aacSXin LI SYSCTL_INT(_vfs_nfsd, OID_AUTO, async, CTLFLAG_RW, &nfs_async, 0,
74e4558aacSXin LI     "Tell client that writes were synced even though they were not");
7590d2dfabSRick Macklem extern int	nfsrv_doflexfile;
7690d2dfabSRick Macklem SYSCTL_INT(_vfs_nfsd, OID_AUTO, default_flexfile, CTLFLAG_RW,
7790d2dfabSRick Macklem     &nfsrv_doflexfile, 0, "Make Flex File Layout the default for pNFS");
78c057a378SRick Macklem static int	nfsrv_linux42server = 1;
79c057a378SRick Macklem SYSCTL_INT(_vfs_nfsd, OID_AUTO, linux42server, CTLFLAG_RW,
80c057a378SRick Macklem     &nfsrv_linux42server, 0,
81c057a378SRick Macklem     "Enable Linux style NFSv4.2 server (non-RFC compliant)");
82b0b7d978SRick Macklem static bool	nfsrv_openaccess = true;
83b0b7d978SRick Macklem SYSCTL_BOOL(_vfs_nfsd, OID_AUTO, v4openaccess, CTLFLAG_RW,
84b0b7d978SRick Macklem     &nfsrv_openaccess, 0,
85b0b7d978SRick Macklem     "Enable Linux style NFSv4 Open access check");
86e4558aacSXin LI 
879ec7b004SRick Macklem /*
889ec7b004SRick Macklem  * This list defines the GSS mechanisms supported.
899ec7b004SRick Macklem  * (Don't ask me how you get these strings from the RFC stuff like
909ec7b004SRick Macklem  *  iso(1), org(3)... but someone did it, so I don't need to know.)
919ec7b004SRick Macklem  */
929ec7b004SRick Macklem static struct nfsgss_mechlist nfsgss_mechlist[] = {
939ec7b004SRick Macklem 	{ 9, "\052\206\110\206\367\022\001\002\002", 11 },
949ec7b004SRick Macklem 	{ 0, "", 0 },
959ec7b004SRick Macklem };
969ec7b004SRick Macklem 
979ec7b004SRick Macklem /* local functions */
989ec7b004SRick Macklem static void nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
999ec7b004SRick Macklem     struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
1009ec7b004SRick Macklem     vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
1019ec7b004SRick Macklem     int *diraft_retp, nfsattrbit_t *attrbitp,
1029ec7b004SRick Macklem     NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
1039ec7b004SRick Macklem     int pathlen);
1049ec7b004SRick Macklem static void nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
1059ec7b004SRick Macklem     struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
1069ec7b004SRick Macklem     vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
1079ec7b004SRick Macklem     int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
1089ec7b004SRick Macklem     NFSPROC_T *p, struct nfsexstuff *exp);
1099ec7b004SRick Macklem 
1109ec7b004SRick Macklem /*
1119ec7b004SRick Macklem  * nfs access service (not a part of NFS V2)
1129ec7b004SRick Macklem  */
113b9cc3262SRyan Moeller int
1149ec7b004SRick Macklem nfsrvd_access(struct nfsrv_descript *nd, __unused int isdgram,
115af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
1169ec7b004SRick Macklem {
1179ec7b004SRick Macklem 	u_int32_t *tl;
1189ec7b004SRick Macklem 	int getret, error = 0;
1199ec7b004SRick Macklem 	struct nfsvattr nva;
1209ec7b004SRick Macklem 	u_int32_t testmode, nfsmode, supported = 0;
1218da45f2cSRick Macklem 	accmode_t deletebit;
122af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
1239ec7b004SRick Macklem 
1249ec7b004SRick Macklem 	if (nd->nd_repstat) {
1259ec7b004SRick Macklem 		nfsrv_postopattr(nd, 1, &nva);
126a9285ae5SZack Kirsch 		goto out;
1279ec7b004SRick Macklem 	}
1289ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1299ec7b004SRick Macklem 	nfsmode = fxdr_unsigned(u_int32_t, *tl);
1309ec7b004SRick Macklem 	if ((nd->nd_flag & ND_NFSV4) &&
1319ec7b004SRick Macklem 	    (nfsmode & ~(NFSACCESS_READ | NFSACCESS_LOOKUP |
1329ec7b004SRick Macklem 	     NFSACCESS_MODIFY | NFSACCESS_EXTEND | NFSACCESS_DELETE |
133c057a378SRick Macklem 	     NFSACCESS_EXECUTE | NFSACCESS_XAREAD | NFSACCESS_XAWRITE |
134c057a378SRick Macklem 	     NFSACCESS_XALIST))) {
1359ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
1369ec7b004SRick Macklem 		vput(vp);
137a9285ae5SZack Kirsch 		goto out;
1389ec7b004SRick Macklem 	}
1399ec7b004SRick Macklem 	if (nfsmode & NFSACCESS_READ) {
1409ec7b004SRick Macklem 		supported |= NFSACCESS_READ;
1418da45f2cSRick Macklem 		if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
1428da45f2cSRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
1439ec7b004SRick Macklem 			nfsmode &= ~NFSACCESS_READ;
1449ec7b004SRick Macklem 	}
1459ec7b004SRick Macklem 	if (nfsmode & NFSACCESS_MODIFY) {
1469ec7b004SRick Macklem 		supported |= NFSACCESS_MODIFY;
1478da45f2cSRick Macklem 		if (nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp, p,
1488da45f2cSRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
1499ec7b004SRick Macklem 			nfsmode &= ~NFSACCESS_MODIFY;
1509ec7b004SRick Macklem 	}
1519ec7b004SRick Macklem 	if (nfsmode & NFSACCESS_EXTEND) {
1529ec7b004SRick Macklem 		supported |= NFSACCESS_EXTEND;
1538da45f2cSRick Macklem 		if (nfsvno_accchk(vp, VWRITE | VAPPEND, nd->nd_cred, exp, p,
1548da45f2cSRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
1559ec7b004SRick Macklem 			nfsmode &= ~NFSACCESS_EXTEND;
1569ec7b004SRick Macklem 	}
157c057a378SRick Macklem 	if (nfsmode & NFSACCESS_XAREAD) {
158c057a378SRick Macklem 		supported |= NFSACCESS_XAREAD;
159c057a378SRick Macklem 		if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
160c057a378SRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
161c057a378SRick Macklem 			nfsmode &= ~NFSACCESS_XAREAD;
162c057a378SRick Macklem 	}
163c057a378SRick Macklem 	if (nfsmode & NFSACCESS_XAWRITE) {
164c057a378SRick Macklem 		supported |= NFSACCESS_XAWRITE;
165c057a378SRick Macklem 		if (nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp, p,
166c057a378SRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
167c057a378SRick Macklem 			nfsmode &= ~NFSACCESS_XAWRITE;
168c057a378SRick Macklem 	}
169c057a378SRick Macklem 	if (nfsmode & NFSACCESS_XALIST) {
170c057a378SRick Macklem 		supported |= NFSACCESS_XALIST;
171c057a378SRick Macklem 		if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
172c057a378SRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
173c057a378SRick Macklem 			nfsmode &= ~NFSACCESS_XALIST;
174c057a378SRick Macklem 	}
1759ec7b004SRick Macklem 	if (nfsmode & NFSACCESS_DELETE) {
1769ec7b004SRick Macklem 		supported |= NFSACCESS_DELETE;
1778da45f2cSRick Macklem 		if (vp->v_type == VDIR)
1788da45f2cSRick Macklem 			deletebit = VDELETE_CHILD;
1798da45f2cSRick Macklem 		else
1808da45f2cSRick Macklem 			deletebit = VDELETE;
1818da45f2cSRick Macklem 		if (nfsvno_accchk(vp, deletebit, nd->nd_cred, exp, p,
1828da45f2cSRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
1839ec7b004SRick Macklem 			nfsmode &= ~NFSACCESS_DELETE;
1849ec7b004SRick Macklem 	}
1859ec7b004SRick Macklem 	if (vnode_vtype(vp) == VDIR)
1869ec7b004SRick Macklem 		testmode = NFSACCESS_LOOKUP;
1879ec7b004SRick Macklem 	else
1889ec7b004SRick Macklem 		testmode = NFSACCESS_EXECUTE;
1899ec7b004SRick Macklem 	if (nfsmode & testmode) {
1909ec7b004SRick Macklem 		supported |= (nfsmode & testmode);
1918da45f2cSRick Macklem 		if (nfsvno_accchk(vp, VEXEC, nd->nd_cred, exp, p,
1928da45f2cSRick Macklem 		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
1939ec7b004SRick Macklem 			nfsmode &= ~testmode;
1949ec7b004SRick Macklem 	}
1959ec7b004SRick Macklem 	nfsmode &= supported;
1969ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
19790d2dfabSRick Macklem 		getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
1989ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &nva);
1999ec7b004SRick Macklem 	}
2009ec7b004SRick Macklem 	vput(vp);
2019ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
2029ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2039ec7b004SRick Macklem 		*tl++ = txdr_unsigned(supported);
2049ec7b004SRick Macklem 	} else
2059ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2069ec7b004SRick Macklem 	*tl = txdr_unsigned(nfsmode);
207a9285ae5SZack Kirsch 
208a9285ae5SZack Kirsch out:
209a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
2109ec7b004SRick Macklem 	return (0);
2119ec7b004SRick Macklem nfsmout:
2129ec7b004SRick Macklem 	vput(vp);
213a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
2149ec7b004SRick Macklem 	return (error);
2159ec7b004SRick Macklem }
2169ec7b004SRick Macklem 
2179ec7b004SRick Macklem /*
2189ec7b004SRick Macklem  * nfs getattr service
2199ec7b004SRick Macklem  */
220b9cc3262SRyan Moeller int
2219ec7b004SRick Macklem nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
222af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
2239ec7b004SRick Macklem {
2249ec7b004SRick Macklem 	struct nfsvattr nva;
2259ec7b004SRick Macklem 	fhandle_t fh;
226a09001a8SRick Macklem 	int at_root = 0, error = 0, supports_nfsv4acls;
2279ec7b004SRick Macklem 	struct nfsreferral *refp;
22853f476caSRick Macklem 	nfsattrbit_t attrbits, tmpbits;
22907c0c166SRick Macklem 	struct mount *mp;
23007c0c166SRick Macklem 	struct vnode *tvp = NULL;
23107c0c166SRick Macklem 	struct vattr va;
23207c0c166SRick Macklem 	uint64_t mounted_on_fileno = 0;
23353f476caSRick Macklem 	accmode_t accmode;
234af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
2359ec7b004SRick Macklem 
2369ec7b004SRick Macklem 	if (nd->nd_repstat)
237a9285ae5SZack Kirsch 		goto out;
2389ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
2399ec7b004SRick Macklem 		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
2409ec7b004SRick Macklem 		if (error) {
2419ec7b004SRick Macklem 			vput(vp);
242a9285ae5SZack Kirsch 			goto out;
2439ec7b004SRick Macklem 		}
2449ec7b004SRick Macklem 
2459ec7b004SRick Macklem 		/*
2469ec7b004SRick Macklem 		 * Check for a referral.
2479ec7b004SRick Macklem 		 */
2489ec7b004SRick Macklem 		refp = nfsv4root_getreferral(vp, NULL, 0);
2499ec7b004SRick Macklem 		if (refp != NULL) {
2509ec7b004SRick Macklem 			(void) nfsrv_putreferralattr(nd, &attrbits, refp, 1,
2519ec7b004SRick Macklem 			    &nd->nd_repstat);
2529ec7b004SRick Macklem 			vput(vp);
253a9285ae5SZack Kirsch 			goto out;
2549ec7b004SRick Macklem 		}
25553f476caSRick Macklem 		if (nd->nd_repstat == 0) {
25653f476caSRick Macklem 			accmode = 0;
25753f476caSRick Macklem 			NFSSET_ATTRBIT(&tmpbits, &attrbits);
258d8a5961fSMarcelo Araujo 
259d8a5961fSMarcelo Araujo 			/*
260d8a5961fSMarcelo Araujo 			 * GETATTR with write-only attr time_access_set and time_modify_set
261d8a5961fSMarcelo Araujo 			 * should return NFS4ERR_INVAL.
262d8a5961fSMarcelo Araujo 			 */
263d8a5961fSMarcelo Araujo 			if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_TIMEACCESSSET) ||
264d8a5961fSMarcelo Araujo 					NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_TIMEMODIFYSET)){
265d8a5961fSMarcelo Araujo 				error = NFSERR_INVAL;
266d8a5961fSMarcelo Araujo 				vput(vp);
267d8a5961fSMarcelo Araujo 				goto out;
268d8a5961fSMarcelo Araujo 			}
26953f476caSRick Macklem 			if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_ACL)) {
27053f476caSRick Macklem 				NFSCLRBIT_ATTRBIT(&tmpbits, NFSATTRBIT_ACL);
27153f476caSRick Macklem 				accmode |= VREAD_ACL;
27253f476caSRick Macklem 			}
27353f476caSRick Macklem 			if (NFSNONZERO_ATTRBIT(&tmpbits))
27453f476caSRick Macklem 				accmode |= VREAD_ATTRIBUTES;
27553f476caSRick Macklem 			if (accmode != 0)
27653f476caSRick Macklem 				nd->nd_repstat = nfsvno_accchk(vp, accmode,
2778da45f2cSRick Macklem 				    nd->nd_cred, exp, p, NFSACCCHK_NOOVERRIDE,
2788da45f2cSRick Macklem 				    NFSACCCHK_VPISLOCKED, NULL);
2799ec7b004SRick Macklem 		}
28053f476caSRick Macklem 	}
2819ec7b004SRick Macklem 	if (!nd->nd_repstat)
28290d2dfabSRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, &attrbits);
2839ec7b004SRick Macklem 	if (!nd->nd_repstat) {
2849ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV4) {
2859ec7b004SRick Macklem 			if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_FILEHANDLE))
2869ec7b004SRick Macklem 				nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
2879ec7b004SRick Macklem 			if (!nd->nd_repstat)
2889ec7b004SRick Macklem 				nd->nd_repstat = nfsrv_checkgetattr(nd, vp,
28990d2dfabSRick Macklem 				    &nva, &attrbits, p);
29007c0c166SRick Macklem 			if (nd->nd_repstat == 0) {
291a09001a8SRick Macklem 				supports_nfsv4acls = nfs_supportsnfsv4acls(vp);
29207c0c166SRick Macklem 				mp = vp->v_mount;
29307c0c166SRick Macklem 				if (nfsrv_enable_crossmntpt != 0 &&
29407c0c166SRick Macklem 				    vp->v_type == VDIR &&
29507c0c166SRick Macklem 				    (vp->v_vflag & VV_ROOT) != 0 &&
29607c0c166SRick Macklem 				    vp != rootvnode) {
29707c0c166SRick Macklem 					tvp = mp->mnt_vnodecovered;
29807c0c166SRick Macklem 					VREF(tvp);
29907c0c166SRick Macklem 					at_root = 1;
30007c0c166SRick Macklem 				} else
30107c0c166SRick Macklem 					at_root = 0;
30207c0c166SRick Macklem 				vfs_ref(mp);
303b249ce48SMateusz Guzik 				NFSVOPUNLOCK(vp);
30407c0c166SRick Macklem 				if (at_root != 0) {
30507c0c166SRick Macklem 					if ((nd->nd_repstat =
30698f234f3SZack Kirsch 					     NFSVOPLOCK(tvp, LK_SHARED)) == 0) {
30707c0c166SRick Macklem 						nd->nd_repstat = VOP_GETATTR(
30807c0c166SRick Macklem 						    tvp, &va, nd->nd_cred);
30907c0c166SRick Macklem 						vput(tvp);
31007c0c166SRick Macklem 					} else
31107c0c166SRick Macklem 						vrele(tvp);
31207c0c166SRick Macklem 					if (nd->nd_repstat == 0)
31307c0c166SRick Macklem 						mounted_on_fileno = (uint64_t)
31407c0c166SRick Macklem 						    va.va_fileid;
31507c0c166SRick Macklem 					else
31607c0c166SRick Macklem 						at_root = 0;
31707c0c166SRick Macklem 				}
31807c0c166SRick Macklem 				if (nd->nd_repstat == 0)
31907c0c166SRick Macklem 					nd->nd_repstat = vfs_busy(mp, 0);
32007c0c166SRick Macklem 				vfs_rel(mp);
32107c0c166SRick Macklem 				if (nd->nd_repstat == 0) {
32207c0c166SRick Macklem 					(void)nfsvno_fillattr(nd, mp, vp, &nva,
32307c0c166SRick Macklem 					    &fh, 0, &attrbits, nd->nd_cred, p,
324a09001a8SRick Macklem 					    isdgram, 1, supports_nfsv4acls,
325a09001a8SRick Macklem 					    at_root, mounted_on_fileno);
32607c0c166SRick Macklem 					vfs_unbusy(mp);
32707c0c166SRick Macklem 				}
3289ec7b004SRick Macklem 				vrele(vp);
32907c0c166SRick Macklem 			} else
33007c0c166SRick Macklem 				vput(vp);
3319ec7b004SRick Macklem 		} else {
3329ec7b004SRick Macklem 			nfsrv_fillattr(nd, &nva);
3339ec7b004SRick Macklem 			vput(vp);
3349ec7b004SRick Macklem 		}
3359ec7b004SRick Macklem 	} else {
3369ec7b004SRick Macklem 		vput(vp);
3379ec7b004SRick Macklem 	}
338a9285ae5SZack Kirsch 
339a9285ae5SZack Kirsch out:
340a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
341a9285ae5SZack Kirsch 	return (error);
3429ec7b004SRick Macklem }
3439ec7b004SRick Macklem 
3449ec7b004SRick Macklem /*
3459ec7b004SRick Macklem  * nfs setattr service
3469ec7b004SRick Macklem  */
347b9cc3262SRyan Moeller int
3489ec7b004SRick Macklem nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
349af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
3509ec7b004SRick Macklem {
3519ec7b004SRick Macklem 	struct nfsvattr nva, nva2;
3529ec7b004SRick Macklem 	u_int32_t *tl;
3539ec7b004SRick Macklem 	int preat_ret = 1, postat_ret = 1, gcheck = 0, error = 0;
35490d2dfabSRick Macklem 	int gotproxystateid;
3559ec7b004SRick Macklem 	struct timespec guard = { 0, 0 };
3569ec7b004SRick Macklem 	nfsattrbit_t attrbits, retbits;
3579ec7b004SRick Macklem 	nfsv4stateid_t stateid;
3589ec7b004SRick Macklem 	NFSACL_T *aclp = NULL;
359af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
3609ec7b004SRick Macklem 
3619ec7b004SRick Macklem 	if (nd->nd_repstat) {
3629ec7b004SRick Macklem 		nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
363a9285ae5SZack Kirsch 		goto out;
3649ec7b004SRick Macklem 	}
3659ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
366c3e22f83SRick Macklem 	aclp = acl_alloc(M_WAITOK);
3679ec7b004SRick Macklem 	aclp->acl_cnt = 0;
3689ec7b004SRick Macklem #endif
36990d2dfabSRick Macklem 	gotproxystateid = 0;
3709ec7b004SRick Macklem 	NFSVNO_ATTRINIT(&nva);
3719ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
3729ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
3739ec7b004SRick Macklem 		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
37490d2dfabSRick Macklem 		stateid.other[0] = *tl++;
37590d2dfabSRick Macklem 		stateid.other[1] = *tl++;
37690d2dfabSRick Macklem 		stateid.other[2] = *tl;
37790d2dfabSRick Macklem 		if (stateid.other[0] == 0x55555555 &&
37890d2dfabSRick Macklem 		    stateid.other[1] == 0x55555555 &&
37990d2dfabSRick Macklem 		    stateid.other[2] == 0x55555555 &&
38090d2dfabSRick Macklem 		    stateid.seqid == 0xffffffff)
38190d2dfabSRick Macklem 			gotproxystateid = 1;
3829ec7b004SRick Macklem 	}
383d8a5961fSMarcelo Araujo 	error = nfsrv_sattr(nd, vp, &nva, &attrbits, aclp, p);
3849ec7b004SRick Macklem 	if (error)
3859ec7b004SRick Macklem 		goto nfsmout;
38690d2dfabSRick Macklem 
38790d2dfabSRick Macklem 	/* For NFSv4, only va_uid is used from nva2. */
38890d2dfabSRick Macklem 	NFSZERO_ATTRBIT(&retbits);
38990d2dfabSRick Macklem 	NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
39090d2dfabSRick Macklem 	preat_ret = nfsvno_getattr(vp, &nva2, nd, p, 1, &retbits);
3919ec7b004SRick Macklem 	if (!nd->nd_repstat)
3929ec7b004SRick Macklem 		nd->nd_repstat = preat_ret;
39390d2dfabSRick Macklem 
39490d2dfabSRick Macklem 	NFSZERO_ATTRBIT(&retbits);
3959ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
3969ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3979ec7b004SRick Macklem 		gcheck = fxdr_unsigned(int, *tl);
3989ec7b004SRick Macklem 		if (gcheck) {
3999ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
4009ec7b004SRick Macklem 			fxdr_nfsv3time(tl, &guard);
4019ec7b004SRick Macklem 		}
4029ec7b004SRick Macklem 		if (!nd->nd_repstat && gcheck &&
4039ec7b004SRick Macklem 		    (nva2.na_ctime.tv_sec != guard.tv_sec ||
4049ec7b004SRick Macklem 		     nva2.na_ctime.tv_nsec != guard.tv_nsec))
4059ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_NOT_SYNC;
4069ec7b004SRick Macklem 		if (nd->nd_repstat) {
4079ec7b004SRick Macklem 			vput(vp);
4089ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
4099ec7b004SRick Macklem 			acl_free(aclp);
4109ec7b004SRick Macklem #endif
4119ec7b004SRick Macklem 			nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
412a9285ae5SZack Kirsch 			goto out;
4139ec7b004SRick Macklem 		}
4149ec7b004SRick Macklem 	} else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
4159ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
4169ec7b004SRick Macklem 
4179ec7b004SRick Macklem 	/*
4189ec7b004SRick Macklem 	 * Now that we have all the fields, lets do it.
4199ec7b004SRick Macklem 	 * If the size is being changed write access is required, otherwise
4209ec7b004SRick Macklem 	 * just check for a read only file system.
4219ec7b004SRick Macklem 	 */
4229ec7b004SRick Macklem 	if (!nd->nd_repstat) {
4239ec7b004SRick Macklem 		if (NFSVNO_NOTSETSIZE(&nva)) {
4249ec7b004SRick Macklem 			if (NFSVNO_EXRDONLY(exp) ||
425eea79fdeSAlan Somers 			    (vfs_flags(vp->v_mount) & MNT_RDONLY))
4269ec7b004SRick Macklem 				nd->nd_repstat = EROFS;
4279ec7b004SRick Macklem 		} else {
4289ec7b004SRick Macklem 			if (vnode_vtype(vp) != VREG)
4299ec7b004SRick Macklem 				nd->nd_repstat = EINVAL;
4309ec7b004SRick Macklem 			else if (nva2.na_uid != nd->nd_cred->cr_uid ||
4319ec7b004SRick Macklem 			    NFSVNO_EXSTRICTACCESS(exp))
4329ec7b004SRick Macklem 				nd->nd_repstat = nfsvno_accchk(vp,
4338da45f2cSRick Macklem 				    VWRITE, nd->nd_cred, exp, p,
4348da45f2cSRick Macklem 				    NFSACCCHK_NOOVERRIDE,
4358da45f2cSRick Macklem 				    NFSACCCHK_VPISLOCKED, NULL);
4369ec7b004SRick Macklem 		}
4379ec7b004SRick Macklem 	}
43890d2dfabSRick Macklem 	/*
43990d2dfabSRick Macklem 	 * Proxy operations from the MDS are allowed via the all 0s special
44090d2dfabSRick Macklem 	 * stateid.
44190d2dfabSRick Macklem 	 */
44290d2dfabSRick Macklem 	if (nd->nd_repstat == 0 && (nd->nd_flag & ND_NFSV4) != 0 &&
44390d2dfabSRick Macklem 	    gotproxystateid == 0)
4449ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_checksetattr(vp, nd, &stateid,
4459ec7b004SRick Macklem 		    &nva, &attrbits, exp, p);
4469ec7b004SRick Macklem 
4479ec7b004SRick Macklem 	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
4489ec7b004SRick Macklem 	    /*
4499ec7b004SRick Macklem 	     * For V4, try setting the attrbutes in sets, so that the
4509ec7b004SRick Macklem 	     * reply bitmap will be correct for an error case.
4519ec7b004SRick Macklem 	     */
4529ec7b004SRick Macklem 	    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER) ||
4539ec7b004SRick Macklem 		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP)) {
4549ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva2);
4559ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, uid, nva.na_uid);
4569ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, gid, nva.na_gid);
4579ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
4589ec7b004SRick Macklem 		    exp);
4599ec7b004SRick Macklem 		if (!nd->nd_repstat) {
4609ec7b004SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER))
4619ec7b004SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
4629ec7b004SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP))
4639ec7b004SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNERGROUP);
4649ec7b004SRick Macklem 		}
4659ec7b004SRick Macklem 	    }
4669ec7b004SRick Macklem 	    if (!nd->nd_repstat &&
4679ec7b004SRick Macklem 		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SIZE)) {
4689ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva2);
4699ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, size, nva.na_size);
4709ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
4719ec7b004SRick Macklem 		    exp);
4729ec7b004SRick Macklem 		if (!nd->nd_repstat)
4739ec7b004SRick Macklem 		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SIZE);
4749ec7b004SRick Macklem 	    }
4759ec7b004SRick Macklem 	    if (!nd->nd_repstat &&
4769ec7b004SRick Macklem 		(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET) ||
4779ec7b004SRick Macklem 		 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))) {
4789ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva2);
4799ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, atime, nva.na_atime);
4809ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, mtime, nva.na_mtime);
4819ec7b004SRick Macklem 		if (nva.na_vaflags & VA_UTIMES_NULL) {
4829ec7b004SRick Macklem 			nva2.na_vaflags |= VA_UTIMES_NULL;
4839ec7b004SRick Macklem 			NFSVNO_SETACTIVE(&nva2, vaflags);
4849ec7b004SRick Macklem 		}
4859ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
4869ec7b004SRick Macklem 		    exp);
4879ec7b004SRick Macklem 		if (!nd->nd_repstat) {
4889ec7b004SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET))
4899ec7b004SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEACCESSSET);
4909ec7b004SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))
4919ec7b004SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEMODIFYSET);
4929ec7b004SRick Macklem 		}
4939ec7b004SRick Macklem 	    }
4949ec7b004SRick Macklem 	    if (!nd->nd_repstat &&
495b4372164SRick Macklem 		(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE) ||
496b4372164SRick Macklem 		 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODESETMASKED))) {
4979ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva2);
4989ec7b004SRick Macklem 		NFSVNO_SETATTRVAL(&nva2, mode, nva.na_mode);
4999ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
5009ec7b004SRick Macklem 		    exp);
501b4372164SRick Macklem 		if (!nd->nd_repstat) {
502b4372164SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE))
5039ec7b004SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODE);
504b4372164SRick Macklem 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODESETMASKED))
505b4372164SRick Macklem 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODESETMASKED);
506b4372164SRick Macklem 		}
5079ec7b004SRick Macklem 	    }
5089ec7b004SRick Macklem 
5099ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
5109ec7b004SRick Macklem 	    if (!nd->nd_repstat && aclp->acl_cnt > 0 &&
5119ec7b004SRick Macklem 		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ACL)) {
5129ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_setacl(vp, aclp, nd->nd_cred, p);
5139ec7b004SRick Macklem 		if (!nd->nd_repstat)
5149ec7b004SRick Macklem 		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_ACL);
5159ec7b004SRick Macklem 	    }
5169ec7b004SRick Macklem #endif
5179ec7b004SRick Macklem 	} else if (!nd->nd_repstat) {
5189ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_setattr(vp, &nva, nd->nd_cred, p,
5199ec7b004SRick Macklem 		    exp);
5209ec7b004SRick Macklem 	}
5219ec7b004SRick Macklem 	if (nd->nd_flag & (ND_NFSV2 | ND_NFSV3)) {
52290d2dfabSRick Macklem 		postat_ret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
5239ec7b004SRick Macklem 		if (!nd->nd_repstat)
5249ec7b004SRick Macklem 			nd->nd_repstat = postat_ret;
5259ec7b004SRick Macklem 	}
5269ec7b004SRick Macklem 	vput(vp);
5279ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
5289ec7b004SRick Macklem 	acl_free(aclp);
5299ec7b004SRick Macklem #endif
5309ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
5319ec7b004SRick Macklem 		nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
5329ec7b004SRick Macklem 	else if (nd->nd_flag & ND_NFSV4)
5339ec7b004SRick Macklem 		(void) nfsrv_putattrbit(nd, &retbits);
5349ec7b004SRick Macklem 	else if (!nd->nd_repstat)
5359ec7b004SRick Macklem 		nfsrv_fillattr(nd, &nva);
536a9285ae5SZack Kirsch 
537a9285ae5SZack Kirsch out:
538a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
5399ec7b004SRick Macklem 	return (0);
5409ec7b004SRick Macklem nfsmout:
5419ec7b004SRick Macklem 	vput(vp);
5429ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
5439ec7b004SRick Macklem 	acl_free(aclp);
5449ec7b004SRick Macklem #endif
5459ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
5469ec7b004SRick Macklem 		/*
5479ec7b004SRick Macklem 		 * For all nd_repstat, the V4 reply includes a bitmap,
5489ec7b004SRick Macklem 		 * even NFSERR_BADXDR, which is what this will end up
5499ec7b004SRick Macklem 		 * returning.
5509ec7b004SRick Macklem 		 */
5519ec7b004SRick Macklem 		(void) nfsrv_putattrbit(nd, &retbits);
5529ec7b004SRick Macklem 	}
553a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
5549ec7b004SRick Macklem 	return (error);
5559ec7b004SRick Macklem }
5569ec7b004SRick Macklem 
5579ec7b004SRick Macklem /*
5589ec7b004SRick Macklem  * nfs lookup rpc
5599ec7b004SRick Macklem  * (Also performs lookup parent for v4)
5609ec7b004SRick Macklem  */
561b9cc3262SRyan Moeller int
5629ec7b004SRick Macklem nfsrvd_lookup(struct nfsrv_descript *nd, __unused int isdgram,
563af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
5649ec7b004SRick Macklem {
5659ec7b004SRick Macklem 	struct nameidata named;
5669ec7b004SRick Macklem 	vnode_t vp, dirp = NULL;
567a9285ae5SZack Kirsch 	int error = 0, dattr_ret = 1;
5689ec7b004SRick Macklem 	struct nfsvattr nva, dattr;
5699ec7b004SRick Macklem 	char *bufp;
5709ec7b004SRick Macklem 	u_long *hashp;
571af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
5729ec7b004SRick Macklem 
5739ec7b004SRick Macklem 	if (nd->nd_repstat) {
5749ec7b004SRick Macklem 		nfsrv_postopattr(nd, dattr_ret, &dattr);
575a9285ae5SZack Kirsch 		goto out;
5769ec7b004SRick Macklem 	}
5779ec7b004SRick Macklem 
5789ec7b004SRick Macklem 	/*
5799ec7b004SRick Macklem 	 * For some reason, if dp is a symlink, the error
5809ec7b004SRick Macklem 	 * returned is supposed to be NFSERR_SYMLINK and not NFSERR_NOTDIR.
5819ec7b004SRick Macklem 	 */
5829ec7b004SRick Macklem 	if (dp->v_type == VLNK && (nd->nd_flag & ND_NFSV4)) {
5839ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_SYMLINK;
5849ec7b004SRick Macklem 		vrele(dp);
585a9285ae5SZack Kirsch 		goto out;
5869ec7b004SRick Macklem 	}
5879ec7b004SRick Macklem 
5889ec7b004SRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
5899ec7b004SRick Macklem 	    LOCKLEAF | SAVESTART);
5909ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
5919ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
5929ec7b004SRick Macklem 	if (error) {
5939ec7b004SRick Macklem 		vrele(dp);
5949ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
595a9285ae5SZack Kirsch 		goto out;
5969ec7b004SRick Macklem 	}
5979ec7b004SRick Macklem 	if (!nd->nd_repstat) {
5989ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
5999ec7b004SRick Macklem 	} else {
6009ec7b004SRick Macklem 		vrele(dp);
6019ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
6029ec7b004SRick Macklem 	}
6039ec7b004SRick Macklem 	if (nd->nd_repstat) {
6049ec7b004SRick Macklem 		if (dirp) {
6059ec7b004SRick Macklem 			if (nd->nd_flag & ND_NFSV3)
60690d2dfabSRick Macklem 				dattr_ret = nfsvno_getattr(dirp, &dattr, nd, p,
60790d2dfabSRick Macklem 				    0, NULL);
6089ec7b004SRick Macklem 			vrele(dirp);
6099ec7b004SRick Macklem 		}
6109ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
6119ec7b004SRick Macklem 			nfsrv_postopattr(nd, dattr_ret, &dattr);
612a9285ae5SZack Kirsch 		goto out;
6139ec7b004SRick Macklem 	}
6149ec7b004SRick Macklem 	if (named.ni_startdir)
6159ec7b004SRick Macklem 		vrele(named.ni_startdir);
6169ec7b004SRick Macklem 	nfsvno_relpathbuf(&named);
6179ec7b004SRick Macklem 	vp = named.ni_vp;
61837b88c2dSRick Macklem 	if ((nd->nd_flag & ND_NFSV4) != 0 && !NFSVNO_EXPORTED(exp) &&
61937b88c2dSRick Macklem 	    vp->v_type != VDIR && vp->v_type != VLNK)
62037b88c2dSRick Macklem 		/*
62137b88c2dSRick Macklem 		 * Only allow lookup of VDIR and VLNK for traversal of
62237b88c2dSRick Macklem 		 * non-exported volumes during NFSv4 mounting.
62337b88c2dSRick Macklem 		 */
62437b88c2dSRick Macklem 		nd->nd_repstat = ENOENT;
62537b88c2dSRick Macklem 	if (nd->nd_repstat == 0)
6269ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
6279ec7b004SRick Macklem 	if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
62890d2dfabSRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
62981f78d99SRick Macklem 	if (vpp != NULL && nd->nd_repstat == 0)
6309ec7b004SRick Macklem 		*vpp = vp;
63181f78d99SRick Macklem 	else
6329ec7b004SRick Macklem 		vput(vp);
6339ec7b004SRick Macklem 	if (dirp) {
6349ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
63590d2dfabSRick Macklem 			dattr_ret = nfsvno_getattr(dirp, &dattr, nd, p, 0,
63690d2dfabSRick Macklem 			    NULL);
6379ec7b004SRick Macklem 		vrele(dirp);
6389ec7b004SRick Macklem 	}
6399ec7b004SRick Macklem 	if (nd->nd_repstat) {
6409ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
6419ec7b004SRick Macklem 			nfsrv_postopattr(nd, dattr_ret, &dattr);
642a9285ae5SZack Kirsch 		goto out;
6439ec7b004SRick Macklem 	}
6449ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
6459ec7b004SRick Macklem 		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
6469ec7b004SRick Macklem 		nfsrv_fillattr(nd, &nva);
6479ec7b004SRick Macklem 	} else if (nd->nd_flag & ND_NFSV3) {
6489ec7b004SRick Macklem 		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
6499ec7b004SRick Macklem 		nfsrv_postopattr(nd, 0, &nva);
6509ec7b004SRick Macklem 		nfsrv_postopattr(nd, dattr_ret, &dattr);
6519ec7b004SRick Macklem 	}
652a9285ae5SZack Kirsch 
653a9285ae5SZack Kirsch out:
654a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
655a9285ae5SZack Kirsch 	return (error);
6569ec7b004SRick Macklem }
6579ec7b004SRick Macklem 
6589ec7b004SRick Macklem /*
6599ec7b004SRick Macklem  * nfs readlink service
6609ec7b004SRick Macklem  */
661b9cc3262SRyan Moeller int
6629ec7b004SRick Macklem nfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram,
663af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
6649ec7b004SRick Macklem {
6659ec7b004SRick Macklem 	u_int32_t *tl;
666ae070589SRick Macklem 	struct mbuf *mp = NULL, *mpend = NULL;
6679ec7b004SRick Macklem 	int getret = 1, len;
6689ec7b004SRick Macklem 	struct nfsvattr nva;
669af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
6709ec7b004SRick Macklem 
6719ec7b004SRick Macklem 	if (nd->nd_repstat) {
6729ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &nva);
673a9285ae5SZack Kirsch 		goto out;
6749ec7b004SRick Macklem 	}
6759ec7b004SRick Macklem 	if (vnode_vtype(vp) != VLNK) {
6769ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV2)
6779ec7b004SRick Macklem 			nd->nd_repstat = ENXIO;
6789ec7b004SRick Macklem 		else
6799ec7b004SRick Macklem 			nd->nd_repstat = EINVAL;
6809ec7b004SRick Macklem 	}
6819ec7b004SRick Macklem 	if (!nd->nd_repstat)
6829ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred, p,
6839ec7b004SRick Macklem 		    &mp, &mpend, &len);
6849ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
68590d2dfabSRick Macklem 		getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
6869ec7b004SRick Macklem 	vput(vp);
6879ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
6889ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &nva);
6899ec7b004SRick Macklem 	if (nd->nd_repstat)
690a9285ae5SZack Kirsch 		goto out;
6919ec7b004SRick Macklem 	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
6929ec7b004SRick Macklem 	*tl = txdr_unsigned(len);
693*18a48314SRick Macklem 	if (mp != NULL) {
6949f6624d3SRick Macklem 		nd->nd_mb->m_next = mp;
6959ec7b004SRick Macklem 		nd->nd_mb = mpend;
6969f6624d3SRick Macklem 		nd->nd_bpos = mtod(mpend, caddr_t) + mpend->m_len;
697*18a48314SRick Macklem 	}
698a9285ae5SZack Kirsch 
699a9285ae5SZack Kirsch out:
700a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
7019ec7b004SRick Macklem 	return (0);
7029ec7b004SRick Macklem }
7039ec7b004SRick Macklem 
7049ec7b004SRick Macklem /*
7059ec7b004SRick Macklem  * nfs read service
7069ec7b004SRick Macklem  */
707b9cc3262SRyan Moeller int
7089ec7b004SRick Macklem nfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram,
709af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
7109ec7b004SRick Macklem {
7119ec7b004SRick Macklem 	u_int32_t *tl;
71290d2dfabSRick Macklem 	int error = 0, cnt, getret = 1, gotproxystateid, reqlen, eof = 0;
713ae070589SRick Macklem 	struct mbuf *m2, *m3;
7149ec7b004SRick Macklem 	struct nfsvattr nva;
7159ec7b004SRick Macklem 	off_t off = 0x0;
7169ec7b004SRick Macklem 	struct nfsstate st, *stp = &st;
7179ec7b004SRick Macklem 	struct nfslock lo, *lop = &lo;
7189ec7b004SRick Macklem 	nfsv4stateid_t stateid;
7199ec7b004SRick Macklem 	nfsquad_t clientid;
720af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
7219ec7b004SRick Macklem 
7229ec7b004SRick Macklem 	if (nd->nd_repstat) {
7239ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &nva);
724a9285ae5SZack Kirsch 		goto out;
7259ec7b004SRick Macklem 	}
7269ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
7279ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
7289ec7b004SRick Macklem 		off = (off_t)fxdr_unsigned(u_int32_t, *tl++);
7299ec7b004SRick Macklem 		reqlen = fxdr_unsigned(int, *tl);
7309ec7b004SRick Macklem 	} else if (nd->nd_flag & ND_NFSV3) {
7319ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
7329ec7b004SRick Macklem 		off = fxdr_hyper(tl);
7339ec7b004SRick Macklem 		tl += 2;
7349ec7b004SRick Macklem 		reqlen = fxdr_unsigned(int, *tl);
7359ec7b004SRick Macklem 	} else {
7369ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3*NFSX_UNSIGNED);
7379ec7b004SRick Macklem 		reqlen = fxdr_unsigned(int, *(tl + 6));
7389ec7b004SRick Macklem 	}
7399ec7b004SRick Macklem 	if (reqlen > NFS_SRVMAXDATA(nd)) {
7409ec7b004SRick Macklem 		reqlen = NFS_SRVMAXDATA(nd);
7419ec7b004SRick Macklem 	} else if (reqlen < 0) {
7429ec7b004SRick Macklem 		error = EBADRPC;
7439ec7b004SRick Macklem 		goto nfsmout;
7449ec7b004SRick Macklem 	}
74590d2dfabSRick Macklem 	gotproxystateid = 0;
7469ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
7479ec7b004SRick Macklem 		stp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
7489ec7b004SRick Macklem 		lop->lo_flags = NFSLCK_READ;
7499ec7b004SRick Macklem 		stp->ls_ownerlen = 0;
7509ec7b004SRick Macklem 		stp->ls_op = NULL;
7519ec7b004SRick Macklem 		stp->ls_uid = nd->nd_cred->cr_uid;
7529ec7b004SRick Macklem 		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
7539ec7b004SRick Macklem 		clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
7549ec7b004SRick Macklem 		clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
755c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
756c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
757c59e4cc3SRick Macklem 				clientid.qval = nd->nd_clientid.qval;
758c59e4cc3SRick Macklem 			else if (nd->nd_clientid.qval != clientid.qval)
759c59e4cc3SRick Macklem 				printf("EEK1 multiple clids\n");
7609ec7b004SRick Macklem 		} else {
761c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
762c59e4cc3SRick Macklem 				printf("EEK! no clientid from session\n");
7639ec7b004SRick Macklem 			nd->nd_flag |= ND_IMPLIEDCLID;
7649ec7b004SRick Macklem 			nd->nd_clientid.qval = clientid.qval;
7659ec7b004SRick Macklem 		}
7669ec7b004SRick Macklem 		stp->ls_stateid.other[2] = *tl++;
76790d2dfabSRick Macklem 		/*
76890d2dfabSRick Macklem 		 * Don't allow the client to use a special stateid for a DS op.
76990d2dfabSRick Macklem 		 */
77090d2dfabSRick Macklem 		if ((nd->nd_flag & ND_DSSERVER) != 0 &&
77190d2dfabSRick Macklem 		    ((stp->ls_stateid.other[0] == 0x0 &&
77290d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0x0 &&
77390d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0x0) ||
77490d2dfabSRick Macklem 		    (stp->ls_stateid.other[0] == 0xffffffff &&
77590d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0xffffffff &&
77690d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0xffffffff) ||
77790d2dfabSRick Macklem 		    stp->ls_stateid.seqid != 0))
77890d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
77990d2dfabSRick Macklem 		/* However, allow the proxy stateid. */
78090d2dfabSRick Macklem 		if (stp->ls_stateid.seqid == 0xffffffff &&
78190d2dfabSRick Macklem 		    stp->ls_stateid.other[0] == 0x55555555 &&
78290d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0x55555555 &&
78390d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0x55555555)
78490d2dfabSRick Macklem 			gotproxystateid = 1;
7859ec7b004SRick Macklem 		off = fxdr_hyper(tl);
7869ec7b004SRick Macklem 		lop->lo_first = off;
7879ec7b004SRick Macklem 		tl += 2;
7889ec7b004SRick Macklem 		lop->lo_end = off + reqlen;
7899ec7b004SRick Macklem 		/*
7909ec7b004SRick Macklem 		 * Paranoia, just in case it wraps around.
7919ec7b004SRick Macklem 		 */
7929ec7b004SRick Macklem 		if (lop->lo_end < off)
7939ec7b004SRick Macklem 			lop->lo_end = NFS64BITSSET;
7949ec7b004SRick Macklem 	}
7959ec7b004SRick Macklem 	if (vnode_vtype(vp) != VREG) {
7969ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
7979ec7b004SRick Macklem 			nd->nd_repstat = EINVAL;
7989ec7b004SRick Macklem 		else
7999ec7b004SRick Macklem 			nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
8009ec7b004SRick Macklem 			    EINVAL;
8019ec7b004SRick Macklem 	}
80290d2dfabSRick Macklem 	getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
8039ec7b004SRick Macklem 	if (!nd->nd_repstat)
8049ec7b004SRick Macklem 		nd->nd_repstat = getret;
8059ec7b004SRick Macklem 	if (!nd->nd_repstat &&
8069ec7b004SRick Macklem 	    (nva.na_uid != nd->nd_cred->cr_uid ||
8079ec7b004SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp))) {
8088da45f2cSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
8099ec7b004SRick Macklem 		    nd->nd_cred, exp, p,
8108da45f2cSRick Macklem 		    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
8119ec7b004SRick Macklem 		if (nd->nd_repstat)
8128da45f2cSRick Macklem 			nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
8138da45f2cSRick Macklem 			    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
8148da45f2cSRick Macklem 			    NFSACCCHK_VPISLOCKED, NULL);
8159ec7b004SRick Macklem 	}
81690d2dfabSRick Macklem 	/*
81790d2dfabSRick Macklem 	 * DS reads are marked by ND_DSSERVER or use the proxy special
81890d2dfabSRick Macklem 	 * stateid.
81990d2dfabSRick Macklem 	 */
82090d2dfabSRick Macklem 	if (nd->nd_repstat == 0 && (nd->nd_flag & (ND_NFSV4 | ND_DSSERVER)) ==
82190d2dfabSRick Macklem 	    ND_NFSV4 && gotproxystateid == 0)
8229ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
8239ec7b004SRick Macklem 		    &stateid, exp, nd, p);
8249ec7b004SRick Macklem 	if (nd->nd_repstat) {
8259ec7b004SRick Macklem 		vput(vp);
8269ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
8279ec7b004SRick Macklem 			nfsrv_postopattr(nd, getret, &nva);
828a9285ae5SZack Kirsch 		goto out;
8299ec7b004SRick Macklem 	}
8309ec7b004SRick Macklem 	if (off >= nva.na_size) {
8319ec7b004SRick Macklem 		cnt = 0;
8329ec7b004SRick Macklem 		eof = 1;
8339ec7b004SRick Macklem 	} else if (reqlen == 0)
8349ec7b004SRick Macklem 		cnt = 0;
83506521fbbSZack Kirsch 	else if ((off + reqlen) >= nva.na_size) {
8369ec7b004SRick Macklem 		cnt = nva.na_size - off;
83706521fbbSZack Kirsch 		eof = 1;
83806521fbbSZack Kirsch 	} else
8399ec7b004SRick Macklem 		cnt = reqlen;
8409ec7b004SRick Macklem 	m3 = NULL;
8419ec7b004SRick Macklem 	if (cnt > 0) {
8429ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred, p,
8439ec7b004SRick Macklem 		    &m3, &m2);
8449ec7b004SRick Macklem 		if (!(nd->nd_flag & ND_NFSV4)) {
84590d2dfabSRick Macklem 			getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
8469ec7b004SRick Macklem 			if (!nd->nd_repstat)
8479ec7b004SRick Macklem 				nd->nd_repstat = getret;
8489ec7b004SRick Macklem 		}
8499ec7b004SRick Macklem 		if (nd->nd_repstat) {
8509ec7b004SRick Macklem 			vput(vp);
8519ec7b004SRick Macklem 			if (m3)
8529f6624d3SRick Macklem 				m_freem(m3);
8539ec7b004SRick Macklem 			if (nd->nd_flag & ND_NFSV3)
8549ec7b004SRick Macklem 				nfsrv_postopattr(nd, getret, &nva);
855a9285ae5SZack Kirsch 			goto out;
8569ec7b004SRick Macklem 		}
8579ec7b004SRick Macklem 	}
8589ec7b004SRick Macklem 	vput(vp);
8599ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
8609ec7b004SRick Macklem 		nfsrv_fillattr(nd, &nva);
8619ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
8629ec7b004SRick Macklem 	} else {
8639ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
8649ec7b004SRick Macklem 			nfsrv_postopattr(nd, getret, &nva);
8659ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
8669ec7b004SRick Macklem 			*tl++ = txdr_unsigned(cnt);
8679ec7b004SRick Macklem 		} else
8689ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
86906521fbbSZack Kirsch 		if (eof)
8709ec7b004SRick Macklem 			*tl++ = newnfs_true;
8719ec7b004SRick Macklem 		else
8729ec7b004SRick Macklem 			*tl++ = newnfs_false;
8739ec7b004SRick Macklem 	}
8749ec7b004SRick Macklem 	*tl = txdr_unsigned(cnt);
8759ec7b004SRick Macklem 	if (m3) {
8769f6624d3SRick Macklem 		nd->nd_mb->m_next = m3;
8779ec7b004SRick Macklem 		nd->nd_mb = m2;
8789f6624d3SRick Macklem 		nd->nd_bpos = mtod(m2, caddr_t) + m2->m_len;
8799ec7b004SRick Macklem 	}
880a9285ae5SZack Kirsch 
881a9285ae5SZack Kirsch out:
882a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
8839ec7b004SRick Macklem 	return (0);
8849ec7b004SRick Macklem nfsmout:
8859ec7b004SRick Macklem 	vput(vp);
886a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
8879ec7b004SRick Macklem 	return (error);
8889ec7b004SRick Macklem }
8899ec7b004SRick Macklem 
8909ec7b004SRick Macklem /*
8919ec7b004SRick Macklem  * nfs write service
8929ec7b004SRick Macklem  */
893b9cc3262SRyan Moeller int
8949ec7b004SRick Macklem nfsrvd_write(struct nfsrv_descript *nd, __unused int isdgram,
895af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
8969ec7b004SRick Macklem {
8979ec7b004SRick Macklem 	u_int32_t *tl;
8989ec7b004SRick Macklem 	struct nfsvattr nva, forat;
8999ec7b004SRick Macklem 	int aftat_ret = 1, retlen, len, error = 0, forat_ret = 1;
90090d2dfabSRick Macklem 	int gotproxystateid, stable = NFSWRITE_FILESYNC;
9019ec7b004SRick Macklem 	off_t off;
9029ec7b004SRick Macklem 	struct nfsstate st, *stp = &st;
9039ec7b004SRick Macklem 	struct nfslock lo, *lop = &lo;
9049ec7b004SRick Macklem 	nfsv4stateid_t stateid;
9059ec7b004SRick Macklem 	nfsquad_t clientid;
90690d2dfabSRick Macklem 	nfsattrbit_t attrbits;
907af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
9089ec7b004SRick Macklem 
9099ec7b004SRick Macklem 	if (nd->nd_repstat) {
9109ec7b004SRick Macklem 		nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
911a9285ae5SZack Kirsch 		goto out;
9129ec7b004SRick Macklem 	}
91390d2dfabSRick Macklem 	gotproxystateid = 0;
9149ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
9159ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
9169ec7b004SRick Macklem 		off = (off_t)fxdr_unsigned(u_int32_t, *++tl);
9179ec7b004SRick Macklem 		tl += 2;
9189ec7b004SRick Macklem 		retlen = len = fxdr_unsigned(int32_t, *tl);
9199ec7b004SRick Macklem 	} else if (nd->nd_flag & ND_NFSV3) {
9209ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
9219ec7b004SRick Macklem 		off = fxdr_hyper(tl);
9229ec7b004SRick Macklem 		tl += 3;
9239ec7b004SRick Macklem 		stable = fxdr_unsigned(int, *tl++);
9249ec7b004SRick Macklem 		retlen = len = fxdr_unsigned(int32_t, *tl);
9259ec7b004SRick Macklem 	} else {
9269ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 4 * NFSX_UNSIGNED);
9279ec7b004SRick Macklem 		stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
9289ec7b004SRick Macklem 		lop->lo_flags = NFSLCK_WRITE;
9299ec7b004SRick Macklem 		stp->ls_ownerlen = 0;
9309ec7b004SRick Macklem 		stp->ls_op = NULL;
9319ec7b004SRick Macklem 		stp->ls_uid = nd->nd_cred->cr_uid;
9329ec7b004SRick Macklem 		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
9339ec7b004SRick Macklem 		clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
9349ec7b004SRick Macklem 		clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
935c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
936c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
937c59e4cc3SRick Macklem 				clientid.qval = nd->nd_clientid.qval;
938c59e4cc3SRick Macklem 			else if (nd->nd_clientid.qval != clientid.qval)
939c59e4cc3SRick Macklem 				printf("EEK2 multiple clids\n");
9409ec7b004SRick Macklem 		} else {
941c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
942c59e4cc3SRick Macklem 				printf("EEK! no clientid from session\n");
9439ec7b004SRick Macklem 			nd->nd_flag |= ND_IMPLIEDCLID;
9449ec7b004SRick Macklem 			nd->nd_clientid.qval = clientid.qval;
9459ec7b004SRick Macklem 		}
9469ec7b004SRick Macklem 		stp->ls_stateid.other[2] = *tl++;
94790d2dfabSRick Macklem 		/*
94890d2dfabSRick Macklem 		 * Don't allow the client to use a special stateid for a DS op.
94990d2dfabSRick Macklem 		 */
95090d2dfabSRick Macklem 		if ((nd->nd_flag & ND_DSSERVER) != 0 &&
95190d2dfabSRick Macklem 		    ((stp->ls_stateid.other[0] == 0x0 &&
95290d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0x0 &&
95390d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0x0) ||
95490d2dfabSRick Macklem 		    (stp->ls_stateid.other[0] == 0xffffffff &&
95590d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0xffffffff &&
95690d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0xffffffff) ||
95790d2dfabSRick Macklem 		    stp->ls_stateid.seqid != 0))
95890d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
95990d2dfabSRick Macklem 		/* However, allow the proxy stateid. */
96090d2dfabSRick Macklem 		if (stp->ls_stateid.seqid == 0xffffffff &&
96190d2dfabSRick Macklem 		    stp->ls_stateid.other[0] == 0x55555555 &&
96290d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0x55555555 &&
96390d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0x55555555)
96490d2dfabSRick Macklem 			gotproxystateid = 1;
9659ec7b004SRick Macklem 		off = fxdr_hyper(tl);
9669ec7b004SRick Macklem 		lop->lo_first = off;
9679ec7b004SRick Macklem 		tl += 2;
9689ec7b004SRick Macklem 		stable = fxdr_unsigned(int, *tl++);
9699ec7b004SRick Macklem 		retlen = len = fxdr_unsigned(int32_t, *tl);
9709ec7b004SRick Macklem 		lop->lo_end = off + len;
9719ec7b004SRick Macklem 		/*
9729ec7b004SRick Macklem 		 * Paranoia, just in case it wraps around, which shouldn't
9739ec7b004SRick Macklem 		 * ever happen anyhow.
9749ec7b004SRick Macklem 		 */
9759ec7b004SRick Macklem 		if (lop->lo_end < lop->lo_first)
9769ec7b004SRick Macklem 			lop->lo_end = NFS64BITSSET;
9779ec7b004SRick Macklem 	}
9789ec7b004SRick Macklem 
97966e80f77SRick Macklem 	if (retlen > NFS_SRVMAXIO || retlen < 0)
9809ec7b004SRick Macklem 		nd->nd_repstat = EIO;
9819ec7b004SRick Macklem 	if (vnode_vtype(vp) != VREG && !nd->nd_repstat) {
9829ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
9839ec7b004SRick Macklem 			nd->nd_repstat = EINVAL;
9849ec7b004SRick Macklem 		else
9859ec7b004SRick Macklem 			nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
9869ec7b004SRick Macklem 			    EINVAL;
9879ec7b004SRick Macklem 	}
98890d2dfabSRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
98990d2dfabSRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
99090d2dfabSRick Macklem 	forat_ret = nfsvno_getattr(vp, &forat, nd, p, 1, &attrbits);
9919ec7b004SRick Macklem 	if (!nd->nd_repstat)
9929ec7b004SRick Macklem 		nd->nd_repstat = forat_ret;
9939ec7b004SRick Macklem 	if (!nd->nd_repstat &&
9949ec7b004SRick Macklem 	    (forat.na_uid != nd->nd_cred->cr_uid ||
9959ec7b004SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
9968da45f2cSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
9979ec7b004SRick Macklem 		    nd->nd_cred, exp, p,
9988da45f2cSRick Macklem 		    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
99990d2dfabSRick Macklem 	/*
100090d2dfabSRick Macklem 	 * DS reads are marked by ND_DSSERVER or use the proxy special
100190d2dfabSRick Macklem 	 * stateid.
100290d2dfabSRick Macklem 	 */
100390d2dfabSRick Macklem 	if (nd->nd_repstat == 0 && (nd->nd_flag & (ND_NFSV4 | ND_DSSERVER)) ==
100490d2dfabSRick Macklem 	    ND_NFSV4 && gotproxystateid == 0)
10059ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
10069ec7b004SRick Macklem 		    &stateid, exp, nd, p);
10079ec7b004SRick Macklem 	if (nd->nd_repstat) {
10089ec7b004SRick Macklem 		vput(vp);
10099ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
10109ec7b004SRick Macklem 			nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
1011a9285ae5SZack Kirsch 		goto out;
10129ec7b004SRick Macklem 	}
10139ec7b004SRick Macklem 
10149ec7b004SRick Macklem 	/*
10159ec7b004SRick Macklem 	 * For NFS Version 2, it is not obvious what a write of zero length
10169ec7b004SRick Macklem 	 * should do, but I might as well be consistent with Version 3,
10179ec7b004SRick Macklem 	 * which is to return ok so long as there are no permission problems.
10189ec7b004SRick Macklem 	 */
10199ec7b004SRick Macklem 	if (retlen > 0) {
1020c057a378SRick Macklem 		nd->nd_repstat = nfsvno_write(vp, off, retlen, &stable,
10219ec7b004SRick Macklem 		    nd->nd_md, nd->nd_dpos, nd->nd_cred, p);
10229ec7b004SRick Macklem 		error = nfsm_advance(nd, NFSM_RNDUP(retlen), -1);
10239ec7b004SRick Macklem 		if (error)
1024ce8d06feSRick Macklem 			goto nfsmout;
10259ec7b004SRick Macklem 	}
10269ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4)
10279ec7b004SRick Macklem 		aftat_ret = 0;
10289ec7b004SRick Macklem 	else
102990d2dfabSRick Macklem 		aftat_ret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
10309ec7b004SRick Macklem 	vput(vp);
10319ec7b004SRick Macklem 	if (!nd->nd_repstat)
10329ec7b004SRick Macklem 		nd->nd_repstat = aftat_ret;
10339ec7b004SRick Macklem 	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
10349ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
10359ec7b004SRick Macklem 			nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
10369ec7b004SRick Macklem 		if (nd->nd_repstat)
1037a9285ae5SZack Kirsch 			goto out;
10389ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
10399ec7b004SRick Macklem 		*tl++ = txdr_unsigned(retlen);
1040e4558aacSXin LI 		/*
1041e4558aacSXin LI 		 * If nfs_async is set, then pretend the write was FILESYNC.
1042e4558aacSXin LI 		 * Warning: Doing this violates RFC1813 and runs a risk
1043e4558aacSXin LI 		 * of data written by a client being lost when the server
1044e4558aacSXin LI 		 * crashes/reboots.
1045e4558aacSXin LI 		 */
1046e4558aacSXin LI 		if (stable == NFSWRITE_UNSTABLE && nfs_async == 0)
10479ec7b004SRick Macklem 			*tl++ = txdr_unsigned(stable);
10489ec7b004SRick Macklem 		else
10499ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSWRITE_FILESYNC);
10509ec7b004SRick Macklem 		/*
10519ec7b004SRick Macklem 		 * Actually, there is no need to txdr these fields,
10529ec7b004SRick Macklem 		 * but it may make the values more human readable,
10539ec7b004SRick Macklem 		 * for debugging purposes.
10549ec7b004SRick Macklem 		 */
10559ec7b004SRick Macklem 		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
10569ec7b004SRick Macklem 		*tl = txdr_unsigned(nfsboottime.tv_usec);
10579ec7b004SRick Macklem 	} else if (!nd->nd_repstat)
10589ec7b004SRick Macklem 		nfsrv_fillattr(nd, &nva);
1059a9285ae5SZack Kirsch 
1060a9285ae5SZack Kirsch out:
1061a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
10629ec7b004SRick Macklem 	return (0);
10639ec7b004SRick Macklem nfsmout:
10649ec7b004SRick Macklem 	vput(vp);
1065a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
10669ec7b004SRick Macklem 	return (error);
10679ec7b004SRick Macklem }
10689ec7b004SRick Macklem 
10699ec7b004SRick Macklem /*
10709ec7b004SRick Macklem  * nfs create service (creates regular files for V2 and V3. Spec. files for V2.)
10719ec7b004SRick Macklem  * now does a truncate to 0 length via. setattr if it already exists
10729ec7b004SRick Macklem  * The core creation routine has been extracted out into nfsrv_creatsub(),
10739ec7b004SRick Macklem  * so it can also be used by nfsrv_open() for V4.
10749ec7b004SRick Macklem  */
1075b9cc3262SRyan Moeller int
10769ec7b004SRick Macklem nfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram,
1077af444b18SEdward Tomasz Napierala     vnode_t dp, struct nfsexstuff *exp)
10789ec7b004SRick Macklem {
10799ec7b004SRick Macklem 	struct nfsvattr nva, dirfor, diraft;
10809ec7b004SRick Macklem 	struct nfsv2_sattr *sp;
10819ec7b004SRick Macklem 	struct nameidata named;
10829ec7b004SRick Macklem 	u_int32_t *tl;
10839ec7b004SRick Macklem 	int error = 0, tsize, dirfor_ret = 1, diraft_ret = 1;
10849ec7b004SRick Macklem 	int how = NFSCREATE_UNCHECKED, exclusive_flag = 0;
10859ec7b004SRick Macklem 	NFSDEV_T rdev = 0;
10869ec7b004SRick Macklem 	vnode_t vp = NULL, dirp = NULL;
10879ec7b004SRick Macklem 	fhandle_t fh;
10889ec7b004SRick Macklem 	char *bufp;
10899ec7b004SRick Macklem 	u_long *hashp;
10909ec7b004SRick Macklem 	enum vtype vtyp;
1091086f6e0cSRick Macklem 	int32_t cverf[2], tverf[2] = { 0, 0 };
1092af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
10939ec7b004SRick Macklem 
10949ec7b004SRick Macklem 	if (nd->nd_repstat) {
10959ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1096a9285ae5SZack Kirsch 		goto out;
10979ec7b004SRick Macklem 	}
10989ec7b004SRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
10996c21f6edSKonstantin Belousov 	    LOCKPARENT | LOCKLEAF | SAVESTART | NOCACHE);
11009ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
11019ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1102a9285ae5SZack Kirsch 	if (error)
1103a9285ae5SZack Kirsch 		goto nfsmout;
11049ec7b004SRick Macklem 	if (!nd->nd_repstat) {
11059ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva);
11069ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV2) {
11079ec7b004SRick Macklem 			NFSM_DISSECT(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
11089ec7b004SRick Macklem 			vtyp = IFTOVT(fxdr_unsigned(u_int32_t, sp->sa_mode));
11099ec7b004SRick Macklem 			if (vtyp == VNON)
11109ec7b004SRick Macklem 				vtyp = VREG;
11119ec7b004SRick Macklem 			NFSVNO_SETATTRVAL(&nva, type, vtyp);
11129ec7b004SRick Macklem 			NFSVNO_SETATTRVAL(&nva, mode,
11139ec7b004SRick Macklem 			    nfstov_mode(sp->sa_mode));
11149ec7b004SRick Macklem 			switch (nva.na_type) {
11159ec7b004SRick Macklem 			case VREG:
11169ec7b004SRick Macklem 				tsize = fxdr_unsigned(int32_t, sp->sa_size);
11179ec7b004SRick Macklem 				if (tsize != -1)
11189ec7b004SRick Macklem 					NFSVNO_SETATTRVAL(&nva, size,
11199ec7b004SRick Macklem 					    (u_quad_t)tsize);
11209ec7b004SRick Macklem 				break;
11219ec7b004SRick Macklem 			case VCHR:
11229ec7b004SRick Macklem 			case VBLK:
11239ec7b004SRick Macklem 			case VFIFO:
11249ec7b004SRick Macklem 				rdev = fxdr_unsigned(NFSDEV_T, sp->sa_size);
11259ec7b004SRick Macklem 				break;
11269ec7b004SRick Macklem 			default:
11279ec7b004SRick Macklem 				break;
112874b8d63dSPedro F. Giffuni 			}
11299ec7b004SRick Macklem 		} else {
11309ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
11319ec7b004SRick Macklem 			how = fxdr_unsigned(int, *tl);
11329ec7b004SRick Macklem 			switch (how) {
11339ec7b004SRick Macklem 			case NFSCREATE_GUARDED:
11349ec7b004SRick Macklem 			case NFSCREATE_UNCHECKED:
1135d8a5961fSMarcelo Araujo 				error = nfsrv_sattr(nd, NULL, &nva, NULL, NULL, p);
11369ec7b004SRick Macklem 				if (error)
11379ec7b004SRick Macklem 					goto nfsmout;
11389ec7b004SRick Macklem 				break;
11399ec7b004SRick Macklem 			case NFSCREATE_EXCLUSIVE:
1140086f6e0cSRick Macklem 				NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
1141086f6e0cSRick Macklem 				cverf[0] = *tl++;
1142086f6e0cSRick Macklem 				cverf[1] = *tl;
11439ec7b004SRick Macklem 				exclusive_flag = 1;
11449ec7b004SRick Macklem 				break;
114574b8d63dSPedro F. Giffuni 			}
11469ec7b004SRick Macklem 			NFSVNO_SETATTRVAL(&nva, type, VREG);
11479ec7b004SRick Macklem 		}
11489ec7b004SRick Macklem 	}
11499ec7b004SRick Macklem 	if (nd->nd_repstat) {
11509ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
11519ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
115290d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dp, &dirfor, nd, p, 1,
115390d2dfabSRick Macklem 			    NULL);
11549ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
11559ec7b004SRick Macklem 			    &diraft);
11569ec7b004SRick Macklem 		}
11579ec7b004SRick Macklem 		vput(dp);
1158a9285ae5SZack Kirsch 		goto out;
11599ec7b004SRick Macklem 	}
11609ec7b004SRick Macklem 
11619ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
11629ec7b004SRick Macklem 	if (dirp) {
11639ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV2) {
11649ec7b004SRick Macklem 			vrele(dirp);
11659ec7b004SRick Macklem 			dirp = NULL;
11669ec7b004SRick Macklem 		} else {
116790d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
116890d2dfabSRick Macklem 			    NULL);
11699ec7b004SRick Macklem 		}
11709ec7b004SRick Macklem 	}
11719ec7b004SRick Macklem 	if (nd->nd_repstat) {
11729ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
11739ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
11749ec7b004SRick Macklem 			    &diraft);
11759ec7b004SRick Macklem 		if (dirp)
11769ec7b004SRick Macklem 			vrele(dirp);
1177a9285ae5SZack Kirsch 		goto out;
11789ec7b004SRick Macklem 	}
11799ec7b004SRick Macklem 
11809ec7b004SRick Macklem 	if (!(nd->nd_flag & ND_NFSV2)) {
11819ec7b004SRick Macklem 		switch (how) {
11829ec7b004SRick Macklem 		case NFSCREATE_GUARDED:
11839ec7b004SRick Macklem 			if (named.ni_vp)
11849ec7b004SRick Macklem 				nd->nd_repstat = EEXIST;
11859ec7b004SRick Macklem 			break;
11869ec7b004SRick Macklem 		case NFSCREATE_UNCHECKED:
11879ec7b004SRick Macklem 			break;
11889ec7b004SRick Macklem 		case NFSCREATE_EXCLUSIVE:
11899ec7b004SRick Macklem 			if (named.ni_vp == NULL)
11909ec7b004SRick Macklem 				NFSVNO_SETATTRVAL(&nva, mode, 0);
11919ec7b004SRick Macklem 			break;
119274b8d63dSPedro F. Giffuni 		}
11939ec7b004SRick Macklem 	}
11949ec7b004SRick Macklem 
11959ec7b004SRick Macklem 	/*
11969ec7b004SRick Macklem 	 * Iff doesn't exist, create it
11979ec7b004SRick Macklem 	 * otherwise just truncate to 0 length
11989ec7b004SRick Macklem 	 *   should I set the mode too ?
11999ec7b004SRick Macklem 	 */
12009ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_createsub(nd, &named, &vp, &nva,
1201127152feSEdward Tomasz Napierala 	    &exclusive_flag, cverf, rdev, exp);
12029ec7b004SRick Macklem 
12039ec7b004SRick Macklem 	if (!nd->nd_repstat) {
12049ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
12059ec7b004SRick Macklem 		if (!nd->nd_repstat)
120690d2dfabSRick Macklem 			nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1,
120790d2dfabSRick Macklem 			    NULL);
12089ec7b004SRick Macklem 		vput(vp);
1209086f6e0cSRick Macklem 		if (!nd->nd_repstat) {
1210086f6e0cSRick Macklem 			tverf[0] = nva.na_atime.tv_sec;
1211086f6e0cSRick Macklem 			tverf[1] = nva.na_atime.tv_nsec;
1212086f6e0cSRick Macklem 		}
12139ec7b004SRick Macklem 	}
12149ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
12159ec7b004SRick Macklem 		if (!nd->nd_repstat) {
12169ec7b004SRick Macklem 			(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
12179ec7b004SRick Macklem 			nfsrv_fillattr(nd, &nva);
12189ec7b004SRick Macklem 		}
12199ec7b004SRick Macklem 	} else {
1220086f6e0cSRick Macklem 		if (exclusive_flag && !nd->nd_repstat && (cverf[0] != tverf[0]
1221086f6e0cSRick Macklem 		    || cverf[1] != tverf[1]))
12229ec7b004SRick Macklem 			nd->nd_repstat = EEXIST;
122390d2dfabSRick Macklem 		diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
12249ec7b004SRick Macklem 		vrele(dirp);
12259ec7b004SRick Macklem 		if (!nd->nd_repstat) {
12269ec7b004SRick Macklem 			(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 1);
12279ec7b004SRick Macklem 			nfsrv_postopattr(nd, 0, &nva);
12289ec7b004SRick Macklem 		}
12299ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
12309ec7b004SRick Macklem 	}
1231a9285ae5SZack Kirsch 
1232a9285ae5SZack Kirsch out:
1233a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
12349ec7b004SRick Macklem 	return (0);
12359ec7b004SRick Macklem nfsmout:
12369ec7b004SRick Macklem 	vput(dp);
12379ec7b004SRick Macklem 	nfsvno_relpathbuf(&named);
1238a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
12399ec7b004SRick Macklem 	return (error);
12409ec7b004SRick Macklem }
12419ec7b004SRick Macklem 
12429ec7b004SRick Macklem /*
12439ec7b004SRick Macklem  * nfs v3 mknod service (and v4 create)
12449ec7b004SRick Macklem  */
1245b9cc3262SRyan Moeller int
12469ec7b004SRick Macklem nfsrvd_mknod(struct nfsrv_descript *nd, __unused int isdgram,
1247af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
12489ec7b004SRick Macklem {
12499ec7b004SRick Macklem 	struct nfsvattr nva, dirfor, diraft;
12509ec7b004SRick Macklem 	u_int32_t *tl;
12519ec7b004SRick Macklem 	struct nameidata named;
12529ec7b004SRick Macklem 	int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
12539ec7b004SRick Macklem 	u_int32_t major, minor;
12549ec7b004SRick Macklem 	enum vtype vtyp = VNON;
12559ec7b004SRick Macklem 	nfstype nfs4type = NFNON;
12569ec7b004SRick Macklem 	vnode_t vp, dirp = NULL;
12579ec7b004SRick Macklem 	nfsattrbit_t attrbits;
12589ec7b004SRick Macklem 	char *bufp = NULL, *pathcp = NULL;
12599ec7b004SRick Macklem 	u_long *hashp, cnflags;
12609ec7b004SRick Macklem 	NFSACL_T *aclp = NULL;
1261af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
12629ec7b004SRick Macklem 
12639ec7b004SRick Macklem 	NFSVNO_ATTRINIT(&nva);
12649ec7b004SRick Macklem 	cnflags = (LOCKPARENT | SAVESTART);
12659ec7b004SRick Macklem 	if (nd->nd_repstat) {
12669ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1267a9285ae5SZack Kirsch 		goto out;
12689ec7b004SRick Macklem 	}
12699ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
1270c3e22f83SRick Macklem 	aclp = acl_alloc(M_WAITOK);
12719ec7b004SRick Macklem 	aclp->acl_cnt = 0;
12729ec7b004SRick Macklem #endif
12739ec7b004SRick Macklem 
12749ec7b004SRick Macklem 	/*
12759ec7b004SRick Macklem 	 * For V4, the creation stuff is here, Yuck!
12769ec7b004SRick Macklem 	 */
12779ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
12789ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
12799ec7b004SRick Macklem 		vtyp = nfsv34tov_type(*tl);
12809ec7b004SRick Macklem 		nfs4type = fxdr_unsigned(nfstype, *tl);
12819ec7b004SRick Macklem 		switch (nfs4type) {
12829ec7b004SRick Macklem 		case NFLNK:
12839ec7b004SRick Macklem 			error = nfsvno_getsymlink(nd, &nva, p, &pathcp,
12849ec7b004SRick Macklem 			    &pathlen);
1285a9285ae5SZack Kirsch 			if (error)
1286a9285ae5SZack Kirsch 				goto nfsmout;
12879ec7b004SRick Macklem 			break;
12889ec7b004SRick Macklem 		case NFCHR:
12899ec7b004SRick Macklem 		case NFBLK:
12909ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
12919ec7b004SRick Macklem 			major = fxdr_unsigned(u_int32_t, *tl++);
12929ec7b004SRick Macklem 			minor = fxdr_unsigned(u_int32_t, *tl);
12939ec7b004SRick Macklem 			nva.na_rdev = NFSMAKEDEV(major, minor);
12949ec7b004SRick Macklem 			break;
12959ec7b004SRick Macklem 		case NFSOCK:
12969ec7b004SRick Macklem 		case NFFIFO:
12979ec7b004SRick Macklem 			break;
12989ec7b004SRick Macklem 		case NFDIR:
1299f61786cbSRick Macklem 			cnflags = (LOCKPARENT | SAVENAME);
13009ec7b004SRick Macklem 			break;
13019ec7b004SRick Macklem 		default:
13029ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_BADTYPE;
13039ec7b004SRick Macklem 			vrele(dp);
13049ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
13059ec7b004SRick Macklem 			acl_free(aclp);
13069ec7b004SRick Macklem #endif
1307a9285ae5SZack Kirsch 			goto out;
1308a9285ae5SZack Kirsch 		}
13099ec7b004SRick Macklem 	}
13106c21f6edSKonstantin Belousov 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, cnflags | NOCACHE);
13119ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
13129ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1313a9285ae5SZack Kirsch 	if (error)
1314a9285ae5SZack Kirsch 		goto nfsmout;
13159ec7b004SRick Macklem 	if (!nd->nd_repstat) {
13169ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
13179ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
13189ec7b004SRick Macklem 			vtyp = nfsv34tov_type(*tl);
13199ec7b004SRick Macklem 		}
1320d8a5961fSMarcelo Araujo 		error = nfsrv_sattr(nd, NULL, &nva, &attrbits, aclp, p);
1321a9285ae5SZack Kirsch 		if (error)
1322a9285ae5SZack Kirsch 			goto nfsmout;
13239ec7b004SRick Macklem 		nva.na_type = vtyp;
13249ec7b004SRick Macklem 		if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV3) &&
13259ec7b004SRick Macklem 		    (vtyp == VCHR || vtyp == VBLK)) {
13269ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
13279ec7b004SRick Macklem 			major = fxdr_unsigned(u_int32_t, *tl++);
13289ec7b004SRick Macklem 			minor = fxdr_unsigned(u_int32_t, *tl);
13299ec7b004SRick Macklem 			nva.na_rdev = NFSMAKEDEV(major, minor);
13309ec7b004SRick Macklem 		}
13319ec7b004SRick Macklem 	}
13329ec7b004SRick Macklem 
133390d2dfabSRick Macklem 	dirfor_ret = nfsvno_getattr(dp, &dirfor, nd, p, 0, NULL);
13349ec7b004SRick Macklem 	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
13359ec7b004SRick Macklem 		if (!dirfor_ret && NFSVNO_ISSETGID(&nva) &&
13369ec7b004SRick Macklem 		    dirfor.na_gid == nva.na_gid)
13379ec7b004SRick Macklem 			NFSVNO_UNSET(&nva, gid);
13389ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
13399ec7b004SRick Macklem 	}
13409ec7b004SRick Macklem 	if (nd->nd_repstat) {
13419ec7b004SRick Macklem 		vrele(dp);
13429ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
13439ec7b004SRick Macklem 		acl_free(aclp);
13449ec7b004SRick Macklem #endif
13459ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
13469ec7b004SRick Macklem 		if (pathcp)
1347222daa42SConrad Meyer 			free(pathcp, M_TEMP);
13489ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
13499ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
13509ec7b004SRick Macklem 			    &diraft);
1351a9285ae5SZack Kirsch 		goto out;
13529ec7b004SRick Macklem 	}
13539ec7b004SRick Macklem 
13549ec7b004SRick Macklem 	/*
13559ec7b004SRick Macklem 	 * Yuck! For V4, mkdir and link are here and some V4 clients don't fill
13569ec7b004SRick Macklem 	 * in va_mode, so we'll have to set a default here.
13579ec7b004SRick Macklem 	 */
13589ec7b004SRick Macklem 	if (NFSVNO_NOTSETMODE(&nva)) {
13599ec7b004SRick Macklem 		if (vtyp == VLNK)
13609ec7b004SRick Macklem 			nva.na_mode = 0755;
13619ec7b004SRick Macklem 		else
13629ec7b004SRick Macklem 			nva.na_mode = 0400;
13639ec7b004SRick Macklem 	}
13649ec7b004SRick Macklem 
13659ec7b004SRick Macklem 	if (vtyp == VDIR)
13669ec7b004SRick Macklem 		named.ni_cnd.cn_flags |= WILLBEDIR;
13679ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
13689ec7b004SRick Macklem 	if (nd->nd_repstat) {
13699ec7b004SRick Macklem 		if (dirp) {
13709ec7b004SRick Macklem 			if (nd->nd_flag & ND_NFSV3)
137190d2dfabSRick Macklem 				dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd,
137290d2dfabSRick Macklem 				    p, 0, NULL);
13739ec7b004SRick Macklem 			vrele(dirp);
13749ec7b004SRick Macklem 		}
13759ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
13769ec7b004SRick Macklem 		acl_free(aclp);
13779ec7b004SRick Macklem #endif
13789ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
13799ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
13809ec7b004SRick Macklem 			    &diraft);
1381a9285ae5SZack Kirsch 		goto out;
13829ec7b004SRick Macklem 	}
13839ec7b004SRick Macklem 	if (dirp)
138490d2dfabSRick Macklem 		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL);
13859ec7b004SRick Macklem 
13869ec7b004SRick Macklem 	if ((nd->nd_flag & ND_NFSV4) && (vtyp == VDIR || vtyp == VLNK)) {
13879ec7b004SRick Macklem 		if (vtyp == VDIR) {
13889ec7b004SRick Macklem 			nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp,
13899ec7b004SRick Macklem 			    &dirfor, &diraft, &diraft_ret, &attrbits, aclp, p,
13909ec7b004SRick Macklem 			    exp);
13919ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
13929ec7b004SRick Macklem 			acl_free(aclp);
13939ec7b004SRick Macklem #endif
1394a9285ae5SZack Kirsch 			goto out;
13959ec7b004SRick Macklem 		} else if (vtyp == VLNK) {
13969ec7b004SRick Macklem 			nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
13979ec7b004SRick Macklem 			    &dirfor, &diraft, &diraft_ret, &attrbits,
13989ec7b004SRick Macklem 			    aclp, p, exp, pathcp, pathlen);
13999ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
14009ec7b004SRick Macklem 			acl_free(aclp);
14019ec7b004SRick Macklem #endif
1402222daa42SConrad Meyer 			free(pathcp, M_TEMP);
1403a9285ae5SZack Kirsch 			goto out;
14049ec7b004SRick Macklem 		}
14059ec7b004SRick Macklem 	}
14069ec7b004SRick Macklem 
14079ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_mknod(&named, &nva, nd->nd_cred, p);
14089ec7b004SRick Macklem 	if (!nd->nd_repstat) {
14099ec7b004SRick Macklem 		vp = named.ni_vp;
14109ec7b004SRick Macklem 		nfsrv_fixattr(nd, vp, &nva, aclp, p, &attrbits, exp);
14119ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
14129ec7b004SRick Macklem 		if ((nd->nd_flag & ND_NFSV3) && !nd->nd_repstat)
141390d2dfabSRick Macklem 			nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1,
141490d2dfabSRick Macklem 			    NULL);
141581f78d99SRick Macklem 		if (vpp != NULL && nd->nd_repstat == 0) {
1416b249ce48SMateusz Guzik 			NFSVOPUNLOCK(vp);
14179ec7b004SRick Macklem 			*vpp = vp;
141881f78d99SRick Macklem 		} else
14199ec7b004SRick Macklem 			vput(vp);
14209ec7b004SRick Macklem 	}
14219ec7b004SRick Macklem 
142290d2dfabSRick Macklem 	diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
14239ec7b004SRick Macklem 	vrele(dirp);
14249ec7b004SRick Macklem 	if (!nd->nd_repstat) {
14259ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
14269ec7b004SRick Macklem 			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
14279ec7b004SRick Macklem 			nfsrv_postopattr(nd, 0, &nva);
14289ec7b004SRick Macklem 		} else {
14299ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
14309ec7b004SRick Macklem 			*tl++ = newnfs_false;
14319ec7b004SRick Macklem 			txdr_hyper(dirfor.na_filerev, tl);
14329ec7b004SRick Macklem 			tl += 2;
14339ec7b004SRick Macklem 			txdr_hyper(diraft.na_filerev, tl);
14349ec7b004SRick Macklem 			(void) nfsrv_putattrbit(nd, &attrbits);
14359ec7b004SRick Macklem 		}
14369ec7b004SRick Macklem 	}
14379ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
14389ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
14399ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
14409ec7b004SRick Macklem 	acl_free(aclp);
14419ec7b004SRick Macklem #endif
1442a9285ae5SZack Kirsch 
1443a9285ae5SZack Kirsch out:
1444a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
14459ec7b004SRick Macklem 	return (0);
14469ec7b004SRick Macklem nfsmout:
14479ec7b004SRick Macklem 	vrele(dp);
14489ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
14499ec7b004SRick Macklem 	acl_free(aclp);
14509ec7b004SRick Macklem #endif
14519ec7b004SRick Macklem 	if (bufp)
14529ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
14539ec7b004SRick Macklem 	if (pathcp)
1454222daa42SConrad Meyer 		free(pathcp, M_TEMP);
1455a9285ae5SZack Kirsch 
1456a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
14579ec7b004SRick Macklem 	return (error);
14589ec7b004SRick Macklem }
14599ec7b004SRick Macklem 
14609ec7b004SRick Macklem /*
14619ec7b004SRick Macklem  * nfs remove service
14629ec7b004SRick Macklem  */
1463b9cc3262SRyan Moeller int
14649ec7b004SRick Macklem nfsrvd_remove(struct nfsrv_descript *nd, __unused int isdgram,
1465af444b18SEdward Tomasz Napierala     vnode_t dp, struct nfsexstuff *exp)
14669ec7b004SRick Macklem {
14679ec7b004SRick Macklem 	struct nameidata named;
14689ec7b004SRick Macklem 	u_int32_t *tl;
1469a9285ae5SZack Kirsch 	int error = 0, dirfor_ret = 1, diraft_ret = 1;
14709ec7b004SRick Macklem 	vnode_t dirp = NULL;
14719ec7b004SRick Macklem 	struct nfsvattr dirfor, diraft;
14729ec7b004SRick Macklem 	char *bufp;
14739ec7b004SRick Macklem 	u_long *hashp;
1474af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
14759ec7b004SRick Macklem 
14769ec7b004SRick Macklem 	if (nd->nd_repstat) {
14779ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1478a9285ae5SZack Kirsch 		goto out;
14799ec7b004SRick Macklem 	}
14809ec7b004SRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, DELETE,
14819ec7b004SRick Macklem 	    LOCKPARENT | LOCKLEAF);
14829ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
14839ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
14849ec7b004SRick Macklem 	if (error) {
14859ec7b004SRick Macklem 		vput(dp);
14869ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
1487a9285ae5SZack Kirsch 		goto out;
14889ec7b004SRick Macklem 	}
14899ec7b004SRick Macklem 	if (!nd->nd_repstat) {
14909ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
14919ec7b004SRick Macklem 	} else {
14929ec7b004SRick Macklem 		vput(dp);
14939ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
14949ec7b004SRick Macklem 	}
14959ec7b004SRick Macklem 	if (dirp) {
14969ec7b004SRick Macklem 		if (!(nd->nd_flag & ND_NFSV2)) {
149790d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
149890d2dfabSRick Macklem 			    NULL);
14999ec7b004SRick Macklem 		} else {
15009ec7b004SRick Macklem 			vrele(dirp);
15019ec7b004SRick Macklem 			dirp = NULL;
15029ec7b004SRick Macklem 		}
15039ec7b004SRick Macklem 	}
15049ec7b004SRick Macklem 	if (!nd->nd_repstat) {
15059ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV4) {
15069ec7b004SRick Macklem 			if (vnode_vtype(named.ni_vp) == VDIR)
15079ec7b004SRick Macklem 				nd->nd_repstat = nfsvno_rmdirsub(&named, 1,
15089ec7b004SRick Macklem 				    nd->nd_cred, p, exp);
15099ec7b004SRick Macklem 			else
15109ec7b004SRick Macklem 				nd->nd_repstat = nfsvno_removesub(&named, 1,
15119ec7b004SRick Macklem 				    nd->nd_cred, p, exp);
15129ec7b004SRick Macklem 		} else if (nd->nd_procnum == NFSPROC_RMDIR) {
15139ec7b004SRick Macklem 			nd->nd_repstat = nfsvno_rmdirsub(&named, 0,
15149ec7b004SRick Macklem 			    nd->nd_cred, p, exp);
15159ec7b004SRick Macklem 		} else {
15169ec7b004SRick Macklem 			nd->nd_repstat = nfsvno_removesub(&named, 0,
15179ec7b004SRick Macklem 			    nd->nd_cred, p, exp);
15189ec7b004SRick Macklem 		}
15199ec7b004SRick Macklem 	}
15209ec7b004SRick Macklem 	if (!(nd->nd_flag & ND_NFSV2)) {
15219ec7b004SRick Macklem 		if (dirp) {
152290d2dfabSRick Macklem 			diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0,
152390d2dfabSRick Macklem 			    NULL);
15249ec7b004SRick Macklem 			vrele(dirp);
15259ec7b004SRick Macklem 		}
15269ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
15279ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
15289ec7b004SRick Macklem 			    &diraft);
15299ec7b004SRick Macklem 		} else if (!nd->nd_repstat) {
15309ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
15319ec7b004SRick Macklem 			*tl++ = newnfs_false;
15329ec7b004SRick Macklem 			txdr_hyper(dirfor.na_filerev, tl);
15339ec7b004SRick Macklem 			tl += 2;
15349ec7b004SRick Macklem 			txdr_hyper(diraft.na_filerev, tl);
15359ec7b004SRick Macklem 		}
15369ec7b004SRick Macklem 	}
1537a9285ae5SZack Kirsch 
1538a9285ae5SZack Kirsch out:
1539a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
1540a9285ae5SZack Kirsch 	return (error);
15419ec7b004SRick Macklem }
15429ec7b004SRick Macklem 
15439ec7b004SRick Macklem /*
15449ec7b004SRick Macklem  * nfs rename service
15459ec7b004SRick Macklem  */
1546b9cc3262SRyan Moeller int
15479ec7b004SRick Macklem nfsrvd_rename(struct nfsrv_descript *nd, int isdgram,
1548af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t todp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
15499ec7b004SRick Macklem {
15509ec7b004SRick Macklem 	u_int32_t *tl;
1551a9285ae5SZack Kirsch 	int error = 0, fdirfor_ret = 1, fdiraft_ret = 1;
15529ec7b004SRick Macklem 	int tdirfor_ret = 1, tdiraft_ret = 1;
15539ec7b004SRick Macklem 	struct nameidata fromnd, tond;
15549ec7b004SRick Macklem 	vnode_t fdirp = NULL, tdirp = NULL, tdp = NULL;
15559ec7b004SRick Macklem 	struct nfsvattr fdirfor, fdiraft, tdirfor, tdiraft;
15569ec7b004SRick Macklem 	struct nfsexstuff tnes;
15579ec7b004SRick Macklem 	struct nfsrvfh tfh;
15589ec7b004SRick Macklem 	char *bufp, *tbufp = NULL;
15599ec7b004SRick Macklem 	u_long *hashp;
15606b3dfc6aSRick Macklem 	fhandle_t fh;
1561af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
15629ec7b004SRick Macklem 
15639ec7b004SRick Macklem 	if (nd->nd_repstat) {
15649ec7b004SRick Macklem 		nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
15659ec7b004SRick Macklem 		nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
1566a9285ae5SZack Kirsch 		goto out;
15679ec7b004SRick Macklem 	}
15689ec7b004SRick Macklem 	if (!(nd->nd_flag & ND_NFSV2))
156990d2dfabSRick Macklem 		fdirfor_ret = nfsvno_getattr(dp, &fdirfor, nd, p, 1, NULL);
15709ec7b004SRick Macklem 	tond.ni_cnd.cn_nameiop = 0;
15719ec7b004SRick Macklem 	tond.ni_startdir = NULL;
15729ec7b004SRick Macklem 	NFSNAMEICNDSET(&fromnd.ni_cnd, nd->nd_cred, DELETE, WANTPARENT | SAVESTART);
15739ec7b004SRick Macklem 	nfsvno_setpathbuf(&fromnd, &bufp, &hashp);
15749ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &fromnd.ni_pathlen);
15759ec7b004SRick Macklem 	if (error) {
15769ec7b004SRick Macklem 		vput(dp);
15779ec7b004SRick Macklem 		if (todp)
15789ec7b004SRick Macklem 			vrele(todp);
15799ec7b004SRick Macklem 		nfsvno_relpathbuf(&fromnd);
1580a9285ae5SZack Kirsch 		goto out;
15819ec7b004SRick Macklem 	}
158225bfde79SXin LI 	/*
158325bfde79SXin LI 	 * Unlock dp in this code section, so it is unlocked before
158425bfde79SXin LI 	 * tdp gets locked. This avoids a potential LOR if tdp is the
158525bfde79SXin LI 	 * parent directory of dp.
158625bfde79SXin LI 	 */
15879ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
15889ec7b004SRick Macklem 		tdp = todp;
15899ec7b004SRick Macklem 		tnes = *toexp;
159025bfde79SXin LI 		if (dp != tdp) {
1591b249ce48SMateusz Guzik 			NFSVOPUNLOCK(dp);
159290d2dfabSRick Macklem 			/* Might lock tdp. */
159390d2dfabSRick Macklem 			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd, p, 0,
159490d2dfabSRick Macklem 			    NULL);
159525bfde79SXin LI 		} else {
159690d2dfabSRick Macklem 			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd, p, 1,
159790d2dfabSRick Macklem 			    NULL);
1598b249ce48SMateusz Guzik 			NFSVOPUNLOCK(dp);
159925bfde79SXin LI 		}
16009ec7b004SRick Macklem 	} else {
16016b3dfc6aSRick Macklem 		tfh.nfsrvfh_len = 0;
16029ec7b004SRick Macklem 		error = nfsrv_mtofh(nd, &tfh);
16036b3dfc6aSRick Macklem 		if (error == 0)
16046b3dfc6aSRick Macklem 			error = nfsvno_getfh(dp, &fh, p);
16059ec7b004SRick Macklem 		if (error) {
16069ec7b004SRick Macklem 			vput(dp);
16079ec7b004SRick Macklem 			/* todp is always NULL except NFSv4 */
16089ec7b004SRick Macklem 			nfsvno_relpathbuf(&fromnd);
1609a9285ae5SZack Kirsch 			goto out;
16109ec7b004SRick Macklem 		}
16116b3dfc6aSRick Macklem 
16126b3dfc6aSRick Macklem 		/* If this is the same file handle, just VREF() the vnode. */
16136b3dfc6aSRick Macklem 		if (tfh.nfsrvfh_len == NFSX_MYFH &&
16146b3dfc6aSRick Macklem 		    !NFSBCMP(tfh.nfsrvfh_data, &fh, NFSX_MYFH)) {
16156b3dfc6aSRick Macklem 			VREF(dp);
16166b3dfc6aSRick Macklem 			tdp = dp;
16176b3dfc6aSRick Macklem 			tnes = *exp;
161890d2dfabSRick Macklem 			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd, p, 1,
161990d2dfabSRick Macklem 			    NULL);
1620b249ce48SMateusz Guzik 			NFSVOPUNLOCK(dp);
16216b3dfc6aSRick Macklem 		} else {
1622b249ce48SMateusz Guzik 			NFSVOPUNLOCK(dp);
16236b3dfc6aSRick Macklem 			nd->nd_cred->cr_uid = nd->nd_saveduid;
16246b3dfc6aSRick Macklem 			nfsd_fhtovp(nd, &tfh, LK_EXCLUSIVE, &tdp, &tnes, NULL,
16255edc9102SEdward Tomasz Napierala 			    0);	/* Locks tdp. */
16266b3dfc6aSRick Macklem 			if (tdp) {
162790d2dfabSRick Macklem 				tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd,
162890d2dfabSRick Macklem 				    p, 1, NULL);
1629b249ce48SMateusz Guzik 				NFSVOPUNLOCK(tdp);
16309ec7b004SRick Macklem 			}
16319ec7b004SRick Macklem 		}
16326b3dfc6aSRick Macklem 	}
16339ec7b004SRick Macklem 	NFSNAMEICNDSET(&tond.ni_cnd, nd->nd_cred, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART);
16349ec7b004SRick Macklem 	nfsvno_setpathbuf(&tond, &tbufp, &hashp);
16359ec7b004SRick Macklem 	if (!nd->nd_repstat) {
16369ec7b004SRick Macklem 		error = nfsrv_parsename(nd, tbufp, hashp, &tond.ni_pathlen);
16379ec7b004SRick Macklem 		if (error) {
16388974bc2fSRick Macklem 			if (tdp)
16399ec7b004SRick Macklem 				vrele(tdp);
164025bfde79SXin LI 			vrele(dp);
16419ec7b004SRick Macklem 			nfsvno_relpathbuf(&fromnd);
16429ec7b004SRick Macklem 			nfsvno_relpathbuf(&tond);
1643a9285ae5SZack Kirsch 			goto out;
16449ec7b004SRick Macklem 		}
16459ec7b004SRick Macklem 	}
16469ec7b004SRick Macklem 	if (nd->nd_repstat) {
16479ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
16489ec7b004SRick Macklem 			nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
16499ec7b004SRick Macklem 			    &fdiraft);
16509ec7b004SRick Macklem 			nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
16519ec7b004SRick Macklem 			    &tdiraft);
16529ec7b004SRick Macklem 		}
16538974bc2fSRick Macklem 		if (tdp)
16549ec7b004SRick Macklem 			vrele(tdp);
165525bfde79SXin LI 		vrele(dp);
16569ec7b004SRick Macklem 		nfsvno_relpathbuf(&fromnd);
16579ec7b004SRick Macklem 		nfsvno_relpathbuf(&tond);
1658a9285ae5SZack Kirsch 		goto out;
16599ec7b004SRick Macklem 	}
16609ec7b004SRick Macklem 
16619ec7b004SRick Macklem 	/*
16629ec7b004SRick Macklem 	 * Done parsing, now down to business.
16639ec7b004SRick Macklem 	 */
166425bfde79SXin LI 	nd->nd_repstat = nfsvno_namei(nd, &fromnd, dp, 0, exp, p, &fdirp);
16659ec7b004SRick Macklem 	if (nd->nd_repstat) {
16669ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
16679ec7b004SRick Macklem 			nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
16689ec7b004SRick Macklem 			    &fdiraft);
16699ec7b004SRick Macklem 			nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
16709ec7b004SRick Macklem 			    &tdiraft);
16719ec7b004SRick Macklem 		}
16729ec7b004SRick Macklem 		if (fdirp)
16739ec7b004SRick Macklem 			vrele(fdirp);
16748974bc2fSRick Macklem 		if (tdp)
16759ec7b004SRick Macklem 			vrele(tdp);
16769ec7b004SRick Macklem 		nfsvno_relpathbuf(&tond);
1677a9285ae5SZack Kirsch 		goto out;
16789ec7b004SRick Macklem 	}
16799ec7b004SRick Macklem 	if (vnode_vtype(fromnd.ni_vp) == VDIR)
16809ec7b004SRick Macklem 		tond.ni_cnd.cn_flags |= WILLBEDIR;
16819ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_namei(nd, &tond, tdp, 0, &tnes, p, &tdirp);
16829ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_rename(&fromnd, &tond, nd->nd_repstat,
16839ec7b004SRick Macklem 	    nd->nd_flag, nd->nd_cred, p);
16849ec7b004SRick Macklem 	if (fdirp)
168590d2dfabSRick Macklem 		fdiraft_ret = nfsvno_getattr(fdirp, &fdiraft, nd, p, 0, NULL);
16869ec7b004SRick Macklem 	if (tdirp)
168790d2dfabSRick Macklem 		tdiraft_ret = nfsvno_getattr(tdirp, &tdiraft, nd, p, 0, NULL);
16889ec7b004SRick Macklem 	if (fdirp)
16899ec7b004SRick Macklem 		vrele(fdirp);
16909ec7b004SRick Macklem 	if (tdirp)
16919ec7b004SRick Macklem 		vrele(tdirp);
16929ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
16939ec7b004SRick Macklem 		nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
16949ec7b004SRick Macklem 		nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
16959ec7b004SRick Macklem 	} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
16969ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 10 * NFSX_UNSIGNED);
16979ec7b004SRick Macklem 		*tl++ = newnfs_false;
16989ec7b004SRick Macklem 		txdr_hyper(fdirfor.na_filerev, tl);
16999ec7b004SRick Macklem 		tl += 2;
17009ec7b004SRick Macklem 		txdr_hyper(fdiraft.na_filerev, tl);
17019ec7b004SRick Macklem 		tl += 2;
17029ec7b004SRick Macklem 		*tl++ = newnfs_false;
17039ec7b004SRick Macklem 		txdr_hyper(tdirfor.na_filerev, tl);
17049ec7b004SRick Macklem 		tl += 2;
17059ec7b004SRick Macklem 		txdr_hyper(tdiraft.na_filerev, tl);
17069ec7b004SRick Macklem 	}
1707a9285ae5SZack Kirsch 
1708a9285ae5SZack Kirsch out:
1709a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
1710a9285ae5SZack Kirsch 	return (error);
17119ec7b004SRick Macklem }
17129ec7b004SRick Macklem 
17139ec7b004SRick Macklem /*
17149ec7b004SRick Macklem  * nfs link service
17159ec7b004SRick Macklem  */
1716b9cc3262SRyan Moeller int
17179ec7b004SRick Macklem nfsrvd_link(struct nfsrv_descript *nd, int isdgram,
1718af444b18SEdward Tomasz Napierala     vnode_t vp, vnode_t tovp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
17199ec7b004SRick Macklem {
17209ec7b004SRick Macklem 	struct nameidata named;
17219ec7b004SRick Macklem 	u_int32_t *tl;
17229ec7b004SRick Macklem 	int error = 0, dirfor_ret = 1, diraft_ret = 1, getret = 1;
17239ec7b004SRick Macklem 	vnode_t dirp = NULL, dp = NULL;
17249ec7b004SRick Macklem 	struct nfsvattr dirfor, diraft, at;
17259ec7b004SRick Macklem 	struct nfsexstuff tnes;
17269ec7b004SRick Macklem 	struct nfsrvfh dfh;
17279ec7b004SRick Macklem 	char *bufp;
17289ec7b004SRick Macklem 	u_long *hashp;
1729af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
17309ec7b004SRick Macklem 
17319ec7b004SRick Macklem 	if (nd->nd_repstat) {
17329ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
17339ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1734a9285ae5SZack Kirsch 		goto out;
17359ec7b004SRick Macklem 	}
1736b249ce48SMateusz Guzik 	NFSVOPUNLOCK(vp);
17379ec7b004SRick Macklem 	if (vnode_vtype(vp) == VDIR) {
17389ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV4)
17399ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_ISDIR;
17409ec7b004SRick Macklem 		else
17419ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
17429ec7b004SRick Macklem 		if (tovp)
17439ec7b004SRick Macklem 			vrele(tovp);
17449ec7b004SRick Macklem 	}
17459ec7b004SRick Macklem 	if (!nd->nd_repstat) {
17469ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV4) {
17479ec7b004SRick Macklem 			dp = tovp;
17489ec7b004SRick Macklem 			tnes = *toexp;
17499ec7b004SRick Macklem 		} else {
17509ec7b004SRick Macklem 			error = nfsrv_mtofh(nd, &dfh);
17519ec7b004SRick Macklem 			if (error) {
17529ec7b004SRick Macklem 				vrele(vp);
17539ec7b004SRick Macklem 				/* tovp is always NULL unless NFSv4 */
1754a9285ae5SZack Kirsch 				goto out;
17559ec7b004SRick Macklem 			}
17565edc9102SEdward Tomasz Napierala 			nfsd_fhtovp(nd, &dfh, LK_EXCLUSIVE, &dp, &tnes, NULL, 0);
17579ec7b004SRick Macklem 			if (dp)
1758b249ce48SMateusz Guzik 				NFSVOPUNLOCK(dp);
17599ec7b004SRick Macklem 		}
17609ec7b004SRick Macklem 	}
1761f61786cbSRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
17626c21f6edSKonstantin Belousov 	    LOCKPARENT | SAVENAME | NOCACHE);
17639ec7b004SRick Macklem 	if (!nd->nd_repstat) {
17649ec7b004SRick Macklem 		nfsvno_setpathbuf(&named, &bufp, &hashp);
17659ec7b004SRick Macklem 		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
17669ec7b004SRick Macklem 		if (error) {
17679ec7b004SRick Macklem 			vrele(vp);
17688974bc2fSRick Macklem 			if (dp)
17699ec7b004SRick Macklem 				vrele(dp);
17709ec7b004SRick Macklem 			nfsvno_relpathbuf(&named);
1771a9285ae5SZack Kirsch 			goto out;
17729ec7b004SRick Macklem 		}
17739ec7b004SRick Macklem 		if (!nd->nd_repstat) {
17749ec7b004SRick Macklem 			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, &tnes,
17759ec7b004SRick Macklem 			    p, &dirp);
17769ec7b004SRick Macklem 		} else {
17779ec7b004SRick Macklem 			if (dp)
17789ec7b004SRick Macklem 				vrele(dp);
17799ec7b004SRick Macklem 			nfsvno_relpathbuf(&named);
17809ec7b004SRick Macklem 		}
17819ec7b004SRick Macklem 	}
17829ec7b004SRick Macklem 	if (dirp) {
17839ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV2) {
17849ec7b004SRick Macklem 			vrele(dirp);
17859ec7b004SRick Macklem 			dirp = NULL;
17869ec7b004SRick Macklem 		} else {
178790d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
178890d2dfabSRick Macklem 			    NULL);
17899ec7b004SRick Macklem 		}
17909ec7b004SRick Macklem 	}
17919ec7b004SRick Macklem 	if (!nd->nd_repstat)
17929ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_link(&named, vp, nd->nd_cred, p, exp);
17939ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
179490d2dfabSRick Macklem 		getret = nfsvno_getattr(vp, &at, nd, p, 0, NULL);
17959ec7b004SRick Macklem 	if (dirp) {
179690d2dfabSRick Macklem 		diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
17979ec7b004SRick Macklem 		vrele(dirp);
17989ec7b004SRick Macklem 	}
17999ec7b004SRick Macklem 	vrele(vp);
18009ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
18019ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
18029ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
18039ec7b004SRick Macklem 	} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
18049ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
18059ec7b004SRick Macklem 		*tl++ = newnfs_false;
18069ec7b004SRick Macklem 		txdr_hyper(dirfor.na_filerev, tl);
18079ec7b004SRick Macklem 		tl += 2;
18089ec7b004SRick Macklem 		txdr_hyper(diraft.na_filerev, tl);
18099ec7b004SRick Macklem 	}
1810a9285ae5SZack Kirsch 
1811a9285ae5SZack Kirsch out:
1812a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
1813a9285ae5SZack Kirsch 	return (error);
18149ec7b004SRick Macklem }
18159ec7b004SRick Macklem 
18169ec7b004SRick Macklem /*
18179ec7b004SRick Macklem  * nfs symbolic link service
18189ec7b004SRick Macklem  */
1819b9cc3262SRyan Moeller int
18209ec7b004SRick Macklem nfsrvd_symlink(struct nfsrv_descript *nd, __unused int isdgram,
1821af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
18229ec7b004SRick Macklem {
18239ec7b004SRick Macklem 	struct nfsvattr nva, dirfor, diraft;
18249ec7b004SRick Macklem 	struct nameidata named;
1825a9285ae5SZack Kirsch 	int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
18269ec7b004SRick Macklem 	vnode_t dirp = NULL;
18279ec7b004SRick Macklem 	char *bufp, *pathcp = NULL;
18289ec7b004SRick Macklem 	u_long *hashp;
1829af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
18309ec7b004SRick Macklem 
18319ec7b004SRick Macklem 	if (nd->nd_repstat) {
18329ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1833a9285ae5SZack Kirsch 		goto out;
18349ec7b004SRick Macklem 	}
18359ec7b004SRick Macklem 	if (vpp)
18369ec7b004SRick Macklem 		*vpp = NULL;
18379ec7b004SRick Macklem 	NFSVNO_ATTRINIT(&nva);
18389ec7b004SRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
18396c21f6edSKonstantin Belousov 	    LOCKPARENT | SAVESTART | NOCACHE);
18409ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
18419ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
18429ec7b004SRick Macklem 	if (!error && !nd->nd_repstat)
18439ec7b004SRick Macklem 		error = nfsvno_getsymlink(nd, &nva, p, &pathcp, &pathlen);
18449ec7b004SRick Macklem 	if (error) {
18459ec7b004SRick Macklem 		vrele(dp);
18469ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
1847a9285ae5SZack Kirsch 		goto out;
18489ec7b004SRick Macklem 	}
18499ec7b004SRick Macklem 	if (!nd->nd_repstat) {
18509ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
18519ec7b004SRick Macklem 	} else {
18529ec7b004SRick Macklem 		vrele(dp);
18539ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
18549ec7b004SRick Macklem 	}
18559ec7b004SRick Macklem 	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
18569ec7b004SRick Macklem 		vrele(dirp);
18579ec7b004SRick Macklem 		dirp = NULL;
18589ec7b004SRick Macklem 	}
18599ec7b004SRick Macklem 
18609ec7b004SRick Macklem 	/*
18619ec7b004SRick Macklem 	 * And call nfsrvd_symlinksub() to do the common code. It will
18629ec7b004SRick Macklem 	 * return EBADRPC upon a parsing error, 0 otherwise.
18639ec7b004SRick Macklem 	 */
18649ec7b004SRick Macklem 	if (!nd->nd_repstat) {
18659ec7b004SRick Macklem 		if (dirp != NULL)
186690d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
186790d2dfabSRick Macklem 			    NULL);
18689ec7b004SRick Macklem 		nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
18699ec7b004SRick Macklem 		    &dirfor, &diraft, &diraft_ret, NULL, NULL, p, exp,
18709ec7b004SRick Macklem 		    pathcp, pathlen);
18719ec7b004SRick Macklem 	} else if (dirp != NULL) {
187290d2dfabSRick Macklem 		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL);
18739ec7b004SRick Macklem 		vrele(dirp);
18749ec7b004SRick Macklem 	}
18759ec7b004SRick Macklem 	if (pathcp)
1876222daa42SConrad Meyer 		free(pathcp, M_TEMP);
18779ec7b004SRick Macklem 
18789ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
18799ec7b004SRick Macklem 		if (!nd->nd_repstat) {
18809ec7b004SRick Macklem 			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
18819ec7b004SRick Macklem 			nfsrv_postopattr(nd, 0, &nva);
18829ec7b004SRick Macklem 		}
18839ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
18849ec7b004SRick Macklem 	}
1885a9285ae5SZack Kirsch 
1886a9285ae5SZack Kirsch out:
1887a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
1888a9285ae5SZack Kirsch 	return (error);
18899ec7b004SRick Macklem }
18909ec7b004SRick Macklem 
18919ec7b004SRick Macklem /*
18929ec7b004SRick Macklem  * Common code for creating a symbolic link.
18939ec7b004SRick Macklem  */
18949ec7b004SRick Macklem static void
18959ec7b004SRick Macklem nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
18969ec7b004SRick Macklem     struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
18979ec7b004SRick Macklem     vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
18989ec7b004SRick Macklem     int *diraft_retp, nfsattrbit_t *attrbitp,
18999ec7b004SRick Macklem     NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
19009ec7b004SRick Macklem     int pathlen)
19019ec7b004SRick Macklem {
19029ec7b004SRick Macklem 	u_int32_t *tl;
19039ec7b004SRick Macklem 
19049ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_symlink(ndp, nvap, pathcp, pathlen,
19059ec7b004SRick Macklem 	    !(nd->nd_flag & ND_NFSV2), nd->nd_saveduid, nd->nd_cred, p, exp);
19069ec7b004SRick Macklem 	if (!nd->nd_repstat && !(nd->nd_flag & ND_NFSV2)) {
19079ec7b004SRick Macklem 		nfsrv_fixattr(nd, ndp->ni_vp, nvap, aclp, p, attrbitp, exp);
19089ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
19099ec7b004SRick Macklem 			nd->nd_repstat = nfsvno_getfh(ndp->ni_vp, fhp, p);
19109ec7b004SRick Macklem 			if (!nd->nd_repstat)
19119ec7b004SRick Macklem 				nd->nd_repstat = nfsvno_getattr(ndp->ni_vp,
191290d2dfabSRick Macklem 				    nvap, nd, p, 1, NULL);
19139ec7b004SRick Macklem 		}
191481f78d99SRick Macklem 		if (vpp != NULL && nd->nd_repstat == 0) {
1915b249ce48SMateusz Guzik 			NFSVOPUNLOCK(ndp->ni_vp);
19169ec7b004SRick Macklem 			*vpp = ndp->ni_vp;
191781f78d99SRick Macklem 		} else
19189ec7b004SRick Macklem 			vput(ndp->ni_vp);
19199ec7b004SRick Macklem 	}
19209ec7b004SRick Macklem 	if (dirp) {
192190d2dfabSRick Macklem 		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd, p, 0, NULL);
19229ec7b004SRick Macklem 		vrele(dirp);
19239ec7b004SRick Macklem 	}
19249ec7b004SRick Macklem 	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
19259ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
19269ec7b004SRick Macklem 		*tl++ = newnfs_false;
19279ec7b004SRick Macklem 		txdr_hyper(dirforp->na_filerev, tl);
19289ec7b004SRick Macklem 		tl += 2;
19299ec7b004SRick Macklem 		txdr_hyper(diraftp->na_filerev, tl);
19309ec7b004SRick Macklem 		(void) nfsrv_putattrbit(nd, attrbitp);
19319ec7b004SRick Macklem 	}
1932a9285ae5SZack Kirsch 
1933a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
19349ec7b004SRick Macklem }
19359ec7b004SRick Macklem 
19369ec7b004SRick Macklem /*
19379ec7b004SRick Macklem  * nfs mkdir service
19389ec7b004SRick Macklem  */
1939b9cc3262SRyan Moeller int
19409ec7b004SRick Macklem nfsrvd_mkdir(struct nfsrv_descript *nd, __unused int isdgram,
1941af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
19429ec7b004SRick Macklem {
19439ec7b004SRick Macklem 	struct nfsvattr nva, dirfor, diraft;
19449ec7b004SRick Macklem 	struct nameidata named;
19459ec7b004SRick Macklem 	u_int32_t *tl;
1946a9285ae5SZack Kirsch 	int error = 0, dirfor_ret = 1, diraft_ret = 1;
19479ec7b004SRick Macklem 	vnode_t dirp = NULL;
19489ec7b004SRick Macklem 	char *bufp;
19499ec7b004SRick Macklem 	u_long *hashp;
1950af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
19519ec7b004SRick Macklem 
19529ec7b004SRick Macklem 	if (nd->nd_repstat) {
19539ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1954a9285ae5SZack Kirsch 		goto out;
19559ec7b004SRick Macklem 	}
1956f61786cbSRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
19576c21f6edSKonstantin Belousov 	    LOCKPARENT | SAVENAME | NOCACHE);
19589ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
19599ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1960a9285ae5SZack Kirsch 	if (error)
1961a9285ae5SZack Kirsch 		goto nfsmout;
19629ec7b004SRick Macklem 	if (!nd->nd_repstat) {
19639ec7b004SRick Macklem 		NFSVNO_ATTRINIT(&nva);
19649ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3) {
1965d8a5961fSMarcelo Araujo 			error = nfsrv_sattr(nd, NULL, &nva, NULL, NULL, p);
1966a9285ae5SZack Kirsch 			if (error)
1967a9285ae5SZack Kirsch 				goto nfsmout;
19689ec7b004SRick Macklem 		} else {
19699ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
19709ec7b004SRick Macklem 			nva.na_mode = nfstov_mode(*tl++);
19719ec7b004SRick Macklem 		}
19729ec7b004SRick Macklem 	}
19739ec7b004SRick Macklem 	if (!nd->nd_repstat) {
19749ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
19759ec7b004SRick Macklem 	} else {
19769ec7b004SRick Macklem 		vrele(dp);
19779ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
19789ec7b004SRick Macklem 	}
19799ec7b004SRick Macklem 	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
19809ec7b004SRick Macklem 		vrele(dirp);
19819ec7b004SRick Macklem 		dirp = NULL;
19829ec7b004SRick Macklem 	}
19839ec7b004SRick Macklem 	if (nd->nd_repstat) {
19849ec7b004SRick Macklem 		if (dirp != NULL) {
198590d2dfabSRick Macklem 			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
198690d2dfabSRick Macklem 			    NULL);
19879ec7b004SRick Macklem 			vrele(dirp);
19889ec7b004SRick Macklem 		}
19899ec7b004SRick Macklem 		if (nd->nd_flag & ND_NFSV3)
19909ec7b004SRick Macklem 			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
19919ec7b004SRick Macklem 			    &diraft);
1992a9285ae5SZack Kirsch 		goto out;
19939ec7b004SRick Macklem 	}
19949ec7b004SRick Macklem 	if (dirp != NULL)
199590d2dfabSRick Macklem 		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL);
19969ec7b004SRick Macklem 
19979ec7b004SRick Macklem 	/*
19989ec7b004SRick Macklem 	 * Call nfsrvd_mkdirsub() for the code common to V4 as well.
19999ec7b004SRick Macklem 	 */
20009ec7b004SRick Macklem 	nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp, &dirfor, &diraft,
20019ec7b004SRick Macklem 	    &diraft_ret, NULL, NULL, p, exp);
20029ec7b004SRick Macklem 
20039ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
20049ec7b004SRick Macklem 		if (!nd->nd_repstat) {
20059ec7b004SRick Macklem 			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
20069ec7b004SRick Macklem 			nfsrv_postopattr(nd, 0, &nva);
20079ec7b004SRick Macklem 		}
20089ec7b004SRick Macklem 		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
20099ec7b004SRick Macklem 	} else if (!nd->nd_repstat) {
20109ec7b004SRick Macklem 		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
20119ec7b004SRick Macklem 		nfsrv_fillattr(nd, &nva);
20129ec7b004SRick Macklem 	}
2013a9285ae5SZack Kirsch 
2014a9285ae5SZack Kirsch out:
2015a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
20169ec7b004SRick Macklem 	return (0);
20179ec7b004SRick Macklem nfsmout:
20189ec7b004SRick Macklem 	vrele(dp);
20199ec7b004SRick Macklem 	nfsvno_relpathbuf(&named);
2020a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
20219ec7b004SRick Macklem 	return (error);
20229ec7b004SRick Macklem }
20239ec7b004SRick Macklem 
20249ec7b004SRick Macklem /*
20259ec7b004SRick Macklem  * Code common to mkdir for V2,3 and 4.
20269ec7b004SRick Macklem  */
20279ec7b004SRick Macklem static void
20289ec7b004SRick Macklem nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
20299ec7b004SRick Macklem     struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
20309ec7b004SRick Macklem     vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
20319ec7b004SRick Macklem     int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
20329ec7b004SRick Macklem     NFSPROC_T *p, struct nfsexstuff *exp)
20339ec7b004SRick Macklem {
20349ec7b004SRick Macklem 	vnode_t vp;
20359ec7b004SRick Macklem 	u_int32_t *tl;
20369ec7b004SRick Macklem 
20379ec7b004SRick Macklem 	NFSVNO_SETATTRVAL(nvap, type, VDIR);
20389ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_mkdir(ndp, nvap, nd->nd_saveduid,
20399ec7b004SRick Macklem 	    nd->nd_cred, p, exp);
20409ec7b004SRick Macklem 	if (!nd->nd_repstat) {
20419ec7b004SRick Macklem 		vp = ndp->ni_vp;
20429ec7b004SRick Macklem 		nfsrv_fixattr(nd, vp, nvap, aclp, p, attrbitp, exp);
20439ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
20449ec7b004SRick Macklem 		if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
204590d2dfabSRick Macklem 			nd->nd_repstat = nfsvno_getattr(vp, nvap, nd, p, 1,
204690d2dfabSRick Macklem 			    NULL);
20479ec7b004SRick Macklem 		if (vpp && !nd->nd_repstat) {
2048b249ce48SMateusz Guzik 			NFSVOPUNLOCK(vp);
20499ec7b004SRick Macklem 			*vpp = vp;
20509ec7b004SRick Macklem 		} else {
20519ec7b004SRick Macklem 			vput(vp);
20529ec7b004SRick Macklem 		}
20539ec7b004SRick Macklem 	}
20549ec7b004SRick Macklem 	if (dirp) {
205590d2dfabSRick Macklem 		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd, p, 0, NULL);
20569ec7b004SRick Macklem 		vrele(dirp);
20579ec7b004SRick Macklem 	}
20589ec7b004SRick Macklem 	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
20599ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
20609ec7b004SRick Macklem 		*tl++ = newnfs_false;
20619ec7b004SRick Macklem 		txdr_hyper(dirforp->na_filerev, tl);
20629ec7b004SRick Macklem 		tl += 2;
20639ec7b004SRick Macklem 		txdr_hyper(diraftp->na_filerev, tl);
20649ec7b004SRick Macklem 		(void) nfsrv_putattrbit(nd, attrbitp);
20659ec7b004SRick Macklem 	}
2066a9285ae5SZack Kirsch 
2067a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
20689ec7b004SRick Macklem }
20699ec7b004SRick Macklem 
20709ec7b004SRick Macklem /*
20719ec7b004SRick Macklem  * nfs commit service
20729ec7b004SRick Macklem  */
2073b9cc3262SRyan Moeller int
20749ec7b004SRick Macklem nfsrvd_commit(struct nfsrv_descript *nd, __unused int isdgram,
2075af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
20769ec7b004SRick Macklem {
20779ec7b004SRick Macklem 	struct nfsvattr bfor, aft;
20789ec7b004SRick Macklem 	u_int32_t *tl;
20799ec7b004SRick Macklem 	int error = 0, for_ret = 1, aft_ret = 1, cnt;
20809ec7b004SRick Macklem 	u_int64_t off;
2081af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
20829ec7b004SRick Macklem 
20839ec7b004SRick Macklem        if (nd->nd_repstat) {
20849ec7b004SRick Macklem 		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
2085a9285ae5SZack Kirsch 		goto out;
20869ec7b004SRick Macklem 	}
2087d8a5961fSMarcelo Araujo 
2088d8a5961fSMarcelo Araujo 	/* Return NFSERR_ISDIR in NFSv4 when commit on a directory. */
2089d8a5961fSMarcelo Araujo 	if (vp->v_type != VREG) {
2090d8a5961fSMarcelo Araujo 		if (nd->nd_flag & ND_NFSV3)
2091d8a5961fSMarcelo Araujo 			error = NFSERR_NOTSUPP;
2092d8a5961fSMarcelo Araujo 		else
2093d8a5961fSMarcelo Araujo 			error = (vp->v_type == VDIR) ? NFSERR_ISDIR : NFSERR_INVAL;
2094d8a5961fSMarcelo Araujo 		goto nfsmout;
2095d8a5961fSMarcelo Araujo 	}
20969ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
2097d8a5961fSMarcelo Araujo 
20989ec7b004SRick Macklem 	/*
20999ec7b004SRick Macklem 	 * XXX At this time VOP_FSYNC() does not accept offset and byte
21009ec7b004SRick Macklem 	 * count parameters, so these arguments are useless (someday maybe).
21019ec7b004SRick Macklem 	 */
21029ec7b004SRick Macklem 	off = fxdr_hyper(tl);
21039ec7b004SRick Macklem 	tl += 2;
21049ec7b004SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
21059ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
210690d2dfabSRick Macklem 		for_ret = nfsvno_getattr(vp, &bfor, nd, p, 1, NULL);
21079ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_fsync(vp, off, cnt, nd->nd_cred, p);
21089ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3) {
210990d2dfabSRick Macklem 		aft_ret = nfsvno_getattr(vp, &aft, nd, p, 1, NULL);
21109ec7b004SRick Macklem 		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
21119ec7b004SRick Macklem 	}
21129ec7b004SRick Macklem 	vput(vp);
21139ec7b004SRick Macklem 	if (!nd->nd_repstat) {
21149ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
21159ec7b004SRick Macklem 		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
21169ec7b004SRick Macklem 		*tl = txdr_unsigned(nfsboottime.tv_usec);
21179ec7b004SRick Macklem 	}
2118a9285ae5SZack Kirsch 
2119a9285ae5SZack Kirsch out:
2120a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
21219ec7b004SRick Macklem 	return (0);
21229ec7b004SRick Macklem nfsmout:
21239ec7b004SRick Macklem 	vput(vp);
2124a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
21259ec7b004SRick Macklem 	return (error);
21269ec7b004SRick Macklem }
21279ec7b004SRick Macklem 
21289ec7b004SRick Macklem /*
21299ec7b004SRick Macklem  * nfs statfs service
21309ec7b004SRick Macklem  */
2131b9cc3262SRyan Moeller int
21329ec7b004SRick Macklem nfsrvd_statfs(struct nfsrv_descript *nd, __unused int isdgram,
2133af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
21349ec7b004SRick Macklem {
21359ec7b004SRick Macklem 	struct statfs *sf;
21369ec7b004SRick Macklem 	u_int32_t *tl;
21379ec7b004SRick Macklem 	int getret = 1;
21389ec7b004SRick Macklem 	struct nfsvattr at;
21399ec7b004SRick Macklem 	u_quad_t tval;
2140af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
21419ec7b004SRick Macklem 
21422f304845SKonstantin Belousov 	sf = NULL;
21439ec7b004SRick Macklem 	if (nd->nd_repstat) {
21449ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
2145a9285ae5SZack Kirsch 		goto out;
21469ec7b004SRick Macklem 	}
21472f304845SKonstantin Belousov 	sf = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
2148dfd233edSAttilio Rao 	nd->nd_repstat = nfsvno_statfs(vp, sf);
214990d2dfabSRick Macklem 	getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL);
21509ec7b004SRick Macklem 	vput(vp);
21519ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV3)
21529ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
21539ec7b004SRick Macklem 	if (nd->nd_repstat)
2154a9285ae5SZack Kirsch 		goto out;
21559ec7b004SRick Macklem 	if (nd->nd_flag & ND_NFSV2) {
21569ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_V2STATFS);
21579ec7b004SRick Macklem 		*tl++ = txdr_unsigned(NFS_V2MAXDATA);
21589ec7b004SRick Macklem 		*tl++ = txdr_unsigned(sf->f_bsize);
21599ec7b004SRick Macklem 		*tl++ = txdr_unsigned(sf->f_blocks);
21609ec7b004SRick Macklem 		*tl++ = txdr_unsigned(sf->f_bfree);
21619ec7b004SRick Macklem 		*tl = txdr_unsigned(sf->f_bavail);
21629ec7b004SRick Macklem 	} else {
21639ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_V3STATFS);
21649ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_blocks;
21659ec7b004SRick Macklem 		tval *= (u_quad_t)sf->f_bsize;
21669ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
21679ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_bfree;
21689ec7b004SRick Macklem 		tval *= (u_quad_t)sf->f_bsize;
21699ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
21709ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_bavail;
21719ec7b004SRick Macklem 		tval *= (u_quad_t)sf->f_bsize;
21729ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
21739ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_files;
21749ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
21759ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_ffree;
21769ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
21779ec7b004SRick Macklem 		tval = (u_quad_t)sf->f_ffree;
21789ec7b004SRick Macklem 		txdr_hyper(tval, tl); tl += 2;
21799ec7b004SRick Macklem 		*tl = 0;
21809ec7b004SRick Macklem 	}
2181a9285ae5SZack Kirsch 
2182a9285ae5SZack Kirsch out:
21832f304845SKonstantin Belousov 	free(sf, M_STATFS);
2184a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
21859ec7b004SRick Macklem 	return (0);
21869ec7b004SRick Macklem }
21879ec7b004SRick Macklem 
21889ec7b004SRick Macklem /*
21899ec7b004SRick Macklem  * nfs fsinfo service
21909ec7b004SRick Macklem  */
2191b9cc3262SRyan Moeller int
21929ec7b004SRick Macklem nfsrvd_fsinfo(struct nfsrv_descript *nd, int isdgram,
2193af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
21949ec7b004SRick Macklem {
21959ec7b004SRick Macklem 	u_int32_t *tl;
21969ec7b004SRick Macklem 	struct nfsfsinfo fs;
21979ec7b004SRick Macklem 	int getret = 1;
21989ec7b004SRick Macklem 	struct nfsvattr at;
2199af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
22009ec7b004SRick Macklem 
22019ec7b004SRick Macklem 	if (nd->nd_repstat) {
22029ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
2203a9285ae5SZack Kirsch 		goto out;
22049ec7b004SRick Macklem 	}
220590d2dfabSRick Macklem 	getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL);
22069ec7b004SRick Macklem 	nfsvno_getfs(&fs, isdgram);
22079ec7b004SRick Macklem 	vput(vp);
22089ec7b004SRick Macklem 	nfsrv_postopattr(nd, getret, &at);
22099ec7b004SRick Macklem 	NFSM_BUILD(tl, u_int32_t *, NFSX_V3FSINFO);
22109ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_rtmax);
22119ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_rtpref);
22129ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_rtmult);
22139ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_wtmax);
22149ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_wtpref);
22159ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_wtmult);
22169ec7b004SRick Macklem 	*tl++ = txdr_unsigned(fs.fs_dtpref);
22179ec7b004SRick Macklem 	txdr_hyper(fs.fs_maxfilesize, tl);
22189ec7b004SRick Macklem 	tl += 2;
22199ec7b004SRick Macklem 	txdr_nfsv3time(&fs.fs_timedelta, tl);
22209ec7b004SRick Macklem 	tl += 2;
22219ec7b004SRick Macklem 	*tl = txdr_unsigned(fs.fs_properties);
2222a9285ae5SZack Kirsch 
2223a9285ae5SZack Kirsch out:
2224a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
22259ec7b004SRick Macklem 	return (0);
22269ec7b004SRick Macklem }
22279ec7b004SRick Macklem 
22289ec7b004SRick Macklem /*
22299ec7b004SRick Macklem  * nfs pathconf service
22309ec7b004SRick Macklem  */
2231b9cc3262SRyan Moeller int
22329ec7b004SRick Macklem nfsrvd_pathconf(struct nfsrv_descript *nd, __unused int isdgram,
2233af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
22349ec7b004SRick Macklem {
22359ec7b004SRick Macklem 	struct nfsv3_pathconf *pc;
22369ec7b004SRick Macklem 	int getret = 1;
2237b1288166SJohn Baldwin 	long linkmax, namemax, chownres, notrunc;
22389ec7b004SRick Macklem 	struct nfsvattr at;
2239af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
22409ec7b004SRick Macklem 
22419ec7b004SRick Macklem 	if (nd->nd_repstat) {
22429ec7b004SRick Macklem 		nfsrv_postopattr(nd, getret, &at);
2243a9285ae5SZack Kirsch 		goto out;
22449ec7b004SRick Macklem 	}
22459ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_pathconf(vp, _PC_LINK_MAX, &linkmax,
22469ec7b004SRick Macklem 	    nd->nd_cred, p);
22479ec7b004SRick Macklem 	if (!nd->nd_repstat)
22489ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NAME_MAX, &namemax,
22499ec7b004SRick Macklem 		    nd->nd_cred, p);
22509ec7b004SRick Macklem 	if (!nd->nd_repstat)
22519ec7b004SRick Macklem 		nd->nd_repstat=nfsvno_pathconf(vp, _PC_CHOWN_RESTRICTED,
22529ec7b004SRick Macklem 		    &chownres, nd->nd_cred, p);
22539ec7b004SRick Macklem 	if (!nd->nd_repstat)
22549ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NO_TRUNC, &notrunc,
22559ec7b004SRick Macklem 		    nd->nd_cred, p);
225690d2dfabSRick Macklem 	getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL);
22579ec7b004SRick Macklem 	vput(vp);
22589ec7b004SRick Macklem 	nfsrv_postopattr(nd, getret, &at);
22599ec7b004SRick Macklem 	if (!nd->nd_repstat) {
22609ec7b004SRick Macklem 		NFSM_BUILD(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
22619ec7b004SRick Macklem 		pc->pc_linkmax = txdr_unsigned(linkmax);
22629ec7b004SRick Macklem 		pc->pc_namemax = txdr_unsigned(namemax);
22639ec7b004SRick Macklem 		pc->pc_notrunc = txdr_unsigned(notrunc);
22649ec7b004SRick Macklem 		pc->pc_chownrestricted = txdr_unsigned(chownres);
22659ec7b004SRick Macklem 
22669ec7b004SRick Macklem 		/*
22679ec7b004SRick Macklem 		 * These should probably be supported by VOP_PATHCONF(), but
22689ec7b004SRick Macklem 		 * until msdosfs is exportable (why would you want to?), the
22699ec7b004SRick Macklem 		 * Unix defaults should be ok.
22709ec7b004SRick Macklem 		 */
22719ec7b004SRick Macklem 		pc->pc_caseinsensitive = newnfs_false;
22729ec7b004SRick Macklem 		pc->pc_casepreserving = newnfs_true;
22739ec7b004SRick Macklem 	}
2274a9285ae5SZack Kirsch 
2275a9285ae5SZack Kirsch out:
2276a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
22779ec7b004SRick Macklem 	return (0);
22789ec7b004SRick Macklem }
22799ec7b004SRick Macklem 
22809ec7b004SRick Macklem /*
22819ec7b004SRick Macklem  * nfsv4 lock service
22829ec7b004SRick Macklem  */
2283b9cc3262SRyan Moeller int
22849ec7b004SRick Macklem nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram,
2285af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
22869ec7b004SRick Macklem {
22879ec7b004SRick Macklem 	u_int32_t *tl;
22889ec7b004SRick Macklem 	int i;
22899ec7b004SRick Macklem 	struct nfsstate *stp = NULL;
22909ec7b004SRick Macklem 	struct nfslock *lop;
22919ec7b004SRick Macklem 	struct nfslockconflict cf;
22929ec7b004SRick Macklem 	int error = 0;
22939ec7b004SRick Macklem 	u_short flags = NFSLCK_LOCK, lflags;
22949ec7b004SRick Macklem 	u_int64_t offset, len;
22959ec7b004SRick Macklem 	nfsv4stateid_t stateid;
22969ec7b004SRick Macklem 	nfsquad_t clientid;
2297af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
22989ec7b004SRick Macklem 
22999ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
23009ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
23019ec7b004SRick Macklem 	switch (i) {
23029ec7b004SRick Macklem 	case NFSV4LOCKT_READW:
23039ec7b004SRick Macklem 		flags |= NFSLCK_BLOCKING;
23049ec7b004SRick Macklem 	case NFSV4LOCKT_READ:
23059ec7b004SRick Macklem 		lflags = NFSLCK_READ;
23069ec7b004SRick Macklem 		break;
23079ec7b004SRick Macklem 	case NFSV4LOCKT_WRITEW:
23089ec7b004SRick Macklem 		flags |= NFSLCK_BLOCKING;
23099ec7b004SRick Macklem 	case NFSV4LOCKT_WRITE:
23109ec7b004SRick Macklem 		lflags = NFSLCK_WRITE;
23119ec7b004SRick Macklem 		break;
23129ec7b004SRick Macklem 	default:
23139ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
23149ec7b004SRick Macklem 		goto nfsmout;
231574b8d63dSPedro F. Giffuni 	}
23169ec7b004SRick Macklem 	if (*tl++ == newnfs_true)
23179ec7b004SRick Macklem 		flags |= NFSLCK_RECLAIM;
23189ec7b004SRick Macklem 	offset = fxdr_hyper(tl);
23199ec7b004SRick Macklem 	tl += 2;
23209ec7b004SRick Macklem 	len = fxdr_hyper(tl);
23219ec7b004SRick Macklem 	tl += 2;
23229ec7b004SRick Macklem 	if (*tl == newnfs_true)
23239ec7b004SRick Macklem 		flags |= NFSLCK_OPENTOLOCK;
23249ec7b004SRick Macklem 	if (flags & NFSLCK_OPENTOLOCK) {
23259ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID);
23269ec7b004SRick Macklem 		i = fxdr_unsigned(int, *(tl+4+(NFSX_STATEID / NFSX_UNSIGNED)));
23272a45247cSRick Macklem 		if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
23282a45247cSRick Macklem 			nd->nd_repstat = NFSERR_BADXDR;
23292a45247cSRick Macklem 			goto nfsmout;
23302a45247cSRick Macklem 		}
2331222daa42SConrad Meyer 		stp = malloc(sizeof (struct nfsstate) + i,
23329ec7b004SRick Macklem 			M_NFSDSTATE, M_WAITOK);
23339ec7b004SRick Macklem 		stp->ls_ownerlen = i;
23349ec7b004SRick Macklem 		stp->ls_op = nd->nd_rp;
23359ec7b004SRick Macklem 		stp->ls_seq = fxdr_unsigned(int, *tl++);
23369ec7b004SRick Macklem 		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
23379ec7b004SRick Macklem 		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
23389ec7b004SRick Macklem 			NFSX_STATEIDOTHER);
23399ec7b004SRick Macklem 		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
234090d2dfabSRick Macklem 
234190d2dfabSRick Macklem 		/*
234290d2dfabSRick Macklem 		 * For the special stateid of other all 0s and seqid == 1, set
234390d2dfabSRick Macklem 		 * the stateid to the current stateid, if it is set.
234490d2dfabSRick Macklem 		 */
234590d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0 &&
234690d2dfabSRick Macklem 		    stp->ls_stateid.seqid == 1 &&
234790d2dfabSRick Macklem 		    stp->ls_stateid.other[0] == 0 &&
234890d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0 &&
234990d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0) {
235090d2dfabSRick Macklem 			if ((nd->nd_flag & ND_CURSTATEID) != 0) {
235190d2dfabSRick Macklem 				stp->ls_stateid = nd->nd_curstateid;
235290d2dfabSRick Macklem 				stp->ls_stateid.seqid = 0;
235390d2dfabSRick Macklem 			} else {
235490d2dfabSRick Macklem 				nd->nd_repstat = NFSERR_BADSTATEID;
235590d2dfabSRick Macklem 				goto nfsmout;
235690d2dfabSRick Macklem 			}
235790d2dfabSRick Macklem 		}
235890d2dfabSRick Macklem 
23599ec7b004SRick Macklem 		stp->ls_opentolockseq = fxdr_unsigned(int, *tl++);
23609ec7b004SRick Macklem 		clientid.lval[0] = *tl++;
23619ec7b004SRick Macklem 		clientid.lval[1] = *tl++;
2362c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2363c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
2364c59e4cc3SRick Macklem 				clientid.qval = nd->nd_clientid.qval;
2365c59e4cc3SRick Macklem 			else if (nd->nd_clientid.qval != clientid.qval)
2366c59e4cc3SRick Macklem 				printf("EEK3 multiple clids\n");
23679ec7b004SRick Macklem 		} else {
2368c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
2369c59e4cc3SRick Macklem 				printf("EEK! no clientid from session\n");
23709ec7b004SRick Macklem 			nd->nd_flag |= ND_IMPLIEDCLID;
23719ec7b004SRick Macklem 			nd->nd_clientid.qval = clientid.qval;
23729ec7b004SRick Macklem 		}
23739ec7b004SRick Macklem 		error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
23749ec7b004SRick Macklem 		if (error)
23759ec7b004SRick Macklem 			goto nfsmout;
23769ec7b004SRick Macklem 	} else {
23779ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
2378222daa42SConrad Meyer 		stp = malloc(sizeof (struct nfsstate),
23799ec7b004SRick Macklem 			M_NFSDSTATE, M_WAITOK);
23809ec7b004SRick Macklem 		stp->ls_ownerlen = 0;
23819ec7b004SRick Macklem 		stp->ls_op = nd->nd_rp;
23829ec7b004SRick Macklem 		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
23839ec7b004SRick Macklem 		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
23849ec7b004SRick Macklem 			NFSX_STATEIDOTHER);
23859ec7b004SRick Macklem 		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
238690d2dfabSRick Macklem 
238790d2dfabSRick Macklem 		/*
238890d2dfabSRick Macklem 		 * For the special stateid of other all 0s and seqid == 1, set
238990d2dfabSRick Macklem 		 * the stateid to the current stateid, if it is set.
239090d2dfabSRick Macklem 		 */
239190d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0 &&
239290d2dfabSRick Macklem 		    stp->ls_stateid.seqid == 1 &&
239390d2dfabSRick Macklem 		    stp->ls_stateid.other[0] == 0 &&
239490d2dfabSRick Macklem 		    stp->ls_stateid.other[1] == 0 &&
239590d2dfabSRick Macklem 		    stp->ls_stateid.other[2] == 0) {
239690d2dfabSRick Macklem 			if ((nd->nd_flag & ND_CURSTATEID) != 0) {
239790d2dfabSRick Macklem 				stp->ls_stateid = nd->nd_curstateid;
239890d2dfabSRick Macklem 				stp->ls_stateid.seqid = 0;
239990d2dfabSRick Macklem 			} else {
240090d2dfabSRick Macklem 				nd->nd_repstat = NFSERR_BADSTATEID;
240190d2dfabSRick Macklem 				goto nfsmout;
240290d2dfabSRick Macklem 			}
240390d2dfabSRick Macklem 		}
240490d2dfabSRick Macklem 
24059ec7b004SRick Macklem 		stp->ls_seq = fxdr_unsigned(int, *tl);
24069ec7b004SRick Macklem 		clientid.lval[0] = stp->ls_stateid.other[0];
24079ec7b004SRick Macklem 		clientid.lval[1] = stp->ls_stateid.other[1];
2408c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2409c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
2410c59e4cc3SRick Macklem 				clientid.qval = nd->nd_clientid.qval;
2411c59e4cc3SRick Macklem 			else if (nd->nd_clientid.qval != clientid.qval)
2412c59e4cc3SRick Macklem 				printf("EEK4 multiple clids\n");
24139ec7b004SRick Macklem 		} else {
2414c59e4cc3SRick Macklem 			if ((nd->nd_flag & ND_NFSV41) != 0)
2415c59e4cc3SRick Macklem 				printf("EEK! no clientid from session\n");
24169ec7b004SRick Macklem 			nd->nd_flag |= ND_IMPLIEDCLID;
24179ec7b004SRick Macklem 			nd->nd_clientid.qval = clientid.qval;
24189ec7b004SRick Macklem 		}
24199ec7b004SRick Macklem 	}
2420222daa42SConrad Meyer 	lop = malloc(sizeof (struct nfslock),
24219ec7b004SRick Macklem 		M_NFSDLOCK, M_WAITOK);
24229ec7b004SRick Macklem 	lop->lo_first = offset;
24239ec7b004SRick Macklem 	if (len == NFS64BITSSET) {
24249ec7b004SRick Macklem 		lop->lo_end = NFS64BITSSET;
24259ec7b004SRick Macklem 	} else {
24269ec7b004SRick Macklem 		lop->lo_end = offset + len;
24279ec7b004SRick Macklem 		if (lop->lo_end <= lop->lo_first)
24289ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
24299ec7b004SRick Macklem 	}
24309ec7b004SRick Macklem 	lop->lo_flags = lflags;
24319ec7b004SRick Macklem 	stp->ls_flags = flags;
24329ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
24339ec7b004SRick Macklem 
24349ec7b004SRick Macklem 	/*
24359ec7b004SRick Macklem 	 * Do basic access checking.
24369ec7b004SRick Macklem 	 */
24379ec7b004SRick Macklem 	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
24389ec7b004SRick Macklem 	    if (vnode_vtype(vp) == VDIR)
24399ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_ISDIR;
24409ec7b004SRick Macklem 	    else
24419ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
24429ec7b004SRick Macklem 	}
24439ec7b004SRick Macklem 	if (!nd->nd_repstat) {
24449ec7b004SRick Macklem 	    if (lflags & NFSLCK_WRITE) {
24458da45f2cSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
24469ec7b004SRick Macklem 		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
24478da45f2cSRick Macklem 		    NFSACCCHK_VPISLOCKED, NULL);
24489ec7b004SRick Macklem 	    } else {
24498da45f2cSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
24509ec7b004SRick Macklem 		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
24518da45f2cSRick Macklem 		    NFSACCCHK_VPISLOCKED, NULL);
24529ec7b004SRick Macklem 		if (nd->nd_repstat)
24538da45f2cSRick Macklem 		    nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
24549ec7b004SRick Macklem 			nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
24558da45f2cSRick Macklem 			NFSACCCHK_VPISLOCKED, NULL);
24569ec7b004SRick Macklem 	    }
24579ec7b004SRick Macklem 	}
24589ec7b004SRick Macklem 
24599ec7b004SRick Macklem 	/*
24609ec7b004SRick Macklem 	 * We call nfsrv_lockctrl() even if nd_repstat set, so that the
24619ec7b004SRick Macklem 	 * seqid# gets updated. nfsrv_lockctrl() will return the value
24629ec7b004SRick Macklem 	 * of nd_repstat, if it gets that far.
24639ec7b004SRick Macklem 	 */
24649ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
24659ec7b004SRick Macklem 		&stateid, exp, nd, p);
24669ec7b004SRick Macklem 	if (lop)
2467222daa42SConrad Meyer 		free(lop, M_NFSDLOCK);
24689ec7b004SRick Macklem 	if (stp)
2469222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
24709ec7b004SRick Macklem 	if (!nd->nd_repstat) {
247190d2dfabSRick Macklem 		/* For NFSv4.1, set the Current StateID. */
247290d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0) {
247390d2dfabSRick Macklem 			nd->nd_curstateid = stateid;
247490d2dfabSRick Macklem 			nd->nd_flag |= ND_CURSTATEID;
247590d2dfabSRick Macklem 		}
24769ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
24779ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
24789ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
24799ec7b004SRick Macklem 	} else if (nd->nd_repstat == NFSERR_DENIED) {
24809ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
24819ec7b004SRick Macklem 		txdr_hyper(cf.cl_first, tl);
24829ec7b004SRick Macklem 		tl += 2;
24839ec7b004SRick Macklem 		if (cf.cl_end == NFS64BITSSET)
24849ec7b004SRick Macklem 			len = NFS64BITSSET;
24859ec7b004SRick Macklem 		else
24869ec7b004SRick Macklem 			len = cf.cl_end - cf.cl_first;
24879ec7b004SRick Macklem 		txdr_hyper(len, tl);
24889ec7b004SRick Macklem 		tl += 2;
24899ec7b004SRick Macklem 		if (cf.cl_flags == NFSLCK_WRITE)
24909ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
24919ec7b004SRick Macklem 		else
24929ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
24939ec7b004SRick Macklem 		*tl++ = stateid.other[0];
24949ec7b004SRick Macklem 		*tl = stateid.other[1];
24959ec7b004SRick Macklem 		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
24969ec7b004SRick Macklem 	}
24979ec7b004SRick Macklem 	vput(vp);
2498a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
24999ec7b004SRick Macklem 	return (0);
25009ec7b004SRick Macklem nfsmout:
25019ec7b004SRick Macklem 	vput(vp);
25029ec7b004SRick Macklem 	if (stp)
2503222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
2504a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
25059ec7b004SRick Macklem 	return (error);
25069ec7b004SRick Macklem }
25079ec7b004SRick Macklem 
25089ec7b004SRick Macklem /*
25099ec7b004SRick Macklem  * nfsv4 lock test service
25109ec7b004SRick Macklem  */
2511b9cc3262SRyan Moeller int
25129ec7b004SRick Macklem nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram,
2513af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
25149ec7b004SRick Macklem {
25159ec7b004SRick Macklem 	u_int32_t *tl;
25169ec7b004SRick Macklem 	int i;
25179ec7b004SRick Macklem 	struct nfsstate *stp = NULL;
25189ec7b004SRick Macklem 	struct nfslock lo, *lop = &lo;
25199ec7b004SRick Macklem 	struct nfslockconflict cf;
25209ec7b004SRick Macklem 	int error = 0;
25219ec7b004SRick Macklem 	nfsv4stateid_t stateid;
25229ec7b004SRick Macklem 	nfsquad_t clientid;
25239ec7b004SRick Macklem 	u_int64_t len;
2524af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
25259ec7b004SRick Macklem 
25269ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
25279ec7b004SRick Macklem 	i = fxdr_unsigned(int, *(tl + 7));
25282a45247cSRick Macklem 	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
25292a45247cSRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
25302a45247cSRick Macklem 		goto nfsmout;
25312a45247cSRick Macklem 	}
2532222daa42SConrad Meyer 	stp = malloc(sizeof (struct nfsstate) + i,
25339ec7b004SRick Macklem 	    M_NFSDSTATE, M_WAITOK);
25349ec7b004SRick Macklem 	stp->ls_ownerlen = i;
25359ec7b004SRick Macklem 	stp->ls_op = NULL;
25369ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_TEST;
25379ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
25389ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
25399ec7b004SRick Macklem 	switch (i) {
25409ec7b004SRick Macklem 	case NFSV4LOCKT_READW:
25419ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_BLOCKING;
25429ec7b004SRick Macklem 	case NFSV4LOCKT_READ:
25439ec7b004SRick Macklem 		lo.lo_flags = NFSLCK_READ;
25449ec7b004SRick Macklem 		break;
25459ec7b004SRick Macklem 	case NFSV4LOCKT_WRITEW:
25469ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_BLOCKING;
25479ec7b004SRick Macklem 	case NFSV4LOCKT_WRITE:
25489ec7b004SRick Macklem 		lo.lo_flags = NFSLCK_WRITE;
25499ec7b004SRick Macklem 		break;
25509ec7b004SRick Macklem 	default:
25519ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
25529ec7b004SRick Macklem 		goto nfsmout;
255374b8d63dSPedro F. Giffuni 	}
25549ec7b004SRick Macklem 	lo.lo_first = fxdr_hyper(tl);
25559ec7b004SRick Macklem 	tl += 2;
25569ec7b004SRick Macklem 	len = fxdr_hyper(tl);
25579ec7b004SRick Macklem 	if (len == NFS64BITSSET) {
25589ec7b004SRick Macklem 		lo.lo_end = NFS64BITSSET;
25599ec7b004SRick Macklem 	} else {
25609ec7b004SRick Macklem 		lo.lo_end = lo.lo_first + len;
25619ec7b004SRick Macklem 		if (lo.lo_end <= lo.lo_first)
25629ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
25639ec7b004SRick Macklem 	}
25649ec7b004SRick Macklem 	tl += 2;
25659ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
25669ec7b004SRick Macklem 	clientid.lval[1] = *tl;
2567c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2568c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2569c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
2570c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
2571c59e4cc3SRick Macklem 			printf("EEK5 multiple clids\n");
25729ec7b004SRick Macklem 	} else {
2573c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2574c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
25759ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
25769ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
25779ec7b004SRick Macklem 	}
25789ec7b004SRick Macklem 	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
25799ec7b004SRick Macklem 	if (error)
25809ec7b004SRick Macklem 		goto nfsmout;
25819ec7b004SRick Macklem 	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
25829ec7b004SRick Macklem 	    if (vnode_vtype(vp) == VDIR)
25839ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_ISDIR;
25849ec7b004SRick Macklem 	    else
25859ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
25869ec7b004SRick Macklem 	}
25879ec7b004SRick Macklem 	if (!nd->nd_repstat)
25889ec7b004SRick Macklem 	  nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
25899ec7b004SRick Macklem 	    &stateid, exp, nd, p);
25909ec7b004SRick Macklem 	if (nd->nd_repstat) {
25919ec7b004SRick Macklem 	    if (nd->nd_repstat == NFSERR_DENIED) {
25929ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
25939ec7b004SRick Macklem 		txdr_hyper(cf.cl_first, tl);
25949ec7b004SRick Macklem 		tl += 2;
25959ec7b004SRick Macklem 		if (cf.cl_end == NFS64BITSSET)
25969ec7b004SRick Macklem 			len = NFS64BITSSET;
25979ec7b004SRick Macklem 		else
25989ec7b004SRick Macklem 			len = cf.cl_end - cf.cl_first;
25999ec7b004SRick Macklem 		txdr_hyper(len, tl);
26009ec7b004SRick Macklem 		tl += 2;
26019ec7b004SRick Macklem 		if (cf.cl_flags == NFSLCK_WRITE)
26029ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
26039ec7b004SRick Macklem 		else
26049ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
26059ec7b004SRick Macklem 		*tl++ = stp->ls_stateid.other[0];
26069ec7b004SRick Macklem 		*tl = stp->ls_stateid.other[1];
26079ec7b004SRick Macklem 		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
26089ec7b004SRick Macklem 	    }
26099ec7b004SRick Macklem 	}
26109ec7b004SRick Macklem 	vput(vp);
26115ecc225fSConrad Meyer 	if (stp)
2612222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
2613a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
26149ec7b004SRick Macklem 	return (0);
26159ec7b004SRick Macklem nfsmout:
26169ec7b004SRick Macklem 	vput(vp);
26179ec7b004SRick Macklem 	if (stp)
2618222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
2619a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
26209ec7b004SRick Macklem 	return (error);
26219ec7b004SRick Macklem }
26229ec7b004SRick Macklem 
26239ec7b004SRick Macklem /*
26249ec7b004SRick Macklem  * nfsv4 unlock service
26259ec7b004SRick Macklem  */
2626b9cc3262SRyan Moeller int
26279ec7b004SRick Macklem nfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram,
2628af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
26299ec7b004SRick Macklem {
26309ec7b004SRick Macklem 	u_int32_t *tl;
26319ec7b004SRick Macklem 	int i;
26329ec7b004SRick Macklem 	struct nfsstate *stp;
26339ec7b004SRick Macklem 	struct nfslock *lop;
26349ec7b004SRick Macklem 	int error = 0;
26359ec7b004SRick Macklem 	nfsv4stateid_t stateid;
26369ec7b004SRick Macklem 	nfsquad_t clientid;
26379ec7b004SRick Macklem 	u_int64_t len;
2638af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
26399ec7b004SRick Macklem 
26409ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED + NFSX_STATEID);
2641222daa42SConrad Meyer 	stp = malloc(sizeof (struct nfsstate),
26429ec7b004SRick Macklem 	    M_NFSDSTATE, M_WAITOK);
2643222daa42SConrad Meyer 	lop = malloc(sizeof (struct nfslock),
26449ec7b004SRick Macklem 	    M_NFSDLOCK, M_WAITOK);
26459ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_UNLOCK;
26469ec7b004SRick Macklem 	lop->lo_flags = NFSLCK_UNLOCK;
26479ec7b004SRick Macklem 	stp->ls_op = nd->nd_rp;
26489ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
26499ec7b004SRick Macklem 	switch (i) {
26509ec7b004SRick Macklem 	case NFSV4LOCKT_READW:
26519ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_BLOCKING;
26529ec7b004SRick Macklem 	case NFSV4LOCKT_READ:
26539ec7b004SRick Macklem 		break;
26549ec7b004SRick Macklem 	case NFSV4LOCKT_WRITEW:
26559ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_BLOCKING;
26569ec7b004SRick Macklem 	case NFSV4LOCKT_WRITE:
26579ec7b004SRick Macklem 		break;
26589ec7b004SRick Macklem 	default:
26599ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
26602a45247cSRick Macklem 		free(stp, M_NFSDSTATE);
26612a45247cSRick Macklem 		free(lop, M_NFSDLOCK);
26629ec7b004SRick Macklem 		goto nfsmout;
266374b8d63dSPedro F. Giffuni 	}
26649ec7b004SRick Macklem 	stp->ls_ownerlen = 0;
26659ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
26669ec7b004SRick Macklem 	stp->ls_seq = fxdr_unsigned(int, *tl++);
26679ec7b004SRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
26689ec7b004SRick Macklem 	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
26699ec7b004SRick Macklem 	    NFSX_STATEIDOTHER);
26709ec7b004SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
267190d2dfabSRick Macklem 
267290d2dfabSRick Macklem 	/*
267390d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
267490d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
267590d2dfabSRick Macklem 	 */
267690d2dfabSRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 1 &&
267790d2dfabSRick Macklem 	    stp->ls_stateid.other[0] == 0 && stp->ls_stateid.other[1] == 0 &&
267890d2dfabSRick Macklem 	    stp->ls_stateid.other[2] == 0) {
267990d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
268090d2dfabSRick Macklem 			stp->ls_stateid = nd->nd_curstateid;
268190d2dfabSRick Macklem 			stp->ls_stateid.seqid = 0;
268290d2dfabSRick Macklem 		} else {
268390d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
268490d2dfabSRick Macklem 			goto nfsmout;
268590d2dfabSRick Macklem 		}
268690d2dfabSRick Macklem 	}
268790d2dfabSRick Macklem 
26889ec7b004SRick Macklem 	lop->lo_first = fxdr_hyper(tl);
26899ec7b004SRick Macklem 	tl += 2;
26909ec7b004SRick Macklem 	len = fxdr_hyper(tl);
26919ec7b004SRick Macklem 	if (len == NFS64BITSSET) {
26929ec7b004SRick Macklem 		lop->lo_end = NFS64BITSSET;
26939ec7b004SRick Macklem 	} else {
26949ec7b004SRick Macklem 		lop->lo_end = lop->lo_first + len;
26959ec7b004SRick Macklem 		if (lop->lo_end <= lop->lo_first)
26969ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
26979ec7b004SRick Macklem 	}
26989ec7b004SRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0];
26999ec7b004SRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1];
2700c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2701c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2702c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
2703c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
2704c59e4cc3SRick Macklem 			printf("EEK6 multiple clids\n");
27059ec7b004SRick Macklem 	} else {
2706c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2707c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
27089ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
27099ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
27109ec7b004SRick Macklem 	}
27119ec7b004SRick Macklem 	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
27129ec7b004SRick Macklem 	    if (vnode_vtype(vp) == VDIR)
27139ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_ISDIR;
27149ec7b004SRick Macklem 	    else
27159ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
27169ec7b004SRick Macklem 	}
27179ec7b004SRick Macklem 	/*
27189ec7b004SRick Macklem 	 * Call nfsrv_lockctrl() even if nd_repstat is set, so that the
27199ec7b004SRick Macklem 	 * seqid# gets incremented. nfsrv_lockctrl() will return the
27209ec7b004SRick Macklem 	 * value of nd_repstat, if it gets that far.
27219ec7b004SRick Macklem 	 */
27229ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
27239ec7b004SRick Macklem 	    &stateid, exp, nd, p);
27249ec7b004SRick Macklem 	if (stp)
2725222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
27269ec7b004SRick Macklem 	if (lop)
2727222daa42SConrad Meyer 		free(lop, M_NFSDLOCK);
27289ec7b004SRick Macklem 	if (!nd->nd_repstat) {
27299ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
27309ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
27319ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
27329ec7b004SRick Macklem 	}
27339ec7b004SRick Macklem nfsmout:
27349ec7b004SRick Macklem 	vput(vp);
2735a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
27369ec7b004SRick Macklem 	return (error);
27379ec7b004SRick Macklem }
27389ec7b004SRick Macklem 
27399ec7b004SRick Macklem /*
27409ec7b004SRick Macklem  * nfsv4 open service
27419ec7b004SRick Macklem  */
2742b9cc3262SRyan Moeller int
27439ec7b004SRick Macklem nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
2744af444b18SEdward Tomasz Napierala     vnode_t dp, vnode_t *vpp, __unused fhandle_t *fhp, struct nfsexstuff *exp)
27459ec7b004SRick Macklem {
27469ec7b004SRick Macklem 	u_int32_t *tl;
2747c59e4cc3SRick Macklem 	int i, retext;
27489ec7b004SRick Macklem 	struct nfsstate *stp = NULL;
2749b0b7d978SRick Macklem 	int error = 0, create, claim, exclusive_flag = 0, override;
27509ec7b004SRick Macklem 	u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask;
27519ec7b004SRick Macklem 	int how = NFSCREATE_UNCHECKED;
2752086f6e0cSRick Macklem 	int32_t cverf[2], tverf[2] = { 0, 0 };
27539ec7b004SRick Macklem 	vnode_t vp = NULL, dirp = NULL;
27549ec7b004SRick Macklem 	struct nfsvattr nva, dirfor, diraft;
27559ec7b004SRick Macklem 	struct nameidata named;
27569ec7b004SRick Macklem 	nfsv4stateid_t stateid, delegstateid;
27579ec7b004SRick Macklem 	nfsattrbit_t attrbits;
27589ec7b004SRick Macklem 	nfsquad_t clientid;
27599ec7b004SRick Macklem 	char *bufp = NULL;
27609ec7b004SRick Macklem 	u_long *hashp;
27619ec7b004SRick Macklem 	NFSACL_T *aclp = NULL;
2762af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
27639ec7b004SRick Macklem 
27649ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
2765c3e22f83SRick Macklem 	aclp = acl_alloc(M_WAITOK);
27669ec7b004SRick Macklem 	aclp->acl_cnt = 0;
27679ec7b004SRick Macklem #endif
27689ec7b004SRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
27699ec7b004SRick Macklem 	named.ni_startdir = NULL;
27709ec7b004SRick Macklem 	named.ni_cnd.cn_nameiop = 0;
27719ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
27729ec7b004SRick Macklem 	i = fxdr_unsigned(int, *(tl + 5));
27732a45247cSRick Macklem 	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
27742a45247cSRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
2775a9285ae5SZack Kirsch 		goto nfsmout;
27762a45247cSRick Macklem 	}
2777222daa42SConrad Meyer 	stp = malloc(sizeof (struct nfsstate) + i,
27789ec7b004SRick Macklem 	    M_NFSDSTATE, M_WAITOK);
27799ec7b004SRick Macklem 	stp->ls_ownerlen = i;
27809ec7b004SRick Macklem 	stp->ls_op = nd->nd_rp;
27819ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_OPEN;
27829ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
27839ec7b004SRick Macklem 	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
27849ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
2785c59e4cc3SRick Macklem 	retext = 0;
2786c59e4cc3SRick Macklem 	if ((i & (NFSV4OPEN_WANTDELEGMASK | NFSV4OPEN_WANTSIGNALDELEG |
2787c59e4cc3SRick Macklem 	    NFSV4OPEN_WANTPUSHDELEG)) != 0 && (nd->nd_flag & ND_NFSV41) != 0) {
2788c59e4cc3SRick Macklem 		retext = 1;
2789c59e4cc3SRick Macklem 		/* For now, ignore these. */
2790c59e4cc3SRick Macklem 		i &= ~(NFSV4OPEN_WANTPUSHDELEG | NFSV4OPEN_WANTSIGNALDELEG);
2791c59e4cc3SRick Macklem 		switch (i & NFSV4OPEN_WANTDELEGMASK) {
2792c59e4cc3SRick Macklem 		case NFSV4OPEN_WANTANYDELEG:
2793c59e4cc3SRick Macklem 			stp->ls_flags |= (NFSLCK_WANTRDELEG |
2794c59e4cc3SRick Macklem 			    NFSLCK_WANTWDELEG);
2795c59e4cc3SRick Macklem 			i &= ~NFSV4OPEN_WANTDELEGMASK;
2796c59e4cc3SRick Macklem 			break;
2797c59e4cc3SRick Macklem 		case NFSV4OPEN_WANTREADDELEG:
2798c59e4cc3SRick Macklem 			stp->ls_flags |= NFSLCK_WANTRDELEG;
2799c59e4cc3SRick Macklem 			i &= ~NFSV4OPEN_WANTDELEGMASK;
2800c59e4cc3SRick Macklem 			break;
2801c59e4cc3SRick Macklem 		case NFSV4OPEN_WANTWRITEDELEG:
2802c59e4cc3SRick Macklem 			stp->ls_flags |= NFSLCK_WANTWDELEG;
2803c59e4cc3SRick Macklem 			i &= ~NFSV4OPEN_WANTDELEGMASK;
2804c59e4cc3SRick Macklem 			break;
2805c59e4cc3SRick Macklem 		case NFSV4OPEN_WANTNODELEG:
2806c59e4cc3SRick Macklem 			stp->ls_flags |= NFSLCK_WANTNODELEG;
2807c59e4cc3SRick Macklem 			i &= ~NFSV4OPEN_WANTDELEGMASK;
2808c59e4cc3SRick Macklem 			break;
2809c59e4cc3SRick Macklem 		case NFSV4OPEN_WANTCANCEL:
2810c59e4cc3SRick Macklem 			printf("NFSv4: ignore Open WantCancel\n");
2811c59e4cc3SRick Macklem 			i &= ~NFSV4OPEN_WANTDELEGMASK;
2812c59e4cc3SRick Macklem 			break;
2813c59e4cc3SRick Macklem 		default:
2814c59e4cc3SRick Macklem 			/* nd_repstat will be set to NFSERR_INVAL below. */
2815c59e4cc3SRick Macklem 			break;
281674b8d63dSPedro F. Giffuni 		}
2817c59e4cc3SRick Macklem 	}
28189ec7b004SRick Macklem 	switch (i) {
28199ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSREAD:
28209ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_READACCESS;
28219ec7b004SRick Macklem 		break;
28229ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSWRITE:
28239ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_WRITEACCESS;
28249ec7b004SRick Macklem 		break;
28259ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSBOTH:
28269ec7b004SRick Macklem 		stp->ls_flags |= (NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
28279ec7b004SRick Macklem 		break;
28289ec7b004SRick Macklem 	default:
28299ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
283074b8d63dSPedro F. Giffuni 	}
28319ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
28329ec7b004SRick Macklem 	switch (i) {
28339ec7b004SRick Macklem 	case NFSV4OPEN_DENYNONE:
28349ec7b004SRick Macklem 		break;
28359ec7b004SRick Macklem 	case NFSV4OPEN_DENYREAD:
28369ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_READDENY;
28379ec7b004SRick Macklem 		break;
28389ec7b004SRick Macklem 	case NFSV4OPEN_DENYWRITE:
28399ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_WRITEDENY;
28409ec7b004SRick Macklem 		break;
28419ec7b004SRick Macklem 	case NFSV4OPEN_DENYBOTH:
28429ec7b004SRick Macklem 		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
28439ec7b004SRick Macklem 		break;
28449ec7b004SRick Macklem 	default:
28459ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
284674b8d63dSPedro F. Giffuni 	}
28479ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
28489ec7b004SRick Macklem 	clientid.lval[1] = *tl;
2849c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2850c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2851c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
2852c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
2853c59e4cc3SRick Macklem 			printf("EEK7 multiple clids\n");
28549ec7b004SRick Macklem 	} else {
2855c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
2856c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
28579ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
28589ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
28599ec7b004SRick Macklem 	}
28609ec7b004SRick Macklem 	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2861a9285ae5SZack Kirsch 	if (error)
2862a9285ae5SZack Kirsch 		goto nfsmout;
28639ec7b004SRick Macklem 	NFSVNO_ATTRINIT(&nva);
28649ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
28659ec7b004SRick Macklem 	create = fxdr_unsigned(int, *tl);
28669ec7b004SRick Macklem 	if (!nd->nd_repstat)
286790d2dfabSRick Macklem 		nd->nd_repstat = nfsvno_getattr(dp, &dirfor, nd, p, 0, NULL);
28689ec7b004SRick Macklem 	if (create == NFSV4OPEN_CREATE) {
28699ec7b004SRick Macklem 		nva.na_type = VREG;
28709ec7b004SRick Macklem 		nva.na_mode = 0;
28719ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
28729ec7b004SRick Macklem 		how = fxdr_unsigned(int, *tl);
28739ec7b004SRick Macklem 		switch (how) {
28749ec7b004SRick Macklem 		case NFSCREATE_UNCHECKED:
28759ec7b004SRick Macklem 		case NFSCREATE_GUARDED:
2876d8a5961fSMarcelo Araujo 			error = nfsv4_sattr(nd, NULL, &nva, &attrbits, aclp, p);
2877a9285ae5SZack Kirsch 			if (error)
2878a9285ae5SZack Kirsch 				goto nfsmout;
28799ec7b004SRick Macklem 			/*
28809ec7b004SRick Macklem 			 * If the na_gid being set is the same as that of
28819ec7b004SRick Macklem 			 * the directory it is going in, clear it, since
28829ec7b004SRick Macklem 			 * that is what will be set by default. This allows
28839ec7b004SRick Macklem 			 * a user that isn't in that group to do the create.
28849ec7b004SRick Macklem 			 */
28859ec7b004SRick Macklem 			if (!nd->nd_repstat && NFSVNO_ISSETGID(&nva) &&
28869ec7b004SRick Macklem 			    nva.na_gid == dirfor.na_gid)
28879ec7b004SRick Macklem 				NFSVNO_UNSET(&nva, gid);
28889ec7b004SRick Macklem 			if (!nd->nd_repstat)
28899ec7b004SRick Macklem 				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
28909ec7b004SRick Macklem 			break;
28919ec7b004SRick Macklem 		case NFSCREATE_EXCLUSIVE:
28929ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2893086f6e0cSRick Macklem 			cverf[0] = *tl++;
2894086f6e0cSRick Macklem 			cverf[1] = *tl;
28959ec7b004SRick Macklem 			break;
2896c59e4cc3SRick Macklem 		case NFSCREATE_EXCLUSIVE41:
2897c59e4cc3SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2898c59e4cc3SRick Macklem 			cverf[0] = *tl++;
2899c59e4cc3SRick Macklem 			cverf[1] = *tl;
2900b4645807SRick Macklem 			error = nfsv4_sattr(nd, NULL, &nva, &attrbits, aclp, p);
2901c59e4cc3SRick Macklem 			if (error != 0)
2902c59e4cc3SRick Macklem 				goto nfsmout;
2903c59e4cc3SRick Macklem 			if (NFSISSET_ATTRBIT(&attrbits,
2904c59e4cc3SRick Macklem 			    NFSATTRBIT_TIMEACCESSSET))
2905c59e4cc3SRick Macklem 				nd->nd_repstat = NFSERR_INVAL;
2906c59e4cc3SRick Macklem 			/*
2907c59e4cc3SRick Macklem 			 * If the na_gid being set is the same as that of
2908c59e4cc3SRick Macklem 			 * the directory it is going in, clear it, since
2909c59e4cc3SRick Macklem 			 * that is what will be set by default. This allows
2910c59e4cc3SRick Macklem 			 * a user that isn't in that group to do the create.
2911c59e4cc3SRick Macklem 			 */
2912c59e4cc3SRick Macklem 			if (nd->nd_repstat == 0 && NFSVNO_ISSETGID(&nva) &&
2913c59e4cc3SRick Macklem 			    nva.na_gid == dirfor.na_gid)
2914c59e4cc3SRick Macklem 				NFSVNO_UNSET(&nva, gid);
2915c59e4cc3SRick Macklem 			if (nd->nd_repstat == 0)
2916c59e4cc3SRick Macklem 				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
2917c59e4cc3SRick Macklem 			break;
29189ec7b004SRick Macklem 		default:
29199ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_BADXDR;
2920a9285ae5SZack Kirsch 			goto nfsmout;
292174b8d63dSPedro F. Giffuni 		}
29229ec7b004SRick Macklem 	} else if (create != NFSV4OPEN_NOCREATE) {
29239ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
2924a9285ae5SZack Kirsch 		goto nfsmout;
29259ec7b004SRick Macklem 	}
29269ec7b004SRick Macklem 
29279ec7b004SRick Macklem 	/*
29289ec7b004SRick Macklem 	 * Now, handle the claim, which usually includes looking up a
29299ec7b004SRick Macklem 	 * name in the directory referenced by dp. The exception is
29309ec7b004SRick Macklem 	 * NFSV4OPEN_CLAIMPREVIOUS.
29319ec7b004SRick Macklem 	 */
29329ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
29339ec7b004SRick Macklem 	claim = fxdr_unsigned(int, *tl);
29349ec7b004SRick Macklem 	if (claim == NFSV4OPEN_CLAIMDELEGATECUR) {
29359ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
29369ec7b004SRick Macklem 		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
29379ec7b004SRick Macklem 		NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
29389ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_DELEGCUR;
29399ec7b004SRick Macklem 	} else if (claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
29409ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_DELEGPREV;
29419ec7b004SRick Macklem 	}
29429ec7b004SRick Macklem 	if (claim == NFSV4OPEN_CLAIMNULL || claim == NFSV4OPEN_CLAIMDELEGATECUR
29439ec7b004SRick Macklem 	    || claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
29449ec7b004SRick Macklem 		if (!nd->nd_repstat && create == NFSV4OPEN_CREATE &&
29459ec7b004SRick Macklem 		    claim != NFSV4OPEN_CLAIMNULL)
29469ec7b004SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
29479ec7b004SRick Macklem 		if (nd->nd_repstat) {
29489ec7b004SRick Macklem 			nd->nd_repstat = nfsrv_opencheck(clientid,
29499ec7b004SRick Macklem 			    &stateid, stp, NULL, nd, p, nd->nd_repstat);
2950a9285ae5SZack Kirsch 			goto nfsmout;
29519ec7b004SRick Macklem 		}
29529ec7b004SRick Macklem 		if (create == NFSV4OPEN_CREATE)
29539ec7b004SRick Macklem 		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
29546c21f6edSKonstantin Belousov 			LOCKPARENT | LOCKLEAF | SAVESTART | NOCACHE);
29559ec7b004SRick Macklem 		else
29569ec7b004SRick Macklem 		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
29579ec7b004SRick Macklem 			LOCKLEAF | SAVESTART);
29589ec7b004SRick Macklem 		nfsvno_setpathbuf(&named, &bufp, &hashp);
29599ec7b004SRick Macklem 		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
29609ec7b004SRick Macklem 		if (error) {
29619ec7b004SRick Macklem 			vrele(dp);
29629ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
29639ec7b004SRick Macklem 			acl_free(aclp);
29649ec7b004SRick Macklem #endif
2965222daa42SConrad Meyer 			free(stp, M_NFSDSTATE);
29669ec7b004SRick Macklem 			nfsvno_relpathbuf(&named);
2967a9285ae5SZack Kirsch 			NFSEXITCODE2(error, nd);
29689ec7b004SRick Macklem 			return (error);
29699ec7b004SRick Macklem 		}
29709ec7b004SRick Macklem 		if (!nd->nd_repstat) {
29719ec7b004SRick Macklem 			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp,
29729ec7b004SRick Macklem 			    p, &dirp);
29739ec7b004SRick Macklem 		} else {
29749ec7b004SRick Macklem 			vrele(dp);
29759ec7b004SRick Macklem 			nfsvno_relpathbuf(&named);
29769ec7b004SRick Macklem 		}
29779ec7b004SRick Macklem 		if (create == NFSV4OPEN_CREATE) {
29789ec7b004SRick Macklem 		    switch (how) {
29799ec7b004SRick Macklem 		    case NFSCREATE_UNCHECKED:
29809ec7b004SRick Macklem 			if (named.ni_vp) {
29819ec7b004SRick Macklem 				/*
29829ec7b004SRick Macklem 				 * Clear the setable attribute bits, except
29839ec7b004SRick Macklem 				 * for Size, if it is being truncated.
29849ec7b004SRick Macklem 				 */
29859ec7b004SRick Macklem 				NFSZERO_ATTRBIT(&attrbits);
29869ec7b004SRick Macklem 				if (NFSVNO_ISSETSIZE(&nva))
29879ec7b004SRick Macklem 					NFSSETBIT_ATTRBIT(&attrbits,
29889ec7b004SRick Macklem 					    NFSATTRBIT_SIZE);
29899ec7b004SRick Macklem 			}
29909ec7b004SRick Macklem 			break;
29919ec7b004SRick Macklem 		    case NFSCREATE_GUARDED:
29929ec7b004SRick Macklem 			if (named.ni_vp && !nd->nd_repstat)
29939ec7b004SRick Macklem 				nd->nd_repstat = EEXIST;
29949ec7b004SRick Macklem 			break;
29959ec7b004SRick Macklem 		    case NFSCREATE_EXCLUSIVE:
29969ec7b004SRick Macklem 			exclusive_flag = 1;
29979ec7b004SRick Macklem 			if (!named.ni_vp)
29989ec7b004SRick Macklem 				nva.na_mode = 0;
2999c59e4cc3SRick Macklem 			break;
3000c59e4cc3SRick Macklem 		    case NFSCREATE_EXCLUSIVE41:
3001c59e4cc3SRick Macklem 			exclusive_flag = 1;
3002c59e4cc3SRick Macklem 			break;
300374b8d63dSPedro F. Giffuni 		    }
30049ec7b004SRick Macklem 		}
30059ec7b004SRick Macklem 		nfsvno_open(nd, &named, clientid, &stateid, stp,
30069ec7b004SRick Macklem 		    &exclusive_flag, &nva, cverf, create, aclp, &attrbits,
300701c27978SEdward Tomasz Napierala 		    nd->nd_cred, exp, &vp);
3008c59e4cc3SRick Macklem 	} else if (claim == NFSV4OPEN_CLAIMPREVIOUS || claim ==
3009c59e4cc3SRick Macklem 	    NFSV4OPEN_CLAIMFH) {
3010c59e4cc3SRick Macklem 		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
30119ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
30129ec7b004SRick Macklem 			i = fxdr_unsigned(int, *tl);
30139ec7b004SRick Macklem 			switch (i) {
30149ec7b004SRick Macklem 			case NFSV4OPEN_DELEGATEREAD:
30159ec7b004SRick Macklem 				stp->ls_flags |= NFSLCK_DELEGREAD;
30169ec7b004SRick Macklem 				break;
30179ec7b004SRick Macklem 			case NFSV4OPEN_DELEGATEWRITE:
30189ec7b004SRick Macklem 				stp->ls_flags |= NFSLCK_DELEGWRITE;
30199ec7b004SRick Macklem 			case NFSV4OPEN_DELEGATENONE:
30209ec7b004SRick Macklem 				break;
30219ec7b004SRick Macklem 			default:
30229ec7b004SRick Macklem 				nd->nd_repstat = NFSERR_BADXDR;
3023a9285ae5SZack Kirsch 				goto nfsmout;
302474b8d63dSPedro F. Giffuni 			}
30259ec7b004SRick Macklem 			stp->ls_flags |= NFSLCK_RECLAIM;
3026c59e4cc3SRick Macklem 		} else {
3027c59e4cc3SRick Macklem 			/* CLAIM_NULL_FH */
3028c59e4cc3SRick Macklem 			if (nd->nd_repstat == 0 && create == NFSV4OPEN_CREATE)
3029c59e4cc3SRick Macklem 				nd->nd_repstat = NFSERR_INVAL;
3030c59e4cc3SRick Macklem 		}
30319ec7b004SRick Macklem 		vp = dp;
303298f234f3SZack Kirsch 		NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
3033abd80ddbSMateusz Guzik 		if (!VN_IS_DOOMED(vp))
3034629fa50eSRick Macklem 			nd->nd_repstat = nfsrv_opencheck(clientid, &stateid,
3035629fa50eSRick Macklem 			    stp, vp, nd, p, nd->nd_repstat);
3036629fa50eSRick Macklem 		else
3037629fa50eSRick Macklem 			nd->nd_repstat = NFSERR_PERM;
30389ec7b004SRick Macklem 	} else {
30399ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
3040a9285ae5SZack Kirsch 		goto nfsmout;
30419ec7b004SRick Macklem 	}
30429ec7b004SRick Macklem 
30439ec7b004SRick Macklem 	/*
30449ec7b004SRick Macklem 	 * Do basic access checking.
30459ec7b004SRick Macklem 	 */
30469ec7b004SRick Macklem 	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
3047de67b496SRick Macklem 		/*
3048de67b496SRick Macklem 		 * The IETF working group decided that this is the correct
3049de67b496SRick Macklem 		 * error return for all non-regular files.
3050de67b496SRick Macklem 		 */
3051d8a5961fSMarcelo Araujo 		nd->nd_repstat = (vp->v_type == VDIR) ? NFSERR_ISDIR : NFSERR_SYMLINK;
30529ec7b004SRick Macklem 	}
3053b0b7d978SRick Macklem 
3054b0b7d978SRick Macklem 	/*
3055b0b7d978SRick Macklem 	 * If the Open is being done for a file that already exists, apply
3056b0b7d978SRick Macklem 	 * normal permission checking including for the file owner, if
3057b0b7d978SRick Macklem 	 * vfs.nfsd.v4openaccess is set.
3058b0b7d978SRick Macklem 	 * Previously, the owner was always allowed to open the file to
3059b0b7d978SRick Macklem 	 * be consistent with the NFS tradition of always allowing the
3060b0b7d978SRick Macklem 	 * owner of the file to write to the file regardless of permissions.
3061b0b7d978SRick Macklem 	 * It now appears that the Linux client expects the owner
3062b0b7d978SRick Macklem 	 * permissions to be checked for opens that are not creating the
3063b0b7d978SRick Macklem 	 * file.  I believe the correct approach is to use the Access
3064b0b7d978SRick Macklem 	 * operation's results to be consistent with NFSv3, but that is
3065b0b7d978SRick Macklem 	 * not what the current Linux client appears to be doing.
3066b0b7d978SRick Macklem 	 * Since both the Linux and OpenSolaris NFSv4 servers do this check,
3067b0b7d978SRick Macklem 	 * I have enabled it by default.
3068b0b7d978SRick Macklem 	 * If this semantic change causes a problem, it can be disabled by
3069b0b7d978SRick Macklem 	 * setting the sysctl vfs.nfsd.v4openaccess to 0 to re-enable the
3070b0b7d978SRick Macklem 	 * previous semantics.
3071b0b7d978SRick Macklem 	 */
3072b0b7d978SRick Macklem 	if (nfsrv_openaccess && create == NFSV4OPEN_NOCREATE)
3073b0b7d978SRick Macklem 		override = NFSACCCHK_NOOVERRIDE;
3074b0b7d978SRick Macklem 	else
3075b0b7d978SRick Macklem 		override = NFSACCCHK_ALLOWOWNER;
30769ec7b004SRick Macklem 	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_WRITEACCESS))
30778da45f2cSRick Macklem 	    nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred,
3078b0b7d978SRick Macklem 	        exp, p, override, NFSACCCHK_VPISLOCKED, NULL);
30799ec7b004SRick Macklem 	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_READACCESS)) {
30808da45f2cSRick Macklem 	    nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred,
3081b0b7d978SRick Macklem 	        exp, p, override, NFSACCCHK_VPISLOCKED, NULL);
30829ec7b004SRick Macklem 	    if (nd->nd_repstat)
30838da45f2cSRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
3084b0b7d978SRick Macklem 		    nd->nd_cred, exp, p, override,
30858da45f2cSRick Macklem 		    NFSACCCHK_VPISLOCKED, NULL);
30869ec7b004SRick Macklem 	}
30879ec7b004SRick Macklem 
3088086f6e0cSRick Macklem 	if (!nd->nd_repstat) {
308990d2dfabSRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
3090086f6e0cSRick Macklem 		if (!nd->nd_repstat) {
3091086f6e0cSRick Macklem 			tverf[0] = nva.na_atime.tv_sec;
3092086f6e0cSRick Macklem 			tverf[1] = nva.na_atime.tv_nsec;
3093086f6e0cSRick Macklem 		}
3094086f6e0cSRick Macklem 	}
3095086f6e0cSRick Macklem 	if (!nd->nd_repstat && exclusive_flag && (cverf[0] != tverf[0] ||
3096086f6e0cSRick Macklem 	    cverf[1] != tverf[1]))
30979ec7b004SRick Macklem 		nd->nd_repstat = EEXIST;
30989ec7b004SRick Macklem 	/*
30999ec7b004SRick Macklem 	 * Do the open locking/delegation stuff.
31009ec7b004SRick Macklem 	 */
31019ec7b004SRick Macklem 	if (!nd->nd_repstat)
31029ec7b004SRick Macklem 	    nd->nd_repstat = nfsrv_openctrl(nd, vp, &stp, clientid, &stateid,
31039ec7b004SRick Macklem 		&delegstateid, &rflags, exp, p, nva.na_filerev);
31049ec7b004SRick Macklem 
31059ec7b004SRick Macklem 	/*
31069ec7b004SRick Macklem 	 * vp must be unlocked before the call to nfsvno_getattr(dirp,...)
31079ec7b004SRick Macklem 	 * below, to avoid a deadlock with the lookup in nfsvno_namei() above.
31089ec7b004SRick Macklem 	 * (ie: Leave the NFSVOPUNLOCK() about here.)
31099ec7b004SRick Macklem 	 */
31109ec7b004SRick Macklem 	if (vp)
3111b249ce48SMateusz Guzik 		NFSVOPUNLOCK(vp);
31129ec7b004SRick Macklem 	if (stp)
3113222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
31149ec7b004SRick Macklem 	if (!nd->nd_repstat && dirp)
311590d2dfabSRick Macklem 		nd->nd_repstat = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
31169ec7b004SRick Macklem 	if (!nd->nd_repstat) {
311790d2dfabSRick Macklem 		/* For NFSv4.1, set the Current StateID. */
311890d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0) {
311990d2dfabSRick Macklem 			nd->nd_curstateid = stateid;
312090d2dfabSRick Macklem 			nd->nd_flag |= ND_CURSTATEID;
312190d2dfabSRick Macklem 		}
31229ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
31239ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
31249ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
31259ec7b004SRick Macklem 		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
31269ec7b004SRick Macklem 		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
31279ec7b004SRick Macklem 			*tl++ = newnfs_true;
31289ec7b004SRick Macklem 			*tl++ = 0;
31299ec7b004SRick Macklem 			*tl++ = 0;
31309ec7b004SRick Macklem 			*tl++ = 0;
31319ec7b004SRick Macklem 			*tl++ = 0;
31329ec7b004SRick Macklem 		} else {
31339ec7b004SRick Macklem 			*tl++ = newnfs_false;	/* Since dirp is not locked */
31349ec7b004SRick Macklem 			txdr_hyper(dirfor.na_filerev, tl);
31359ec7b004SRick Macklem 			tl += 2;
31369ec7b004SRick Macklem 			txdr_hyper(diraft.na_filerev, tl);
31379ec7b004SRick Macklem 			tl += 2;
31389ec7b004SRick Macklem 		}
31399ec7b004SRick Macklem 		*tl = txdr_unsigned(rflags & NFSV4OPEN_RFLAGS);
31409ec7b004SRick Macklem 		(void) nfsrv_putattrbit(nd, &attrbits);
31419ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
31429ec7b004SRick Macklem 		if (rflags & NFSV4OPEN_READDELEGATE)
31439ec7b004SRick Macklem 			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEREAD);
31449ec7b004SRick Macklem 		else if (rflags & NFSV4OPEN_WRITEDELEGATE)
31459ec7b004SRick Macklem 			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEWRITE);
3146c59e4cc3SRick Macklem 		else if (retext != 0) {
3147c59e4cc3SRick Macklem 			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONEEXT);
31485d54f186SRick Macklem 			if ((rflags & NFSV4OPEN_WDNOTWANTED) != 0) {
31495d54f186SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
31505d54f186SRick Macklem 				*tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
31515d54f186SRick Macklem 			} else if ((rflags & NFSV4OPEN_WDSUPPFTYPE) != 0) {
31525d54f186SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
31535d54f186SRick Macklem 				*tl = txdr_unsigned(NFSV4OPEN_NOTSUPPFTYPE);
31545d54f186SRick Macklem 			} else if ((rflags & NFSV4OPEN_WDCONTENTION) != 0) {
3155c59e4cc3SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3156c59e4cc3SRick Macklem 				*tl++ = txdr_unsigned(NFSV4OPEN_CONTENTION);
3157c59e4cc3SRick Macklem 				*tl = newnfs_false;
3158c59e4cc3SRick Macklem 			} else if ((rflags & NFSV4OPEN_WDRESOURCE) != 0) {
3159c59e4cc3SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3160c59e4cc3SRick Macklem 				*tl++ = txdr_unsigned(NFSV4OPEN_RESOURCE);
3161c59e4cc3SRick Macklem 				*tl = newnfs_false;
3162c59e4cc3SRick Macklem 			} else {
3163c59e4cc3SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3164c59e4cc3SRick Macklem 				*tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
3165c59e4cc3SRick Macklem 			}
3166c59e4cc3SRick Macklem 		} else
31679ec7b004SRick Macklem 			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONE);
31689ec7b004SRick Macklem 		if (rflags & (NFSV4OPEN_READDELEGATE|NFSV4OPEN_WRITEDELEGATE)) {
31699ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID+NFSX_UNSIGNED);
31709ec7b004SRick Macklem 			*tl++ = txdr_unsigned(delegstateid.seqid);
31719ec7b004SRick Macklem 			NFSBCOPY((caddr_t)delegstateid.other, (caddr_t)tl,
31729ec7b004SRick Macklem 			    NFSX_STATEIDOTHER);
31739ec7b004SRick Macklem 			tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
31749ec7b004SRick Macklem 			if (rflags & NFSV4OPEN_RECALL)
31759ec7b004SRick Macklem 				*tl = newnfs_true;
31769ec7b004SRick Macklem 			else
31779ec7b004SRick Macklem 				*tl = newnfs_false;
31789ec7b004SRick Macklem 			if (rflags & NFSV4OPEN_WRITEDELEGATE) {
31799ec7b004SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
31809ec7b004SRick Macklem 				*tl++ = txdr_unsigned(NFSV4OPEN_LIMITSIZE);
31819ec7b004SRick Macklem 				txdr_hyper(nva.na_size, tl);
31829ec7b004SRick Macklem 			}
31839ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
31849ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSV4ACE_ALLOWEDTYPE);
31859ec7b004SRick Macklem 			*tl++ = txdr_unsigned(0x0);
31869ec7b004SRick Macklem 			acemask = NFSV4ACE_ALLFILESMASK;
31879ec7b004SRick Macklem 			if (nva.na_mode & S_IRUSR)
31889ec7b004SRick Macklem 			    acemask |= NFSV4ACE_READMASK;
31899ec7b004SRick Macklem 			if (nva.na_mode & S_IWUSR)
31909ec7b004SRick Macklem 			    acemask |= NFSV4ACE_WRITEMASK;
31919ec7b004SRick Macklem 			if (nva.na_mode & S_IXUSR)
31929ec7b004SRick Macklem 			    acemask |= NFSV4ACE_EXECUTEMASK;
31939ec7b004SRick Macklem 			*tl = txdr_unsigned(acemask);
31949ec7b004SRick Macklem 			(void) nfsm_strtom(nd, "OWNER@", 6);
31959ec7b004SRick Macklem 		}
31969ec7b004SRick Macklem 		*vpp = vp;
31979ec7b004SRick Macklem 	} else if (vp) {
31989ec7b004SRick Macklem 		vrele(vp);
31999ec7b004SRick Macklem 	}
32009ec7b004SRick Macklem 	if (dirp)
32019ec7b004SRick Macklem 		vrele(dirp);
32029ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
32039ec7b004SRick Macklem 	acl_free(aclp);
32049ec7b004SRick Macklem #endif
3205a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
32069ec7b004SRick Macklem 	return (0);
32079ec7b004SRick Macklem nfsmout:
32089ec7b004SRick Macklem 	vrele(dp);
32099ec7b004SRick Macklem #ifdef NFS4_ACL_EXTATTR_NAME
32109ec7b004SRick Macklem 	acl_free(aclp);
32119ec7b004SRick Macklem #endif
32129ec7b004SRick Macklem 	if (stp)
3213222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
3214a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
32159ec7b004SRick Macklem 	return (error);
32169ec7b004SRick Macklem }
32179ec7b004SRick Macklem 
32189ec7b004SRick Macklem /*
32199ec7b004SRick Macklem  * nfsv4 close service
32209ec7b004SRick Macklem  */
3221b9cc3262SRyan Moeller int
32229ec7b004SRick Macklem nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram,
3223af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
32249ec7b004SRick Macklem {
32259ec7b004SRick Macklem 	u_int32_t *tl;
32269ec7b004SRick Macklem 	struct nfsstate st, *stp = &st;
322790d2dfabSRick Macklem 	int error = 0, writeacc;
32289ec7b004SRick Macklem 	nfsv4stateid_t stateid;
32299ec7b004SRick Macklem 	nfsquad_t clientid;
323090d2dfabSRick Macklem 	struct nfsvattr na;
3231af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
32329ec7b004SRick Macklem 
32339ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
32349ec7b004SRick Macklem 	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
32359ec7b004SRick Macklem 	stp->ls_ownerlen = 0;
32369ec7b004SRick Macklem 	stp->ls_op = nd->nd_rp;
32379ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
32389ec7b004SRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
32399ec7b004SRick Macklem 	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
32409ec7b004SRick Macklem 	    NFSX_STATEIDOTHER);
324190d2dfabSRick Macklem 
324290d2dfabSRick Macklem 	/*
324390d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
324490d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
324590d2dfabSRick Macklem 	 */
324690d2dfabSRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 1 &&
324790d2dfabSRick Macklem 	    stp->ls_stateid.other[0] == 0 && stp->ls_stateid.other[1] == 0 &&
324890d2dfabSRick Macklem 	    stp->ls_stateid.other[2] == 0) {
324990d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0)
325090d2dfabSRick Macklem 			stp->ls_stateid = nd->nd_curstateid;
325190d2dfabSRick Macklem 		else {
325290d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
325390d2dfabSRick Macklem 			goto nfsmout;
325490d2dfabSRick Macklem 		}
325590d2dfabSRick Macklem 	}
325690d2dfabSRick Macklem 
32579ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_CLOSE;
32589ec7b004SRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0];
32599ec7b004SRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1];
3260c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3261c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3262c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3263c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3264c59e4cc3SRick Macklem 			printf("EEK8 multiple clids\n");
32659ec7b004SRick Macklem 	} else {
3266c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3267c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
32689ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
32699ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
32709ec7b004SRick Macklem 	}
327190d2dfabSRick Macklem 	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p,
327290d2dfabSRick Macklem 	    &writeacc);
327390d2dfabSRick Macklem 	/* For pNFS, update the attributes. */
327490d2dfabSRick Macklem 	if (writeacc != 0 || nfsrv_pnfsatime != 0)
327590d2dfabSRick Macklem 		nfsrv_updatemdsattr(vp, &na, p);
32769ec7b004SRick Macklem 	vput(vp);
32779ec7b004SRick Macklem 	if (!nd->nd_repstat) {
327890d2dfabSRick Macklem 		/*
327990d2dfabSRick Macklem 		 * If the stateid that has been closed is the current stateid,
328090d2dfabSRick Macklem 		 * unset it.
328190d2dfabSRick Macklem 		 */
328290d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0 &&
328390d2dfabSRick Macklem 		    stateid.other[0] == nd->nd_curstateid.other[0] &&
328490d2dfabSRick Macklem 		    stateid.other[1] == nd->nd_curstateid.other[1] &&
328590d2dfabSRick Macklem 		    stateid.other[2] == nd->nd_curstateid.other[2])
328690d2dfabSRick Macklem 			nd->nd_flag &= ~ND_CURSTATEID;
32879ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
32889ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
32899ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
32909ec7b004SRick Macklem 	}
3291a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
32929ec7b004SRick Macklem 	return (0);
32939ec7b004SRick Macklem nfsmout:
32949ec7b004SRick Macklem 	vput(vp);
3295a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
32969ec7b004SRick Macklem 	return (error);
32979ec7b004SRick Macklem }
32989ec7b004SRick Macklem 
32999ec7b004SRick Macklem /*
33009ec7b004SRick Macklem  * nfsv4 delegpurge service
33019ec7b004SRick Macklem  */
3302b9cc3262SRyan Moeller int
33039ec7b004SRick Macklem nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram,
3304af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
33059ec7b004SRick Macklem {
33069ec7b004SRick Macklem 	u_int32_t *tl;
33079ec7b004SRick Macklem 	int error = 0;
33089ec7b004SRick Macklem 	nfsquad_t clientid;
3309af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
33109ec7b004SRick Macklem 
3311c9aad40fSRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3312b1cfc0d9SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
3313a9285ae5SZack Kirsch 		goto nfsmout;
3314b1cfc0d9SRick Macklem 	}
33159ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
33169ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
33179ec7b004SRick Macklem 	clientid.lval[1] = *tl;
3318c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3319c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3320c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3321c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3322c59e4cc3SRick Macklem 			printf("EEK9 multiple clids\n");
33239ec7b004SRick Macklem 	} else {
3324c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3325c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
33269ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
33279ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
33289ec7b004SRick Macklem 	}
3329c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_delegupdate(nd, clientid, NULL, NULL,
333090d2dfabSRick Macklem 	    NFSV4OP_DELEGPURGE, nd->nd_cred, p, NULL);
33319ec7b004SRick Macklem nfsmout:
3332a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
33339ec7b004SRick Macklem 	return (error);
33349ec7b004SRick Macklem }
33359ec7b004SRick Macklem 
33369ec7b004SRick Macklem /*
33379ec7b004SRick Macklem  * nfsv4 delegreturn service
33389ec7b004SRick Macklem  */
3339b9cc3262SRyan Moeller int
33409ec7b004SRick Macklem nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram,
3341af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
33429ec7b004SRick Macklem {
33439ec7b004SRick Macklem 	u_int32_t *tl;
334490d2dfabSRick Macklem 	int error = 0, writeacc;
33459ec7b004SRick Macklem 	nfsv4stateid_t stateid;
33469ec7b004SRick Macklem 	nfsquad_t clientid;
334790d2dfabSRick Macklem 	struct nfsvattr na;
3348af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
33499ec7b004SRick Macklem 
33509ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
33519ec7b004SRick Macklem 	stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
33529ec7b004SRick Macklem 	NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, NFSX_STATEIDOTHER);
33539ec7b004SRick Macklem 	clientid.lval[0] = stateid.other[0];
33549ec7b004SRick Macklem 	clientid.lval[1] = stateid.other[1];
3355c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3356c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3357c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3358c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3359c59e4cc3SRick Macklem 			printf("EEK10 multiple clids\n");
33609ec7b004SRick Macklem 	} else {
3361c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3362c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
33639ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
33649ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
33659ec7b004SRick Macklem 	}
3366c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_delegupdate(nd, clientid, &stateid, vp,
336790d2dfabSRick Macklem 	    NFSV4OP_DELEGRETURN, nd->nd_cred, p, &writeacc);
336890d2dfabSRick Macklem 	/* For pNFS, update the attributes. */
336990d2dfabSRick Macklem 	if (writeacc != 0 || nfsrv_pnfsatime != 0)
337090d2dfabSRick Macklem 		nfsrv_updatemdsattr(vp, &na, p);
33719ec7b004SRick Macklem nfsmout:
33729ec7b004SRick Macklem 	vput(vp);
3373a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
33749ec7b004SRick Macklem 	return (error);
33759ec7b004SRick Macklem }
33769ec7b004SRick Macklem 
33779ec7b004SRick Macklem /*
33789ec7b004SRick Macklem  * nfsv4 get file handle service
33799ec7b004SRick Macklem  */
3380b9cc3262SRyan Moeller int
33819ec7b004SRick Macklem nfsrvd_getfh(struct nfsrv_descript *nd, __unused int isdgram,
3382af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
33839ec7b004SRick Macklem {
33849ec7b004SRick Macklem 	fhandle_t fh;
3385af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
33869ec7b004SRick Macklem 
33879ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
33889ec7b004SRick Macklem 	vput(vp);
33899ec7b004SRick Macklem 	if (!nd->nd_repstat)
33909ec7b004SRick Macklem 		(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
3391a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
33929ec7b004SRick Macklem 	return (0);
33939ec7b004SRick Macklem }
33949ec7b004SRick Macklem 
33959ec7b004SRick Macklem /*
33969ec7b004SRick Macklem  * nfsv4 open confirm service
33979ec7b004SRick Macklem  */
3398b9cc3262SRyan Moeller int
33999ec7b004SRick Macklem nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram,
3400af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
34019ec7b004SRick Macklem {
34029ec7b004SRick Macklem 	u_int32_t *tl;
34039ec7b004SRick Macklem 	struct nfsstate st, *stp = &st;
34049ec7b004SRick Macklem 	int error = 0;
34059ec7b004SRick Macklem 	nfsv4stateid_t stateid;
34069ec7b004SRick Macklem 	nfsquad_t clientid;
3407af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
34089ec7b004SRick Macklem 
3409c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0) {
3410c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
3411c59e4cc3SRick Macklem 		goto nfsmout;
3412c59e4cc3SRick Macklem 	}
34139ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
34149ec7b004SRick Macklem 	stp->ls_ownerlen = 0;
34159ec7b004SRick Macklem 	stp->ls_op = nd->nd_rp;
34169ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
34179ec7b004SRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
34189ec7b004SRick Macklem 	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
34199ec7b004SRick Macklem 	    NFSX_STATEIDOTHER);
34209ec7b004SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
34219ec7b004SRick Macklem 	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl);
34229ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_CONFIRM;
34239ec7b004SRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0];
34249ec7b004SRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1];
3425c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3426c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3427c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3428c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3429c59e4cc3SRick Macklem 			printf("EEK11 multiple clids\n");
34309ec7b004SRick Macklem 	} else {
3431c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3432c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
34339ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
34349ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
34359ec7b004SRick Macklem 	}
343690d2dfabSRick Macklem 	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p,
343790d2dfabSRick Macklem 	    NULL);
34389ec7b004SRick Macklem 	if (!nd->nd_repstat) {
34399ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
34409ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
34419ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
34429ec7b004SRick Macklem 	}
34439ec7b004SRick Macklem nfsmout:
34449ec7b004SRick Macklem 	vput(vp);
3445a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
34469ec7b004SRick Macklem 	return (error);
34479ec7b004SRick Macklem }
34489ec7b004SRick Macklem 
34499ec7b004SRick Macklem /*
34509ec7b004SRick Macklem  * nfsv4 open downgrade service
34519ec7b004SRick Macklem  */
3452b9cc3262SRyan Moeller int
34539ec7b004SRick Macklem nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram,
3454af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
34559ec7b004SRick Macklem {
34569ec7b004SRick Macklem 	u_int32_t *tl;
34579ec7b004SRick Macklem 	int i;
34589ec7b004SRick Macklem 	struct nfsstate st, *stp = &st;
34599ec7b004SRick Macklem 	int error = 0;
34609ec7b004SRick Macklem 	nfsv4stateid_t stateid;
34619ec7b004SRick Macklem 	nfsquad_t clientid;
3462af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
34639ec7b004SRick Macklem 
3464d8a5961fSMarcelo Araujo 	/* opendowngrade can only work on a file object.*/
3465d8a5961fSMarcelo Araujo 	if (vp->v_type != VREG) {
3466d8a5961fSMarcelo Araujo 		error = NFSERR_INVAL;
3467d8a5961fSMarcelo Araujo 		goto nfsmout;
3468d8a5961fSMarcelo Araujo 	}
34699ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
34709ec7b004SRick Macklem 	stp->ls_ownerlen = 0;
34719ec7b004SRick Macklem 	stp->ls_op = nd->nd_rp;
34729ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
34739ec7b004SRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
34749ec7b004SRick Macklem 	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
34759ec7b004SRick Macklem 	    NFSX_STATEIDOTHER);
34769ec7b004SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
347790d2dfabSRick Macklem 
347890d2dfabSRick Macklem 	/*
347990d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
348090d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
348190d2dfabSRick Macklem 	 */
348290d2dfabSRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 1 &&
348390d2dfabSRick Macklem 	    stp->ls_stateid.other[0] == 0 && stp->ls_stateid.other[1] == 0 &&
348490d2dfabSRick Macklem 	    stp->ls_stateid.other[2] == 0) {
348590d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0)
348690d2dfabSRick Macklem 			stp->ls_stateid = nd->nd_curstateid;
348790d2dfabSRick Macklem 		else {
348890d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
348990d2dfabSRick Macklem 			goto nfsmout;
349090d2dfabSRick Macklem 		}
349190d2dfabSRick Macklem 	}
349290d2dfabSRick Macklem 
34939ec7b004SRick Macklem 	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
34949ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl++);
34956269d663SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0)
34966269d663SRick Macklem 		i &= ~NFSV4OPEN_WANTDELEGMASK;
34979ec7b004SRick Macklem 	switch (i) {
34989ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSREAD:
34999ec7b004SRick Macklem 		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_DOWNGRADE);
35009ec7b004SRick Macklem 		break;
35019ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSWRITE:
35029ec7b004SRick Macklem 		stp->ls_flags = (NFSLCK_WRITEACCESS | NFSLCK_DOWNGRADE);
35039ec7b004SRick Macklem 		break;
35049ec7b004SRick Macklem 	case NFSV4OPEN_ACCESSBOTH:
35059ec7b004SRick Macklem 		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_WRITEACCESS |
35069ec7b004SRick Macklem 		    NFSLCK_DOWNGRADE);
35079ec7b004SRick Macklem 		break;
35089ec7b004SRick Macklem 	default:
35096269d663SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
351074b8d63dSPedro F. Giffuni 	}
35119ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl);
35129ec7b004SRick Macklem 	switch (i) {
35139ec7b004SRick Macklem 	case NFSV4OPEN_DENYNONE:
35149ec7b004SRick Macklem 		break;
35159ec7b004SRick Macklem 	case NFSV4OPEN_DENYREAD:
35169ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_READDENY;
35179ec7b004SRick Macklem 		break;
35189ec7b004SRick Macklem 	case NFSV4OPEN_DENYWRITE:
35199ec7b004SRick Macklem 		stp->ls_flags |= NFSLCK_WRITEDENY;
35209ec7b004SRick Macklem 		break;
35219ec7b004SRick Macklem 	case NFSV4OPEN_DENYBOTH:
35229ec7b004SRick Macklem 		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
35239ec7b004SRick Macklem 		break;
35249ec7b004SRick Macklem 	default:
35256269d663SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
352674b8d63dSPedro F. Giffuni 	}
35279ec7b004SRick Macklem 
35289ec7b004SRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0];
35299ec7b004SRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1];
3530c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3531c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3532c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3533c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3534c59e4cc3SRick Macklem 			printf("EEK12 multiple clids\n");
35359ec7b004SRick Macklem 	} else {
3536c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3537c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
35389ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
35399ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
35409ec7b004SRick Macklem 	}
35419ec7b004SRick Macklem 	if (!nd->nd_repstat)
35429ec7b004SRick Macklem 		nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid,
354390d2dfabSRick Macklem 		    nd, p, NULL);
35449ec7b004SRick Macklem 	if (!nd->nd_repstat) {
354590d2dfabSRick Macklem 		/* For NFSv4.1, set the Current StateID. */
354690d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0) {
354790d2dfabSRick Macklem 			nd->nd_curstateid = stateid;
354890d2dfabSRick Macklem 			nd->nd_flag |= ND_CURSTATEID;
354990d2dfabSRick Macklem 		}
35509ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
35519ec7b004SRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
35529ec7b004SRick Macklem 		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
35539ec7b004SRick Macklem 	}
35549ec7b004SRick Macklem nfsmout:
35559ec7b004SRick Macklem 	vput(vp);
3556a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
35579ec7b004SRick Macklem 	return (error);
35589ec7b004SRick Macklem }
35599ec7b004SRick Macklem 
35609ec7b004SRick Macklem /*
35619ec7b004SRick Macklem  * nfsv4 renew lease service
35629ec7b004SRick Macklem  */
3563b9cc3262SRyan Moeller int
35649ec7b004SRick Macklem nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram,
3565af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
35669ec7b004SRick Macklem {
35679ec7b004SRick Macklem 	u_int32_t *tl;
35689ec7b004SRick Macklem 	int error = 0;
35699ec7b004SRick Macklem 	nfsquad_t clientid;
3570af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
35719ec7b004SRick Macklem 
3572c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0) {
3573c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
3574c59e4cc3SRick Macklem 		goto nfsmout;
3575c59e4cc3SRick Macklem 	}
3576c9aad40fSRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3577b1cfc0d9SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
3578a9285ae5SZack Kirsch 		goto nfsmout;
3579b1cfc0d9SRick Macklem 	}
35809ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
35819ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
35829ec7b004SRick Macklem 	clientid.lval[1] = *tl;
3583c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3584c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3585c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
3586c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
3587c59e4cc3SRick Macklem 			printf("EEK13 multiple clids\n");
35889ec7b004SRick Macklem 	} else {
3589c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
3590c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
35919ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
35929ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
35939ec7b004SRick Macklem 	}
35949ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW),
3595c59e4cc3SRick Macklem 	    NULL, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p);
35969ec7b004SRick Macklem nfsmout:
3597a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
35989ec7b004SRick Macklem 	return (error);
35999ec7b004SRick Macklem }
36009ec7b004SRick Macklem 
36019ec7b004SRick Macklem /*
36029ec7b004SRick Macklem  * nfsv4 security info service
36039ec7b004SRick Macklem  */
3604b9cc3262SRyan Moeller int
36059ec7b004SRick Macklem nfsrvd_secinfo(struct nfsrv_descript *nd, int isdgram,
3606af444b18SEdward Tomasz Napierala     vnode_t dp, struct nfsexstuff *exp)
36079ec7b004SRick Macklem {
36089ec7b004SRick Macklem 	u_int32_t *tl;
36099ec7b004SRick Macklem 	int len;
36109ec7b004SRick Macklem 	struct nameidata named;
36119ec7b004SRick Macklem 	vnode_t dirp = NULL, vp;
36129ec7b004SRick Macklem 	struct nfsrvfh fh;
36139ec7b004SRick Macklem 	struct nfsexstuff retnes;
36149ec7b004SRick Macklem 	u_int32_t *sizp;
3615a9285ae5SZack Kirsch 	int error = 0, savflag, i;
36169ec7b004SRick Macklem 	char *bufp;
36179ec7b004SRick Macklem 	u_long *hashp;
3618af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
36199ec7b004SRick Macklem 
36209ec7b004SRick Macklem 	/*
36219ec7b004SRick Macklem 	 * All this just to get the export flags for the name.
36229ec7b004SRick Macklem 	 */
36239ec7b004SRick Macklem 	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
36249ec7b004SRick Macklem 	    LOCKLEAF | SAVESTART);
36259ec7b004SRick Macklem 	nfsvno_setpathbuf(&named, &bufp, &hashp);
36269ec7b004SRick Macklem 	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
36279ec7b004SRick Macklem 	if (error) {
36289ec7b004SRick Macklem 		vput(dp);
36299ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
3630a9285ae5SZack Kirsch 		goto out;
36319ec7b004SRick Macklem 	}
36329ec7b004SRick Macklem 	if (!nd->nd_repstat) {
36339ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
36349ec7b004SRick Macklem 	} else {
36359ec7b004SRick Macklem 		vput(dp);
36369ec7b004SRick Macklem 		nfsvno_relpathbuf(&named);
36379ec7b004SRick Macklem 	}
36389ec7b004SRick Macklem 	if (dirp)
36399ec7b004SRick Macklem 		vrele(dirp);
36409ec7b004SRick Macklem 	if (nd->nd_repstat)
3641a9285ae5SZack Kirsch 		goto out;
36429ec7b004SRick Macklem 	vrele(named.ni_startdir);
36439ec7b004SRick Macklem 	nfsvno_relpathbuf(&named);
36449ec7b004SRick Macklem 	fh.nfsrvfh_len = NFSX_MYFH;
36459ec7b004SRick Macklem 	vp = named.ni_vp;
36469ec7b004SRick Macklem 	nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
36479ec7b004SRick Macklem 	vput(vp);
36489ec7b004SRick Macklem 	savflag = nd->nd_flag;
36499ec7b004SRick Macklem 	if (!nd->nd_repstat) {
36505edc9102SEdward Tomasz Napierala 		nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0);
36519ec7b004SRick Macklem 		if (vp)
36529ec7b004SRick Macklem 			vput(vp);
36539ec7b004SRick Macklem 	}
36549ec7b004SRick Macklem 	nd->nd_flag = savflag;
36559ec7b004SRick Macklem 	if (nd->nd_repstat)
3656a9285ae5SZack Kirsch 		goto out;
36579ec7b004SRick Macklem 
36589ec7b004SRick Macklem 	/*
36599ec7b004SRick Macklem 	 * Finally have the export flags for name, so we can create
36609ec7b004SRick Macklem 	 * the security info.
36619ec7b004SRick Macklem 	 */
36629ec7b004SRick Macklem 	len = 0;
36639ec7b004SRick Macklem 	NFSM_BUILD(sizp, u_int32_t *, NFSX_UNSIGNED);
366498ad4453SRick Macklem 	for (i = 0; i < retnes.nes_numsecflavor; i++) {
366598ad4453SRick Macklem 		if (retnes.nes_secflavors[i] == AUTH_SYS) {
36669ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
36679ec7b004SRick Macklem 			*tl = txdr_unsigned(RPCAUTH_UNIX);
36689ec7b004SRick Macklem 			len++;
366998ad4453SRick Macklem 		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) {
36709ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
36719ec7b004SRick Macklem 			*tl++ = txdr_unsigned(RPCAUTH_GSS);
36729ec7b004SRick Macklem 			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
36739ec7b004SRick Macklem 			    nfsgss_mechlist[KERBV_MECH].len);
36749ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
36759ec7b004SRick Macklem 			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
367698ad4453SRick Macklem 			*tl = txdr_unsigned(RPCAUTHGSS_SVCNONE);
36779ec7b004SRick Macklem 			len++;
367898ad4453SRick Macklem 		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) {
367998ad4453SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
368098ad4453SRick Macklem 			*tl++ = txdr_unsigned(RPCAUTH_GSS);
368198ad4453SRick Macklem 			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
368298ad4453SRick Macklem 			    nfsgss_mechlist[KERBV_MECH].len);
368398ad4453SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
368498ad4453SRick Macklem 			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
368598ad4453SRick Macklem 			*tl = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
368698ad4453SRick Macklem 			len++;
368798ad4453SRick Macklem 		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) {
368898ad4453SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
368998ad4453SRick Macklem 			*tl++ = txdr_unsigned(RPCAUTH_GSS);
369098ad4453SRick Macklem 			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
369198ad4453SRick Macklem 			    nfsgss_mechlist[KERBV_MECH].len);
369298ad4453SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
369398ad4453SRick Macklem 			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
369498ad4453SRick Macklem 			*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
369598ad4453SRick Macklem 			len++;
369698ad4453SRick Macklem 		}
36979ec7b004SRick Macklem 	}
36989ec7b004SRick Macklem 	*sizp = txdr_unsigned(len);
3699a9285ae5SZack Kirsch 
3700a9285ae5SZack Kirsch out:
3701a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
3702a9285ae5SZack Kirsch 	return (error);
37039ec7b004SRick Macklem }
37049ec7b004SRick Macklem 
37059ec7b004SRick Macklem /*
37069ec7b004SRick Macklem  * nfsv4 set client id service
37079ec7b004SRick Macklem  */
3708b9cc3262SRyan Moeller int
37099ec7b004SRick Macklem nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram,
3710af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
37119ec7b004SRick Macklem {
37129ec7b004SRick Macklem 	u_int32_t *tl;
37139ec7b004SRick Macklem 	int i;
37149ec7b004SRick Macklem 	int error = 0, idlen;
37159ec7b004SRick Macklem 	struct nfsclient *clp = NULL;
3716ed2f1001SRick Macklem #ifdef INET
3717ed2f1001SRick Macklem 	struct sockaddr_in *rin;
3718ed2f1001SRick Macklem #endif
3719ed2f1001SRick Macklem #ifdef INET6
3720ed2f1001SRick Macklem 	struct sockaddr_in6 *rin6;
3721ed2f1001SRick Macklem #endif
3722ed2f1001SRick Macklem #if defined(INET) || defined(INET6)
3723ed2f1001SRick Macklem 	u_char *ucp, *ucp2;
3724ed2f1001SRick Macklem #endif
3725ed2f1001SRick Macklem 	u_char *verf, *addrbuf;
37269ec7b004SRick Macklem 	nfsquad_t clientid, confirm;
3727af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
37289ec7b004SRick Macklem 
3729c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0) {
3730c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
3731c59e4cc3SRick Macklem 		goto nfsmout;
3732c59e4cc3SRick Macklem 	}
3733c9aad40fSRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
37349ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
3735a9285ae5SZack Kirsch 		goto out;
37369ec7b004SRick Macklem 	}
37379ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
37389ec7b004SRick Macklem 	verf = (u_char *)tl;
37399ec7b004SRick Macklem 	tl += (NFSX_VERF / NFSX_UNSIGNED);
37409ec7b004SRick Macklem 	i = fxdr_unsigned(int, *tl);
37419ec7b004SRick Macklem 	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
37429ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
3743a9285ae5SZack Kirsch 		goto nfsmout;
37449ec7b004SRick Macklem 	}
37459ec7b004SRick Macklem 	idlen = i;
37469ec7b004SRick Macklem 	if (nd->nd_flag & ND_GSS)
37479ec7b004SRick Macklem 		i += nd->nd_princlen;
37481f54e596SRick Macklem 	clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
37491f54e596SRick Macklem 	    M_ZERO);
37501f54e596SRick Macklem 	clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
37511f54e596SRick Macklem 	    nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
37529ec7b004SRick Macklem 	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
3753ed2f1001SRick Macklem 	/* Allocated large enough for an AF_INET or AF_INET6 socket. */
3754ed2f1001SRick Macklem 	clp->lc_req.nr_nam = malloc(sizeof(struct sockaddr_in6), M_SONAME,
3755b97b91b5SConrad Meyer 	    M_WAITOK | M_ZERO);
37569ec7b004SRick Macklem 	clp->lc_req.nr_cred = NULL;
37579ec7b004SRick Macklem 	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
37589ec7b004SRick Macklem 	clp->lc_idlen = idlen;
37599ec7b004SRick Macklem 	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
37609ec7b004SRick Macklem 	if (error)
37619ec7b004SRick Macklem 		goto nfsmout;
37629ec7b004SRick Macklem 	if (nd->nd_flag & ND_GSS) {
37639ec7b004SRick Macklem 		clp->lc_flags = LCL_GSS;
37649ec7b004SRick Macklem 		if (nd->nd_flag & ND_GSSINTEGRITY)
37659ec7b004SRick Macklem 			clp->lc_flags |= LCL_GSSINTEGRITY;
37669ec7b004SRick Macklem 		else if (nd->nd_flag & ND_GSSPRIVACY)
37679ec7b004SRick Macklem 			clp->lc_flags |= LCL_GSSPRIVACY;
37689ec7b004SRick Macklem 	} else {
37699ec7b004SRick Macklem 		clp->lc_flags = 0;
37709ec7b004SRick Macklem 	}
37719ec7b004SRick Macklem 	if ((nd->nd_flag & ND_GSS) && nd->nd_princlen > 0) {
37729ec7b004SRick Macklem 		clp->lc_flags |= LCL_NAME;
37739ec7b004SRick Macklem 		clp->lc_namelen = nd->nd_princlen;
37749ec7b004SRick Macklem 		clp->lc_name = &clp->lc_id[idlen];
37759ec7b004SRick Macklem 		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
37769ec7b004SRick Macklem 	} else {
37779ec7b004SRick Macklem 		clp->lc_uid = nd->nd_cred->cr_uid;
37789ec7b004SRick Macklem 		clp->lc_gid = nd->nd_cred->cr_gid;
37799ec7b004SRick Macklem 	}
37809ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
37819ec7b004SRick Macklem 	clp->lc_program = fxdr_unsigned(u_int32_t, *tl);
37829ec7b004SRick Macklem 	error = nfsrv_getclientipaddr(nd, clp);
37839ec7b004SRick Macklem 	if (error)
37849ec7b004SRick Macklem 		goto nfsmout;
37859ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
37869ec7b004SRick Macklem 	clp->lc_callback = fxdr_unsigned(u_int32_t, *tl);
37879ec7b004SRick Macklem 
37889ec7b004SRick Macklem 	/*
37899ec7b004SRick Macklem 	 * nfsrv_setclient() does the actual work of adding it to the
37909ec7b004SRick Macklem 	 * client list. If there is no error, the structure has been
37919ec7b004SRick Macklem 	 * linked into the client list and clp should no longer be used
37929ec7b004SRick Macklem 	 * here. When an error is returned, it has not been linked in,
37939ec7b004SRick Macklem 	 * so it should be free'd.
37949ec7b004SRick Macklem 	 */
37959ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
37969ec7b004SRick Macklem 	if (nd->nd_repstat == NFSERR_CLIDINUSE) {
3797ed2f1001SRick Macklem 		/*
3798ed2f1001SRick Macklem 		 * 8 is the maximum length of the port# string.
3799ed2f1001SRick Macklem 		 */
3800ed2f1001SRick Macklem 		addrbuf = malloc(INET6_ADDRSTRLEN + 8, M_TEMP, M_WAITOK);
3801ed2f1001SRick Macklem 		switch (clp->lc_req.nr_nam->sa_family) {
3802ed2f1001SRick Macklem #ifdef INET
3803ed2f1001SRick Macklem 		case AF_INET:
38049ec7b004SRick Macklem 			if (clp->lc_flags & LCL_TCPCALLBACK)
38059ec7b004SRick Macklem 				(void) nfsm_strtom(nd, "tcp", 3);
38069ec7b004SRick Macklem 			else
38079ec7b004SRick Macklem 				(void) nfsm_strtom(nd, "udp", 3);
3808ed2f1001SRick Macklem 			rin = (struct sockaddr_in *)clp->lc_req.nr_nam;
3809ed2f1001SRick Macklem 			ucp = (u_char *)&rin->sin_addr.s_addr;
3810ed2f1001SRick Macklem 			ucp2 = (u_char *)&rin->sin_port;
38119ec7b004SRick Macklem 			sprintf(addrbuf, "%d.%d.%d.%d.%d.%d", ucp[0] & 0xff,
38129ec7b004SRick Macklem 			    ucp[1] & 0xff, ucp[2] & 0xff, ucp[3] & 0xff,
38139ec7b004SRick Macklem 			    ucp2[0] & 0xff, ucp2[1] & 0xff);
3814ed2f1001SRick Macklem 			break;
3815ed2f1001SRick Macklem #endif
3816ed2f1001SRick Macklem #ifdef INET6
3817ed2f1001SRick Macklem 		case AF_INET6:
3818ed2f1001SRick Macklem 			if (clp->lc_flags & LCL_TCPCALLBACK)
3819ed2f1001SRick Macklem 				(void) nfsm_strtom(nd, "tcp6", 4);
3820ed2f1001SRick Macklem 			else
3821ed2f1001SRick Macklem 				(void) nfsm_strtom(nd, "udp6", 4);
3822ed2f1001SRick Macklem 			rin6 = (struct sockaddr_in6 *)clp->lc_req.nr_nam;
3823ed2f1001SRick Macklem 			ucp = inet_ntop(AF_INET6, &rin6->sin6_addr, addrbuf,
3824ed2f1001SRick Macklem 			    INET6_ADDRSTRLEN);
3825ed2f1001SRick Macklem 			if (ucp != NULL)
3826ed2f1001SRick Macklem 				i = strlen(ucp);
3827ed2f1001SRick Macklem 			else
3828ed2f1001SRick Macklem 				i = 0;
3829ed2f1001SRick Macklem 			ucp2 = (u_char *)&rin6->sin6_port;
3830ed2f1001SRick Macklem 			sprintf(&addrbuf[i], ".%d.%d", ucp2[0] & 0xff,
3831ed2f1001SRick Macklem 			    ucp2[1] & 0xff);
3832ed2f1001SRick Macklem 			break;
3833ed2f1001SRick Macklem #endif
3834ed2f1001SRick Macklem 		}
38359ec7b004SRick Macklem 		(void) nfsm_strtom(nd, addrbuf, strlen(addrbuf));
3836ed2f1001SRick Macklem 		free(addrbuf, M_TEMP);
38379ec7b004SRick Macklem 	}
38389ec7b004SRick Macklem 	if (clp) {
3839b97b91b5SConrad Meyer 		free(clp->lc_req.nr_nam, M_SONAME);
38409ec7b004SRick Macklem 		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
38411f54e596SRick Macklem 		free(clp->lc_stateid, M_NFSDCLIENT);
38421f54e596SRick Macklem 		free(clp, M_NFSDCLIENT);
38439ec7b004SRick Macklem 	}
38449ec7b004SRick Macklem 	if (!nd->nd_repstat) {
38459ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_HYPER);
38469ec7b004SRick Macklem 		*tl++ = clientid.lval[0];
38479ec7b004SRick Macklem 		*tl++ = clientid.lval[1];
38489ec7b004SRick Macklem 		*tl++ = confirm.lval[0];
38499ec7b004SRick Macklem 		*tl = confirm.lval[1];
38509ec7b004SRick Macklem 	}
3851a9285ae5SZack Kirsch 
3852a9285ae5SZack Kirsch out:
3853a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
38549ec7b004SRick Macklem 	return (0);
38559ec7b004SRick Macklem nfsmout:
38569ec7b004SRick Macklem 	if (clp) {
3857b97b91b5SConrad Meyer 		free(clp->lc_req.nr_nam, M_SONAME);
38589ec7b004SRick Macklem 		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
38591f54e596SRick Macklem 		free(clp->lc_stateid, M_NFSDCLIENT);
38601f54e596SRick Macklem 		free(clp, M_NFSDCLIENT);
38619ec7b004SRick Macklem 	}
3862a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
38639ec7b004SRick Macklem 	return (error);
38649ec7b004SRick Macklem }
38659ec7b004SRick Macklem 
38669ec7b004SRick Macklem /*
38679ec7b004SRick Macklem  * nfsv4 set client id confirm service
38689ec7b004SRick Macklem  */
3869b9cc3262SRyan Moeller int
38709ec7b004SRick Macklem nfsrvd_setclientidcfrm(struct nfsrv_descript *nd,
3871af444b18SEdward Tomasz Napierala     __unused int isdgram, __unused vnode_t vp,
38729ec7b004SRick Macklem     __unused struct nfsexstuff *exp)
38739ec7b004SRick Macklem {
38749ec7b004SRick Macklem 	u_int32_t *tl;
38759ec7b004SRick Macklem 	int error = 0;
38769ec7b004SRick Macklem 	nfsquad_t clientid, confirm;
3877af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
38789ec7b004SRick Macklem 
3879c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0) {
3880c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
3881c59e4cc3SRick Macklem 		goto nfsmout;
3882c59e4cc3SRick Macklem 	}
3883c9aad40fSRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
38849ec7b004SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
3885a9285ae5SZack Kirsch 		goto nfsmout;
38869ec7b004SRick Macklem 	}
38879ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER);
38889ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
38899ec7b004SRick Macklem 	clientid.lval[1] = *tl++;
38909ec7b004SRick Macklem 	confirm.lval[0] = *tl++;
38919ec7b004SRick Macklem 	confirm.lval[1] = *tl;
38929ec7b004SRick Macklem 
38939ec7b004SRick Macklem 	/*
38949ec7b004SRick Macklem 	 * nfsrv_getclient() searches the client list for a match and
38959ec7b004SRick Macklem 	 * returns the appropriate NFSERR status.
38969ec7b004SRick Macklem 	 */
38979ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_CONFIRM|CLOPS_RENEW),
3898c59e4cc3SRick Macklem 	    NULL, NULL, confirm, 0, nd, p);
38999ec7b004SRick Macklem nfsmout:
3900a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
39019ec7b004SRick Macklem 	return (error);
39029ec7b004SRick Macklem }
39039ec7b004SRick Macklem 
39049ec7b004SRick Macklem /*
39059ec7b004SRick Macklem  * nfsv4 verify service
39069ec7b004SRick Macklem  */
3907b9cc3262SRyan Moeller int
39089ec7b004SRick Macklem nfsrvd_verify(struct nfsrv_descript *nd, int isdgram,
3909af444b18SEdward Tomasz Napierala     vnode_t vp, __unused struct nfsexstuff *exp)
39109ec7b004SRick Macklem {
39119ec7b004SRick Macklem 	int error = 0, ret, fhsize = NFSX_MYFH;
39129ec7b004SRick Macklem 	struct nfsvattr nva;
39132f304845SKonstantin Belousov 	struct statfs *sf;
39149ec7b004SRick Macklem 	struct nfsfsinfo fs;
39159ec7b004SRick Macklem 	fhandle_t fh;
3916af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
39179ec7b004SRick Macklem 
39182f304845SKonstantin Belousov 	sf = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
391990d2dfabSRick Macklem 	nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
39209ec7b004SRick Macklem 	if (!nd->nd_repstat)
39212f304845SKonstantin Belousov 		nd->nd_repstat = nfsvno_statfs(vp, sf);
39229ec7b004SRick Macklem 	if (!nd->nd_repstat)
39239ec7b004SRick Macklem 		nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
39249ec7b004SRick Macklem 	if (!nd->nd_repstat) {
39259ec7b004SRick Macklem 		nfsvno_getfs(&fs, isdgram);
39269ec7b004SRick Macklem 		error = nfsv4_loadattr(nd, vp, &nva, NULL, &fh, fhsize, NULL,
39272f304845SKonstantin Belousov 		    sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, p, nd->nd_cred);
39289ec7b004SRick Macklem 		if (!error) {
39299ec7b004SRick Macklem 			if (nd->nd_procnum == NFSV4OP_NVERIFY) {
39309ec7b004SRick Macklem 				if (ret == 0)
39319ec7b004SRick Macklem 					nd->nd_repstat = NFSERR_SAME;
39329ec7b004SRick Macklem 				else if (ret != NFSERR_NOTSAME)
39339ec7b004SRick Macklem 					nd->nd_repstat = ret;
39349ec7b004SRick Macklem 			} else if (ret)
39359ec7b004SRick Macklem 				nd->nd_repstat = ret;
39369ec7b004SRick Macklem 		}
39379ec7b004SRick Macklem 	}
39389ec7b004SRick Macklem 	vput(vp);
39392f304845SKonstantin Belousov 	free(sf, M_STATFS);
3940a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
39419ec7b004SRick Macklem 	return (error);
39429ec7b004SRick Macklem }
39439ec7b004SRick Macklem 
39449ec7b004SRick Macklem /*
39459ec7b004SRick Macklem  * nfs openattr rpc
39469ec7b004SRick Macklem  */
3947b9cc3262SRyan Moeller int
39489ec7b004SRick Macklem nfsrvd_openattr(struct nfsrv_descript *nd, __unused int isdgram,
39499ec7b004SRick Macklem     vnode_t dp, __unused vnode_t *vpp, __unused fhandle_t *fhp,
3950af444b18SEdward Tomasz Napierala     __unused struct nfsexstuff *exp)
39519ec7b004SRick Macklem {
39529ec7b004SRick Macklem 	u_int32_t *tl;
39538014c971SRick Macklem 	int error = 0, createdir __unused;
39549ec7b004SRick Macklem 
39559ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
39569ec7b004SRick Macklem 	createdir = fxdr_unsigned(int, *tl);
39579ec7b004SRick Macklem 	nd->nd_repstat = NFSERR_NOTSUPP;
39589ec7b004SRick Macklem nfsmout:
39599ec7b004SRick Macklem 	vrele(dp);
3960a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
39619ec7b004SRick Macklem 	return (error);
39629ec7b004SRick Macklem }
39639ec7b004SRick Macklem 
39649ec7b004SRick Macklem /*
39659ec7b004SRick Macklem  * nfsv4 release lock owner service
39669ec7b004SRick Macklem  */
3967b9cc3262SRyan Moeller int
39689ec7b004SRick Macklem nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram,
3969af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
39709ec7b004SRick Macklem {
39719ec7b004SRick Macklem 	u_int32_t *tl;
39729ec7b004SRick Macklem 	struct nfsstate *stp = NULL;
39739ec7b004SRick Macklem 	int error = 0, len;
39749ec7b004SRick Macklem 	nfsquad_t clientid;
3975af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
39769ec7b004SRick Macklem 
3977c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_NFSV41) != 0) {
3978c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
3979c59e4cc3SRick Macklem 		goto nfsmout;
3980c59e4cc3SRick Macklem 	}
3981c9aad40fSRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3982b1cfc0d9SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
3983a9285ae5SZack Kirsch 		goto nfsmout;
3984b1cfc0d9SRick Macklem 	}
39859ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
39869ec7b004SRick Macklem 	len = fxdr_unsigned(int, *(tl + 2));
39872a45247cSRick Macklem 	if (len <= 0 || len > NFSV4_OPAQUELIMIT) {
39882a45247cSRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
3989a9285ae5SZack Kirsch 		goto nfsmout;
39902a45247cSRick Macklem 	}
3991222daa42SConrad Meyer 	stp = malloc(sizeof (struct nfsstate) + len,
39929ec7b004SRick Macklem 	    M_NFSDSTATE, M_WAITOK);
39939ec7b004SRick Macklem 	stp->ls_ownerlen = len;
39949ec7b004SRick Macklem 	stp->ls_op = NULL;
39959ec7b004SRick Macklem 	stp->ls_flags = NFSLCK_RELEASE;
39969ec7b004SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
39979ec7b004SRick Macklem 	clientid.lval[0] = *tl++;
39989ec7b004SRick Macklem 	clientid.lval[1] = *tl;
3999c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
4000c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
4001c59e4cc3SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
4002c59e4cc3SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
4003c59e4cc3SRick Macklem 			printf("EEK14 multiple clids\n");
40049ec7b004SRick Macklem 	} else {
4005c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
4006c59e4cc3SRick Macklem 			printf("EEK! no clientid from session\n");
40079ec7b004SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
40089ec7b004SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
40099ec7b004SRick Macklem 	}
40109ec7b004SRick Macklem 	error = nfsrv_mtostr(nd, stp->ls_owner, len);
40119ec7b004SRick Macklem 	if (error)
40129ec7b004SRick Macklem 		goto nfsmout;
40139ec7b004SRick Macklem 	nd->nd_repstat = nfsrv_releaselckown(stp, clientid, p);
4014222daa42SConrad Meyer 	free(stp, M_NFSDSTATE);
4015a9285ae5SZack Kirsch 
4016a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
40179ec7b004SRick Macklem 	return (0);
40189ec7b004SRick Macklem nfsmout:
40199ec7b004SRick Macklem 	if (stp)
4020222daa42SConrad Meyer 		free(stp, M_NFSDSTATE);
4021a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
40229ec7b004SRick Macklem 	return (error);
40239ec7b004SRick Macklem }
4024c59e4cc3SRick Macklem 
4025c59e4cc3SRick Macklem /*
4026c59e4cc3SRick Macklem  * nfsv4 exchange_id service
4027c59e4cc3SRick Macklem  */
4028b9cc3262SRyan Moeller int
4029c59e4cc3SRick Macklem nfsrvd_exchangeid(struct nfsrv_descript *nd, __unused int isdgram,
4030af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4031c59e4cc3SRick Macklem {
4032c59e4cc3SRick Macklem 	uint32_t *tl;
4033c59e4cc3SRick Macklem 	int error = 0, i, idlen;
4034c59e4cc3SRick Macklem 	struct nfsclient *clp = NULL;
4035c59e4cc3SRick Macklem 	nfsquad_t clientid, confirm;
4036c59e4cc3SRick Macklem 	uint8_t *verf;
4037c59e4cc3SRick Macklem 	uint32_t sp4type, v41flags;
4038c59e4cc3SRick Macklem 	uint64_t owner_minor;
4039c59e4cc3SRick Macklem 	struct timespec verstime;
4040ed2f1001SRick Macklem #ifdef INET
4041ed2f1001SRick Macklem 	struct sockaddr_in *sin, *rin;
4042ed2f1001SRick Macklem #endif
4043ed2f1001SRick Macklem #ifdef INET6
4044ed2f1001SRick Macklem 	struct sockaddr_in6 *sin6, *rin6;
4045ed2f1001SRick Macklem #endif
4046af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
4047c59e4cc3SRick Macklem 
4048c59e4cc3SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4049c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
4050c59e4cc3SRick Macklem 		goto nfsmout;
4051c59e4cc3SRick Macklem 	}
4052c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
4053c59e4cc3SRick Macklem 	verf = (uint8_t *)tl;
4054c59e4cc3SRick Macklem 	tl += (NFSX_VERF / NFSX_UNSIGNED);
4055c59e4cc3SRick Macklem 	i = fxdr_unsigned(int, *tl);
4056c59e4cc3SRick Macklem 	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
4057c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
4058c59e4cc3SRick Macklem 		goto nfsmout;
4059c59e4cc3SRick Macklem 	}
4060c59e4cc3SRick Macklem 	idlen = i;
4061c59e4cc3SRick Macklem 	if (nd->nd_flag & ND_GSS)
4062c59e4cc3SRick Macklem 		i += nd->nd_princlen;
40631f54e596SRick Macklem 	clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
40641f54e596SRick Macklem 	    M_ZERO);
40651f54e596SRick Macklem 	clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
40661f54e596SRick Macklem 	    nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
4067c59e4cc3SRick Macklem 	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
4068ed2f1001SRick Macklem 	/* Allocated large enough for an AF_INET or AF_INET6 socket. */
4069ed2f1001SRick Macklem 	clp->lc_req.nr_nam = malloc(sizeof(struct sockaddr_in6), M_SONAME,
4070b97b91b5SConrad Meyer 	    M_WAITOK | M_ZERO);
4071ed2f1001SRick Macklem 	switch (nd->nd_nam->sa_family) {
4072ed2f1001SRick Macklem #ifdef INET
4073ed2f1001SRick Macklem 	case AF_INET:
4074ed2f1001SRick Macklem 		rin = (struct sockaddr_in *)clp->lc_req.nr_nam;
4075ed2f1001SRick Macklem 		sin = (struct sockaddr_in *)nd->nd_nam;
4076ed2f1001SRick Macklem 		rin->sin_family = AF_INET;
4077ed2f1001SRick Macklem 		rin->sin_len = sizeof(struct sockaddr_in);
4078ed2f1001SRick Macklem 		rin->sin_port = 0;
4079ed2f1001SRick Macklem 		rin->sin_addr.s_addr = sin->sin_addr.s_addr;
4080ed2f1001SRick Macklem 		break;
4081ed2f1001SRick Macklem #endif
4082ed2f1001SRick Macklem #ifdef INET6
4083ed2f1001SRick Macklem 	case AF_INET6:
4084ed2f1001SRick Macklem 		rin6 = (struct sockaddr_in6 *)clp->lc_req.nr_nam;
4085ed2f1001SRick Macklem 		sin6 = (struct sockaddr_in6 *)nd->nd_nam;
4086ed2f1001SRick Macklem 		rin6->sin6_family = AF_INET6;
4087ed2f1001SRick Macklem 		rin6->sin6_len = sizeof(struct sockaddr_in6);
4088ed2f1001SRick Macklem 		rin6->sin6_port = 0;
4089ed2f1001SRick Macklem 		rin6->sin6_addr = sin6->sin6_addr;
4090ed2f1001SRick Macklem 		break;
4091ed2f1001SRick Macklem #endif
4092ed2f1001SRick Macklem 	}
4093c59e4cc3SRick Macklem 	clp->lc_req.nr_cred = NULL;
4094c59e4cc3SRick Macklem 	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
4095c59e4cc3SRick Macklem 	clp->lc_idlen = idlen;
4096c59e4cc3SRick Macklem 	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
4097c59e4cc3SRick Macklem 	if (error != 0)
4098c59e4cc3SRick Macklem 		goto nfsmout;
4099c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_GSS) != 0) {
4100c59e4cc3SRick Macklem 		clp->lc_flags = LCL_GSS | LCL_NFSV41;
4101c59e4cc3SRick Macklem 		if ((nd->nd_flag & ND_GSSINTEGRITY) != 0)
4102c59e4cc3SRick Macklem 			clp->lc_flags |= LCL_GSSINTEGRITY;
4103c59e4cc3SRick Macklem 		else if ((nd->nd_flag & ND_GSSPRIVACY) != 0)
4104c59e4cc3SRick Macklem 			clp->lc_flags |= LCL_GSSPRIVACY;
4105c59e4cc3SRick Macklem 	} else
4106c59e4cc3SRick Macklem 		clp->lc_flags = LCL_NFSV41;
4107c057a378SRick Macklem 	if ((nd->nd_flag & ND_NFSV42) != 0)
4108c057a378SRick Macklem 		clp->lc_flags |= LCL_NFSV42;
4109c59e4cc3SRick Macklem 	if ((nd->nd_flag & ND_GSS) != 0 && nd->nd_princlen > 0) {
4110c59e4cc3SRick Macklem 		clp->lc_flags |= LCL_NAME;
4111c59e4cc3SRick Macklem 		clp->lc_namelen = nd->nd_princlen;
4112c59e4cc3SRick Macklem 		clp->lc_name = &clp->lc_id[idlen];
4113c59e4cc3SRick Macklem 		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
4114c59e4cc3SRick Macklem 	} else {
4115c59e4cc3SRick Macklem 		clp->lc_uid = nd->nd_cred->cr_uid;
4116c59e4cc3SRick Macklem 		clp->lc_gid = nd->nd_cred->cr_gid;
4117c59e4cc3SRick Macklem 	}
4118c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
4119c59e4cc3SRick Macklem 	v41flags = fxdr_unsigned(uint32_t, *tl++);
4120c59e4cc3SRick Macklem 	if ((v41flags & ~(NFSV4EXCH_SUPPMOVEDREFER | NFSV4EXCH_SUPPMOVEDMIGR |
4121c59e4cc3SRick Macklem 	    NFSV4EXCH_BINDPRINCSTATEID | NFSV4EXCH_MASKPNFS |
4122c59e4cc3SRick Macklem 	    NFSV4EXCH_UPDCONFIRMEDRECA)) != 0) {
4123c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
4124c59e4cc3SRick Macklem 		goto nfsmout;
4125c59e4cc3SRick Macklem 	}
4126c59e4cc3SRick Macklem 	if ((v41flags & NFSV4EXCH_UPDCONFIRMEDRECA) != 0)
4127c59e4cc3SRick Macklem 		confirm.lval[1] = 1;
4128c59e4cc3SRick Macklem 	else
4129c59e4cc3SRick Macklem 		confirm.lval[1] = 0;
413090d2dfabSRick Macklem 	if (nfsrv_devidcnt == 0)
413190d2dfabSRick Macklem 		v41flags = NFSV4EXCH_USENONPNFS | NFSV4EXCH_USEPNFSDS;
413290d2dfabSRick Macklem  	else
413390d2dfabSRick Macklem  		v41flags = NFSV4EXCH_USEPNFSMDS;
4134c59e4cc3SRick Macklem 	sp4type = fxdr_unsigned(uint32_t, *tl);
4135c59e4cc3SRick Macklem 	if (sp4type != NFSV4EXCH_SP4NONE) {
4136c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
4137c59e4cc3SRick Macklem 		goto nfsmout;
4138c59e4cc3SRick Macklem 	}
4139c59e4cc3SRick Macklem 
4140c59e4cc3SRick Macklem 	/*
4141c59e4cc3SRick Macklem 	 * nfsrv_setclient() does the actual work of adding it to the
4142c59e4cc3SRick Macklem 	 * client list. If there is no error, the structure has been
4143c59e4cc3SRick Macklem 	 * linked into the client list and clp should no longer be used
4144c59e4cc3SRick Macklem 	 * here. When an error is returned, it has not been linked in,
4145c59e4cc3SRick Macklem 	 * so it should be free'd.
4146c59e4cc3SRick Macklem 	 */
4147c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
4148c59e4cc3SRick Macklem 	if (clp != NULL) {
4149b97b91b5SConrad Meyer 		free(clp->lc_req.nr_nam, M_SONAME);
4150c59e4cc3SRick Macklem 		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
41511f54e596SRick Macklem 		free(clp->lc_stateid, M_NFSDCLIENT);
4152c59e4cc3SRick Macklem 		free(clp, M_NFSDCLIENT);
4153c59e4cc3SRick Macklem 	}
4154c59e4cc3SRick Macklem 	if (nd->nd_repstat == 0) {
4155c59e4cc3SRick Macklem 		if (confirm.lval[1] != 0)
4156c59e4cc3SRick Macklem 			v41flags |= NFSV4EXCH_CONFIRMEDR;
4157c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + 3 * NFSX_UNSIGNED);
4158c59e4cc3SRick Macklem 		*tl++ = clientid.lval[0];			/* ClientID */
4159c59e4cc3SRick Macklem 		*tl++ = clientid.lval[1];
4160c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(confirm.lval[0]);		/* SequenceID */
4161c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(v41flags);		/* Exch flags */
4162c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(NFSV4EXCH_SP4NONE);	/* No SSV */
4163c59e4cc3SRick Macklem 		owner_minor = 0;				/* Owner */
4164c59e4cc3SRick Macklem 		txdr_hyper(owner_minor, tl);			/* Minor */
4165c59e4cc3SRick Macklem 		(void)nfsm_strtom(nd, nd->nd_cred->cr_prison->pr_hostuuid,
4166c59e4cc3SRick Macklem 		    strlen(nd->nd_cred->cr_prison->pr_hostuuid)); /* Major */
41678932a483SRick Macklem 		(void)nfsm_strtom(nd, nd->nd_cred->cr_prison->pr_hostuuid,
41688932a483SRick Macklem 		    strlen(nd->nd_cred->cr_prison->pr_hostuuid)); /* Scope */
41698932a483SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
4170c59e4cc3SRick Macklem 		*tl = txdr_unsigned(1);
4171c59e4cc3SRick Macklem 		(void)nfsm_strtom(nd, "freebsd.org", strlen("freebsd.org"));
4172c59e4cc3SRick Macklem 		(void)nfsm_strtom(nd, version, strlen(version));
4173c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_V4TIME);
4174c59e4cc3SRick Macklem 		verstime.tv_sec = 1293840000;		/* Jan 1, 2011 */
4175c59e4cc3SRick Macklem 		verstime.tv_nsec = 0;
4176c59e4cc3SRick Macklem 		txdr_nfsv4time(&verstime, tl);
4177c59e4cc3SRick Macklem 	}
4178c59e4cc3SRick Macklem 	NFSEXITCODE2(0, nd);
4179c59e4cc3SRick Macklem 	return (0);
4180c59e4cc3SRick Macklem nfsmout:
4181c59e4cc3SRick Macklem 	if (clp != NULL) {
4182b97b91b5SConrad Meyer 		free(clp->lc_req.nr_nam, M_SONAME);
4183c59e4cc3SRick Macklem 		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
41841f54e596SRick Macklem 		free(clp->lc_stateid, M_NFSDCLIENT);
4185c59e4cc3SRick Macklem 		free(clp, M_NFSDCLIENT);
4186c59e4cc3SRick Macklem 	}
4187c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4188c59e4cc3SRick Macklem 	return (error);
4189c59e4cc3SRick Macklem }
4190c59e4cc3SRick Macklem 
4191c59e4cc3SRick Macklem /*
4192c59e4cc3SRick Macklem  * nfsv4 create session service
4193c59e4cc3SRick Macklem  */
4194b9cc3262SRyan Moeller int
4195c59e4cc3SRick Macklem nfsrvd_createsession(struct nfsrv_descript *nd, __unused int isdgram,
4196af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4197c59e4cc3SRick Macklem {
4198c59e4cc3SRick Macklem 	uint32_t *tl;
4199c59e4cc3SRick Macklem 	int error = 0;
4200c59e4cc3SRick Macklem 	nfsquad_t clientid, confirm;
4201c59e4cc3SRick Macklem 	struct nfsdsession *sep = NULL;
4202c59e4cc3SRick Macklem 	uint32_t rdmacnt;
4203af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
4204c59e4cc3SRick Macklem 
4205c59e4cc3SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4206c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
4207c59e4cc3SRick Macklem 		goto nfsmout;
4208c59e4cc3SRick Macklem 	}
4209c59e4cc3SRick Macklem 	sep = (struct nfsdsession *)malloc(sizeof(struct nfsdsession),
4210c59e4cc3SRick Macklem 	    M_NFSDSESSION, M_WAITOK | M_ZERO);
4211c59e4cc3SRick Macklem 	sep->sess_refcnt = 1;
4212c59e4cc3SRick Macklem 	mtx_init(&sep->sess_cbsess.nfsess_mtx, "nfscbsession", NULL, MTX_DEF);
4213c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + 2 * NFSX_UNSIGNED);
4214c59e4cc3SRick Macklem 	clientid.lval[0] = *tl++;
4215c59e4cc3SRick Macklem 	clientid.lval[1] = *tl++;
4216c59e4cc3SRick Macklem 	confirm.lval[0] = fxdr_unsigned(uint32_t, *tl++);
4217c59e4cc3SRick Macklem 	sep->sess_crflags = fxdr_unsigned(uint32_t, *tl);
4218c59e4cc3SRick Macklem 	/* Persistent sessions and RDMA are not supported. */
4219c59e4cc3SRick Macklem 	sep->sess_crflags &= NFSV4CRSESS_CONNBACKCHAN;
4220c59e4cc3SRick Macklem 
4221c59e4cc3SRick Macklem 	/* Fore channel attributes. */
4222c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
4223c59e4cc3SRick Macklem 	tl++;					/* Header pad always 0. */
4224c59e4cc3SRick Macklem 	sep->sess_maxreq = fxdr_unsigned(uint32_t, *tl++);
422590d2dfabSRick Macklem 	if (sep->sess_maxreq > sb_max_adj - NFS_MAXXDR) {
422690d2dfabSRick Macklem 		sep->sess_maxreq = sb_max_adj - NFS_MAXXDR;
422790d2dfabSRick Macklem 		printf("Consider increasing kern.ipc.maxsockbuf\n");
422890d2dfabSRick Macklem 	}
4229c59e4cc3SRick Macklem 	sep->sess_maxresp = fxdr_unsigned(uint32_t, *tl++);
423090d2dfabSRick Macklem 	if (sep->sess_maxresp > sb_max_adj - NFS_MAXXDR) {
423190d2dfabSRick Macklem 		sep->sess_maxresp = sb_max_adj - NFS_MAXXDR;
423290d2dfabSRick Macklem 		printf("Consider increasing kern.ipc.maxsockbuf\n");
423390d2dfabSRick Macklem 	}
4234c59e4cc3SRick Macklem 	sep->sess_maxrespcached = fxdr_unsigned(uint32_t, *tl++);
4235c59e4cc3SRick Macklem 	sep->sess_maxops = fxdr_unsigned(uint32_t, *tl++);
4236c59e4cc3SRick Macklem 	sep->sess_maxslots = fxdr_unsigned(uint32_t, *tl++);
4237c59e4cc3SRick Macklem 	if (sep->sess_maxslots > NFSV4_SLOTS)
4238c59e4cc3SRick Macklem 		sep->sess_maxslots = NFSV4_SLOTS;
4239c59e4cc3SRick Macklem 	rdmacnt = fxdr_unsigned(uint32_t, *tl);
4240c59e4cc3SRick Macklem 	if (rdmacnt > 1) {
4241c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
4242c59e4cc3SRick Macklem 		goto nfsmout;
4243c59e4cc3SRick Macklem 	} else if (rdmacnt == 1)
4244c59e4cc3SRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4245c59e4cc3SRick Macklem 
4246c59e4cc3SRick Macklem 	/* Back channel attributes. */
4247c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
4248c59e4cc3SRick Macklem 	tl++;					/* Header pad always 0. */
4249c59e4cc3SRick Macklem 	sep->sess_cbmaxreq = fxdr_unsigned(uint32_t, *tl++);
4250c59e4cc3SRick Macklem 	sep->sess_cbmaxresp = fxdr_unsigned(uint32_t, *tl++);
4251c59e4cc3SRick Macklem 	sep->sess_cbmaxrespcached = fxdr_unsigned(uint32_t, *tl++);
4252c59e4cc3SRick Macklem 	sep->sess_cbmaxops = fxdr_unsigned(uint32_t, *tl++);
4253c59e4cc3SRick Macklem 	sep->sess_cbsess.nfsess_foreslots = fxdr_unsigned(uint32_t, *tl++);
4254c59e4cc3SRick Macklem 	rdmacnt = fxdr_unsigned(uint32_t, *tl);
4255c59e4cc3SRick Macklem 	if (rdmacnt > 1) {
4256c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
4257c59e4cc3SRick Macklem 		goto nfsmout;
4258c59e4cc3SRick Macklem 	} else if (rdmacnt == 1)
4259c59e4cc3SRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4260c59e4cc3SRick Macklem 
4261c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4262c59e4cc3SRick Macklem 	sep->sess_cbprogram = fxdr_unsigned(uint32_t, *tl);
4263c59e4cc3SRick Macklem 
4264c59e4cc3SRick Macklem 	/*
4265c59e4cc3SRick Macklem 	 * nfsrv_getclient() searches the client list for a match and
4266c59e4cc3SRick Macklem 	 * returns the appropriate NFSERR status.
4267c59e4cc3SRick Macklem 	 */
4268c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_getclient(clientid, CLOPS_CONFIRM | CLOPS_RENEW,
4269c59e4cc3SRick Macklem 	    NULL, sep, confirm, sep->sess_cbprogram, nd, p);
4270c59e4cc3SRick Macklem 	if (nd->nd_repstat == 0) {
4271c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
4272c59e4cc3SRick Macklem 		NFSBCOPY(sep->sess_sessionid, tl, NFSX_V4SESSIONID);
4273c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 18 * NFSX_UNSIGNED);
4274c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(confirm.lval[0]);	/* sequenceid */
4275c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_crflags);
4276c59e4cc3SRick Macklem 
4277c59e4cc3SRick Macklem 		/* Fore channel attributes. */
4278c59e4cc3SRick Macklem 		*tl++ = 0;
4279c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_maxreq);
4280c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_maxresp);
4281c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_maxrespcached);
4282c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_maxops);
4283c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_maxslots);
4284c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(1);
4285c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(0);			/* No RDMA. */
4286c59e4cc3SRick Macklem 
4287c59e4cc3SRick Macklem 		/* Back channel attributes. */
4288c59e4cc3SRick Macklem 		*tl++ = 0;
4289c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_cbmaxreq);
4290c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_cbmaxresp);
4291c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_cbmaxrespcached);
4292c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_cbmaxops);
4293c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sep->sess_cbsess.nfsess_foreslots);
4294c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(1);
4295c59e4cc3SRick Macklem 		*tl = txdr_unsigned(0);			/* No RDMA. */
4296c59e4cc3SRick Macklem 	}
4297c59e4cc3SRick Macklem nfsmout:
4298c59e4cc3SRick Macklem 	if (nd->nd_repstat != 0 && sep != NULL)
4299c59e4cc3SRick Macklem 		free(sep, M_NFSDSESSION);
4300c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4301c59e4cc3SRick Macklem 	return (error);
4302c59e4cc3SRick Macklem }
4303c59e4cc3SRick Macklem 
4304c59e4cc3SRick Macklem /*
4305c59e4cc3SRick Macklem  * nfsv4 sequence service
4306c59e4cc3SRick Macklem  */
4307b9cc3262SRyan Moeller int
4308c59e4cc3SRick Macklem nfsrvd_sequence(struct nfsrv_descript *nd, __unused int isdgram,
4309af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4310c59e4cc3SRick Macklem {
4311c59e4cc3SRick Macklem 	uint32_t *tl;
4312c59e4cc3SRick Macklem 	uint32_t highest_slotid, sequenceid, sflags, target_highest_slotid;
4313c59e4cc3SRick Macklem 	int cache_this, error = 0;
4314af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
4315c59e4cc3SRick Macklem 
4316c59e4cc3SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4317c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
4318c59e4cc3SRick Macklem 		goto nfsmout;
4319c59e4cc3SRick Macklem 	}
4320c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID);
4321c59e4cc3SRick Macklem 	NFSBCOPY(tl, nd->nd_sessionid, NFSX_V4SESSIONID);
4322c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
4323c59e4cc3SRick Macklem 	sequenceid = fxdr_unsigned(uint32_t, *tl++);
4324c59e4cc3SRick Macklem 	nd->nd_slotid = fxdr_unsigned(uint32_t, *tl++);
4325c59e4cc3SRick Macklem 	highest_slotid = fxdr_unsigned(uint32_t, *tl++);
4326c59e4cc3SRick Macklem 	if (*tl == newnfs_true)
4327c59e4cc3SRick Macklem 		cache_this = 1;
4328c59e4cc3SRick Macklem 	else
4329c59e4cc3SRick Macklem 		cache_this = 0;
4330c59e4cc3SRick Macklem 	nd->nd_flag |= ND_HASSEQUENCE;
4331c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_checksequence(nd, sequenceid, &highest_slotid,
4332c59e4cc3SRick Macklem 	    &target_highest_slotid, cache_this, &sflags, p);
4333c59e4cc3SRick Macklem 	if (nd->nd_repstat == 0) {
4334c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
4335c59e4cc3SRick Macklem 		NFSBCOPY(nd->nd_sessionid, tl, NFSX_V4SESSIONID);
4336c59e4cc3SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 5 * NFSX_UNSIGNED);
4337c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(sequenceid);
4338c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(nd->nd_slotid);
4339c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(highest_slotid);
4340c59e4cc3SRick Macklem 		*tl++ = txdr_unsigned(target_highest_slotid);
4341c59e4cc3SRick Macklem 		*tl = txdr_unsigned(sflags);
4342c59e4cc3SRick Macklem 	}
4343c59e4cc3SRick Macklem nfsmout:
4344c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4345c59e4cc3SRick Macklem 	return (error);
4346c59e4cc3SRick Macklem }
4347c59e4cc3SRick Macklem 
4348c59e4cc3SRick Macklem /*
4349c59e4cc3SRick Macklem  * nfsv4 reclaim complete service
4350c59e4cc3SRick Macklem  */
4351b9cc3262SRyan Moeller int
4352c59e4cc3SRick Macklem nfsrvd_reclaimcomplete(struct nfsrv_descript *nd, __unused int isdgram,
4353af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4354c59e4cc3SRick Macklem {
4355c59e4cc3SRick Macklem 	uint32_t *tl;
4356a3e709cdSRick Macklem 	int error = 0, onefs;
4357c59e4cc3SRick Macklem 
4358c59e4cc3SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4359c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
4360c59e4cc3SRick Macklem 		goto nfsmout;
4361c59e4cc3SRick Macklem 	}
4362c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4363a3e709cdSRick Macklem 	/*
4364a3e709cdSRick Macklem 	 * I believe that a ReclaimComplete with rca_one_fs == TRUE is only
4365a3e709cdSRick Macklem 	 * to be used after a file system has been transferred to a different
4366a3e709cdSRick Macklem 	 * file server.  However, RFC5661 is somewhat vague w.r.t. this and
4367a3e709cdSRick Macklem 	 * the ESXi 6.7 client does both a ReclaimComplete with rca_one_fs
4368a3e709cdSRick Macklem 	 * == TRUE and one with ReclaimComplete with rca_one_fs == FALSE.
4369a3e709cdSRick Macklem 	 * Therefore, just ignore the rca_one_fs == TRUE operation and return
4370a3e709cdSRick Macklem 	 * NFS_OK without doing anything.
4371a3e709cdSRick Macklem 	 */
4372a3e709cdSRick Macklem 	onefs = 0;
4373c59e4cc3SRick Macklem 	if (*tl == newnfs_true)
4374a3e709cdSRick Macklem 		onefs = 1;
4375a3e709cdSRick Macklem 	nd->nd_repstat = nfsrv_checkreclaimcomplete(nd, onefs);
4376c59e4cc3SRick Macklem nfsmout:
4377c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4378c59e4cc3SRick Macklem 	return (error);
4379c59e4cc3SRick Macklem }
4380c59e4cc3SRick Macklem 
4381c59e4cc3SRick Macklem /*
4382c59e4cc3SRick Macklem  * nfsv4 destroy clientid service
4383c59e4cc3SRick Macklem  */
4384b9cc3262SRyan Moeller int
4385c59e4cc3SRick Macklem nfsrvd_destroyclientid(struct nfsrv_descript *nd, __unused int isdgram,
4386af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4387c59e4cc3SRick Macklem {
4388c59e4cc3SRick Macklem 	uint32_t *tl;
4389c59e4cc3SRick Macklem 	nfsquad_t clientid;
4390c59e4cc3SRick Macklem 	int error = 0;
4391af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
4392c59e4cc3SRick Macklem 
4393c59e4cc3SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4394c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
4395c59e4cc3SRick Macklem 		goto nfsmout;
4396c59e4cc3SRick Macklem 	}
4397c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
4398c59e4cc3SRick Macklem 	clientid.lval[0] = *tl++;
4399c59e4cc3SRick Macklem 	clientid.lval[1] = *tl;
4400c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_destroyclient(clientid, p);
4401c59e4cc3SRick Macklem nfsmout:
4402c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4403c59e4cc3SRick Macklem 	return (error);
4404c59e4cc3SRick Macklem }
4405c59e4cc3SRick Macklem 
4406c59e4cc3SRick Macklem /*
44079442a64eSRick Macklem  * nfsv4 bind connection to session service
44089442a64eSRick Macklem  */
4409b9cc3262SRyan Moeller int
44109442a64eSRick Macklem nfsrvd_bindconnsess(struct nfsrv_descript *nd, __unused int isdgram,
4411af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
44129442a64eSRick Macklem {
44139442a64eSRick Macklem 	uint32_t *tl;
44149442a64eSRick Macklem 	uint8_t sessid[NFSX_V4SESSIONID];
44159442a64eSRick Macklem 	int error = 0, foreaft;
44169442a64eSRick Macklem 
44179442a64eSRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
44189442a64eSRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
44199442a64eSRick Macklem 		goto nfsmout;
44209442a64eSRick Macklem 	}
44219442a64eSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID + 2 * NFSX_UNSIGNED);
44229442a64eSRick Macklem 	NFSBCOPY(tl, sessid, NFSX_V4SESSIONID);
44239442a64eSRick Macklem 	tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
44249442a64eSRick Macklem 	foreaft = fxdr_unsigned(int, *tl++);
44259442a64eSRick Macklem 	if (*tl == newnfs_true) {
44269442a64eSRick Macklem 		/* RDMA is not supported. */
44279442a64eSRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
44289442a64eSRick Macklem 		goto nfsmout;
44299442a64eSRick Macklem 	}
44309442a64eSRick Macklem 
44319442a64eSRick Macklem 	nd->nd_repstat = nfsrv_bindconnsess(nd, sessid, &foreaft);
44329442a64eSRick Macklem 	if (nd->nd_repstat == 0) {
44339442a64eSRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 2 *
44349442a64eSRick Macklem 		    NFSX_UNSIGNED);
44359442a64eSRick Macklem 		NFSBCOPY(sessid, tl, NFSX_V4SESSIONID);
44369442a64eSRick Macklem 		tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
44379442a64eSRick Macklem 		*tl++ = txdr_unsigned(foreaft);
44389442a64eSRick Macklem 		*tl = newnfs_false;
44399442a64eSRick Macklem 	}
44409442a64eSRick Macklem nfsmout:
44419442a64eSRick Macklem 	NFSEXITCODE2(error, nd);
44429442a64eSRick Macklem 	return (error);
44439442a64eSRick Macklem }
44449442a64eSRick Macklem 
44459442a64eSRick Macklem /*
4446c59e4cc3SRick Macklem  * nfsv4 destroy session service
4447c59e4cc3SRick Macklem  */
4448b9cc3262SRyan Moeller int
4449c59e4cc3SRick Macklem nfsrvd_destroysession(struct nfsrv_descript *nd, __unused int isdgram,
4450af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4451c59e4cc3SRick Macklem {
4452c59e4cc3SRick Macklem 	uint8_t *cp, sessid[NFSX_V4SESSIONID];
4453c59e4cc3SRick Macklem 	int error = 0;
4454c59e4cc3SRick Macklem 
4455c59e4cc3SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4456c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
4457c59e4cc3SRick Macklem 		goto nfsmout;
4458c59e4cc3SRick Macklem 	}
4459c59e4cc3SRick Macklem 	NFSM_DISSECT(cp, uint8_t *, NFSX_V4SESSIONID);
4460c59e4cc3SRick Macklem 	NFSBCOPY(cp, sessid, NFSX_V4SESSIONID);
4461c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_destroysession(nd, sessid);
4462c59e4cc3SRick Macklem nfsmout:
4463c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
4464c59e4cc3SRick Macklem 	return (error);
4465c59e4cc3SRick Macklem }
4466c59e4cc3SRick Macklem 
4467c59e4cc3SRick Macklem /*
4468c59e4cc3SRick Macklem  * nfsv4 free stateid service
4469c59e4cc3SRick Macklem  */
4470b9cc3262SRyan Moeller int
4471c59e4cc3SRick Macklem nfsrvd_freestateid(struct nfsrv_descript *nd, __unused int isdgram,
4472af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
4473c59e4cc3SRick Macklem {
4474c59e4cc3SRick Macklem 	uint32_t *tl;
4475c59e4cc3SRick Macklem 	nfsv4stateid_t stateid;
4476c59e4cc3SRick Macklem 	int error = 0;
4477af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
4478c59e4cc3SRick Macklem 
4479c59e4cc3SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4480c59e4cc3SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
4481c59e4cc3SRick Macklem 		goto nfsmout;
4482c59e4cc3SRick Macklem 	}
4483c59e4cc3SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
4484c59e4cc3SRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
4485c59e4cc3SRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
448690d2dfabSRick Macklem 
448790d2dfabSRick Macklem 	/*
448890d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
448990d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
449090d2dfabSRick Macklem 	 */
449190d2dfabSRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
449290d2dfabSRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
449390d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
449490d2dfabSRick Macklem 			stateid = nd->nd_curstateid;
449590d2dfabSRick Macklem 			stateid.seqid = 0;
449690d2dfabSRick Macklem 		} else {
449790d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
449890d2dfabSRick Macklem 			goto nfsmout;
449990d2dfabSRick Macklem 		}
450090d2dfabSRick Macklem 	}
450190d2dfabSRick Macklem 
4502c59e4cc3SRick Macklem 	nd->nd_repstat = nfsrv_freestateid(nd, &stateid, p);
450390d2dfabSRick Macklem 
450490d2dfabSRick Macklem 	/* If the current stateid has been free'd, unset it. */
450590d2dfabSRick Macklem 	if (nd->nd_repstat == 0 && (nd->nd_flag & ND_CURSTATEID) != 0 &&
450690d2dfabSRick Macklem 	    stateid.other[0] == nd->nd_curstateid.other[0] &&
450790d2dfabSRick Macklem 	    stateid.other[1] == nd->nd_curstateid.other[1] &&
450890d2dfabSRick Macklem 	    stateid.other[2] == nd->nd_curstateid.other[2])
450990d2dfabSRick Macklem 		nd->nd_flag &= ~ND_CURSTATEID;
451090d2dfabSRick Macklem nfsmout:
451190d2dfabSRick Macklem 	NFSEXITCODE2(error, nd);
451290d2dfabSRick Macklem 	return (error);
451390d2dfabSRick Macklem }
451490d2dfabSRick Macklem 
451590d2dfabSRick Macklem /*
451690d2dfabSRick Macklem  * nfsv4 layoutget service
451790d2dfabSRick Macklem  */
4518b9cc3262SRyan Moeller int
451990d2dfabSRick Macklem nfsrvd_layoutget(struct nfsrv_descript *nd, __unused int isdgram,
4520af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
452190d2dfabSRick Macklem {
452290d2dfabSRick Macklem 	uint32_t *tl;
452390d2dfabSRick Macklem 	nfsv4stateid_t stateid;
452490d2dfabSRick Macklem 	int error = 0, layoutlen, layouttype, iomode, maxcnt, retonclose;
452590d2dfabSRick Macklem 	uint64_t offset, len, minlen;
452690d2dfabSRick Macklem 	char *layp;
4527af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
452890d2dfabSRick Macklem 
452990d2dfabSRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
453090d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
453190d2dfabSRick Macklem 		goto nfsmout;
453290d2dfabSRick Macklem 	}
453390d2dfabSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED + 3 * NFSX_HYPER +
453490d2dfabSRick Macklem 	    NFSX_STATEID);
453590d2dfabSRick Macklem 	tl++;		/* Signal layout available. Ignore for now. */
453690d2dfabSRick Macklem 	layouttype = fxdr_unsigned(int, *tl++);
453790d2dfabSRick Macklem 	iomode = fxdr_unsigned(int, *tl++);
453890d2dfabSRick Macklem 	offset = fxdr_hyper(tl); tl += 2;
453990d2dfabSRick Macklem 	len = fxdr_hyper(tl); tl += 2;
454090d2dfabSRick Macklem 	minlen = fxdr_hyper(tl); tl += 2;
454190d2dfabSRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
454290d2dfabSRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
454390d2dfabSRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
454490d2dfabSRick Macklem 	maxcnt = fxdr_unsigned(int, *tl);
454590d2dfabSRick Macklem 	NFSD_DEBUG(4, "layoutget ltyp=%d iom=%d off=%ju len=%ju mlen=%ju\n",
454690d2dfabSRick Macklem 	    layouttype, iomode, (uintmax_t)offset, (uintmax_t)len,
454790d2dfabSRick Macklem 	    (uintmax_t)minlen);
454890d2dfabSRick Macklem 	if (len < minlen ||
454990d2dfabSRick Macklem 	    (minlen != UINT64_MAX && offset + minlen < offset) ||
455090d2dfabSRick Macklem 	    (len != UINT64_MAX && offset + len < offset)) {
455190d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
455290d2dfabSRick Macklem 		goto nfsmout;
455390d2dfabSRick Macklem 	}
455490d2dfabSRick Macklem 
455590d2dfabSRick Macklem 	/*
455690d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
455790d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
455890d2dfabSRick Macklem 	 */
455990d2dfabSRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
456090d2dfabSRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
456190d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
456290d2dfabSRick Macklem 			stateid = nd->nd_curstateid;
456390d2dfabSRick Macklem 			stateid.seqid = 0;
456490d2dfabSRick Macklem 		} else {
456590d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
456690d2dfabSRick Macklem 			goto nfsmout;
456790d2dfabSRick Macklem 		}
456890d2dfabSRick Macklem 	}
456990d2dfabSRick Macklem 
457090d2dfabSRick Macklem 	layp = NULL;
457190d2dfabSRick Macklem 	if (layouttype == NFSLAYOUT_NFSV4_1_FILES && nfsrv_maxpnfsmirror == 1)
457290d2dfabSRick Macklem 		layp = malloc(NFSX_V4FILELAYOUT, M_TEMP, M_WAITOK);
457390d2dfabSRick Macklem 	else if (layouttype == NFSLAYOUT_FLEXFILE)
457490d2dfabSRick Macklem 		layp = malloc(NFSX_V4FLEXLAYOUT(nfsrv_maxpnfsmirror), M_TEMP,
457590d2dfabSRick Macklem 		    M_WAITOK);
457690d2dfabSRick Macklem 	else
457790d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_UNKNLAYOUTTYPE;
457890d2dfabSRick Macklem 	if (layp != NULL)
457990d2dfabSRick Macklem 		nd->nd_repstat = nfsrv_layoutget(nd, vp, exp, layouttype,
458090d2dfabSRick Macklem 		    &iomode, &offset, &len, minlen, &stateid, maxcnt,
458190d2dfabSRick Macklem 		    &retonclose, &layoutlen, layp, nd->nd_cred, p);
458290d2dfabSRick Macklem 	NFSD_DEBUG(4, "nfsrv_layoutget stat=%u layoutlen=%d\n", nd->nd_repstat,
458390d2dfabSRick Macklem 	    layoutlen);
458490d2dfabSRick Macklem 	if (nd->nd_repstat == 0) {
458590d2dfabSRick Macklem 		/* For NFSv4.1, set the Current StateID. */
458690d2dfabSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0) {
458790d2dfabSRick Macklem 			nd->nd_curstateid = stateid;
458890d2dfabSRick Macklem 			nd->nd_flag |= ND_CURSTATEID;
458990d2dfabSRick Macklem 		}
459090d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED + NFSX_STATEID +
459190d2dfabSRick Macklem 		    2 * NFSX_HYPER);
459290d2dfabSRick Macklem 		*tl++ = txdr_unsigned(retonclose);
459390d2dfabSRick Macklem 		*tl++ = txdr_unsigned(stateid.seqid);
459490d2dfabSRick Macklem 		NFSBCOPY(stateid.other, tl, NFSX_STATEIDOTHER);
459590d2dfabSRick Macklem 		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
459690d2dfabSRick Macklem 		*tl++ = txdr_unsigned(1);	/* Only returns one layout. */
459790d2dfabSRick Macklem 		txdr_hyper(offset, tl); tl += 2;
459890d2dfabSRick Macklem 		txdr_hyper(len, tl); tl += 2;
459990d2dfabSRick Macklem 		*tl++ = txdr_unsigned(iomode);
460090d2dfabSRick Macklem 		*tl = txdr_unsigned(layouttype);
460190d2dfabSRick Macklem 		nfsm_strtom(nd, layp, layoutlen);
460290d2dfabSRick Macklem 	} else if (nd->nd_repstat == NFSERR_LAYOUTTRYLATER) {
460390d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
460490d2dfabSRick Macklem 		*tl = newnfs_false;
460590d2dfabSRick Macklem 	}
460690d2dfabSRick Macklem 	free(layp, M_TEMP);
460790d2dfabSRick Macklem nfsmout:
460890d2dfabSRick Macklem 	vput(vp);
460990d2dfabSRick Macklem 	NFSEXITCODE2(error, nd);
461090d2dfabSRick Macklem 	return (error);
461190d2dfabSRick Macklem }
461290d2dfabSRick Macklem 
461390d2dfabSRick Macklem /*
461490d2dfabSRick Macklem  * nfsv4 layoutcommit service
461590d2dfabSRick Macklem  */
4616b9cc3262SRyan Moeller int
461790d2dfabSRick Macklem nfsrvd_layoutcommit(struct nfsrv_descript *nd, __unused int isdgram,
4618af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
461990d2dfabSRick Macklem {
462090d2dfabSRick Macklem 	uint32_t *tl;
462190d2dfabSRick Macklem 	nfsv4stateid_t stateid;
462290d2dfabSRick Macklem 	int error = 0, hasnewoff, hasnewmtime, layouttype, maxcnt, reclaim;
462390d2dfabSRick Macklem 	int hasnewsize;
4624f808cf72SRick Macklem 	uint64_t offset, len, newoff = 0, newsize;
462590d2dfabSRick Macklem 	struct timespec newmtime;
462690d2dfabSRick Macklem 	char *layp;
4627af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
462890d2dfabSRick Macklem 
462990d2dfabSRick Macklem 	layp = NULL;
463090d2dfabSRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
463190d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
463290d2dfabSRick Macklem 		goto nfsmout;
463390d2dfabSRick Macklem 	}
463490d2dfabSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED + 2 * NFSX_HYPER +
463590d2dfabSRick Macklem 	    NFSX_STATEID);
463690d2dfabSRick Macklem 	offset = fxdr_hyper(tl); tl += 2;
463790d2dfabSRick Macklem 	len = fxdr_hyper(tl); tl += 2;
463890d2dfabSRick Macklem 	reclaim = fxdr_unsigned(int, *tl++);
463990d2dfabSRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
464090d2dfabSRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
464190d2dfabSRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
464290d2dfabSRick Macklem 	/*
464390d2dfabSRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set the
464490d2dfabSRick Macklem 	 * stateid to the current stateid, if it is set.
464590d2dfabSRick Macklem 	 */
464690d2dfabSRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
464790d2dfabSRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
464890d2dfabSRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
464990d2dfabSRick Macklem 			stateid = nd->nd_curstateid;
465090d2dfabSRick Macklem 			stateid.seqid = 0;
465190d2dfabSRick Macklem 		} else {
465290d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
465390d2dfabSRick Macklem 			goto nfsmout;
465490d2dfabSRick Macklem 		}
465590d2dfabSRick Macklem 	}
465690d2dfabSRick Macklem 
465790d2dfabSRick Macklem 	hasnewoff = fxdr_unsigned(int, *tl);
465890d2dfabSRick Macklem 	if (hasnewoff != 0) {
465990d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + NFSX_UNSIGNED);
466090d2dfabSRick Macklem 		newoff = fxdr_hyper(tl); tl += 2;
466190d2dfabSRick Macklem 	} else
466290d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
466390d2dfabSRick Macklem 	hasnewmtime = fxdr_unsigned(int, *tl);
466490d2dfabSRick Macklem 	if (hasnewmtime != 0) {
466590d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_V4TIME + 2 * NFSX_UNSIGNED);
466690d2dfabSRick Macklem 		fxdr_nfsv4time(tl, &newmtime);
466790d2dfabSRick Macklem 		tl += (NFSX_V4TIME / NFSX_UNSIGNED);
466890d2dfabSRick Macklem 	} else
466990d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
467090d2dfabSRick Macklem 	layouttype = fxdr_unsigned(int, *tl++);
467190d2dfabSRick Macklem 	maxcnt = fxdr_unsigned(int, *tl);
467290d2dfabSRick Macklem 	if (maxcnt > 0) {
467390d2dfabSRick Macklem 		layp = malloc(maxcnt + 1, M_TEMP, M_WAITOK);
467490d2dfabSRick Macklem 		error = nfsrv_mtostr(nd, layp, maxcnt);
467590d2dfabSRick Macklem 		if (error != 0)
467690d2dfabSRick Macklem 			goto nfsmout;
467790d2dfabSRick Macklem 	}
467890d2dfabSRick Macklem 	nd->nd_repstat = nfsrv_layoutcommit(nd, vp, layouttype, hasnewoff,
467990d2dfabSRick Macklem 	    newoff, offset, len, hasnewmtime, &newmtime, reclaim, &stateid,
468090d2dfabSRick Macklem 	    maxcnt, layp, &hasnewsize, &newsize, nd->nd_cred, p);
468190d2dfabSRick Macklem 	NFSD_DEBUG(4, "nfsrv_layoutcommit stat=%u\n", nd->nd_repstat);
468290d2dfabSRick Macklem 	if (nd->nd_repstat == 0) {
468390d2dfabSRick Macklem 		if (hasnewsize != 0) {
468490d2dfabSRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED + NFSX_HYPER);
468590d2dfabSRick Macklem 			*tl++ = newnfs_true;
468690d2dfabSRick Macklem 			txdr_hyper(newsize, tl);
468790d2dfabSRick Macklem 		} else {
468890d2dfabSRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
468990d2dfabSRick Macklem 			*tl = newnfs_false;
469090d2dfabSRick Macklem 		}
469190d2dfabSRick Macklem 	}
469290d2dfabSRick Macklem nfsmout:
469390d2dfabSRick Macklem 	free(layp, M_TEMP);
469490d2dfabSRick Macklem 	vput(vp);
469590d2dfabSRick Macklem 	NFSEXITCODE2(error, nd);
469690d2dfabSRick Macklem 	return (error);
469790d2dfabSRick Macklem }
469890d2dfabSRick Macklem 
469990d2dfabSRick Macklem /*
470090d2dfabSRick Macklem  * nfsv4 layoutreturn service
470190d2dfabSRick Macklem  */
4702b9cc3262SRyan Moeller int
470390d2dfabSRick Macklem nfsrvd_layoutreturn(struct nfsrv_descript *nd, __unused int isdgram,
4704af444b18SEdward Tomasz Napierala     vnode_t vp, struct nfsexstuff *exp)
470590d2dfabSRick Macklem {
470690d2dfabSRick Macklem 	uint32_t *tl, *layp;
470790d2dfabSRick Macklem 	nfsv4stateid_t stateid;
470890d2dfabSRick Macklem 	int error = 0, fnd, kind, layouttype, iomode, maxcnt, reclaim;
470990d2dfabSRick Macklem 	uint64_t offset, len;
4710af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
471190d2dfabSRick Macklem 
471290d2dfabSRick Macklem 	layp = NULL;
471390d2dfabSRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
471490d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
471590d2dfabSRick Macklem 		goto nfsmout;
471690d2dfabSRick Macklem 	}
471790d2dfabSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
471890d2dfabSRick Macklem 	reclaim = *tl++;
471990d2dfabSRick Macklem 	layouttype = fxdr_unsigned(int, *tl++);
472090d2dfabSRick Macklem 	iomode = fxdr_unsigned(int, *tl++);
472190d2dfabSRick Macklem 	kind = fxdr_unsigned(int, *tl);
472290d2dfabSRick Macklem 	NFSD_DEBUG(4, "layoutreturn recl=%d ltyp=%d iom=%d kind=%d\n", reclaim,
472390d2dfabSRick Macklem 	    layouttype, iomode, kind);
472490d2dfabSRick Macklem 	if (kind == NFSV4LAYOUTRET_FILE) {
472590d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_STATEID +
472690d2dfabSRick Macklem 		    NFSX_UNSIGNED);
472790d2dfabSRick Macklem 		offset = fxdr_hyper(tl); tl += 2;
472890d2dfabSRick Macklem 		len = fxdr_hyper(tl); tl += 2;
472990d2dfabSRick Macklem 		stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
473090d2dfabSRick Macklem 		NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
473190d2dfabSRick Macklem 		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
473290d2dfabSRick Macklem 
473390d2dfabSRick Macklem 		/*
473490d2dfabSRick Macklem 		 * For the special stateid of other all 0s and seqid == 1, set
473590d2dfabSRick Macklem 		 * the stateid to the current stateid, if it is set.
473690d2dfabSRick Macklem 		 */
473790d2dfabSRick Macklem 		if (stateid.seqid == 1 && stateid.other[0] == 0 &&
473890d2dfabSRick Macklem 		    stateid.other[1] == 0 && stateid.other[2] == 0) {
473990d2dfabSRick Macklem 			if ((nd->nd_flag & ND_CURSTATEID) != 0) {
474090d2dfabSRick Macklem 				stateid = nd->nd_curstateid;
474190d2dfabSRick Macklem 				stateid.seqid = 0;
474290d2dfabSRick Macklem 			} else {
474390d2dfabSRick Macklem 				nd->nd_repstat = NFSERR_BADSTATEID;
474490d2dfabSRick Macklem 				goto nfsmout;
474590d2dfabSRick Macklem 			}
474690d2dfabSRick Macklem 		}
474790d2dfabSRick Macklem 
474890d2dfabSRick Macklem 		maxcnt = fxdr_unsigned(int, *tl);
474990d2dfabSRick Macklem 		if (maxcnt > 0) {
475090d2dfabSRick Macklem 			layp = malloc(maxcnt + 1, M_TEMP, M_WAITOK);
475190d2dfabSRick Macklem 			error = nfsrv_mtostr(nd, (char *)layp, maxcnt);
475290d2dfabSRick Macklem 			if (error != 0)
475390d2dfabSRick Macklem 				goto nfsmout;
475490d2dfabSRick Macklem 		}
475590d2dfabSRick Macklem 	} else {
475690d2dfabSRick Macklem 		if (reclaim == newnfs_true) {
475790d2dfabSRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
475890d2dfabSRick Macklem 			goto nfsmout;
475990d2dfabSRick Macklem 		}
476090d2dfabSRick Macklem 		offset = len = 0;
476190d2dfabSRick Macklem 		maxcnt = 0;
476290d2dfabSRick Macklem 	}
476390d2dfabSRick Macklem 	nd->nd_repstat = nfsrv_layoutreturn(nd, vp, layouttype, iomode,
476490d2dfabSRick Macklem 	    offset, len, reclaim, kind, &stateid, maxcnt, layp, &fnd,
476590d2dfabSRick Macklem 	    nd->nd_cred, p);
476690d2dfabSRick Macklem 	NFSD_DEBUG(4, "nfsrv_layoutreturn stat=%u fnd=%d\n", nd->nd_repstat,
476790d2dfabSRick Macklem 	    fnd);
476890d2dfabSRick Macklem 	if (nd->nd_repstat == 0) {
476990d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
477090d2dfabSRick Macklem 		if (fnd != 0) {
477190d2dfabSRick Macklem 			*tl = newnfs_true;
477290d2dfabSRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_STATEID);
477390d2dfabSRick Macklem 			*tl++ = txdr_unsigned(stateid.seqid);
477490d2dfabSRick Macklem 			NFSBCOPY(stateid.other, tl, NFSX_STATEIDOTHER);
477590d2dfabSRick Macklem 		} else
477690d2dfabSRick Macklem 			*tl = newnfs_false;
477790d2dfabSRick Macklem 	}
477890d2dfabSRick Macklem nfsmout:
477990d2dfabSRick Macklem 	free(layp, M_TEMP);
478090d2dfabSRick Macklem 	vput(vp);
478190d2dfabSRick Macklem 	NFSEXITCODE2(error, nd);
478290d2dfabSRick Macklem 	return (error);
478390d2dfabSRick Macklem }
478490d2dfabSRick Macklem 
478590d2dfabSRick Macklem /*
4786c057a378SRick Macklem  * nfsv4 layout error service
4787c057a378SRick Macklem  */
4788b9cc3262SRyan Moeller int
4789c057a378SRick Macklem nfsrvd_layouterror(struct nfsrv_descript *nd, __unused int isdgram,
4790c057a378SRick Macklem     vnode_t vp, struct nfsexstuff *exp)
4791c057a378SRick Macklem {
4792c057a378SRick Macklem 	uint32_t *tl;
4793c057a378SRick Macklem 	nfsv4stateid_t stateid;
4794c057a378SRick Macklem 	int cnt, error = 0, i, stat;
4795c057a378SRick Macklem 	int opnum __unused;
4796c057a378SRick Macklem 	char devid[NFSX_V4DEVICEID];
4797c057a378SRick Macklem 	uint64_t offset, len;
4798c057a378SRick Macklem 
4799c057a378SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4800c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
4801c057a378SRick Macklem 		goto nfsmout;
4802c057a378SRick Macklem 	}
4803c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_STATEID +
4804c057a378SRick Macklem 	    NFSX_UNSIGNED);
4805c057a378SRick Macklem 	offset = fxdr_hyper(tl); tl += 2;
4806c057a378SRick Macklem 	len = fxdr_hyper(tl); tl += 2;
4807c057a378SRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
4808c057a378SRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
4809c057a378SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
4810c057a378SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
4811c057a378SRick Macklem 	NFSD_DEBUG(4, "layouterror off=%ju len=%ju cnt=%d\n", (uintmax_t)offset,
4812c057a378SRick Macklem 	    (uintmax_t)len, cnt);
4813c057a378SRick Macklem 	/*
4814c057a378SRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set
4815c057a378SRick Macklem 	 * the stateid to the current stateid, if it is set.
4816c057a378SRick Macklem 	 */
4817c057a378SRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
4818c057a378SRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
4819c057a378SRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
4820c057a378SRick Macklem 			stateid = nd->nd_curstateid;
4821c057a378SRick Macklem 			stateid.seqid = 0;
4822c057a378SRick Macklem 		} else {
4823c057a378SRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
4824c057a378SRick Macklem 			goto nfsmout;
4825c057a378SRick Macklem 		}
4826c057a378SRick Macklem 	}
4827c057a378SRick Macklem 
4828c057a378SRick Macklem 	/*
4829c057a378SRick Macklem 	 * Ignore offset, len and stateid for now.
4830c057a378SRick Macklem 	 */
4831c057a378SRick Macklem 	for (i = 0; i < cnt; i++) {
4832c057a378SRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_V4DEVICEID + 2 *
4833c057a378SRick Macklem 		    NFSX_UNSIGNED);
4834c057a378SRick Macklem 		NFSBCOPY(tl, devid, NFSX_V4DEVICEID);
4835c057a378SRick Macklem 		tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
4836c057a378SRick Macklem 		stat = fxdr_unsigned(int, *tl++);
4837c057a378SRick Macklem 		opnum = fxdr_unsigned(int, *tl);
4838c057a378SRick Macklem 		NFSD_DEBUG(4, "nfsrvd_layouterr op=%d stat=%d\n", opnum, stat);
4839c057a378SRick Macklem 		/*
4840c057a378SRick Macklem 		 * Except for NFSERR_ACCES and NFSERR_STALE errors,
4841c057a378SRick Macklem 		 * disable the mirror.
4842c057a378SRick Macklem 		 */
4843c057a378SRick Macklem 		if (stat != NFSERR_ACCES && stat != NFSERR_STALE)
4844c057a378SRick Macklem 			nfsrv_delds(devid, curthread);
4845c057a378SRick Macklem 	}
4846c057a378SRick Macklem nfsmout:
4847c057a378SRick Macklem 	vput(vp);
4848c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
4849c057a378SRick Macklem 	return (error);
4850c057a378SRick Macklem }
4851c057a378SRick Macklem 
4852c057a378SRick Macklem /*
4853c057a378SRick Macklem  * nfsv4 layout stats service
4854c057a378SRick Macklem  */
4855b9cc3262SRyan Moeller int
4856c057a378SRick Macklem nfsrvd_layoutstats(struct nfsrv_descript *nd, __unused int isdgram,
4857c057a378SRick Macklem     vnode_t vp, struct nfsexstuff *exp)
4858c057a378SRick Macklem {
4859c057a378SRick Macklem 	uint32_t *tl;
4860c057a378SRick Macklem 	nfsv4stateid_t stateid;
4861c057a378SRick Macklem 	int cnt, error = 0;
4862c057a378SRick Macklem 	int layouttype __unused;
4863c057a378SRick Macklem 	char devid[NFSX_V4DEVICEID] __unused;
4864c057a378SRick Macklem 	uint64_t offset, len, readcount, readbytes, writecount, writebytes
4865c057a378SRick Macklem 	    __unused;
4866c057a378SRick Macklem 
4867c057a378SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4868c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
4869c057a378SRick Macklem 		goto nfsmout;
4870c057a378SRick Macklem 	}
4871c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 6 * NFSX_HYPER + NFSX_STATEID +
4872c057a378SRick Macklem 	    NFSX_V4DEVICEID + 2 * NFSX_UNSIGNED);
4873c057a378SRick Macklem 	offset = fxdr_hyper(tl); tl += 2;
4874c057a378SRick Macklem 	len = fxdr_hyper(tl); tl += 2;
4875c057a378SRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
4876c057a378SRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
4877c057a378SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
4878c057a378SRick Macklem 	readcount = fxdr_hyper(tl); tl += 2;
4879c057a378SRick Macklem 	readbytes = fxdr_hyper(tl); tl += 2;
4880c057a378SRick Macklem 	writecount = fxdr_hyper(tl); tl += 2;
4881c057a378SRick Macklem 	writebytes = fxdr_hyper(tl); tl += 2;
4882c057a378SRick Macklem 	NFSBCOPY(tl, devid, NFSX_V4DEVICEID);
4883c057a378SRick Macklem 	tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
4884c057a378SRick Macklem 	layouttype = fxdr_unsigned(int, *tl++);
4885c057a378SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
4886c057a378SRick Macklem 	error = nfsm_advance(nd, NFSM_RNDUP(cnt), -1);
4887c057a378SRick Macklem 	if (error != 0)
4888c057a378SRick Macklem 		goto nfsmout;
4889c057a378SRick Macklem 	NFSD_DEBUG(4, "layoutstats cnt=%d\n", cnt);
4890c057a378SRick Macklem 	/*
4891c057a378SRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set
4892c057a378SRick Macklem 	 * the stateid to the current stateid, if it is set.
4893c057a378SRick Macklem 	 */
4894c057a378SRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
4895c057a378SRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
4896c057a378SRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
4897c057a378SRick Macklem 			stateid = nd->nd_curstateid;
4898c057a378SRick Macklem 			stateid.seqid = 0;
4899c057a378SRick Macklem 		} else {
4900c057a378SRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
4901c057a378SRick Macklem 			goto nfsmout;
4902c057a378SRick Macklem 		}
4903c057a378SRick Macklem 	}
4904c057a378SRick Macklem 
4905c057a378SRick Macklem 	/*
4906c057a378SRick Macklem 	 * No use for the stats for now.
4907c057a378SRick Macklem 	 */
4908c057a378SRick Macklem nfsmout:
4909c057a378SRick Macklem 	vput(vp);
4910c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
4911c057a378SRick Macklem 	return (error);
4912c057a378SRick Macklem }
4913c057a378SRick Macklem 
4914c057a378SRick Macklem /*
4915c057a378SRick Macklem  * nfsv4 io_advise service
4916c057a378SRick Macklem  */
4917b9cc3262SRyan Moeller int
4918c057a378SRick Macklem nfsrvd_ioadvise(struct nfsrv_descript *nd, __unused int isdgram,
4919c057a378SRick Macklem     vnode_t vp, struct nfsexstuff *exp)
4920c057a378SRick Macklem {
4921c057a378SRick Macklem 	uint32_t *tl;
4922c057a378SRick Macklem 	nfsv4stateid_t stateid;
4923c057a378SRick Macklem 	nfsattrbit_t hints;
4924c057a378SRick Macklem 	int error = 0, ret;
4925c057a378SRick Macklem 	off_t offset, len;
4926c057a378SRick Macklem 
4927c057a378SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4928c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
4929c057a378SRick Macklem 		goto nfsmout;
4930c057a378SRick Macklem 	}
4931c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + 2 * NFSX_HYPER);
4932c057a378SRick Macklem 	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
4933c057a378SRick Macklem 	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
4934c057a378SRick Macklem 	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
4935c057a378SRick Macklem 	offset = fxdr_hyper(tl); tl += 2;
4936c057a378SRick Macklem 	len = fxdr_hyper(tl);
4937c057a378SRick Macklem 	error = nfsrv_getattrbits(nd, &hints, NULL, NULL);
4938c057a378SRick Macklem 	if (error != 0)
4939c057a378SRick Macklem 		goto nfsmout;
4940c057a378SRick Macklem 	/*
4941c057a378SRick Macklem 	 * For the special stateid of other all 0s and seqid == 1, set
4942c057a378SRick Macklem 	 * the stateid to the current stateid, if it is set.
4943c057a378SRick Macklem 	 */
4944c057a378SRick Macklem 	if (stateid.seqid == 1 && stateid.other[0] == 0 &&
4945c057a378SRick Macklem 	    stateid.other[1] == 0 && stateid.other[2] == 0) {
4946c057a378SRick Macklem 		if ((nd->nd_flag & ND_CURSTATEID) != 0) {
4947c057a378SRick Macklem 			stateid = nd->nd_curstateid;
4948c057a378SRick Macklem 			stateid.seqid = 0;
4949c057a378SRick Macklem 		} else {
4950c057a378SRick Macklem 			nd->nd_repstat = NFSERR_BADSTATEID;
4951c057a378SRick Macklem 			goto nfsmout;
4952c057a378SRick Macklem 		}
4953c057a378SRick Macklem 	}
4954c057a378SRick Macklem 
4955c057a378SRick Macklem 	if (offset < 0) {
4956c057a378SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
4957c057a378SRick Macklem 		goto nfsmout;
4958c057a378SRick Macklem 	}
4959c057a378SRick Macklem 	if (len < 0)
4960c057a378SRick Macklem 		len = 0;
4961c057a378SRick Macklem 	if (vp->v_type != VREG) {
4962c057a378SRick Macklem 		if (vp->v_type == VDIR)
4963c057a378SRick Macklem 			nd->nd_repstat = NFSERR_ISDIR;
4964c057a378SRick Macklem 		else
4965c057a378SRick Macklem 			nd->nd_repstat = NFSERR_WRONGTYPE;
4966c057a378SRick Macklem 		goto nfsmout;
4967c057a378SRick Macklem 	}
4968c057a378SRick Macklem 
4969c057a378SRick Macklem 	/*
4970c057a378SRick Macklem 	 * For now, we can only handle WILLNEED and DONTNEED and don't use
4971c057a378SRick Macklem 	 * the stateid.
4972c057a378SRick Macklem 	 */
4973c057a378SRick Macklem 	if ((NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED) &&
4974c057a378SRick Macklem 	    !NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_DONTNEED)) ||
4975c057a378SRick Macklem 	    (NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_DONTNEED) &&
4976c057a378SRick Macklem 	    !NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED))) {
4977b249ce48SMateusz Guzik 		NFSVOPUNLOCK(vp);
4978c057a378SRick Macklem 		if (NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED)) {
4979c057a378SRick Macklem 			ret = VOP_ADVISE(vp, offset, len, POSIX_FADV_WILLNEED);
4980c057a378SRick Macklem 			NFSZERO_ATTRBIT(&hints);
4981c057a378SRick Macklem 			if (ret == 0)
4982c057a378SRick Macklem 				NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED);
4983c057a378SRick Macklem 			else
4984c057a378SRick Macklem 				NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_NORMAL);
4985c057a378SRick Macklem 		} else {
4986c057a378SRick Macklem 			ret = VOP_ADVISE(vp, offset, len, POSIX_FADV_DONTNEED);
4987c057a378SRick Macklem 			NFSZERO_ATTRBIT(&hints);
4988c057a378SRick Macklem 			if (ret == 0)
4989c057a378SRick Macklem 				NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_DONTNEED);
4990c057a378SRick Macklem 			else
4991c057a378SRick Macklem 				NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_NORMAL);
4992c057a378SRick Macklem 		}
4993c057a378SRick Macklem 		vrele(vp);
4994c057a378SRick Macklem 	} else {
4995c057a378SRick Macklem 		NFSZERO_ATTRBIT(&hints);
4996c057a378SRick Macklem 		NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_NORMAL);
4997c057a378SRick Macklem 		vput(vp);
4998c057a378SRick Macklem 	}
4999c057a378SRick Macklem 	nfsrv_putattrbit(nd, &hints);
5000c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5001c057a378SRick Macklem 	return (error);
5002c057a378SRick Macklem nfsmout:
5003c057a378SRick Macklem 	vput(vp);
5004c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5005c057a378SRick Macklem 	return (error);
5006c057a378SRick Macklem }
5007c057a378SRick Macklem 
5008c057a378SRick Macklem /*
500990d2dfabSRick Macklem  * nfsv4 getdeviceinfo service
501090d2dfabSRick Macklem  */
5011b9cc3262SRyan Moeller int
501290d2dfabSRick Macklem nfsrvd_getdevinfo(struct nfsrv_descript *nd, __unused int isdgram,
5013af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
501490d2dfabSRick Macklem {
501590d2dfabSRick Macklem 	uint32_t *tl, maxcnt, notify[NFSV4_NOTIFYBITMAP];
501690d2dfabSRick Macklem 	int cnt, devaddrlen, error = 0, i, layouttype;
501790d2dfabSRick Macklem 	char devid[NFSX_V4DEVICEID], *devaddr;
501890d2dfabSRick Macklem 	time_t dev_time;
501990d2dfabSRick Macklem 
502090d2dfabSRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
502190d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
502290d2dfabSRick Macklem 		goto nfsmout;
502390d2dfabSRick Macklem 	}
502490d2dfabSRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 3 * NFSX_UNSIGNED + NFSX_V4DEVICEID);
502590d2dfabSRick Macklem 	NFSBCOPY(tl, devid, NFSX_V4DEVICEID);
502690d2dfabSRick Macklem 	tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
502790d2dfabSRick Macklem 	layouttype = fxdr_unsigned(int, *tl++);
502890d2dfabSRick Macklem 	maxcnt = fxdr_unsigned(uint32_t, *tl++);
502990d2dfabSRick Macklem 	cnt = fxdr_unsigned(int, *tl);
503090d2dfabSRick Macklem 	NFSD_DEBUG(4, "getdevinfo ltyp=%d maxcnt=%u bitcnt=%d\n", layouttype,
503190d2dfabSRick Macklem 	    maxcnt, cnt);
503290d2dfabSRick Macklem 	if (cnt > NFSV4_NOTIFYBITMAP || cnt < 0) {
503390d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
503490d2dfabSRick Macklem 		goto nfsmout;
503590d2dfabSRick Macklem 	}
503690d2dfabSRick Macklem 	if (cnt > 0) {
503790d2dfabSRick Macklem 		NFSM_DISSECT(tl, uint32_t *, cnt * NFSX_UNSIGNED);
503890d2dfabSRick Macklem 		for (i = 0; i < cnt; i++)
503990d2dfabSRick Macklem 			notify[i] = fxdr_unsigned(uint32_t, *tl++);
504090d2dfabSRick Macklem 	}
504190d2dfabSRick Macklem 	for (i = cnt; i < NFSV4_NOTIFYBITMAP; i++)
504290d2dfabSRick Macklem 		notify[i] = 0;
504390d2dfabSRick Macklem 
504490d2dfabSRick Macklem 	/*
504590d2dfabSRick Macklem 	 * Check that the device id is not stale.  Device ids are recreated
504690d2dfabSRick Macklem 	 * each time the nfsd threads are restarted.
504790d2dfabSRick Macklem 	 */
504890d2dfabSRick Macklem 	NFSBCOPY(devid, &dev_time, sizeof(dev_time));
504990d2dfabSRick Macklem 	if (dev_time != nfsdev_time) {
505090d2dfabSRick Macklem 		nd->nd_repstat = NFSERR_NOENT;
505190d2dfabSRick Macklem 		goto nfsmout;
505290d2dfabSRick Macklem 	}
505390d2dfabSRick Macklem 
505490d2dfabSRick Macklem 	/* Look for the device id. */
505590d2dfabSRick Macklem 	nd->nd_repstat = nfsrv_getdevinfo(devid, layouttype, &maxcnt,
505690d2dfabSRick Macklem 	    notify, &devaddrlen, &devaddr);
505790d2dfabSRick Macklem 	NFSD_DEBUG(4, "nfsrv_getdevinfo stat=%u\n", nd->nd_repstat);
505890d2dfabSRick Macklem 	if (nd->nd_repstat == 0) {
505990d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
506090d2dfabSRick Macklem 		*tl = txdr_unsigned(layouttype);
506190d2dfabSRick Macklem 		nfsm_strtom(nd, devaddr, devaddrlen);
506290d2dfabSRick Macklem 		cnt = 0;
506390d2dfabSRick Macklem 		for (i = 0; i < NFSV4_NOTIFYBITMAP; i++) {
506490d2dfabSRick Macklem 			if (notify[i] != 0)
506590d2dfabSRick Macklem 				cnt = i + 1;
506690d2dfabSRick Macklem 		}
506790d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, (cnt + 1) * NFSX_UNSIGNED);
506890d2dfabSRick Macklem 		*tl++ = txdr_unsigned(cnt);
506990d2dfabSRick Macklem 		for (i = 0; i < cnt; i++)
507090d2dfabSRick Macklem 			*tl++ = txdr_unsigned(notify[i]);
507190d2dfabSRick Macklem 	} else if (nd->nd_repstat == NFSERR_TOOSMALL) {
507290d2dfabSRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
507390d2dfabSRick Macklem 		*tl = txdr_unsigned(maxcnt);
507490d2dfabSRick Macklem 	}
5075c59e4cc3SRick Macklem nfsmout:
5076c59e4cc3SRick Macklem 	NFSEXITCODE2(error, nd);
5077c59e4cc3SRick Macklem 	return (error);
5078c59e4cc3SRick Macklem }
5079c59e4cc3SRick Macklem 
5080c59e4cc3SRick Macklem /*
50815d4835e4SRick Macklem  * nfsv4 test stateid service
50825d4835e4SRick Macklem  */
5083b9cc3262SRyan Moeller int
50845d4835e4SRick Macklem nfsrvd_teststateid(struct nfsrv_descript *nd, __unused int isdgram,
5085af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
50865d4835e4SRick Macklem {
50875d4835e4SRick Macklem 	uint32_t *tl;
50885d4835e4SRick Macklem 	nfsv4stateid_t *stateidp = NULL, *tstateidp;
50895d4835e4SRick Macklem 	int cnt, error = 0, i, ret;
5090af444b18SEdward Tomasz Napierala 	struct thread *p = curthread;
50915d4835e4SRick Macklem 
50925d4835e4SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
50935d4835e4SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
50945d4835e4SRick Macklem 		goto nfsmout;
50955d4835e4SRick Macklem 	}
50965d4835e4SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
50975d4835e4SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
50985d4835e4SRick Macklem 	if (cnt <= 0 || cnt > 1024) {
50995d4835e4SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
51005d4835e4SRick Macklem 		goto nfsmout;
51015d4835e4SRick Macklem 	}
51025d4835e4SRick Macklem 	stateidp = mallocarray(cnt, sizeof(nfsv4stateid_t), M_TEMP, M_WAITOK);
51035d4835e4SRick Macklem 	tstateidp = stateidp;
51045d4835e4SRick Macklem 	for (i = 0; i < cnt; i++) {
51055d4835e4SRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
51065d4835e4SRick Macklem 		tstateidp->seqid = fxdr_unsigned(uint32_t, *tl++);
51075d4835e4SRick Macklem 		NFSBCOPY(tl, tstateidp->other, NFSX_STATEIDOTHER);
51085d4835e4SRick Macklem 		tstateidp++;
51095d4835e4SRick Macklem 	}
51105d4835e4SRick Macklem 	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
51115d4835e4SRick Macklem 	*tl = txdr_unsigned(cnt);
51125d4835e4SRick Macklem 	tstateidp = stateidp;
51135d4835e4SRick Macklem 	for (i = 0; i < cnt; i++) {
51145d4835e4SRick Macklem 		ret = nfsrv_teststateid(nd, tstateidp, p);
51155d4835e4SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
51165d4835e4SRick Macklem 		*tl = txdr_unsigned(ret);
51175d4835e4SRick Macklem 		tstateidp++;
51185d4835e4SRick Macklem 	}
51195d4835e4SRick Macklem nfsmout:
51205d4835e4SRick Macklem 	free(stateidp, M_TEMP);
51215d4835e4SRick Macklem 	NFSEXITCODE2(error, nd);
51225d4835e4SRick Macklem 	return (error);
51235d4835e4SRick Macklem }
51245d4835e4SRick Macklem 
51255d4835e4SRick Macklem /*
5126c057a378SRick Macklem  * nfs allocate service
5127c057a378SRick Macklem  */
5128b9cc3262SRyan Moeller int
5129c057a378SRick Macklem nfsrvd_allocate(struct nfsrv_descript *nd, __unused int isdgram,
5130c057a378SRick Macklem     vnode_t vp, struct nfsexstuff *exp)
5131c057a378SRick Macklem {
5132c057a378SRick Macklem 	uint32_t *tl;
5133c057a378SRick Macklem 	struct nfsvattr forat;
5134c057a378SRick Macklem 	int error = 0, forat_ret = 1, gotproxystateid;
5135c057a378SRick Macklem 	off_t off, len;
5136c057a378SRick Macklem 	struct nfsstate st, *stp = &st;
5137c057a378SRick Macklem 	struct nfslock lo, *lop = &lo;
5138c057a378SRick Macklem 	nfsv4stateid_t stateid;
5139c057a378SRick Macklem 	nfsquad_t clientid;
5140c057a378SRick Macklem 	nfsattrbit_t attrbits;
5141c057a378SRick Macklem 
5142c057a378SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
5143c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
5144c057a378SRick Macklem 		goto nfsmout;
5145c057a378SRick Macklem 	}
5146c057a378SRick Macklem 	gotproxystateid = 0;
5147c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + 2 * NFSX_HYPER);
5148c057a378SRick Macklem 	stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
5149c057a378SRick Macklem 	lop->lo_flags = NFSLCK_WRITE;
5150c057a378SRick Macklem 	stp->ls_ownerlen = 0;
5151c057a378SRick Macklem 	stp->ls_op = NULL;
5152c057a378SRick Macklem 	stp->ls_uid = nd->nd_cred->cr_uid;
5153c057a378SRick Macklem 	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
5154c057a378SRick Macklem 	clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
5155c057a378SRick Macklem 	clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
5156c057a378SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
5157c057a378SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
5158c057a378SRick Macklem 			clientid.qval = nd->nd_clientid.qval;
5159c057a378SRick Macklem 		else if (nd->nd_clientid.qval != clientid.qval)
5160c057a378SRick Macklem 			printf("EEK2 multiple clids\n");
5161c057a378SRick Macklem 	} else {
5162c057a378SRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0)
5163c057a378SRick Macklem 			printf("EEK! no clientid from session\n");
5164c057a378SRick Macklem 		nd->nd_flag |= ND_IMPLIEDCLID;
5165c057a378SRick Macklem 		nd->nd_clientid.qval = clientid.qval;
5166c057a378SRick Macklem 	}
5167c057a378SRick Macklem 	stp->ls_stateid.other[2] = *tl++;
5168c057a378SRick Macklem 	/*
5169c057a378SRick Macklem 	 * Don't allow this to be done for a DS.
5170c057a378SRick Macklem 	 */
5171c057a378SRick Macklem 	if ((nd->nd_flag & ND_DSSERVER) != 0)
5172c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5173c057a378SRick Macklem 	/* However, allow the proxy stateid. */
5174c057a378SRick Macklem 	if (stp->ls_stateid.seqid == 0xffffffff &&
5175c057a378SRick Macklem 	    stp->ls_stateid.other[0] == 0x55555555 &&
5176c057a378SRick Macklem 	    stp->ls_stateid.other[1] == 0x55555555 &&
5177c057a378SRick Macklem 	    stp->ls_stateid.other[2] == 0x55555555)
5178c057a378SRick Macklem 		gotproxystateid = 1;
5179c057a378SRick Macklem 	off = fxdr_hyper(tl); tl += 2;
5180c057a378SRick Macklem 	lop->lo_first = off;
5181c057a378SRick Macklem 	len = fxdr_hyper(tl);
5182c057a378SRick Macklem 	lop->lo_end = off + len;
5183c057a378SRick Macklem 	/*
5184c057a378SRick Macklem 	 * Paranoia, just in case it wraps around, which shouldn't
5185c057a378SRick Macklem 	 * ever happen anyhow.
5186c057a378SRick Macklem 	 */
5187c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (lop->lo_end < lop->lo_first || len <= 0))
5188c057a378SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
5189c057a378SRick Macklem 
5190c057a378SRick Macklem 	if (nd->nd_repstat == 0 && vnode_vtype(vp) != VREG)
5191c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGTYPE;
5192c057a378SRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
5193c057a378SRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
5194c057a378SRick Macklem 	forat_ret = nfsvno_getattr(vp, &forat, nd, curthread, 1, &attrbits);
5195c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5196c057a378SRick Macklem 		nd->nd_repstat = forat_ret;
5197c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (forat.na_uid != nd->nd_cred->cr_uid ||
5198c057a378SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
5199c057a378SRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp,
5200c057a378SRick Macklem 		    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
5201c057a378SRick Macklem 		    NULL);
5202c057a378SRick Macklem 	if (nd->nd_repstat == 0 && gotproxystateid == 0)
5203c057a378SRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
5204c057a378SRick Macklem 		    &stateid, exp, nd, curthread);
5205c057a378SRick Macklem 
5206c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5207c057a378SRick Macklem 		nd->nd_repstat = nfsvno_allocate(vp, off, len, nd->nd_cred,
5208c057a378SRick Macklem 		    curthread);
5209c057a378SRick Macklem 	vput(vp);
5210c057a378SRick Macklem 	NFSEXITCODE2(0, nd);
5211c057a378SRick Macklem 	return (0);
5212c057a378SRick Macklem nfsmout:
5213c057a378SRick Macklem 	vput(vp);
5214c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5215c057a378SRick Macklem 	return (error);
5216c057a378SRick Macklem }
5217c057a378SRick Macklem 
5218c057a378SRick Macklem /*
5219c057a378SRick Macklem  * nfs copy service
5220c057a378SRick Macklem  */
5221b9cc3262SRyan Moeller int
5222c057a378SRick Macklem nfsrvd_copy_file_range(struct nfsrv_descript *nd, __unused int isdgram,
5223c057a378SRick Macklem     vnode_t vp, vnode_t tovp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
5224c057a378SRick Macklem {
5225c057a378SRick Macklem 	uint32_t *tl;
5226c057a378SRick Macklem 	struct nfsvattr at;
5227c057a378SRick Macklem 	int cnt, error = 0, ret;
5228c057a378SRick Macklem 	off_t inoff, outoff;
5229c057a378SRick Macklem 	uint64_t len;
5230c057a378SRick Macklem 	size_t xfer;
5231c057a378SRick Macklem 	struct nfsstate inst, outst, *instp = &inst, *outstp = &outst;
5232c057a378SRick Macklem 	struct nfslock inlo, outlo, *inlop = &inlo, *outlop = &outlo;
5233c057a378SRick Macklem 	nfsquad_t clientid;
5234c057a378SRick Macklem 	nfsv4stateid_t stateid;
5235c057a378SRick Macklem 	nfsattrbit_t attrbits;
5236c057a378SRick Macklem 	void *rl_rcookie, *rl_wcookie;
5237c057a378SRick Macklem 
5238c057a378SRick Macklem 	rl_rcookie = rl_wcookie = NULL;
5239c057a378SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
5240c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
5241c057a378SRick Macklem 		goto nfsmout;
5242c057a378SRick Macklem 	}
5243c057a378SRick Macklem 	if (nfsrv_devidcnt > 0) {
5244c057a378SRick Macklem 		/*
5245c057a378SRick Macklem 		 * For a pNFS server, reply NFSERR_NOTSUPP so that the client
5246c057a378SRick Macklem 		 * will do the copy via I/O on the DS(s).
5247c057a378SRick Macklem 		 */
5248c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5249c057a378SRick Macklem 		goto nfsmout;
5250c057a378SRick Macklem 	}
5251c057a378SRick Macklem 	if (vp == tovp) {
5252c057a378SRick Macklem 		/* Copying a byte range within the same file is not allowed. */
5253c057a378SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
5254c057a378SRick Macklem 		goto nfsmout;
5255c057a378SRick Macklem 	}
5256c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_STATEID + 3 * NFSX_HYPER +
5257c057a378SRick Macklem 	    3 * NFSX_UNSIGNED);
5258c057a378SRick Macklem 	instp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
5259c057a378SRick Macklem 	inlop->lo_flags = NFSLCK_READ;
5260c057a378SRick Macklem 	instp->ls_ownerlen = 0;
5261c057a378SRick Macklem 	instp->ls_op = NULL;
5262c057a378SRick Macklem 	instp->ls_uid = nd->nd_cred->cr_uid;
5263c057a378SRick Macklem 	instp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
5264c057a378SRick Macklem 	clientid.lval[0] = instp->ls_stateid.other[0] = *tl++;
5265c057a378SRick Macklem 	clientid.lval[1] = instp->ls_stateid.other[1] = *tl++;
5266c057a378SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0)
5267c057a378SRick Macklem 		clientid.qval = nd->nd_clientid.qval;
5268c057a378SRick Macklem 	instp->ls_stateid.other[2] = *tl++;
5269c057a378SRick Macklem 	outstp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
5270c057a378SRick Macklem 	outlop->lo_flags = NFSLCK_WRITE;
5271c057a378SRick Macklem 	outstp->ls_ownerlen = 0;
5272c057a378SRick Macklem 	outstp->ls_op = NULL;
5273c057a378SRick Macklem 	outstp->ls_uid = nd->nd_cred->cr_uid;
5274c057a378SRick Macklem 	outstp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
5275c057a378SRick Macklem 	outstp->ls_stateid.other[0] = *tl++;
5276c057a378SRick Macklem 	outstp->ls_stateid.other[1] = *tl++;
5277c057a378SRick Macklem 	outstp->ls_stateid.other[2] = *tl++;
5278c057a378SRick Macklem 	inoff = fxdr_hyper(tl); tl += 2;
5279c057a378SRick Macklem 	inlop->lo_first = inoff;
5280c057a378SRick Macklem 	outoff = fxdr_hyper(tl); tl += 2;
5281c057a378SRick Macklem 	outlop->lo_first = outoff;
5282c057a378SRick Macklem 	len = fxdr_hyper(tl); tl += 2;
5283c057a378SRick Macklem 	if (len == 0) {
5284c057a378SRick Macklem 		/* len == 0 means to EOF. */
5285c057a378SRick Macklem 		inlop->lo_end = OFF_MAX;
5286c057a378SRick Macklem 		outlop->lo_end = OFF_MAX;
5287c057a378SRick Macklem 	} else {
5288c057a378SRick Macklem 		inlop->lo_end = inlop->lo_first + len;
5289c057a378SRick Macklem 		outlop->lo_end = outlop->lo_first + len;
5290c057a378SRick Macklem 	}
5291c057a378SRick Macklem 
5292c057a378SRick Macklem 	/*
5293c057a378SRick Macklem 	 * At this time only consecutive, synchronous copy is supported,
5294c057a378SRick Macklem 	 * so ca_consecutive and ca_synchronous can be ignored.
5295c057a378SRick Macklem 	 */
5296c057a378SRick Macklem 	tl += 2;
5297c057a378SRick Macklem 
5298c057a378SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
5299c057a378SRick Macklem 	if ((nd->nd_flag & ND_DSSERVER) != 0 || cnt != 0)
5300c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5301c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (inoff > OFF_MAX || outoff > OFF_MAX ||
5302c057a378SRick Macklem 	    inlop->lo_end > OFF_MAX || outlop->lo_end > OFF_MAX ||
5303c057a378SRick Macklem 	    inlop->lo_end < inlop->lo_first || outlop->lo_end <
5304c057a378SRick Macklem 	    outlop->lo_first))
5305c057a378SRick Macklem 		nd->nd_repstat = NFSERR_INVAL;
5306c057a378SRick Macklem 
5307c057a378SRick Macklem 	if (nd->nd_repstat == 0 && vnode_vtype(vp) != VREG)
5308c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGTYPE;
5309c057a378SRick Macklem 
5310c057a378SRick Macklem 	/* Check permissions for the input file. */
5311c057a378SRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
5312c057a378SRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
5313c057a378SRick Macklem 	ret = nfsvno_getattr(vp, &at, nd, curthread, 1, &attrbits);
5314c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5315c057a378SRick Macklem 		nd->nd_repstat = ret;
5316c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
5317c057a378SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
5318c057a378SRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred, exp,
5319c057a378SRick Macklem 		    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
5320c057a378SRick Macklem 		    NULL);
5321c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5322c057a378SRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(vp, &instp, &inlop, NULL,
5323c057a378SRick Macklem 		    clientid, &stateid, exp, nd, curthread);
5324b249ce48SMateusz Guzik 	NFSVOPUNLOCK(vp);
5325c057a378SRick Macklem 	if (nd->nd_repstat != 0)
5326c057a378SRick Macklem 		goto out;
5327c057a378SRick Macklem 
5328c057a378SRick Macklem 	error = NFSVOPLOCK(tovp, LK_SHARED);
5329c057a378SRick Macklem 	if (error != 0)
5330c057a378SRick Macklem 		goto out;
5331c057a378SRick Macklem 	if (vnode_vtype(tovp) != VREG)
5332c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGTYPE;
5333c057a378SRick Macklem 
5334c057a378SRick Macklem 	/* For the output file, we only need the Owner attribute. */
5335c057a378SRick Macklem 	ret = nfsvno_getattr(tovp, &at, nd, curthread, 1, &attrbits);
5336c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5337c057a378SRick Macklem 		nd->nd_repstat = ret;
5338c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
5339c057a378SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
5340c057a378SRick Macklem 		nd->nd_repstat = nfsvno_accchk(tovp, VWRITE, nd->nd_cred, toexp,
5341c057a378SRick Macklem 		    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
5342c057a378SRick Macklem 		    NULL);
5343c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5344c057a378SRick Macklem 		nd->nd_repstat = nfsrv_lockctrl(tovp, &outstp, &outlop, NULL,
5345c057a378SRick Macklem 		    clientid, &stateid, toexp, nd, curthread);
5346b249ce48SMateusz Guzik 	NFSVOPUNLOCK(tovp);
5347c057a378SRick Macklem 
5348c057a378SRick Macklem 	/* Range lock the byte ranges for both invp and outvp. */
5349c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5350c057a378SRick Macklem 		for (;;) {
5351c057a378SRick Macklem 			if (len == 0) {
5352c057a378SRick Macklem 				rl_wcookie = vn_rangelock_wlock(tovp, outoff,
5353c057a378SRick Macklem 				    OFF_MAX);
5354c057a378SRick Macklem 				rl_rcookie = vn_rangelock_tryrlock(vp, inoff,
5355c057a378SRick Macklem 				    OFF_MAX);
5356c057a378SRick Macklem 			} else {
5357c057a378SRick Macklem 				rl_wcookie = vn_rangelock_wlock(tovp, outoff,
5358c057a378SRick Macklem 				    outoff + len);
5359c057a378SRick Macklem 				rl_rcookie = vn_rangelock_tryrlock(vp, inoff,
5360c057a378SRick Macklem 				    inoff + len);
5361c057a378SRick Macklem 			}
5362c057a378SRick Macklem 			if (rl_rcookie != NULL)
5363c057a378SRick Macklem 				break;
5364c057a378SRick Macklem 			vn_rangelock_unlock(tovp, rl_wcookie);
5365c057a378SRick Macklem 			if (len == 0)
5366c057a378SRick Macklem 				rl_rcookie = vn_rangelock_rlock(vp, inoff,
5367c057a378SRick Macklem 				    OFF_MAX);
5368c057a378SRick Macklem 			else
5369c057a378SRick Macklem 				rl_rcookie = vn_rangelock_rlock(vp, inoff,
5370c057a378SRick Macklem 				    inoff + len);
5371c057a378SRick Macklem 			vn_rangelock_unlock(vp, rl_rcookie);
5372c057a378SRick Macklem 		}
5373c057a378SRick Macklem 
5374c057a378SRick Macklem 		error = NFSVOPLOCK(vp, LK_SHARED);
5375c057a378SRick Macklem 		if (error == 0) {
5376c057a378SRick Macklem 			ret = nfsvno_getattr(vp, &at, nd, curthread, 1, NULL);
5377c057a378SRick Macklem 			if (ret == 0) {
5378c057a378SRick Macklem 				/*
5379c057a378SRick Macklem 				 * Since invp is range locked, na_size should
5380c057a378SRick Macklem 				 * not change.
5381c057a378SRick Macklem 				 */
5382c057a378SRick Macklem 				if (len == 0 && at.na_size > inoff) {
5383c057a378SRick Macklem 					/*
5384c057a378SRick Macklem 					 * If len == 0, set it based on invp's
5385c057a378SRick Macklem 					 * size. If offset is past EOF, just
5386c057a378SRick Macklem 					 * leave len == 0.
5387c057a378SRick Macklem 					 */
5388c057a378SRick Macklem 					len = at.na_size - inoff;
5389c057a378SRick Macklem 				} else if (nfsrv_linux42server == 0 &&
5390c057a378SRick Macklem 				    inoff + len > at.na_size) {
5391c057a378SRick Macklem 					/*
5392c057a378SRick Macklem 					 * RFC-7862 says that NFSERR_INVAL must
5393c057a378SRick Macklem 					 * be returned when inoff + len exceeds
5394c057a378SRick Macklem 					 * the file size, however the NFSv4.2
5395c057a378SRick Macklem 					 * Linux client likes to do this, so
5396c057a378SRick Macklem 					 * only check if nfsrv_linux42server
5397c057a378SRick Macklem 					 * is not set.
5398c057a378SRick Macklem 					 */
5399c057a378SRick Macklem 					nd->nd_repstat = NFSERR_INVAL;
5400c057a378SRick Macklem 				}
5401c057a378SRick Macklem 			}
5402b249ce48SMateusz Guzik 			NFSVOPUNLOCK(vp);
5403c057a378SRick Macklem 			if (ret != 0 && nd->nd_repstat == 0)
5404c057a378SRick Macklem 				nd->nd_repstat = ret;
5405c057a378SRick Macklem 		} else if (nd->nd_repstat == 0)
5406c057a378SRick Macklem 			nd->nd_repstat = error;
5407c057a378SRick Macklem 	}
5408c057a378SRick Macklem 
5409c057a378SRick Macklem 	/*
5410c057a378SRick Macklem 	 * Do the actual copy to an upper limit of vfs.nfs.maxcopyrange.
5411c057a378SRick Macklem 	 * This limit is applied to ensure that the RPC replies in a
5412c057a378SRick Macklem 	 * reasonable time.
5413c057a378SRick Macklem 	 */
5414c057a378SRick Macklem 	if (len > nfs_maxcopyrange)
5415c057a378SRick Macklem 		xfer = nfs_maxcopyrange;
5416c057a378SRick Macklem 	else
5417c057a378SRick Macklem 		xfer = len;
5418c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5419c057a378SRick Macklem 		nd->nd_repstat = vn_copy_file_range(vp, &inoff, tovp, &outoff,
5420c057a378SRick Macklem 		    &xfer, 0, nd->nd_cred, nd->nd_cred, NULL);
5421c057a378SRick Macklem 		if (nd->nd_repstat == 0)
5422c057a378SRick Macklem 			len = xfer;
5423c057a378SRick Macklem 	}
5424c057a378SRick Macklem 
5425c057a378SRick Macklem 	/* Unlock the ranges. */
5426c057a378SRick Macklem 	if (rl_rcookie != NULL)
5427c057a378SRick Macklem 		vn_rangelock_unlock(vp, rl_rcookie);
5428c057a378SRick Macklem 	if (rl_wcookie != NULL)
5429c057a378SRick Macklem 		vn_rangelock_unlock(tovp, rl_wcookie);
5430c057a378SRick Macklem 
5431c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5432c057a378SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED + NFSX_HYPER +
5433c057a378SRick Macklem 		    NFSX_VERF);
5434c057a378SRick Macklem 		*tl++ = txdr_unsigned(0);	/* No callback ids. */
5435c057a378SRick Macklem 		txdr_hyper(len, tl); tl += 2;
5436c057a378SRick Macklem 		*tl++ = txdr_unsigned(NFSWRITE_UNSTABLE);
5437c057a378SRick Macklem 		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
5438c057a378SRick Macklem 		*tl++ = txdr_unsigned(nfsboottime.tv_usec);
5439c057a378SRick Macklem 		*tl++ = newnfs_true;
5440c057a378SRick Macklem 		*tl = newnfs_true;
5441c057a378SRick Macklem 	}
5442c057a378SRick Macklem out:
5443c057a378SRick Macklem 	vrele(vp);
5444c057a378SRick Macklem 	vrele(tovp);
5445c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5446c057a378SRick Macklem 	return (error);
5447c057a378SRick Macklem nfsmout:
5448c057a378SRick Macklem 	vput(vp);
5449c057a378SRick Macklem 	vrele(tovp);
5450c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5451c057a378SRick Macklem 	return (error);
5452c057a378SRick Macklem }
5453c057a378SRick Macklem 
5454c057a378SRick Macklem /*
5455c057a378SRick Macklem  * nfs seek service
5456c057a378SRick Macklem  */
5457b9cc3262SRyan Moeller int
5458c057a378SRick Macklem nfsrvd_seek(struct nfsrv_descript *nd, __unused int isdgram,
5459c057a378SRick Macklem     vnode_t vp, struct nfsexstuff *exp)
5460c057a378SRick Macklem {
5461c057a378SRick Macklem 	uint32_t *tl;
5462c057a378SRick Macklem 	struct nfsvattr at;
5463c057a378SRick Macklem 	int content, error = 0;
5464c057a378SRick Macklem 	off_t off;
5465c057a378SRick Macklem 	u_long cmd;
5466c057a378SRick Macklem 	nfsattrbit_t attrbits;
5467c057a378SRick Macklem 	bool eof;
5468c057a378SRick Macklem 
5469c057a378SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
5470c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
5471c057a378SRick Macklem 		goto nfsmout;
5472c057a378SRick Macklem 	}
5473c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + NFSX_HYPER + NFSX_UNSIGNED);
5474c057a378SRick Macklem 	/* Ignore the stateid for now. */
5475c057a378SRick Macklem 	tl += (NFSX_STATEID / NFSX_UNSIGNED);
5476c057a378SRick Macklem 	off = fxdr_hyper(tl); tl += 2;
5477c057a378SRick Macklem 	content = fxdr_unsigned(int, *tl);
5478c057a378SRick Macklem 	if (content == NFSV4CONTENT_DATA)
5479c057a378SRick Macklem 		cmd = FIOSEEKDATA;
5480c057a378SRick Macklem 	else if (content == NFSV4CONTENT_HOLE)
5481c057a378SRick Macklem 		cmd = FIOSEEKHOLE;
5482c057a378SRick Macklem 	else
5483c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
5484c057a378SRick Macklem 	if (nd->nd_repstat == 0 && vnode_vtype(vp) == VDIR)
5485c057a378SRick Macklem 		nd->nd_repstat = NFSERR_ISDIR;
5486c057a378SRick Macklem 	if (nd->nd_repstat == 0 && vnode_vtype(vp) != VREG)
5487c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGTYPE;
5488c057a378SRick Macklem 	if (nd->nd_repstat == 0 && off < 0)
5489c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NXIO;
5490c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5491c057a378SRick Macklem 		/* Check permissions for the input file. */
5492c057a378SRick Macklem 		NFSZERO_ATTRBIT(&attrbits);
5493c057a378SRick Macklem 		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
5494c057a378SRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &at, nd, curthread, 1,
5495c057a378SRick Macklem 		    &attrbits);
5496c057a378SRick Macklem 	}
5497c057a378SRick Macklem 	if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
5498c057a378SRick Macklem 	     NFSVNO_EXSTRICTACCESS(exp)))
5499c057a378SRick Macklem 		nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred, exp,
5500c057a378SRick Macklem 		    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
5501c057a378SRick Macklem 		    NULL);
5502c057a378SRick Macklem 	if (nd->nd_repstat != 0)
5503c057a378SRick Macklem 		goto nfsmout;
5504c057a378SRick Macklem 
5505c057a378SRick Macklem 	/* nfsvno_seek() unlocks and vrele()s the vp. */
5506c057a378SRick Macklem 	nd->nd_repstat = nfsvno_seek(nd, vp, cmd, &off, content, &eof,
5507c057a378SRick Macklem 	    nd->nd_cred, curthread);
5508c057a378SRick Macklem 	if (nd->nd_repstat == 0 && eof && content == NFSV4CONTENT_DATA &&
5509c057a378SRick Macklem 	    nfsrv_linux42server != 0)
5510c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NXIO;
5511c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5512c057a378SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED + NFSX_HYPER);
5513c057a378SRick Macklem 		if (eof)
5514c057a378SRick Macklem 			*tl++ = newnfs_true;
5515c057a378SRick Macklem 		else
5516c057a378SRick Macklem 			*tl++ = newnfs_false;
5517c057a378SRick Macklem 		txdr_hyper(off, tl);
5518c057a378SRick Macklem 	}
5519c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5520c057a378SRick Macklem 	return (error);
5521c057a378SRick Macklem nfsmout:
5522c057a378SRick Macklem 	vput(vp);
5523c057a378SRick Macklem 	NFSEXITCODE2(error, nd);
5524c057a378SRick Macklem 	return (error);
5525c057a378SRick Macklem }
5526c057a378SRick Macklem 
5527c057a378SRick Macklem /*
5528c057a378SRick Macklem  * nfs get extended attribute service
5529c057a378SRick Macklem  */
5530b9cc3262SRyan Moeller int
5531c057a378SRick Macklem nfsrvd_getxattr(struct nfsrv_descript *nd, __unused int isdgram,
5532c057a378SRick Macklem     vnode_t vp, __unused struct nfsexstuff *exp)
5533c057a378SRick Macklem {
5534c057a378SRick Macklem 	uint32_t *tl;
5535ae070589SRick Macklem 	struct mbuf *mp = NULL, *mpend = NULL;
5536c057a378SRick Macklem 	int error, len;
5537c057a378SRick Macklem 	char *name;
5538c057a378SRick Macklem 	struct thread *p = curthread;
5539c057a378SRick Macklem 
5540c057a378SRick Macklem 	error = 0;
5541c057a378SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
5542c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
5543c057a378SRick Macklem 		goto nfsmout;
5544c057a378SRick Macklem 	}
5545c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
5546c057a378SRick Macklem 	len = fxdr_unsigned(int, *tl);
5547c057a378SRick Macklem 	if (len <= 0) {
5548c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
5549c057a378SRick Macklem 		goto nfsmout;
5550c057a378SRick Macklem 	}
5551c057a378SRick Macklem 	if (len > EXTATTR_MAXNAMELEN) {
5552c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOXATTR;
5553c057a378SRick Macklem 		goto nfsmout;
5554c057a378SRick Macklem 	}
5555c057a378SRick Macklem 	name = malloc(len + 1, M_TEMP, M_WAITOK);
5556c057a378SRick Macklem 	nd->nd_repstat = nfsrv_mtostr(nd, name, len);
5557c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5558c057a378SRick Macklem 		nd->nd_repstat = nfsvno_getxattr(vp, name, nd->nd_maxresp,
5559c057a378SRick Macklem 		    nd->nd_cred, p, &mp, &mpend, &len);
5560c057a378SRick Macklem 	if (nd->nd_repstat == ENOATTR)
5561c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOXATTR;
5562c057a378SRick Macklem 	else if (nd->nd_repstat == EOPNOTSUPP)
5563c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5564c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5565c057a378SRick Macklem 		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
5566c057a378SRick Macklem 		*tl = txdr_unsigned(len);
5567fb8ed4c5SRick Macklem 		if (len > 0) {
55689f6624d3SRick Macklem 			nd->nd_mb->m_next = mp;
5569c057a378SRick Macklem 			nd->nd_mb = mpend;
55709f6624d3SRick Macklem 			nd->nd_bpos = mtod(mpend, caddr_t) + mpend->m_len;
5571c057a378SRick Macklem 		}
5572fb8ed4c5SRick Macklem 	}
5573c057a378SRick Macklem 	free(name, M_TEMP);
5574c057a378SRick Macklem 
5575c057a378SRick Macklem nfsmout:
5576c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5577c057a378SRick Macklem 		nd->nd_repstat = error;
5578c057a378SRick Macklem 	vput(vp);
5579c057a378SRick Macklem 	NFSEXITCODE2(0, nd);
5580c057a378SRick Macklem 	return (0);
5581c057a378SRick Macklem }
5582c057a378SRick Macklem 
5583c057a378SRick Macklem /*
5584c057a378SRick Macklem  * nfs set extended attribute service
5585c057a378SRick Macklem  */
5586b9cc3262SRyan Moeller int
5587c057a378SRick Macklem nfsrvd_setxattr(struct nfsrv_descript *nd, __unused int isdgram,
5588c057a378SRick Macklem     vnode_t vp, __unused struct nfsexstuff *exp)
5589c057a378SRick Macklem {
5590c057a378SRick Macklem 	uint32_t *tl;
5591c057a378SRick Macklem 	struct nfsvattr ova, nva;
5592c057a378SRick Macklem 	nfsattrbit_t attrbits;
5593c057a378SRick Macklem 	int error, len, opt;
5594c057a378SRick Macklem 	char *name;
5595c057a378SRick Macklem 	size_t siz;
5596c057a378SRick Macklem 	struct thread *p = curthread;
5597c057a378SRick Macklem 
5598c057a378SRick Macklem 	error = 0;
5599c057a378SRick Macklem 	name = NULL;
5600c057a378SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
5601c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
5602c057a378SRick Macklem 		goto nfsmout;
5603c057a378SRick Macklem 	}
5604c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
5605c057a378SRick Macklem 	opt = fxdr_unsigned(int, *tl++);
5606c057a378SRick Macklem 	len = fxdr_unsigned(int, *tl);
5607c057a378SRick Macklem 	if (len <= 0) {
5608c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
5609c057a378SRick Macklem 		goto nfsmout;
5610c057a378SRick Macklem 	}
5611c057a378SRick Macklem 	if (len > EXTATTR_MAXNAMELEN) {
5612c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOXATTR;
5613c057a378SRick Macklem 		goto nfsmout;
5614c057a378SRick Macklem 	}
5615c057a378SRick Macklem 	name = malloc(len + 1, M_TEMP, M_WAITOK);
5616c057a378SRick Macklem 	error = nfsrv_mtostr(nd, name, len);
5617c057a378SRick Macklem 	if (error != 0)
5618c057a378SRick Macklem 		goto nfsmout;
5619c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
5620c057a378SRick Macklem 	len = fxdr_unsigned(int, *tl);
5621fb8ed4c5SRick Macklem 	if (len < 0 || len > IOSIZE_MAX) {
5622c057a378SRick Macklem 		nd->nd_repstat = NFSERR_XATTR2BIG;
5623c057a378SRick Macklem 		goto nfsmout;
5624c057a378SRick Macklem 	}
5625c057a378SRick Macklem 	switch (opt) {
5626c057a378SRick Macklem 	case NFSV4SXATTR_CREATE:
5627c057a378SRick Macklem 		error = VOP_GETEXTATTR(vp, EXTATTR_NAMESPACE_USER, name, NULL,
5628c057a378SRick Macklem 		    &siz, nd->nd_cred, p);
5629c057a378SRick Macklem 		if (error != ENOATTR)
5630c057a378SRick Macklem 			nd->nd_repstat = NFSERR_EXIST;
5631c057a378SRick Macklem 		error = 0;
5632c057a378SRick Macklem 		break;
5633c057a378SRick Macklem 	case NFSV4SXATTR_REPLACE:
5634c057a378SRick Macklem 		error = VOP_GETEXTATTR(vp, EXTATTR_NAMESPACE_USER, name, NULL,
5635c057a378SRick Macklem 		    &siz, nd->nd_cred, p);
5636c057a378SRick Macklem 		if (error != 0)
5637c057a378SRick Macklem 			nd->nd_repstat = NFSERR_NOXATTR;
5638c057a378SRick Macklem 		break;
5639c057a378SRick Macklem 	case NFSV4SXATTR_EITHER:
5640c057a378SRick Macklem 		break;
5641c057a378SRick Macklem 	default:
5642c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
5643c057a378SRick Macklem 	}
5644c057a378SRick Macklem 	if (nd->nd_repstat != 0)
5645c057a378SRick Macklem 		goto nfsmout;
5646c057a378SRick Macklem 
5647c057a378SRick Macklem 	/* Now, do the Set Extended attribute, with Change before and after. */
5648c057a378SRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
5649c057a378SRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
5650c057a378SRick Macklem 	nd->nd_repstat = nfsvno_getattr(vp, &ova, nd, p, 1, &attrbits);
5651c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5652c057a378SRick Macklem 		nd->nd_repstat = nfsvno_setxattr(vp, name, len, nd->nd_md,
5653c057a378SRick Macklem 		    nd->nd_dpos, nd->nd_cred, p);
5654c057a378SRick Macklem 		if (nd->nd_repstat == ENXIO)
5655c057a378SRick Macklem 			nd->nd_repstat = NFSERR_XATTR2BIG;
5656c057a378SRick Macklem 	}
5657fb8ed4c5SRick Macklem 	if (nd->nd_repstat == 0 && len > 0)
5658c057a378SRick Macklem 		nd->nd_repstat = nfsm_advance(nd, NFSM_RNDUP(len), -1);
5659c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5660c057a378SRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, &attrbits);
5661c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5662c057a378SRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_UNSIGNED);
5663c057a378SRick Macklem 		*tl++ = newnfs_true;
5664c057a378SRick Macklem 		txdr_hyper(ova.na_filerev, tl); tl += 2;
5665c057a378SRick Macklem 		txdr_hyper(nva.na_filerev, tl);
5666c057a378SRick Macklem 	}
5667c057a378SRick Macklem 
5668c057a378SRick Macklem nfsmout:
5669c057a378SRick Macklem 	free(name, M_TEMP);
5670c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5671c057a378SRick Macklem 		nd->nd_repstat = error;
5672c057a378SRick Macklem 	vput(vp);
5673c057a378SRick Macklem 	NFSEXITCODE2(0, nd);
5674c057a378SRick Macklem 	return (0);
5675c057a378SRick Macklem }
5676c057a378SRick Macklem 
5677c057a378SRick Macklem /*
5678c057a378SRick Macklem  * nfs remove extended attribute service
5679c057a378SRick Macklem  */
5680b9cc3262SRyan Moeller int
5681c057a378SRick Macklem nfsrvd_rmxattr(struct nfsrv_descript *nd, __unused int isdgram,
5682c057a378SRick Macklem     vnode_t vp, __unused struct nfsexstuff *exp)
5683c057a378SRick Macklem {
5684c057a378SRick Macklem 	uint32_t *tl;
5685c057a378SRick Macklem 	struct nfsvattr ova, nva;
5686c057a378SRick Macklem 	nfsattrbit_t attrbits;
5687c057a378SRick Macklem 	int error, len;
5688c057a378SRick Macklem 	char *name;
5689c057a378SRick Macklem 	struct thread *p = curthread;
5690c057a378SRick Macklem 
5691c057a378SRick Macklem 	error = 0;
5692c057a378SRick Macklem 	name = NULL;
5693c057a378SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
5694c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
5695c057a378SRick Macklem 		goto nfsmout;
5696c057a378SRick Macklem 	}
5697c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
5698c057a378SRick Macklem 	len = fxdr_unsigned(int, *tl);
5699c057a378SRick Macklem 	if (len <= 0) {
5700c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
5701c057a378SRick Macklem 		goto nfsmout;
5702c057a378SRick Macklem 	}
5703c057a378SRick Macklem 	if (len > EXTATTR_MAXNAMELEN) {
5704c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOXATTR;
5705c057a378SRick Macklem 		goto nfsmout;
5706c057a378SRick Macklem 	}
5707c057a378SRick Macklem 	name = malloc(len + 1, M_TEMP, M_WAITOK);
5708c057a378SRick Macklem 	error = nfsrv_mtostr(nd, name, len);
5709c057a378SRick Macklem 	if (error != 0)
5710c057a378SRick Macklem 		goto nfsmout;
5711c057a378SRick Macklem 
5712c057a378SRick Macklem 	if ((nd->nd_flag & ND_IMPLIEDCLID) == 0) {
5713c057a378SRick Macklem 		printf("EEK! nfsrvd_rmxattr: no implied clientid\n");
5714c057a378SRick Macklem 		error = NFSERR_NOXATTR;
5715c057a378SRick Macklem 		goto nfsmout;
5716c057a378SRick Macklem 	}
5717c057a378SRick Macklem 	/*
5718c057a378SRick Macklem 	 * Now, do the Remove Extended attribute, with Change before and
5719c057a378SRick Macklem 	 * after.
5720c057a378SRick Macklem 	*/
5721c057a378SRick Macklem 	NFSZERO_ATTRBIT(&attrbits);
5722c057a378SRick Macklem 	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
5723c057a378SRick Macklem 	nd->nd_repstat = nfsvno_getattr(vp, &ova, nd, p, 1, &attrbits);
5724c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5725c057a378SRick Macklem 		nd->nd_repstat = nfsvno_rmxattr(nd, vp, name, nd->nd_cred, p);
5726c057a378SRick Macklem 		if (nd->nd_repstat == ENOATTR)
5727c057a378SRick Macklem 			nd->nd_repstat = NFSERR_NOXATTR;
5728c057a378SRick Macklem 	}
5729c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5730c057a378SRick Macklem 		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, &attrbits);
5731c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
57320bda1dddSRick Macklem 		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_UNSIGNED);
57330bda1dddSRick Macklem 		*tl++ = newnfs_true;
5734c057a378SRick Macklem 		txdr_hyper(ova.na_filerev, tl); tl += 2;
5735c057a378SRick Macklem 		txdr_hyper(nva.na_filerev, tl);
5736c057a378SRick Macklem 	}
5737c057a378SRick Macklem 
5738c057a378SRick Macklem nfsmout:
5739c057a378SRick Macklem 	free(name, M_TEMP);
5740c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5741c057a378SRick Macklem 		nd->nd_repstat = error;
5742c057a378SRick Macklem 	vput(vp);
5743c057a378SRick Macklem 	NFSEXITCODE2(0, nd);
5744c057a378SRick Macklem 	return (0);
5745c057a378SRick Macklem }
5746c057a378SRick Macklem 
5747c057a378SRick Macklem /*
5748c057a378SRick Macklem  * nfs list extended attribute service
5749c057a378SRick Macklem  */
5750b9cc3262SRyan Moeller int
5751c057a378SRick Macklem nfsrvd_listxattr(struct nfsrv_descript *nd, __unused int isdgram,
5752c057a378SRick Macklem     vnode_t vp, __unused struct nfsexstuff *exp)
5753c057a378SRick Macklem {
5754c057a378SRick Macklem 	uint32_t cnt, *tl, len, len2, i, pos, retlen;
5755c057a378SRick Macklem 	int error;
5756c057a378SRick Macklem 	uint64_t cookie, cookie2;
5757c057a378SRick Macklem 	u_char *buf;
5758c057a378SRick Macklem 	bool eof;
5759c057a378SRick Macklem 	struct thread *p = curthread;
5760c057a378SRick Macklem 
5761c057a378SRick Macklem 	error = 0;
5762c057a378SRick Macklem 	buf = NULL;
5763c057a378SRick Macklem 	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
5764c057a378SRick Macklem 		nd->nd_repstat = NFSERR_WRONGSEC;
5765c057a378SRick Macklem 		goto nfsmout;
5766c057a378SRick Macklem 	}
5767c057a378SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + NFSX_UNSIGNED);
5768c057a378SRick Macklem 	/*
5769c057a378SRick Macklem 	 * The cookie doesn't need to be in net byte order, but FreeBSD
5770c057a378SRick Macklem 	 * does so to make it more readable in packet traces.
5771c057a378SRick Macklem 	 */
5772c057a378SRick Macklem 	cookie = fxdr_hyper(tl); tl += 2;
5773c057a378SRick Macklem 	len = fxdr_unsigned(uint32_t, *tl);
5774c057a378SRick Macklem 	if (len == 0 || cookie >= IOSIZE_MAX) {
5775c057a378SRick Macklem 		nd->nd_repstat = NFSERR_BADXDR;
5776c057a378SRick Macklem 		goto nfsmout;
5777c057a378SRick Macklem 	}
5778c057a378SRick Macklem 	if (len > nd->nd_maxresp - NFS_MAXXDR)
5779c057a378SRick Macklem 		len = nd->nd_maxresp - NFS_MAXXDR;
5780c057a378SRick Macklem 	len2 = len;
5781c057a378SRick Macklem 	nd->nd_repstat = nfsvno_listxattr(vp, cookie, nd->nd_cred, p, &buf,
5782c057a378SRick Macklem 	    &len, &eof);
5783c057a378SRick Macklem 	if (nd->nd_repstat == EOPNOTSUPP)
5784c057a378SRick Macklem 		nd->nd_repstat = NFSERR_NOTSUPP;
5785c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5786c057a378SRick Macklem 		cookie2 = cookie + len;
5787c057a378SRick Macklem 		if (cookie2 < cookie)
5788c057a378SRick Macklem 			nd->nd_repstat = NFSERR_BADXDR;
5789c057a378SRick Macklem 	}
5790c057a378SRick Macklem 	if (nd->nd_repstat == 0) {
5791c057a378SRick Macklem 		/* Now copy the entries out. */
5792c057a378SRick Macklem 		retlen = NFSX_HYPER + 2 * NFSX_UNSIGNED;
5793c057a378SRick Macklem 		if (len == 0 && retlen <= len2) {
5794c057a378SRick Macklem 			/* The cookie was at eof. */
5795c057a378SRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_HYPER + 2 *
5796c057a378SRick Macklem 			    NFSX_UNSIGNED);
5797c057a378SRick Macklem 			txdr_hyper(cookie2, tl); tl += 2;
5798c057a378SRick Macklem 			*tl++ = txdr_unsigned(0);
5799c057a378SRick Macklem 			*tl = newnfs_true;
5800c057a378SRick Macklem 			goto nfsmout;
5801c057a378SRick Macklem 		}
5802c057a378SRick Macklem 
5803c057a378SRick Macklem 		/* Sanity check the cookie. */
5804c057a378SRick Macklem 		for (pos = 0; pos < len; pos += (i + 1)) {
5805c057a378SRick Macklem 			if (pos == cookie)
5806c057a378SRick Macklem 				break;
5807c057a378SRick Macklem 			i = buf[pos];
5808c057a378SRick Macklem 		}
5809c057a378SRick Macklem 		if (pos != cookie) {
5810c057a378SRick Macklem 			nd->nd_repstat = NFSERR_INVAL;
5811c057a378SRick Macklem 			goto nfsmout;
5812c057a378SRick Macklem 		}
5813c057a378SRick Macklem 
5814c057a378SRick Macklem 		/* Loop around copying the entrie(s) out. */
5815c057a378SRick Macklem 		cnt = 0;
5816c057a378SRick Macklem 		len -= cookie;
5817c057a378SRick Macklem 		i = buf[pos];
5818c057a378SRick Macklem 		while (i < len && len2 >= retlen + NFSM_RNDUP(i) +
5819c057a378SRick Macklem 		    NFSX_UNSIGNED) {
5820c057a378SRick Macklem 			if (cnt == 0) {
5821c057a378SRick Macklem 				NFSM_BUILD(tl, uint32_t *, NFSX_HYPER +
5822c057a378SRick Macklem 				    NFSX_UNSIGNED);
5823c057a378SRick Macklem 				txdr_hyper(cookie2, tl); tl += 2;
5824c057a378SRick Macklem 			}
5825c057a378SRick Macklem 			retlen += nfsm_strtom(nd, &buf[pos + 1], i);
5826c057a378SRick Macklem 			len -= (i + 1);
5827c057a378SRick Macklem 			pos += (i + 1);
5828c057a378SRick Macklem 			i = buf[pos];
5829c057a378SRick Macklem 			cnt++;
5830c057a378SRick Macklem 		}
5831c057a378SRick Macklem 		/*
5832c057a378SRick Macklem 		 * eof is set true/false by nfsvno_listxattr(), but if we
5833c057a378SRick Macklem 		 * can't copy all entries returned by nfsvno_listxattr(),
5834c057a378SRick Macklem 		 * we are not at eof.
5835c057a378SRick Macklem 		 */
5836c057a378SRick Macklem 		if (len > 0)
5837c057a378SRick Macklem 			eof = false;
5838c057a378SRick Macklem 		if (cnt > 0) {
5839c057a378SRick Macklem 			/* *tl is set above. */
5840c057a378SRick Macklem 			*tl = txdr_unsigned(cnt);
5841c057a378SRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
5842c057a378SRick Macklem 			if (eof)
5843c057a378SRick Macklem 				*tl = newnfs_true;
5844c057a378SRick Macklem 			else
5845c057a378SRick Macklem 				*tl = newnfs_false;
5846c057a378SRick Macklem 		} else
5847c057a378SRick Macklem 			nd->nd_repstat = NFSERR_TOOSMALL;
5848c057a378SRick Macklem 	}
5849c057a378SRick Macklem 
5850c057a378SRick Macklem nfsmout:
5851c057a378SRick Macklem 	free(buf, M_TEMP);
5852c057a378SRick Macklem 	if (nd->nd_repstat == 0)
5853c057a378SRick Macklem 		nd->nd_repstat = error;
5854c057a378SRick Macklem 	vput(vp);
5855c057a378SRick Macklem 	NFSEXITCODE2(0, nd);
5856c057a378SRick Macklem 	return (0);
5857c057a378SRick Macklem }
5858c057a378SRick Macklem 
5859c057a378SRick Macklem /*
5860c59e4cc3SRick Macklem  * nfsv4 service not supported
5861c59e4cc3SRick Macklem  */
5862b9cc3262SRyan Moeller int
5863c59e4cc3SRick Macklem nfsrvd_notsupp(struct nfsrv_descript *nd, __unused int isdgram,
5864af444b18SEdward Tomasz Napierala     __unused vnode_t vp, __unused struct nfsexstuff *exp)
5865c59e4cc3SRick Macklem {
5866c59e4cc3SRick Macklem 
5867c59e4cc3SRick Macklem 	nd->nd_repstat = NFSERR_NOTSUPP;
5868c59e4cc3SRick Macklem 	NFSEXITCODE2(0, nd);
5869c59e4cc3SRick Macklem 	return (0);
5870c59e4cc3SRick Macklem }
5871c59e4cc3SRick Macklem 
5872