nfs_nfsdserv.c revision 300778
197403Sobrien/*-
297403Sobrien * Copyright (c) 1989, 1993
3169691Skan *	The Regents of the University of California.  All rights reserved.
4169691Skan *
597403Sobrien * This code is derived from software contributed to Berkeley by
697403Sobrien * Rick Macklem at The University of Guelph.
797403Sobrien *
897403Sobrien * Redistribution and use in source and binary forms, with or without
997403Sobrien * modification, are permitted provided that the following conditions
1097403Sobrien * are met:
1197403Sobrien * 1. Redistributions of source code must retain the above copyright
1297403Sobrien *    notice, this list of conditions and the following disclaimer.
1397403Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1497403Sobrien *    notice, this list of conditions and the following disclaimer in the
1597403Sobrien *    documentation and/or other materials provided with the distribution.
1697403Sobrien * 4. Neither the name of the University nor the names of its contributors
1797403Sobrien *    may be used to endorse or promote products derived from this software
1897403Sobrien *    without specific prior written permission.
1997403Sobrien *
20169691Skan * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2197403Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2297403Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2397403Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2497403Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2597403Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2697403Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2797403Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2897403Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2997403Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3097403Sobrien * SUCH DAMAGE.
3197403Sobrien *
3297403Sobrien */
3397403Sobrien
3497403Sobrien#include <sys/cdefs.h>
3597403Sobrien__FBSDID("$FreeBSD: stable/10/sys/fs/nfsserver/nfs_nfsdserv.c 300778 2016-05-26 21:32:16Z rmacklem $");
3697403Sobrien
37169691Skan/*
38169691Skan * nfs version 2, 3 and 4 server calls to vnode ops
39169691Skan * - these routines generally have 3 phases
40169691Skan *   1 - break down and validate rpc request in mbuf list
41132720Skan *   2 - do the vnode ops for the request, usually by calling a nfsvno_XXX()
42132720Skan *       function in nfsd_port.c
4397403Sobrien *   3 - build the rpc reply in an mbuf list
4497403Sobrien * For nfsv4, these functions are called for each Op within the Compound RPC.
4597403Sobrien */
46169691Skan
47132720Skan#ifndef APPLEKEXT
4897403Sobrien#include <fs/nfs/nfsport.h>
49169691Skan
50169691Skan/* Global vars */
51117397Skanextern u_int32_t newnfs_false, newnfs_true;
52117397Skanextern enum vtype nv34tov_type[8];
53117397Skanextern struct timeval nfsboottime;
54117397Skanextern int nfs_rootfhset;
55117397Skanextern int nfsrv_enable_crossmntpt;
56117397Skanextern int nfsrv_statehashsize;
57117397Skan#endif	/* !APPLEKEXT */
58117397Skan
59117397Skanstatic int	nfs_async = 0;
60117397SkanSYSCTL_DECL(_vfs_nfsd);
61117397SkanSYSCTL_INT(_vfs_nfsd, OID_AUTO, async, CTLFLAG_RW, &nfs_async, 0,
62117397Skan    "Tell client that writes were synced even though they were not");
63117397Skan
64117397Skan/*
65117397Skan * This list defines the GSS mechanisms supported.
66117397Skan * (Don't ask me how you get these strings from the RFC stuff like
67117397Skan *  iso(1), org(3)... but someone did it, so I don't need to know.)
68117397Skan */
69117397Skanstatic struct nfsgss_mechlist nfsgss_mechlist[] = {
70117397Skan	{ 9, "\052\206\110\206\367\022\001\002\002", 11 },
71117397Skan	{ 0, "", 0 },
72117397Skan};
73117397Skan
74117397Skan/* local functions */
75117397Skanstatic void nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
76117397Skan    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
77132720Skan    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
78117397Skan    int *diraft_retp, nfsattrbit_t *attrbitp,
79117397Skan    NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
80117397Skan    int pathlen);
81117397Skanstatic void nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
82117397Skan    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
83117397Skan    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
84117397Skan    int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
85117397Skan    NFSPROC_T *p, struct nfsexstuff *exp);
86117397Skan
87117397Skan/*
88117397Skan * nfs access service (not a part of NFS V2)
89117397Skan */
90117397SkanAPPLESTATIC int
91117397Skannfsrvd_access(struct nfsrv_descript *nd, __unused int isdgram,
92117397Skan    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
93117397Skan{
94117397Skan	u_int32_t *tl;
95117397Skan	int getret, error = 0;
96117397Skan	struct nfsvattr nva;
97117397Skan	u_int32_t testmode, nfsmode, supported = 0;
98117397Skan	accmode_t deletebit;
99117397Skan
100117397Skan	if (nd->nd_repstat) {
101117397Skan		nfsrv_postopattr(nd, 1, &nva);
102117397Skan		goto out;
103117397Skan	}
104117397Skan	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
105117397Skan	nfsmode = fxdr_unsigned(u_int32_t, *tl);
106117397Skan	if ((nd->nd_flag & ND_NFSV4) &&
107117397Skan	    (nfsmode & ~(NFSACCESS_READ | NFSACCESS_LOOKUP |
108117397Skan	     NFSACCESS_MODIFY | NFSACCESS_EXTEND | NFSACCESS_DELETE |
10997403Sobrien	     NFSACCESS_EXECUTE))) {
11097403Sobrien		nd->nd_repstat = NFSERR_INVAL;
11197403Sobrien		vput(vp);
11297403Sobrien		goto out;
113169691Skan	}
114169691Skan	if (nfsmode & NFSACCESS_READ) {
11597403Sobrien		supported |= NFSACCESS_READ;
11697403Sobrien		if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
117132720Skan		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
118132720Skan			nfsmode &= ~NFSACCESS_READ;
119132720Skan	}
120169691Skan	if (nfsmode & NFSACCESS_MODIFY) {
121169691Skan		supported |= NFSACCESS_MODIFY;
122169691Skan		if (nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp, p,
123169691Skan		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
124169691Skan			nfsmode &= ~NFSACCESS_MODIFY;
125169691Skan	}
12697403Sobrien	if (nfsmode & NFSACCESS_EXTEND) {
12797403Sobrien		supported |= NFSACCESS_EXTEND;
12897403Sobrien		if (nfsvno_accchk(vp, VWRITE | VAPPEND, nd->nd_cred, exp, p,
129132720Skan		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
130132720Skan			nfsmode &= ~NFSACCESS_EXTEND;
131117397Skan	}
13297403Sobrien	if (nfsmode & NFSACCESS_DELETE) {
13397403Sobrien		supported |= NFSACCESS_DELETE;
13497403Sobrien		if (vp->v_type == VDIR)
135132720Skan			deletebit = VDELETE_CHILD;
136132720Skan		else
13797403Sobrien			deletebit = VDELETE;
138132720Skan		if (nfsvno_accchk(vp, deletebit, nd->nd_cred, exp, p,
139132720Skan		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
14097403Sobrien			nfsmode &= ~NFSACCESS_DELETE;
14197403Sobrien	}
14297403Sobrien	if (vnode_vtype(vp) == VDIR)
14397403Sobrien		testmode = NFSACCESS_LOOKUP;
14497403Sobrien	else
14597403Sobrien		testmode = NFSACCESS_EXECUTE;
146132720Skan	if (nfsmode & testmode) {
147132720Skan		supported |= (nfsmode & testmode);
14897403Sobrien		if (nfsvno_accchk(vp, VEXEC, nd->nd_cred, exp, p,
149132720Skan		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
150132720Skan			nfsmode &= ~testmode;
151132720Skan	}
152132720Skan	nfsmode &= supported;
153132720Skan	if (nd->nd_flag & ND_NFSV3) {
154132720Skan		getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
155132720Skan		nfsrv_postopattr(nd, getret, &nva);
15697403Sobrien	}
15797403Sobrien	vput(vp);
15897403Sobrien	if (nd->nd_flag & ND_NFSV4) {
159117397Skan		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
16097403Sobrien		*tl++ = txdr_unsigned(supported);
16197403Sobrien	} else
16297403Sobrien		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
16397403Sobrien	*tl = txdr_unsigned(nfsmode);
16497403Sobrien
16597403Sobrienout:
16697403Sobrien	NFSEXITCODE2(0, nd);
167117397Skan	return (0);
16897403Sobriennfsmout:
16997403Sobrien	vput(vp);
170117397Skan	NFSEXITCODE2(error, nd);
171132720Skan	return (error);
172132720Skan}
173132720Skan
17497403Sobrien/*
175132720Skan * nfs getattr service
176132720Skan */
177132720SkanAPPLESTATIC int
178117397Skannfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
179132720Skan    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
180132720Skan{
181169691Skan	struct nfsvattr nva;
182169691Skan	fhandle_t fh;
183169691Skan	int at_root = 0, error = 0, supports_nfsv4acls;
184169691Skan	struct nfsreferral *refp;
185169691Skan	nfsattrbit_t attrbits, tmpbits;
186169691Skan	struct mount *mp;
187169691Skan	struct vnode *tvp = NULL;
188132720Skan	struct vattr va;
18997403Sobrien	uint64_t mounted_on_fileno = 0;
19097403Sobrien	accmode_t accmode;
191132720Skan
19297403Sobrien	if (nd->nd_repstat)
19397403Sobrien		goto out;
19497403Sobrien	if (nd->nd_flag & ND_NFSV4) {
195132720Skan		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
19697403Sobrien		if (error) {
19797403Sobrien			vput(vp);
198117397Skan			goto out;
199132720Skan		}
20097403Sobrien
20197403Sobrien		/*
202117397Skan		 * Check for a referral.
203132720Skan		 */
20497403Sobrien		refp = nfsv4root_getreferral(vp, NULL, 0);
205169691Skan		if (refp != NULL) {
206169691Skan			(void) nfsrv_putreferralattr(nd, &attrbits, refp, 1,
207169691Skan			    &nd->nd_repstat);
208169691Skan			vput(vp);
209169691Skan			goto out;
210169691Skan		}
211169691Skan		if (nd->nd_repstat == 0) {
212169691Skan			accmode = 0;
213169691Skan			NFSSET_ATTRBIT(&tmpbits, &attrbits);
214169691Skan			if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_ACL)) {
215117397Skan				NFSCLRBIT_ATTRBIT(&tmpbits, NFSATTRBIT_ACL);
21697403Sobrien				accmode |= VREAD_ACL;
21797403Sobrien			}
21897403Sobrien			if (NFSNONZERO_ATTRBIT(&tmpbits))
219117397Skan				accmode |= VREAD_ATTRIBUTES;
22097403Sobrien			if (accmode != 0)
221117397Skan				nd->nd_repstat = nfsvno_accchk(vp, accmode,
222117397Skan				    nd->nd_cred, exp, p, NFSACCCHK_NOOVERRIDE,
223117397Skan				    NFSACCCHK_VPISLOCKED, NULL);
22497403Sobrien		}
22597403Sobrien	}
22697403Sobrien	if (!nd->nd_repstat)
227117397Skan		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
228132720Skan	if (!nd->nd_repstat) {
22997403Sobrien		if (nd->nd_flag & ND_NFSV4) {
230117397Skan			if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_FILEHANDLE))
23197403Sobrien				nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
232117397Skan			if (!nd->nd_repstat)
233146897Skan				nd->nd_repstat = nfsrv_checkgetattr(nd, vp,
234132720Skan				    &nva, &attrbits, nd->nd_cred, p);
235146897Skan			if (nd->nd_repstat == 0) {
236169691Skan				supports_nfsv4acls = nfs_supportsnfsv4acls(vp);
237169691Skan				mp = vp->v_mount;
238132720Skan				if (nfsrv_enable_crossmntpt != 0 &&
23997403Sobrien				    vp->v_type == VDIR &&
24097403Sobrien				    (vp->v_vflag & VV_ROOT) != 0 &&
241117397Skan				    vp != rootvnode) {
24297403Sobrien					tvp = mp->mnt_vnodecovered;
24397403Sobrien					VREF(tvp);
244117397Skan					at_root = 1;
24597403Sobrien				} else
246117397Skan					at_root = 0;
247146897Skan				vfs_ref(mp);
248132720Skan				NFSVOPUNLOCK(vp, 0);
249146897Skan				if (at_root != 0) {
250169691Skan					if ((nd->nd_repstat =
251117397Skan					     NFSVOPLOCK(tvp, LK_SHARED)) == 0) {
25297403Sobrien						nd->nd_repstat = VOP_GETATTR(
25397403Sobrien						    tvp, &va, nd->nd_cred);
254117397Skan						vput(tvp);
25597403Sobrien					} else
25697403Sobrien						vrele(tvp);
25797403Sobrien					if (nd->nd_repstat == 0)
25897403Sobrien						mounted_on_fileno = (uint64_t)
25997403Sobrien						    va.va_fileid;
26097403Sobrien					else
26197403Sobrien						at_root = 0;
26297403Sobrien				}
26397403Sobrien				if (nd->nd_repstat == 0)
26497403Sobrien					nd->nd_repstat = vfs_busy(mp, 0);
26597403Sobrien				vfs_rel(mp);
26697403Sobrien				if (nd->nd_repstat == 0) {
26797403Sobrien					(void)nfsvno_fillattr(nd, mp, vp, &nva,
26897403Sobrien					    &fh, 0, &attrbits, nd->nd_cred, p,
26997403Sobrien					    isdgram, 1, supports_nfsv4acls,
27097403Sobrien					    at_root, mounted_on_fileno);
271169691Skan					vfs_unbusy(mp);
272132720Skan				}
27397403Sobrien				vrele(vp);
27497403Sobrien			} else
27597403Sobrien				vput(vp);
276132720Skan		} else {
27797403Sobrien			nfsrv_fillattr(nd, &nva);
278117397Skan			vput(vp);
279117397Skan		}
28097403Sobrien	} else {
28197403Sobrien		vput(vp);
282117397Skan	}
283117397Skan
28497403Sobrienout:
28597403Sobrien	NFSEXITCODE2(error, nd);
286117397Skan	return (error);
28797403Sobrien}
28897403Sobrien
28997403Sobrien/*
29097403Sobrien * nfs setattr service
29197403Sobrien */
292117397SkanAPPLESTATIC int
293169691Skannfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
294169691Skan    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
29597403Sobrien{
296117397Skan	struct nfsvattr nva, nva2;
297169691Skan	u_int32_t *tl;
298169691Skan	int preat_ret = 1, postat_ret = 1, gcheck = 0, error = 0;
29997403Sobrien	struct timespec guard = { 0, 0 };
300117397Skan	nfsattrbit_t attrbits, retbits;
30197403Sobrien	nfsv4stateid_t stateid;
302117397Skan	NFSACL_T *aclp = NULL;
303117397Skan
304117397Skan	if (nd->nd_repstat) {
30597403Sobrien		nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
30697403Sobrien		goto out;
307132720Skan	}
308132720Skan#ifdef NFS4_ACL_EXTATTR_NAME
309117397Skan	aclp = acl_alloc(M_WAITOK);
31097403Sobrien	aclp->acl_cnt = 0;
311132720Skan#endif
312132720Skan	NFSVNO_ATTRINIT(&nva);
31397403Sobrien	NFSZERO_ATTRBIT(&retbits);
31497403Sobrien	if (nd->nd_flag & ND_NFSV4) {
315169691Skan		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
316169691Skan		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
317169691Skan		NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
318169691Skan	}
319169691Skan	error = nfsrv_sattr(nd, &nva, &attrbits, aclp, p);
320169691Skan	if (error)
321169691Skan		goto nfsmout;
322132720Skan	preat_ret = nfsvno_getattr(vp, &nva2, nd->nd_cred, p, 1);
323132720Skan	if (!nd->nd_repstat)
324132720Skan		nd->nd_repstat = preat_ret;
325117397Skan	if (nd->nd_flag & ND_NFSV3) {
326132720Skan		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
327132720Skan		gcheck = fxdr_unsigned(int, *tl);
32897403Sobrien		if (gcheck) {
32997403Sobrien			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
330169691Skan			fxdr_nfsv3time(tl, &guard);
331169691Skan		}
332169691Skan		if (!nd->nd_repstat && gcheck &&
333169691Skan		    (nva2.na_ctime.tv_sec != guard.tv_sec ||
334169691Skan		     nva2.na_ctime.tv_nsec != guard.tv_nsec))
335169691Skan			nd->nd_repstat = NFSERR_NOT_SYNC;
336169691Skan		if (nd->nd_repstat) {
337169691Skan			vput(vp);
338169691Skan#ifdef NFS4_ACL_EXTATTR_NAME
339169691Skan			acl_free(aclp);
340169691Skan#endif
341169691Skan			nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
342169691Skan			goto out;
343169691Skan		}
344169691Skan	} else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
345169691Skan		nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
346169691Skan
347169691Skan	/*
348169691Skan	 * Now that we have all the fields, lets do it.
349169691Skan	 * If the size is being changed write access is required, otherwise
350169691Skan	 * just check for a read only file system.
351169691Skan	 */
352169691Skan	if (!nd->nd_repstat) {
353169691Skan		if (NFSVNO_NOTSETSIZE(&nva)) {
354169691Skan			if (NFSVNO_EXRDONLY(exp) ||
355169691Skan			    (vfs_flags(vnode_mount(vp)) & MNT_RDONLY))
356169691Skan				nd->nd_repstat = EROFS;
357169691Skan		} else {
358169691Skan			if (vnode_vtype(vp) != VREG)
359169691Skan				nd->nd_repstat = EINVAL;
360169691Skan			else if (nva2.na_uid != nd->nd_cred->cr_uid ||
361169691Skan			    NFSVNO_EXSTRICTACCESS(exp))
362169691Skan				nd->nd_repstat = nfsvno_accchk(vp,
363169691Skan				    VWRITE, nd->nd_cred, exp, p,
364169691Skan				    NFSACCCHK_NOOVERRIDE,
365169691Skan				    NFSACCCHK_VPISLOCKED, NULL);
366169691Skan		}
36797403Sobrien	}
36897403Sobrien	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
36997403Sobrien		nd->nd_repstat = nfsrv_checksetattr(vp, nd, &stateid,
37097403Sobrien		    &nva, &attrbits, exp, p);
37197403Sobrien
372117397Skan	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
373117397Skan	    /*
37497403Sobrien	     * For V4, try setting the attrbutes in sets, so that the
37597403Sobrien	     * reply bitmap will be correct for an error case.
37697403Sobrien	     */
37797403Sobrien	    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER) ||
37897403Sobrien		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP)) {
37997403Sobrien		NFSVNO_ATTRINIT(&nva2);
38097403Sobrien		NFSVNO_SETATTRVAL(&nva2, uid, nva.na_uid);
38197403Sobrien		NFSVNO_SETATTRVAL(&nva2, gid, nva.na_gid);
38297403Sobrien		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
38397403Sobrien		    exp);
384117397Skan		if (!nd->nd_repstat) {
38597403Sobrien		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER))
38697403Sobrien			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
387169691Skan		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP))
38897403Sobrien			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNERGROUP);
38997403Sobrien		}
39097403Sobrien	    }
391169691Skan	    if (!nd->nd_repstat &&
39297403Sobrien		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SIZE)) {
393259705Spfg		NFSVNO_ATTRINIT(&nva2);
394259705Spfg		NFSVNO_SETATTRVAL(&nva2, size, nva.na_size);
395259705Spfg		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
396259705Spfg		    exp);
397259705Spfg		if (!nd->nd_repstat)
398259705Spfg		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SIZE);
399259705Spfg	    }
400259705Spfg	    if (!nd->nd_repstat &&
401259705Spfg		(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET) ||
402259705Spfg		 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))) {
403117397Skan		NFSVNO_ATTRINIT(&nva2);
40497403Sobrien		NFSVNO_SETATTRVAL(&nva2, atime, nva.na_atime);
40597403Sobrien		NFSVNO_SETATTRVAL(&nva2, mtime, nva.na_mtime);
406117397Skan		if (nva.na_vaflags & VA_UTIMES_NULL) {
40797403Sobrien			nva2.na_vaflags |= VA_UTIMES_NULL;
40897403Sobrien			NFSVNO_SETACTIVE(&nva2, vaflags);
409117397Skan		}
41097403Sobrien		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
411132720Skan		    exp);
41297403Sobrien		if (!nd->nd_repstat) {
41397403Sobrien		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET))
41497403Sobrien			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEACCESSSET);
41597403Sobrien		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))
41697403Sobrien			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEMODIFYSET);
41797403Sobrien		}
418132720Skan	    }
419132720Skan	    if (!nd->nd_repstat &&
420132720Skan		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE)) {
421117397Skan		NFSVNO_ATTRINIT(&nva2);
42297403Sobrien		NFSVNO_SETATTRVAL(&nva2, mode, nva.na_mode);
42397403Sobrien		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
424132720Skan		    exp);
425169691Skan		if (!nd->nd_repstat)
426132720Skan		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODE);
427117397Skan	    }
42897403Sobrien
42997403Sobrien#ifdef NFS4_ACL_EXTATTR_NAME
43097403Sobrien	    if (!nd->nd_repstat && aclp->acl_cnt > 0 &&
431132720Skan		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ACL)) {
432132720Skan		nd->nd_repstat = nfsrv_setacl(vp, aclp, nd->nd_cred, p);
433132720Skan		if (!nd->nd_repstat)
434132720Skan		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_ACL);
43597403Sobrien	    }
436132720Skan#endif
437132720Skan	} else if (!nd->nd_repstat) {
438132720Skan		nd->nd_repstat = nfsvno_setattr(vp, &nva, nd->nd_cred, p,
439132720Skan		    exp);
440132720Skan	}
441132720Skan	if (nd->nd_flag & (ND_NFSV2 | ND_NFSV3)) {
44297403Sobrien		postat_ret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
44397403Sobrien		if (!nd->nd_repstat)
444132720Skan			nd->nd_repstat = postat_ret;
445132720Skan	}
446132720Skan	vput(vp);
447132720Skan#ifdef NFS4_ACL_EXTATTR_NAME
448132720Skan	acl_free(aclp);
449132720Skan#endif
450132720Skan	if (nd->nd_flag & ND_NFSV3)
45197403Sobrien		nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
45297403Sobrien	else if (nd->nd_flag & ND_NFSV4)
45397403Sobrien		(void) nfsrv_putattrbit(nd, &retbits);
454132720Skan	else if (!nd->nd_repstat)
455132720Skan		nfsrv_fillattr(nd, &nva);
456132720Skan
457132720Skanout:
458132720Skan	NFSEXITCODE2(0, nd);
459132720Skan	return (0);
460169691Skannfsmout:
461132720Skan	vput(vp);
462132720Skan#ifdef NFS4_ACL_EXTATTR_NAME
46397403Sobrien	acl_free(aclp);
46497403Sobrien#endif
465132720Skan	if (nd->nd_flag & ND_NFSV4) {
466132720Skan		/*
467132720Skan		 * For all nd_repstat, the V4 reply includes a bitmap,
468132720Skan		 * even NFSERR_BADXDR, which is what this will end up
469132720Skan		 * returning.
47097403Sobrien		 */
471132720Skan		(void) nfsrv_putattrbit(nd, &retbits);
472132720Skan	}
473132720Skan	NFSEXITCODE2(error, nd);
474132720Skan	return (error);
475132720Skan}
476132720Skan
47797403Sobrien/*
47897403Sobrien * nfs lookup rpc
479132720Skan * (Also performs lookup parent for v4)
480132720Skan */
481132720SkanAPPLESTATIC int
482132720Skannfsrvd_lookup(struct nfsrv_descript *nd, __unused int isdgram,
483132720Skan    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
484132720Skan    struct nfsexstuff *exp)
48597403Sobrien{
48697403Sobrien	struct nameidata named;
48797403Sobrien	vnode_t vp, dirp = NULL;
48897403Sobrien	int error = 0, dattr_ret = 1;
489132720Skan	struct nfsvattr nva, dattr;
490132720Skan	char *bufp;
491132720Skan	u_long *hashp;
492117397Skan
49397403Sobrien	if (nd->nd_repstat) {
49497403Sobrien		nfsrv_postopattr(nd, dattr_ret, &dattr);
495132720Skan		goto out;
496132720Skan	}
497132720Skan
498132720Skan	/*
499117397Skan	 * For some reason, if dp is a symlink, the error
500132720Skan	 * returned is supposed to be NFSERR_SYMLINK and not NFSERR_NOTDIR.
501169691Skan	 */
50297403Sobrien	if (dp->v_type == VLNK && (nd->nd_flag & ND_NFSV4)) {
503132720Skan		nd->nd_repstat = NFSERR_SYMLINK;
504132720Skan		vrele(dp);
505132720Skan		goto out;
506132720Skan	}
507117397Skan
508132720Skan	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
509169691Skan	    LOCKLEAF | SAVESTART);
51097403Sobrien	nfsvno_setpathbuf(&named, &bufp, &hashp);
511132720Skan	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
512132720Skan	if (error) {
513132720Skan		vrele(dp);
514132720Skan		nfsvno_relpathbuf(&named);
515132720Skan		goto out;
516132720Skan	}
517132720Skan	if (!nd->nd_repstat) {
518117397Skan		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
519132720Skan	} else {
520132720Skan		vrele(dp);
521132720Skan		nfsvno_relpathbuf(&named);
522132720Skan	}
523132720Skan	if (nd->nd_repstat) {
52497403Sobrien		if (dirp) {
52597403Sobrien			if (nd->nd_flag & ND_NFSV3)
526132720Skan				dattr_ret = nfsvno_getattr(dirp, &dattr,
527132720Skan				    nd->nd_cred, p, 0);
528132720Skan			vrele(dirp);
529132720Skan		}
530117397Skan		if (nd->nd_flag & ND_NFSV3)
531117397Skan			nfsrv_postopattr(nd, dattr_ret, &dattr);
532117397Skan		goto out;
533117397Skan	}
53497403Sobrien	if (named.ni_startdir)
53597403Sobrien		vrele(named.ni_startdir);
53697403Sobrien	nfsvno_relpathbuf(&named);
537132720Skan	vp = named.ni_vp;
538132720Skan	if ((nd->nd_flag & ND_NFSV4) != 0 && !NFSVNO_EXPORTED(exp) &&
539132720Skan	    vp->v_type != VDIR && vp->v_type != VLNK)
540132720Skan		/*
541117397Skan		 * Only allow lookup of VDIR and VLNK for traversal of
542117397Skan		 * non-exported volumes during NFSv4 mounting.
54397403Sobrien		 */
54497403Sobrien		nd->nd_repstat = ENOENT;
545132720Skan	if (nd->nd_repstat == 0)
546132720Skan		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
547132720Skan	if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
548132720Skan		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
549117397Skan	if (vpp != NULL && nd->nd_repstat == 0)
55097403Sobrien		*vpp = vp;
55197403Sobrien	else
552132720Skan		vput(vp);
553132720Skan	if (dirp) {
55497403Sobrien		if (nd->nd_flag & ND_NFSV3)
55597403Sobrien			dattr_ret = nfsvno_getattr(dirp, &dattr, nd->nd_cred,
556132720Skan			    p, 0);
557132720Skan		vrele(dirp);
558132720Skan	}
559132720Skan	if (nd->nd_repstat) {
560117397Skan		if (nd->nd_flag & ND_NFSV3)
56197403Sobrien			nfsrv_postopattr(nd, dattr_ret, &dattr);
56297403Sobrien		goto out;
56397403Sobrien	}
564132720Skan	if (nd->nd_flag & ND_NFSV2) {
565132720Skan		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
566132720Skan		nfsrv_fillattr(nd, &nva);
567132720Skan	} else if (nd->nd_flag & ND_NFSV3) {
568132720Skan		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
569117397Skan		nfsrv_postopattr(nd, 0, &nva);
570117397Skan		nfsrv_postopattr(nd, dattr_ret, &dattr);
57197403Sobrien	}
57297403Sobrien
573132720Skanout:
574132720Skan	NFSEXITCODE2(error, nd);
575132720Skan	return (error);
576132720Skan}
577132720Skan
578117397Skan/*
579117397Skan * nfs readlink service
58097403Sobrien */
58197403SobrienAPPLESTATIC int
582132720Skannfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram,
583132720Skan    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
584132720Skan{
585132720Skan	u_int32_t *tl;
586132720Skan	mbuf_t mp = NULL, mpend = NULL;
587117397Skan	int getret = 1, len;
588117397Skan	struct nfsvattr nva;
58997403Sobrien
59097403Sobrien	if (nd->nd_repstat) {
591132720Skan		nfsrv_postopattr(nd, getret, &nva);
592132720Skan		goto out;
593132720Skan	}
594132720Skan	if (vnode_vtype(vp) != VLNK) {
595132720Skan		if (nd->nd_flag & ND_NFSV2)
596117397Skan			nd->nd_repstat = ENXIO;
597117397Skan		else
59897403Sobrien			nd->nd_repstat = EINVAL;
59997403Sobrien	}
60097403Sobrien	if (!nd->nd_repstat)
60197403Sobrien		nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred, p,
602132720Skan		    &mp, &mpend, &len);
603132720Skan	if (nd->nd_flag & ND_NFSV3)
604117397Skan		getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
605169691Skan	vput(vp);
606169691Skan	if (nd->nd_flag & ND_NFSV3)
60797403Sobrien		nfsrv_postopattr(nd, getret, &nva);
608132720Skan	if (nd->nd_repstat)
609132720Skan		goto out;
610117397Skan	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
611169691Skan	*tl = txdr_unsigned(len);
612169691Skan	mbuf_setnext(nd->nd_mb, mp);
61397403Sobrien	nd->nd_mb = mpend;
614132720Skan	nd->nd_bpos = NFSMTOD(mpend, caddr_t) + mbuf_len(mpend);
615117397Skan
616169691Skanout:
617169691Skan	NFSEXITCODE2(0, nd);
61897403Sobrien	return (0);
619132720Skan}
620132720Skan
621132720Skan/*
622132720Skan * nfs read service
623132720Skan */
624132720SkanAPPLESTATIC int
625132720Skannfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram,
626132720Skan    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
627132720Skan{
628132720Skan	u_int32_t *tl;
629117397Skan	int error = 0, cnt, getret = 1, reqlen, eof = 0;
63097403Sobrien	mbuf_t m2, m3;
63197403Sobrien	struct nfsvattr nva;
632132720Skan	off_t off = 0x0;
633132720Skan	struct nfsstate st, *stp = &st;
634132720Skan	struct nfslock lo, *lop = &lo;
635132720Skan	nfsv4stateid_t stateid;
636132720Skan	nfsquad_t clientid;
637132720Skan
638132720Skan	if (nd->nd_repstat) {
639132720Skan		nfsrv_postopattr(nd, getret, &nva);
640132720Skan		goto out;
641132720Skan	}
642117397Skan	if (nd->nd_flag & ND_NFSV2) {
643169691Skan		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
644169691Skan		off = (off_t)fxdr_unsigned(u_int32_t, *tl++);
64597403Sobrien		reqlen = fxdr_unsigned(int, *tl);
646132720Skan	} else if (nd->nd_flag & ND_NFSV3) {
647132720Skan		NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
648132720Skan		off = fxdr_hyper(tl);
649132720Skan		tl += 2;
650117397Skan		reqlen = fxdr_unsigned(int, *tl);
651169691Skan	} else {
652169691Skan		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3*NFSX_UNSIGNED);
65397403Sobrien		reqlen = fxdr_unsigned(int, *(tl + 6));
654132720Skan	}
655132720Skan	if (reqlen > NFS_SRVMAXDATA(nd)) {
656132720Skan		reqlen = NFS_SRVMAXDATA(nd);
657169691Skan	} else if (reqlen < 0) {
658169691Skan		error = EBADRPC;
659132720Skan		goto nfsmout;
660132720Skan	}
661132720Skan	if (nd->nd_flag & ND_NFSV4) {
662132720Skan		stp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
663132720Skan		lop->lo_flags = NFSLCK_READ;
664132720Skan		stp->ls_ownerlen = 0;
665132720Skan		stp->ls_op = NULL;
666132720Skan		stp->ls_uid = nd->nd_cred->cr_uid;
667132720Skan		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
668132720Skan		clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
669132720Skan		clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
670132720Skan		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
671117397Skan			if ((nd->nd_flag & ND_NFSV41) != 0)
67297403Sobrien				clientid.qval = nd->nd_clientid.qval;
67397403Sobrien			else if (nd->nd_clientid.qval != clientid.qval)
674132720Skan				printf("EEK1 multiple clids\n");
675132720Skan		} else {
676132720Skan			if ((nd->nd_flag & ND_NFSV41) != 0)
677117397Skan				printf("EEK! no clientid from session\n");
678169691Skan			nd->nd_flag |= ND_IMPLIEDCLID;
679169691Skan			nd->nd_clientid.qval = clientid.qval;
68097403Sobrien		}
681132720Skan		stp->ls_stateid.other[2] = *tl++;
682132720Skan		off = fxdr_hyper(tl);
683132720Skan		lop->lo_first = off;
684117397Skan		tl += 2;
685169691Skan		lop->lo_end = off + reqlen;
686169691Skan		/*
68797403Sobrien		 * Paranoia, just in case it wraps around.
68897403Sobrien		 */
689132720Skan		if (lop->lo_end < off)
690132720Skan			lop->lo_end = NFS64BITSSET;
691169691Skan	}
692132720Skan	if (vnode_vtype(vp) != VREG) {
693132720Skan		if (nd->nd_flag & ND_NFSV3)
694132720Skan			nd->nd_repstat = EINVAL;
695132720Skan		else
696132720Skan			nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
697132720Skan			    EINVAL;
698132720Skan	}
699117397Skan	getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
700117397Skan	if (!nd->nd_repstat)
701132720Skan		nd->nd_repstat = getret;
702132720Skan	if (!nd->nd_repstat &&
703132720Skan	    (nva.na_uid != nd->nd_cred->cr_uid ||
704132720Skan	     NFSVNO_EXSTRICTACCESS(exp))) {
70597403Sobrien		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
706132720Skan		    nd->nd_cred, exp, p,
707132720Skan		    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
708169691Skan		if (nd->nd_repstat)
709132720Skan			nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
710132720Skan			    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
711132720Skan			    NFSACCCHK_VPISLOCKED, NULL);
712132720Skan	}
713132720Skan	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
714132720Skan		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
715132720Skan		    &stateid, exp, nd, p);
716117397Skan	if (nd->nd_repstat) {
717117397Skan		vput(vp);
718117397Skan		if (nd->nd_flag & ND_NFSV3)
719169691Skan			nfsrv_postopattr(nd, getret, &nva);
720169691Skan		goto out;
721169691Skan	}
722169691Skan	if (off >= nva.na_size) {
723117397Skan		cnt = 0;
724117397Skan		eof = 1;
72597403Sobrien	} else if (reqlen == 0)
72697403Sobrien		cnt = 0;
727132720Skan	else if ((off + reqlen) >= nva.na_size) {
728132720Skan		cnt = nva.na_size - off;
729132720Skan		eof = 1;
730132720Skan	} else
731132720Skan		cnt = reqlen;
732132720Skan	m3 = NULL;
733132720Skan	if (cnt > 0) {
734132720Skan		nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred, p,
735132720Skan		    &m3, &m2);
736132720Skan		if (!(nd->nd_flag & ND_NFSV4)) {
737117397Skan			getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
73897403Sobrien			if (!nd->nd_repstat)
73997403Sobrien				nd->nd_repstat = getret;
74097403Sobrien		}
741132720Skan		if (nd->nd_repstat) {
742117397Skan			vput(vp);
74397403Sobrien			if (m3)
74497403Sobrien				mbuf_freem(m3);
745132720Skan			if (nd->nd_flag & ND_NFSV3)
746132720Skan				nfsrv_postopattr(nd, getret, &nva);
747132720Skan			goto out;
748132720Skan		}
749132720Skan	}
750132720Skan	vput(vp);
751132720Skan	if (nd->nd_flag & ND_NFSV2) {
752132720Skan		nfsrv_fillattr(nd, &nva);
753132720Skan		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
754132720Skan	} else {
755132720Skan		if (nd->nd_flag & ND_NFSV3) {
756117397Skan			nfsrv_postopattr(nd, getret, &nva);
75797403Sobrien			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
75897403Sobrien			*tl++ = txdr_unsigned(cnt);
75997403Sobrien		} else
760132720Skan			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
761117397Skan		if (eof)
762117397Skan			*tl++ = newnfs_true;
76397403Sobrien		else
76497403Sobrien			*tl++ = newnfs_false;
76597403Sobrien	}
766132720Skan	*tl = txdr_unsigned(cnt);
767132720Skan	if (m3) {
768132720Skan		mbuf_setnext(nd->nd_mb, m3);
769132720Skan		nd->nd_mb = m2;
770132720Skan		nd->nd_bpos = NFSMTOD(m2, caddr_t) + mbuf_len(m2);
771117397Skan	}
772169691Skan
773169691Skanout:
77497403Sobrien	NFSEXITCODE2(0, nd);
775132720Skan	return (0);
776132720Skannfsmout:
777132720Skan	vput(vp);
778132720Skan	NFSEXITCODE2(error, nd);
779132720Skan	return (error);
780117397Skan}
781169691Skan
782169691Skan/*
78397403Sobrien * nfs write service
784132720Skan */
785132720SkanAPPLESTATIC int
786169691Skannfsrvd_write(struct nfsrv_descript *nd, __unused int isdgram,
787132720Skan    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
788132720Skan{
789117397Skan	int i, cnt;
790169691Skan	u_int32_t *tl;
791169691Skan	mbuf_t mp;
792169691Skan	struct nfsvattr nva, forat;
793169691Skan	int aftat_ret = 1, retlen, len, error = 0, forat_ret = 1;
794169691Skan	int stable = NFSWRITE_FILESYNC;
79597403Sobrien	off_t off;
796132720Skan	struct nfsstate st, *stp = &st;
797132720Skan	struct nfslock lo, *lop = &lo;
798132720Skan	nfsv4stateid_t stateid;
799132720Skan	nfsquad_t clientid;
800132720Skan
801117397Skan	if (nd->nd_repstat) {
80297403Sobrien		nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
80397403Sobrien		goto out;
804132720Skan	}
805132720Skan	if (nd->nd_flag & ND_NFSV2) {
806132720Skan		NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
807132720Skan		off = (off_t)fxdr_unsigned(u_int32_t, *++tl);
808132720Skan		tl += 2;
809132720Skan		retlen = len = fxdr_unsigned(int32_t, *tl);
810132720Skan	} else if (nd->nd_flag & ND_NFSV3) {
811132720Skan		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
812132720Skan		off = fxdr_hyper(tl);
813132720Skan		tl += 3;
814132720Skan		stable = fxdr_unsigned(int, *tl++);
815132720Skan		retlen = len = fxdr_unsigned(int32_t, *tl);
816117397Skan	} else {
81797403Sobrien		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 4 * NFSX_UNSIGNED);
81897403Sobrien		stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
819132720Skan		lop->lo_flags = NFSLCK_WRITE;
820132720Skan		stp->ls_ownerlen = 0;
821132720Skan		stp->ls_op = NULL;
822132720Skan		stp->ls_uid = nd->nd_cred->cr_uid;
823132720Skan		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
824132720Skan		clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
825117397Skan		clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
82697403Sobrien		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
82797403Sobrien			if ((nd->nd_flag & ND_NFSV41) != 0)
828132720Skan				clientid.qval = nd->nd_clientid.qval;
829132720Skan			else if (nd->nd_clientid.qval != clientid.qval)
830132720Skan				printf("EEK2 multiple clids\n");
831132720Skan		} else {
832132720Skan			if ((nd->nd_flag & ND_NFSV41) != 0)
833117397Skan				printf("EEK! no clientid from session\n");
83497403Sobrien			nd->nd_flag |= ND_IMPLIEDCLID;
835132720Skan			nd->nd_clientid.qval = clientid.qval;
836132720Skan		}
837132720Skan		stp->ls_stateid.other[2] = *tl++;
838132720Skan		off = fxdr_hyper(tl);
83997403Sobrien		lop->lo_first = off;
840132720Skan		tl += 2;
841132720Skan		stable = fxdr_unsigned(int, *tl++);
842132720Skan		retlen = len = fxdr_unsigned(int32_t, *tl);
843132720Skan		lop->lo_end = off + len;
844132720Skan		/*
845132720Skan		 * Paranoia, just in case it wraps around, which shouldn't
846132720Skan		 * ever happen anyhow.
847132720Skan		 */
848117397Skan		if (lop->lo_end < lop->lo_first)
849169691Skan			lop->lo_end = NFS64BITSSET;
85097403Sobrien	}
851132720Skan
852132720Skan	/*
853132720Skan	 * Loop through the mbuf chain, counting how many mbufs are a
854132720Skan	 * part of this write operation, so the iovec size is known.
855132720Skan	 */
856132720Skan	cnt = 0;
857132720Skan	mp = nd->nd_md;
858132720Skan	i = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - nd->nd_dpos;
85997403Sobrien	while (len > 0) {
860117397Skan		if (i > 0) {
86197403Sobrien			len -= i;
86297403Sobrien			cnt++;
86397403Sobrien		}
864132720Skan		mp = mbuf_next(mp);
865132720Skan		if (!mp) {
866132720Skan			if (len > 0) {
867132720Skan				error = EBADRPC;
868117397Skan				goto nfsmout;
86997403Sobrien			}
870169691Skan		} else
871169691Skan			i = mbuf_len(mp);
872169691Skan	}
873169691Skan
874169691Skan	if (retlen > NFS_SRVMAXIO || retlen < 0)
875169691Skan		nd->nd_repstat = EIO;
876169691Skan	if (vnode_vtype(vp) != VREG && !nd->nd_repstat) {
87797403Sobrien		if (nd->nd_flag & ND_NFSV3)
878132720Skan			nd->nd_repstat = EINVAL;
879132720Skan		else
880132720Skan			nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
881132720Skan			    EINVAL;
882132720Skan	}
883117397Skan	forat_ret = nfsvno_getattr(vp, &forat, nd->nd_cred, p, 1);
88497403Sobrien	if (!nd->nd_repstat)
88597403Sobrien		nd->nd_repstat = forat_ret;
886132720Skan	if (!nd->nd_repstat &&
887132720Skan	    (forat.na_uid != nd->nd_cred->cr_uid ||
888132720Skan	     NFSVNO_EXSTRICTACCESS(exp)))
889132720Skan		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
890132720Skan		    nd->nd_cred, exp, p,
891132720Skan		    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
892132720Skan	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
893132720Skan		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
894132720Skan		    &stateid, exp, nd, p);
895132720Skan	}
896132720Skan	if (nd->nd_repstat) {
897132720Skan		vput(vp);
898117397Skan		if (nd->nd_flag & ND_NFSV3)
899132720Skan			nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
900132720Skan		goto out;
901132720Skan	}
902132720Skan
90397403Sobrien	/*
904132720Skan	 * For NFS Version 2, it is not obvious what a write of zero length
905132720Skan	 * should do, but I might as well be consistent with Version 3,
906132720Skan	 * which is to return ok so long as there are no permission problems.
907132720Skan	 */
908132720Skan	if (retlen > 0) {
909132720Skan		nd->nd_repstat = nfsvno_write(vp, off, retlen, cnt, stable,
910132720Skan		    nd->nd_md, nd->nd_dpos, nd->nd_cred, p);
911132720Skan		error = nfsm_advance(nd, NFSM_RNDUP(retlen), -1);
912132720Skan		if (error)
913132720Skan			panic("nfsrv_write mbuf");
914117397Skan	}
915117397Skan	if (nd->nd_flag & ND_NFSV4)
91697403Sobrien		aftat_ret = 0;
917132720Skan	else
918132720Skan		aftat_ret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
919132720Skan	vput(vp);
920132720Skan	if (!nd->nd_repstat)
921132720Skan		nd->nd_repstat = aftat_ret;
922132720Skan	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
923132720Skan		if (nd->nd_flag & ND_NFSV3)
924132720Skan			nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
925132720Skan		if (nd->nd_repstat)
926117397Skan			goto out;
92797403Sobrien		NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
928132720Skan		*tl++ = txdr_unsigned(retlen);
929132720Skan		/*
930132720Skan		 * If nfs_async is set, then pretend the write was FILESYNC.
931132720Skan		 * Warning: Doing this violates RFC1813 and runs a risk
93297403Sobrien		 * of data written by a client being lost when the server
933132720Skan		 * crashes/reboots.
934132720Skan		 */
935132720Skan		if (stable == NFSWRITE_UNSTABLE && nfs_async == 0)
936132720Skan			*tl++ = txdr_unsigned(stable);
937132720Skan		else
938132720Skan			*tl++ = txdr_unsigned(NFSWRITE_FILESYNC);
939132720Skan		/*
940132720Skan		 * Actually, there is no need to txdr these fields,
941132720Skan		 * but it may make the values more human readable,
942117397Skan		 * for debugging purposes.
94397403Sobrien		 */
944132720Skan		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
94597403Sobrien		*tl = txdr_unsigned(nfsboottime.tv_usec);
946132720Skan	} else if (!nd->nd_repstat)
947132720Skan		nfsrv_fillattr(nd, &nva);
948132720Skan
949132720Skanout:
950132720Skan	NFSEXITCODE2(0, nd);
951132720Skan	return (0);
952132720Skannfsmout:
953132720Skan	vput(vp);
95497403Sobrien	NFSEXITCODE2(error, nd);
955117397Skan	return (error);
95697403Sobrien}
95797403Sobrien
95897403Sobrien/*
959132720Skan * nfs create service (creates regular files for V2 and V3. Spec. files for V2.)
960132720Skan * now does a truncate to 0 length via. setattr if it already exists
961132720Skan * The core creation routine has been extracted out into nfsrv_creatsub(),
962132720Skan * so it can also be used by nfsrv_open() for V4.
963132720Skan */
964132720SkanAPPLESTATIC int
965132720Skannfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram,
966132720Skan    vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
967132720Skan{
968132720Skan	struct nfsvattr nva, dirfor, diraft;
969132720Skan	struct nfsv2_sattr *sp;
970132720Skan	struct nameidata named;
971117397Skan	u_int32_t *tl;
97297403Sobrien	int error = 0, tsize, dirfor_ret = 1, diraft_ret = 1;
97397403Sobrien	int how = NFSCREATE_UNCHECKED, exclusive_flag = 0;
97497403Sobrien	NFSDEV_T rdev = 0;
975132720Skan	vnode_t vp = NULL, dirp = NULL;
976132720Skan	fhandle_t fh;
977132720Skan	char *bufp;
978132720Skan	u_long *hashp;
979132720Skan	enum vtype vtyp;
980132720Skan	int32_t cverf[2], tverf[2] = { 0, 0 };
981132720Skan
982132720Skan	if (nd->nd_repstat) {
983132720Skan		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
984132720Skan		goto out;
985132720Skan	}
98697403Sobrien	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
987169691Skan	    LOCKPARENT | LOCKLEAF | SAVESTART | NOCACHE);
988169691Skan	nfsvno_setpathbuf(&named, &bufp, &hashp);
98997403Sobrien	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
99097403Sobrien	if (error)
991132720Skan		goto nfsmout;
992132720Skan	if (!nd->nd_repstat) {
993132720Skan		NFSVNO_ATTRINIT(&nva);
994132720Skan		if (nd->nd_flag & ND_NFSV2) {
995132720Skan			NFSM_DISSECT(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
996132720Skan			vtyp = IFTOVT(fxdr_unsigned(u_int32_t, sp->sa_mode));
997132720Skan			if (vtyp == VNON)
998132720Skan				vtyp = VREG;
999132720Skan			NFSVNO_SETATTRVAL(&nva, type, vtyp);
1000132720Skan			NFSVNO_SETATTRVAL(&nva, mode,
1001132720Skan			    nfstov_mode(sp->sa_mode));
1002117397Skan			switch (nva.na_type) {
100397403Sobrien			case VREG:
1004132720Skan				tsize = fxdr_unsigned(int32_t, sp->sa_size);
100597403Sobrien				if (tsize != -1)
1006132720Skan					NFSVNO_SETATTRVAL(&nva, size,
1007132720Skan					    (u_quad_t)tsize);
1008132720Skan				break;
1009132720Skan			case VCHR:
1010132720Skan			case VBLK:
1011132720Skan			case VFIFO:
1012132720Skan				rdev = fxdr_unsigned(NFSDEV_T, sp->sa_size);
1013132720Skan				break;
1014132720Skan			default:
1015132720Skan				break;
1016132720Skan			};
1017132720Skan		} else {
1018132720Skan			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1019132720Skan			how = fxdr_unsigned(int, *tl);
1020132720Skan			switch (how) {
1021132720Skan			case NFSCREATE_GUARDED:
1022132720Skan			case NFSCREATE_UNCHECKED:
1023132720Skan				error = nfsrv_sattr(nd, &nva, NULL, NULL, p);
1024117397Skan				if (error)
102597403Sobrien					goto nfsmout;
1026132720Skan				break;
1027132720Skan			case NFSCREATE_EXCLUSIVE:
1028132720Skan				NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
1029132720Skan				cverf[0] = *tl++;
103097403Sobrien				cverf[1] = *tl;
1031132720Skan				exclusive_flag = 1;
1032132720Skan				break;
1033132720Skan			};
1034132720Skan			NFSVNO_SETATTRVAL(&nva, type, VREG);
1035132720Skan		}
1036132720Skan	}
1037132720Skan	if (nd->nd_repstat) {
1038132720Skan		nfsvno_relpathbuf(&named);
1039132720Skan		if (nd->nd_flag & ND_NFSV3) {
1040132720Skan			dirfor_ret = nfsvno_getattr(dp, &dirfor, nd->nd_cred,
1041132720Skan			    p, 1);
1042132720Skan			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1043132720Skan			    &diraft);
1044132720Skan		}
1045132720Skan		vput(dp);
1046132720Skan		goto out;
1047117397Skan	}
1048117397Skan
104997403Sobrien	nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
1050132720Skan	if (dirp) {
1051132720Skan		if (nd->nd_flag & ND_NFSV2) {
1052132720Skan			vrele(dirp);
1053132720Skan			dirp = NULL;
1054132720Skan		} else {
1055132720Skan			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
1056132720Skan			    p, 0);
1057132720Skan		}
1058132720Skan	}
1059132720Skan	if (nd->nd_repstat) {
1060132720Skan		if (nd->nd_flag & ND_NFSV3)
1061132720Skan			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1062132720Skan			    &diraft);
1063132720Skan		if (dirp)
1064132720Skan			vrele(dirp);
1065117397Skan		goto out;
106697403Sobrien	}
1067132720Skan
1068132720Skan	if (!(nd->nd_flag & ND_NFSV2)) {
1069132720Skan		switch (how) {
1070132720Skan		case NFSCREATE_GUARDED:
107197403Sobrien			if (named.ni_vp)
1072132720Skan				nd->nd_repstat = EEXIST;
1073132720Skan			break;
1074132720Skan		case NFSCREATE_UNCHECKED:
1075132720Skan			break;
1076132720Skan		case NFSCREATE_EXCLUSIVE:
1077132720Skan			if (named.ni_vp == NULL)
1078132720Skan				NFSVNO_SETATTRVAL(&nva, mode, 0);
1079132720Skan			break;
1080132720Skan		};
1081132720Skan	}
1082132720Skan
1083132720Skan	/*
1084132720Skan	 * Iff doesn't exist, create it
1085132720Skan	 * otherwise just truncate to 0 length
1086132720Skan	 *   should I set the mode too ?
1087132720Skan	 */
1088117397Skan	nd->nd_repstat = nfsvno_createsub(nd, &named, &vp, &nva,
108997403Sobrien	    &exclusive_flag, cverf, rdev, p, exp);
1090132720Skan
1091132720Skan	if (!nd->nd_repstat) {
109297403Sobrien		nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
1093132720Skan		if (!nd->nd_repstat)
1094132720Skan			nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred,
1095132720Skan			    p, 1);
1096132720Skan		vput(vp);
1097132720Skan		if (!nd->nd_repstat) {
1098132720Skan			tverf[0] = nva.na_atime.tv_sec;
1099132720Skan			tverf[1] = nva.na_atime.tv_nsec;
1100132720Skan		}
1101132720Skan	}
1102132720Skan	if (nd->nd_flag & ND_NFSV2) {
1103132720Skan		if (!nd->nd_repstat) {
1104132720Skan			(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
1105117397Skan			nfsrv_fillattr(nd, &nva);
1106132720Skan		}
110797403Sobrien	} else {
1108132720Skan		if (exclusive_flag && !nd->nd_repstat && (cverf[0] != tverf[0]
1109132720Skan		    || cverf[1] != tverf[1]))
1110132720Skan			nd->nd_repstat = EEXIST;
1111117397Skan		diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0);
1112169691Skan		vrele(dirp);
111397403Sobrien		if (!nd->nd_repstat) {
111497403Sobrien			(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 1);
1115132720Skan			nfsrv_postopattr(nd, 0, &nva);
1116132720Skan		}
1117132720Skan		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1118132720Skan	}
1119132720Skan
1120132720Skanout:
1121132720Skan	NFSEXITCODE2(0, nd);
1122132720Skan	return (0);
1123132720Skannfsmout:
1124132720Skan	vput(dp);
1125132720Skan	nfsvno_relpathbuf(&named);
1126132720Skan	NFSEXITCODE2(error, nd);
1127132720Skan	return (error);
1128132720Skan}
1129117397Skan
113097403Sobrien/*
1131169691Skan * nfs v3 mknod service (and v4 create)
1132169691Skan */
1133169691SkanAPPLESTATIC int
1134169691Skannfsrvd_mknod(struct nfsrv_descript *nd, __unused int isdgram,
1135169691Skan    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
113697403Sobrien    struct nfsexstuff *exp)
1137132720Skan{
1138132720Skan	struct nfsvattr nva, dirfor, diraft;
1139132720Skan	u_int32_t *tl;
1140132720Skan	struct nameidata named;
1141132720Skan	int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
1142132720Skan	u_int32_t major, minor;
1143132720Skan	enum vtype vtyp = VNON;
1144132720Skan	nfstype nfs4type = NFNON;
1145117397Skan	vnode_t vp, dirp = NULL;
114697403Sobrien	nfsattrbit_t attrbits;
114797403Sobrien	char *bufp = NULL, *pathcp = NULL;
1148132720Skan	u_long *hashp, cnflags;
1149132720Skan	NFSACL_T *aclp = NULL;
1150132720Skan
1151169691Skan	NFSVNO_ATTRINIT(&nva);
1152117397Skan	cnflags = (LOCKPARENT | SAVESTART);
1153169691Skan	if (nd->nd_repstat) {
115497403Sobrien		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
115597403Sobrien		goto out;
1156132720Skan	}
1157132720Skan#ifdef NFS4_ACL_EXTATTR_NAME
1158132720Skan	aclp = acl_alloc(M_WAITOK);
1159132720Skan	aclp->acl_cnt = 0;
1160132720Skan#endif
1161132720Skan
1162132720Skan	/*
1163132720Skan	 * For V4, the creation stuff is here, Yuck!
1164132720Skan	 */
1165117397Skan	if (nd->nd_flag & ND_NFSV4) {
116697403Sobrien		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
116797403Sobrien		vtyp = nfsv34tov_type(*tl);
1168132720Skan		nfs4type = fxdr_unsigned(nfstype, *tl);
1169132720Skan		switch (nfs4type) {
1170132720Skan		case NFLNK:
1171169691Skan			error = nfsvno_getsymlink(nd, &nva, p, &pathcp,
117297403Sobrien			    &pathlen);
1173169691Skan			if (error)
117497403Sobrien				goto nfsmout;
117597403Sobrien			break;
1176132720Skan		case NFCHR:
1177132720Skan		case NFBLK:
1178132720Skan			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1179132720Skan			major = fxdr_unsigned(u_int32_t, *tl++);
1180132720Skan			minor = fxdr_unsigned(u_int32_t, *tl);
1181132720Skan			nva.na_rdev = NFSMAKEDEV(major, minor);
1182132720Skan			break;
1183132720Skan		case NFSOCK:
1184132720Skan		case NFFIFO:
1185132720Skan			break;
1186132720Skan		case NFDIR:
1187132720Skan			cnflags = (LOCKPARENT | SAVENAME);
1188132720Skan			break;
1189132720Skan		default:
1190132720Skan			nd->nd_repstat = NFSERR_BADTYPE;
1191132720Skan			vrele(dp);
1192117397Skan#ifdef NFS4_ACL_EXTATTR_NAME
119397403Sobrien			acl_free(aclp);
119497403Sobrien#endif
119597403Sobrien			goto out;
1196132720Skan		}
1197132720Skan	}
1198132720Skan	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, cnflags | NOCACHE);
1199132720Skan	nfsvno_setpathbuf(&named, &bufp, &hashp);
1200132720Skan	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1201132720Skan	if (error)
1202132720Skan		goto nfsmout;
1203132720Skan	if (!nd->nd_repstat) {
1204132720Skan		if (nd->nd_flag & ND_NFSV3) {
1205132720Skan			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1206132720Skan			vtyp = nfsv34tov_type(*tl);
1207132720Skan		}
1208132720Skan		error = nfsrv_sattr(nd, &nva, &attrbits, aclp, p);
1209132720Skan		if (error)
1210132720Skan			goto nfsmout;
1211132720Skan		nva.na_type = vtyp;
1212132720Skan		if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV3) &&
1213132720Skan		    (vtyp == VCHR || vtyp == VBLK)) {
1214117397Skan			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
121597403Sobrien			major = fxdr_unsigned(u_int32_t, *tl++);
1216132720Skan			minor = fxdr_unsigned(u_int32_t, *tl);
1217132720Skan			nva.na_rdev = NFSMAKEDEV(major, minor);
1218132720Skan		}
1219132720Skan	}
122097403Sobrien
1221132720Skan	dirfor_ret = nfsvno_getattr(dp, &dirfor, nd->nd_cred, p, 0);
1222132720Skan	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
1223132720Skan		if (!dirfor_ret && NFSVNO_ISSETGID(&nva) &&
1224132720Skan		    dirfor.na_gid == nva.na_gid)
1225169691Skan			NFSVNO_UNSET(&nva, gid);
1226169691Skan		nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
1227132720Skan	}
1228132720Skan	if (nd->nd_repstat) {
1229132720Skan		vrele(dp);
1230132720Skan#ifdef NFS4_ACL_EXTATTR_NAME
1231132720Skan		acl_free(aclp);
1232169691Skan#endif
1233169691Skan		nfsvno_relpathbuf(&named);
1234132720Skan		if (pathcp)
1235132720Skan			FREE(pathcp, M_TEMP);
1236132720Skan		if (nd->nd_flag & ND_NFSV3)
1237132720Skan			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1238117397Skan			    &diraft);
123997403Sobrien		goto out;
1240117397Skan	}
124197403Sobrien
1242132720Skan	/*
1243132720Skan	 * Yuck! For V4, mkdir and link are here and some V4 clients don't fill
1244132720Skan	 * in va_mode, so we'll have to set a default here.
1245132720Skan	 */
1246169691Skan	if (NFSVNO_NOTSETMODE(&nva)) {
1247132720Skan		if (vtyp == VLNK)
1248132720Skan			nva.na_mode = 0755;
1249132720Skan		else
1250132720Skan			nva.na_mode = 0400;
1251132720Skan	}
1252169691Skan
1253132720Skan	if (vtyp == VDIR)
1254132720Skan		named.ni_cnd.cn_flags |= WILLBEDIR;
1255132720Skan	nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
1256132720Skan	if (nd->nd_repstat) {
1257117397Skan		if (dirp) {
125897403Sobrien			if (nd->nd_flag & ND_NFSV3)
1259132720Skan				dirfor_ret = nfsvno_getattr(dirp, &dirfor,
1260132720Skan				    nd->nd_cred, p, 0);
1261132720Skan			vrele(dirp);
1262132720Skan		}
126397403Sobrien#ifdef NFS4_ACL_EXTATTR_NAME
1264132720Skan		acl_free(aclp);
1265132720Skan#endif
1266132720Skan		if (nd->nd_flag & ND_NFSV3)
1267132720Skan			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1268132720Skan			    &diraft);
1269132720Skan		goto out;
1270132720Skan	}
1271132720Skan	if (dirp)
1272132720Skan		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
1273132720Skan
1274132720Skan	if ((nd->nd_flag & ND_NFSV4) && (vtyp == VDIR || vtyp == VLNK)) {
1275132720Skan		if (vtyp == VDIR) {
1276132720Skan			nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp,
1277132720Skan			    &dirfor, &diraft, &diraft_ret, &attrbits, aclp, p,
1278132720Skan			    exp);
1279132720Skan#ifdef NFS4_ACL_EXTATTR_NAME
1280117397Skan			acl_free(aclp);
128197403Sobrien#endif
1282132720Skan			goto out;
1283132720Skan		} else if (vtyp == VLNK) {
128497403Sobrien			nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
1285132720Skan			    &dirfor, &diraft, &diraft_ret, &attrbits,
1286132720Skan			    aclp, p, exp, pathcp, pathlen);
1287132720Skan#ifdef NFS4_ACL_EXTATTR_NAME
1288132720Skan			acl_free(aclp);
1289132720Skan#endif
1290132720Skan			FREE(pathcp, M_TEMP);
1291132720Skan			goto out;
1292132720Skan		}
1293132720Skan	}
1294132720Skan
1295132720Skan	nd->nd_repstat = nfsvno_mknod(&named, &nva, nd->nd_cred, p);
1296132720Skan	if (!nd->nd_repstat) {
1297132720Skan		vp = named.ni_vp;
1298117397Skan		nfsrv_fixattr(nd, vp, &nva, aclp, p, &attrbits, exp);
129997403Sobrien		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
130097403Sobrien		if ((nd->nd_flag & ND_NFSV3) && !nd->nd_repstat)
130197403Sobrien			nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred,
1302132720Skan			    p, 1);
1303132720Skan		if (vpp != NULL && nd->nd_repstat == 0) {
1304132720Skan			NFSVOPUNLOCK(vp, 0);
1305132720Skan			*vpp = vp;
1306132720Skan		} else
1307132720Skan			vput(vp);
1308132720Skan	}
1309132720Skan
1310132720Skan	diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0);
1311132720Skan	vrele(dirp);
1312132720Skan	if (!nd->nd_repstat) {
1313132720Skan		if (nd->nd_flag & ND_NFSV3) {
1314132720Skan			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
1315132720Skan			nfsrv_postopattr(nd, 0, &nva);
1316117397Skan		} else {
1317132720Skan			NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1318132720Skan			*tl++ = newnfs_false;
1319132720Skan			txdr_hyper(dirfor.na_filerev, tl);
1320132720Skan			tl += 2;
1321132720Skan			txdr_hyper(diraft.na_filerev, tl);
1322132720Skan			(void) nfsrv_putattrbit(nd, &attrbits);
132397403Sobrien		}
1324132720Skan	}
1325132720Skan	if (nd->nd_flag & ND_NFSV3)
1326132720Skan		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1327132720Skan#ifdef NFS4_ACL_EXTATTR_NAME
1328132720Skan	acl_free(aclp);
1329132720Skan#endif
1330132720Skan
1331132720Skanout:
1332132720Skan	NFSEXITCODE2(0, nd);
1333132720Skan	return (0);
1334132720Skannfsmout:
1335132720Skan	vrele(dp);
1336132720Skan#ifdef NFS4_ACL_EXTATTR_NAME
1337117397Skan	acl_free(aclp);
133897403Sobrien#endif
1339132720Skan	if (bufp)
1340132720Skan		nfsvno_relpathbuf(&named);
1341132720Skan	if (pathcp)
1342132720Skan		FREE(pathcp, M_TEMP);
134397403Sobrien
1344132720Skan	NFSEXITCODE2(error, nd);
1345132720Skan	return (error);
1346132720Skan}
1347132720Skan
1348132720Skan/*
1349132720Skan * nfs remove service
1350132720Skan */
1351132720SkanAPPLESTATIC int
1352132720Skannfsrvd_remove(struct nfsrv_descript *nd, __unused int isdgram,
1353132720Skan    vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
1354132720Skan{
1355132720Skan	struct nameidata named;
1356132720Skan	u_int32_t *tl;
1357132720Skan	int error = 0, dirfor_ret = 1, diraft_ret = 1;
1358117397Skan	vnode_t dirp = NULL;
1359132720Skan	struct nfsvattr dirfor, diraft;
1360132720Skan	char *bufp;
1361132720Skan	u_long *hashp;
1362132720Skan
1363132720Skan	if (nd->nd_repstat) {
1364132720Skan		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
136597403Sobrien		goto out;
1366132720Skan	}
1367132720Skan	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, DELETE,
1368132720Skan	    LOCKPARENT | LOCKLEAF);
1369132720Skan	nfsvno_setpathbuf(&named, &bufp, &hashp);
1370132720Skan	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1371132720Skan	if (error) {
1372132720Skan		vput(dp);
1373132720Skan		nfsvno_relpathbuf(&named);
1374132720Skan		goto out;
1375132720Skan	}
1376132720Skan	if (!nd->nd_repstat) {
1377132720Skan		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
1378132720Skan	} else {
1379132720Skan		vput(dp);
138097403Sobrien		nfsvno_relpathbuf(&named);
1381117397Skan	}
138297403Sobrien	if (dirp) {
138397403Sobrien		if (!(nd->nd_flag & ND_NFSV2)) {
1384132720Skan			dirfor_ret = nfsvno_getattr(dirp, &dirfor,
1385132720Skan			    nd->nd_cred, p, 0);
1386132720Skan		} else {
1387132720Skan			vrele(dirp);
1388169691Skan			dirp = NULL;
1389132720Skan		}
1390132720Skan	}
139197403Sobrien	if (!nd->nd_repstat) {
1392102782Skan		if (nd->nd_flag & ND_NFSV4) {
1393102782Skan			if (vnode_vtype(named.ni_vp) == VDIR)
1394117397Skan				nd->nd_repstat = nfsvno_rmdirsub(&named, 1,
1395169691Skan				    nd->nd_cred, p, exp);
1396169691Skan			else
1397169691Skan				nd->nd_repstat = nfsvno_removesub(&named, 1,
1398169691Skan				    nd->nd_cred, p, exp);
1399169691Skan		} else if (nd->nd_procnum == NFSPROC_RMDIR) {
1400169691Skan			nd->nd_repstat = nfsvno_rmdirsub(&named, 0,
1401169691Skan			    nd->nd_cred, p, exp);
1402169691Skan		} else {
1403102782Skan			nd->nd_repstat = nfsvno_removesub(&named, 0,
1404117397Skan			    nd->nd_cred, p, exp);
1405169691Skan		}
1406169691Skan	}
1407169691Skan	if (!(nd->nd_flag & ND_NFSV2)) {
1408169691Skan		if (dirp) {
1409169691Skan			diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred,
1410169691Skan			    p, 0);
1411169691Skan			vrele(dirp);
1412169691Skan		}
1413169691Skan		if (nd->nd_flag & ND_NFSV3) {
1414102782Skan			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1415117397Skan			    &diraft);
1416169691Skan		} else if (!nd->nd_repstat) {
1417169691Skan			NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1418169691Skan			*tl++ = newnfs_false;
1419169691Skan			txdr_hyper(dirfor.na_filerev, tl);
1420169691Skan			tl += 2;
1421169691Skan			txdr_hyper(diraft.na_filerev, tl);
1422169691Skan		}
1423169691Skan	}
1424102782Skan
1425117397Skanout:
1426169691Skan	NFSEXITCODE2(error, nd);
1427169691Skan	return (error);
1428169691Skan}
1429169691Skan
1430169691Skan/*
1431169691Skan * nfs rename service
1432169691Skan */
1433169691SkanAPPLESTATIC int
1434169691Skannfsrvd_rename(struct nfsrv_descript *nd, int isdgram,
1435169691Skan    vnode_t dp, vnode_t todp, NFSPROC_T *p, struct nfsexstuff *exp,
143697403Sobrien    struct nfsexstuff *toexp)
1437132720Skan{
1438132720Skan	u_int32_t *tl;
1439132720Skan	int error = 0, fdirfor_ret = 1, fdiraft_ret = 1;
1440132720Skan	int tdirfor_ret = 1, tdiraft_ret = 1;
1441132720Skan	struct nameidata fromnd, tond;
1442132720Skan	vnode_t fdirp = NULL, tdirp = NULL, tdp = NULL;
144397403Sobrien	struct nfsvattr fdirfor, fdiraft, tdirfor, tdiraft;
1444132720Skan	struct nfsexstuff tnes;
1445132720Skan	struct nfsrvfh tfh;
1446132720Skan	char *bufp, *tbufp = NULL;
144797403Sobrien	u_long *hashp;
1448132720Skan	fhandle_t fh;
1449132720Skan
1450169691Skan	if (nd->nd_repstat) {
145197403Sobrien		nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
1452132720Skan		nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
1453132720Skan		goto out;
1454169691Skan	}
1455132720Skan	if (!(nd->nd_flag & ND_NFSV2))
145697403Sobrien		fdirfor_ret = nfsvno_getattr(dp, &fdirfor, nd->nd_cred, p, 1);
145797403Sobrien	tond.ni_cnd.cn_nameiop = 0;
1458132720Skan	tond.ni_startdir = NULL;
145997403Sobrien	NFSNAMEICNDSET(&fromnd.ni_cnd, nd->nd_cred, DELETE, WANTPARENT | SAVESTART);
1460132720Skan	nfsvno_setpathbuf(&fromnd, &bufp, &hashp);
1461132720Skan	error = nfsrv_parsename(nd, bufp, hashp, &fromnd.ni_pathlen);
146297403Sobrien	if (error) {
1463132720Skan		vput(dp);
146497403Sobrien		if (todp)
146597403Sobrien			vrele(todp);
1466117397Skan		nfsvno_relpathbuf(&fromnd);
1467132720Skan		goto out;
146897403Sobrien	}
1469132720Skan	/*
1470132720Skan	 * Unlock dp in this code section, so it is unlocked before
1471132720Skan	 * tdp gets locked. This avoids a potential LOR if tdp is the
1472132720Skan	 * parent directory of dp.
1473117397Skan	 */
1474132720Skan	if (nd->nd_flag & ND_NFSV4) {
147597403Sobrien		tdp = todp;
1476132720Skan		tnes = *toexp;
147797403Sobrien		if (dp != tdp) {
1478169691Skan			NFSVOPUNLOCK(dp, 0);
147997403Sobrien			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred,
148097403Sobrien			    p, 0);	/* Might lock tdp. */
148197403Sobrien		} else {
148297403Sobrien			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred,
1483132720Skan			    p, 1);
148497403Sobrien			NFSVOPUNLOCK(dp, 0);
1485132720Skan		}
148697403Sobrien	} else {
1487117397Skan		tfh.nfsrvfh_len = 0;
148897403Sobrien		error = nfsrv_mtofh(nd, &tfh);
148997403Sobrien		if (error == 0)
1490132720Skan			error = nfsvno_getfh(dp, &fh, p);
149197403Sobrien		if (error) {
1492132720Skan			vput(dp);
149397403Sobrien			/* todp is always NULL except NFSv4 */
149497403Sobrien			nfsvno_relpathbuf(&fromnd);
1495117397Skan			goto out;
149697403Sobrien		}
149797403Sobrien
149897403Sobrien		/* If this is the same file handle, just VREF() the vnode. */
149997403Sobrien		if (tfh.nfsrvfh_len == NFSX_MYFH &&
1500132720Skan		    !NFSBCMP(tfh.nfsrvfh_data, &fh, NFSX_MYFH)) {
1501132720Skan			VREF(dp);
1502132720Skan			tdp = dp;
1503132720Skan			tnes = *exp;
1504132720Skan			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred,
1505132720Skan			    p, 1);
1506132720Skan			NFSVOPUNLOCK(dp, 0);
1507132720Skan		} else {
1508132720Skan			NFSVOPUNLOCK(dp, 0);
1509132720Skan			nd->nd_cred->cr_uid = nd->nd_saveduid;
1510132720Skan			nfsd_fhtovp(nd, &tfh, LK_EXCLUSIVE, &tdp, &tnes, NULL,
1511117397Skan			    0, p);	/* Locks tdp. */
151297403Sobrien			if (tdp) {
151397403Sobrien				tdirfor_ret = nfsvno_getattr(tdp, &tdirfor,
1514132720Skan				    nd->nd_cred, p, 1);
1515132720Skan				NFSVOPUNLOCK(tdp, 0);
1516132720Skan			}
1517132720Skan		}
1518132720Skan	}
1519132720Skan	NFSNAMEICNDSET(&tond.ni_cnd, nd->nd_cred, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART);
1520132720Skan	nfsvno_setpathbuf(&tond, &tbufp, &hashp);
1521117397Skan	if (!nd->nd_repstat) {
1522132720Skan		error = nfsrv_parsename(nd, tbufp, hashp, &tond.ni_pathlen);
152397403Sobrien		if (error) {
152497403Sobrien			if (tdp)
1525132720Skan				vrele(tdp);
1526132720Skan			vrele(dp);
1527132720Skan			nfsvno_relpathbuf(&fromnd);
1528132720Skan			nfsvno_relpathbuf(&tond);
1529132720Skan			goto out;
1530132720Skan		}
1531117397Skan	}
1532169691Skan	if (nd->nd_repstat) {
1533169691Skan		if (nd->nd_flag & ND_NFSV3) {
153497403Sobrien			nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
1535132720Skan			    &fdiraft);
1536132720Skan			nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
1537132720Skan			    &tdiraft);
1538132720Skan		}
1539132720Skan		if (tdp)
1540132720Skan			vrele(tdp);
1541117397Skan		vrele(dp);
1542169691Skan		nfsvno_relpathbuf(&fromnd);
1543169691Skan		nfsvno_relpathbuf(&tond);
154497403Sobrien		goto out;
1545132720Skan	}
1546132720Skan
1547132720Skan	/*
1548117397Skan	 * Done parsing, now down to business.
1549169691Skan	 */
1550169691Skan	nd->nd_repstat = nfsvno_namei(nd, &fromnd, dp, 0, exp, p, &fdirp);
155197403Sobrien	if (nd->nd_repstat) {
1552132720Skan		if (nd->nd_flag & ND_NFSV3) {
1553132720Skan			nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
1554132720Skan			    &fdiraft);
1555132720Skan			nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
1556132720Skan			    &tdiraft);
1557132720Skan		}
1558132720Skan		if (fdirp)
1559132720Skan			vrele(fdirp);
1560132720Skan		if (tdp)
1561132720Skan			vrele(tdp);
1562132720Skan		nfsvno_relpathbuf(&tond);
1563117397Skan		goto out;
156497403Sobrien	}
156597403Sobrien	if (vnode_vtype(fromnd.ni_vp) == VDIR)
1566132720Skan		tond.ni_cnd.cn_flags |= WILLBEDIR;
1567132720Skan	nd->nd_repstat = nfsvno_namei(nd, &tond, tdp, 0, &tnes, p, &tdirp);
1568132720Skan	nd->nd_repstat = nfsvno_rename(&fromnd, &tond, nd->nd_repstat,
1569132720Skan	    nd->nd_flag, nd->nd_cred, p);
1570132720Skan	if (fdirp)
1571132720Skan		fdiraft_ret = nfsvno_getattr(fdirp, &fdiraft, nd->nd_cred, p,
1572132720Skan		    0);
1573132720Skan	if (tdirp)
1574132720Skan		tdiraft_ret = nfsvno_getattr(tdirp, &tdiraft, nd->nd_cred, p,
1575132720Skan		    0);
1576117397Skan	if (fdirp)
157797403Sobrien		vrele(fdirp);
157897403Sobrien	if (tdirp)
157997403Sobrien		vrele(tdirp);
1580132720Skan	if (nd->nd_flag & ND_NFSV3) {
1581132720Skan		nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
1582132720Skan		nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
1583132720Skan	} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1584132720Skan		NFSM_BUILD(tl, u_int32_t *, 10 * NFSX_UNSIGNED);
1585132720Skan		*tl++ = newnfs_false;
1586132720Skan		txdr_hyper(fdirfor.na_filerev, tl);
1587132720Skan		tl += 2;
1588132720Skan		txdr_hyper(fdiraft.na_filerev, tl);
1589132720Skan		tl += 2;
1590117397Skan		*tl++ = newnfs_false;
159197403Sobrien		txdr_hyper(tdirfor.na_filerev, tl);
1592132720Skan		tl += 2;
1593132720Skan		txdr_hyper(tdiraft.na_filerev, tl);
1594132720Skan	}
1595132720Skan
159697403Sobrienout:
1597132720Skan	NFSEXITCODE2(error, nd);
1598132720Skan	return (error);
1599132720Skan}
1600132720Skan
1601132720Skan/*
1602132720Skan * nfs link service
1603132720Skan */
1604132720SkanAPPLESTATIC int
1605132720Skannfsrvd_link(struct nfsrv_descript *nd, int isdgram,
1606132720Skan    vnode_t vp, vnode_t tovp, NFSPROC_T *p, struct nfsexstuff *exp,
1607117397Skan    struct nfsexstuff *toexp)
160897403Sobrien{
160997403Sobrien	struct nameidata named;
1610132720Skan	u_int32_t *tl;
1611132720Skan	int error = 0, dirfor_ret = 1, diraft_ret = 1, getret = 1;
1612132720Skan	vnode_t dirp = NULL, dp = NULL;
1613132720Skan	struct nfsvattr dirfor, diraft, at;
1614132720Skan	struct nfsexstuff tnes;
1615132720Skan	struct nfsrvfh dfh;
1616132720Skan	char *bufp;
1617132720Skan	u_long *hashp;
1618132720Skan
1619132720Skan	if (nd->nd_repstat) {
1620117397Skan		nfsrv_postopattr(nd, getret, &at);
162197403Sobrien		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
162297403Sobrien		goto out;
162397403Sobrien	}
1624132720Skan	NFSVOPUNLOCK(vp, 0);
1625132720Skan	if (vnode_vtype(vp) == VDIR) {
1626132720Skan		if (nd->nd_flag & ND_NFSV4)
1627132720Skan			nd->nd_repstat = NFSERR_ISDIR;
1628132720Skan		else
1629132720Skan			nd->nd_repstat = NFSERR_INVAL;
1630132720Skan		if (tovp)
1631132720Skan			vrele(tovp);
1632132720Skan	}
1633132720Skan	if (!nd->nd_repstat) {
1634132720Skan		if (nd->nd_flag & ND_NFSV4) {
1635117397Skan			dp = tovp;
163697403Sobrien			tnes = *toexp;
163797403Sobrien		} else {
1638132720Skan			error = nfsrv_mtofh(nd, &dfh);
1639132720Skan			if (error) {
1640132720Skan				vrele(vp);
1641169691Skan				/* tovp is always NULL unless NFSv4 */
1642132720Skan				goto out;
1643132720Skan			}
1644132720Skan			nfsd_fhtovp(nd, &dfh, LK_EXCLUSIVE, &dp, &tnes, NULL, 0,
1645132720Skan			    p);
1646132720Skan			if (dp)
1647132720Skan				NFSVOPUNLOCK(dp, 0);
1648117397Skan		}
164997403Sobrien	}
1650132720Skan	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
1651132720Skan	    LOCKPARENT | SAVENAME | NOCACHE);
1652132720Skan	if (!nd->nd_repstat) {
1653132720Skan		nfsvno_setpathbuf(&named, &bufp, &hashp);
165497403Sobrien		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1655132720Skan		if (error) {
1656132720Skan			vrele(vp);
1657132720Skan			if (dp)
1658169691Skan				vrele(dp);
1659132720Skan			nfsvno_relpathbuf(&named);
1660132720Skan			goto out;
1661132720Skan		}
1662132720Skan		if (!nd->nd_repstat) {
1663132720Skan			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, &tnes,
1664132720Skan			    p, &dirp);
1665117397Skan		} else {
166697403Sobrien			if (dp)
166797403Sobrien				vrele(dp);
1668132720Skan			nfsvno_relpathbuf(&named);
1669132720Skan		}
1670132720Skan	}
1671132720Skan	if (dirp) {
1672132720Skan		if (nd->nd_flag & ND_NFSV2) {
1673132720Skan			vrele(dirp);
1674132720Skan			dirp = NULL;
1675132720Skan		} else {
1676132720Skan			dirfor_ret = nfsvno_getattr(dirp, &dirfor,
1677132720Skan			    nd->nd_cred, p, 0);
1678117397Skan		}
167997403Sobrien	}
168097403Sobrien	if (!nd->nd_repstat)
168197403Sobrien		nd->nd_repstat = nfsvno_link(&named, vp, nd->nd_cred, p, exp);
1682132720Skan	if (nd->nd_flag & ND_NFSV3)
1683132720Skan		getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 0);
1684132720Skan	if (dirp) {
1685229551Spfg		diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0);
1686132720Skan		vrele(dirp);
1687132720Skan	}
1688132720Skan	vrele(vp);
1689132720Skan	if (nd->nd_flag & ND_NFSV3) {
1690132720Skan		nfsrv_postopattr(nd, getret, &at);
1691132720Skan		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1692132720Skan	} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1693117397Skan		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
169497403Sobrien		*tl++ = newnfs_false;
169597403Sobrien		txdr_hyper(dirfor.na_filerev, tl);
1696132720Skan		tl += 2;
1697132720Skan		txdr_hyper(diraft.na_filerev, tl);
1698132720Skan	}
1699132720Skan
1700132720Skanout:
1701132720Skan	NFSEXITCODE2(error, nd);
1702132720Skan	return (error);
1703132720Skan}
1704132720Skan
1705132720Skan/*
1706117397Skan * nfs symbolic link service
170797403Sobrien */
1708132720SkanAPPLESTATIC int
1709132720Skannfsrvd_symlink(struct nfsrv_descript *nd, __unused int isdgram,
1710132720Skan    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
1711132720Skan    struct nfsexstuff *exp)
171297403Sobrien{
1713132720Skan	struct nfsvattr nva, dirfor, diraft;
1714132720Skan	struct nameidata named;
1715132720Skan	int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
1716132720Skan	vnode_t dirp = NULL;
1717132720Skan	char *bufp, *pathcp = NULL;
1718132720Skan	u_long *hashp;
1719132720Skan
1720132720Skan	if (nd->nd_repstat) {
1721132720Skan		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1722132720Skan		goto out;
1723132720Skan	}
1724132720Skan	if (vpp)
1725117397Skan		*vpp = NULL;
172697403Sobrien	NFSVNO_ATTRINIT(&nva);
172797403Sobrien	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
172897403Sobrien	    LOCKPARENT | SAVESTART | NOCACHE);
1729132720Skan	nfsvno_setpathbuf(&named, &bufp, &hashp);
1730132720Skan	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1731132720Skan	if (!error && !nd->nd_repstat)
1732132720Skan		error = nfsvno_getsymlink(nd, &nva, p, &pathcp, &pathlen);
1733132720Skan	if (error) {
1734132720Skan		vrele(dp);
1735132720Skan		nfsvno_relpathbuf(&named);
1736132720Skan		goto out;
1737132720Skan	}
1738132720Skan	if (!nd->nd_repstat) {
1739117397Skan		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
174097403Sobrien	} else {
174197403Sobrien		vrele(dp);
174297403Sobrien		nfsvno_relpathbuf(&named);
1743132720Skan	}
1744132720Skan	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
1745132720Skan		vrele(dirp);
1746229551Spfg		dirp = NULL;
1747132720Skan	}
1748132720Skan
1749132720Skan	/*
1750132720Skan	 * And call nfsrvd_symlinksub() to do the common code. It will
1751132720Skan	 * return EBADRPC upon a parsing error, 0 otherwise.
1752132720Skan	 */
1753132720Skan	if (!nd->nd_repstat) {
1754117397Skan		if (dirp != NULL)
175597403Sobrien			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
175697403Sobrien			    p, 0);
1757132720Skan		nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
1758132720Skan		    &dirfor, &diraft, &diraft_ret, NULL, NULL, p, exp,
1759132720Skan		    pathcp, pathlen);
1760132720Skan	} else if (dirp != NULL) {
1761132720Skan		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
1762132720Skan		vrele(dirp);
1763132720Skan	}
1764132720Skan	if (pathcp)
1765132720Skan		FREE(pathcp, M_TEMP);
1766132720Skan
1767117397Skan	if (nd->nd_flag & ND_NFSV3) {
176897403Sobrien		if (!nd->nd_repstat) {
1769132720Skan			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
1770132720Skan			nfsrv_postopattr(nd, 0, &nva);
1771132720Skan		}
1772132720Skan		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
177397403Sobrien	}
1774132720Skan
1775132720Skanout:
1776132720Skan	NFSEXITCODE2(error, nd);
1777229551Spfg	return (error);
1778132720Skan}
1779132720Skan
1780132720Skan/*
1781132720Skan * Common code for creating a symbolic link.
1782132720Skan */
1783132720Skanstatic void
1784132720Skannfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
1785132720Skan    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
1786117397Skan    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
178797403Sobrien    int *diraft_retp, nfsattrbit_t *attrbitp,
178897403Sobrien    NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
178997403Sobrien    int pathlen)
1790132720Skan{
1791132720Skan	u_int32_t *tl;
1792132720Skan
1793132720Skan	nd->nd_repstat = nfsvno_symlink(ndp, nvap, pathcp, pathlen,
1794132720Skan	    !(nd->nd_flag & ND_NFSV2), nd->nd_saveduid, nd->nd_cred, p, exp);
1795132720Skan	if (!nd->nd_repstat && !(nd->nd_flag & ND_NFSV2)) {
1796132720Skan		nfsrv_fixattr(nd, ndp->ni_vp, nvap, aclp, p, attrbitp, exp);
1797132720Skan		if (nd->nd_flag & ND_NFSV3) {
1798132720Skan			nd->nd_repstat = nfsvno_getfh(ndp->ni_vp, fhp, p);
1799132720Skan			if (!nd->nd_repstat)
1800117397Skan				nd->nd_repstat = nfsvno_getattr(ndp->ni_vp,
180197403Sobrien				    nvap, nd->nd_cred, p, 1);
180297403Sobrien		}
180397403Sobrien		if (vpp != NULL && nd->nd_repstat == 0) {
1804132720Skan			NFSVOPUNLOCK(ndp->ni_vp, 0);
1805132720Skan			*vpp = ndp->ni_vp;
1806132720Skan		} else
1807229551Spfg			vput(ndp->ni_vp);
1808132720Skan	}
1809132720Skan	if (dirp) {
1810132720Skan		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p, 0);
1811132720Skan		vrele(dirp);
1812132720Skan	}
1813132720Skan	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1814132720Skan		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1815117397Skan		*tl++ = newnfs_false;
1816117397Skan		txdr_hyper(dirforp->na_filerev, tl);
181797403Sobrien		tl += 2;
181897403Sobrien		txdr_hyper(diraftp->na_filerev, tl);
1819132720Skan		(void) nfsrv_putattrbit(nd, attrbitp);
1820132720Skan	}
1821132720Skan
1822132720Skan	NFSEXITCODE2(0, nd);
1823132720Skan}
1824132720Skan
1825132720Skan/*
1826132720Skan * nfs mkdir service
1827132720Skan */
1828132720SkanAPPLESTATIC int
1829117397Skannfsrvd_mkdir(struct nfsrv_descript *nd, __unused int isdgram,
183097403Sobrien    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
1831132720Skan    struct nfsexstuff *exp)
1832132720Skan{
1833132720Skan	struct nfsvattr nva, dirfor, diraft;
1834132720Skan	struct nameidata named;
183597403Sobrien	u_int32_t *tl;
1836132720Skan	int error = 0, dirfor_ret = 1, diraft_ret = 1;
1837132720Skan	vnode_t dirp = NULL;
1838132720Skan	char *bufp;
1839132720Skan	u_long *hashp;
1840132720Skan
1841132720Skan	if (nd->nd_repstat) {
1842132720Skan		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1843132720Skan		goto out;
1844132720Skan	}
1845132720Skan	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
1846117397Skan	    LOCKPARENT | SAVENAME | NOCACHE);
184797403Sobrien	nfsvno_setpathbuf(&named, &bufp, &hashp);
184897403Sobrien	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1849132720Skan	if (error)
1850132720Skan		goto nfsmout;
1851132720Skan	if (!nd->nd_repstat) {
1852229551Spfg		NFSVNO_ATTRINIT(&nva);
1853229551Spfg		if (nd->nd_flag & ND_NFSV3) {
1854132720Skan			error = nfsrv_sattr(nd, &nva, NULL, NULL, p);
1855132720Skan			if (error)
1856132720Skan				goto nfsmout;
1857132720Skan		} else {
1858132720Skan			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1859117397Skan			nva.na_mode = nfstov_mode(*tl++);
186097403Sobrien		}
186197403Sobrien	}
186297403Sobrien	if (!nd->nd_repstat) {
1863132720Skan		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
1864132720Skan	} else {
1865132720Skan		vrele(dp);
1866229551Spfg		nfsvno_relpathbuf(&named);
1867132720Skan	}
1868229551Spfg	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
1869132720Skan		vrele(dirp);
1870132720Skan		dirp = NULL;
1871132720Skan	}
1872132720Skan	if (nd->nd_repstat) {
1873132720Skan		if (dirp != NULL) {
1874132720Skan			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
1875117397Skan			    p, 0);
1876117397Skan			vrele(dirp);
187797403Sobrien		}
1878132720Skan		if (nd->nd_flag & ND_NFSV3)
1879229551Spfg			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1880132720Skan			    &diraft);
1881229551Spfg		goto out;
1882229551Spfg	}
1883132720Skan	if (dirp != NULL)
1884132720Skan		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
1885132720Skan
1886132720Skan	/*
1887132720Skan	 * Call nfsrvd_mkdirsub() for the code common to V4 as well.
1888117397Skan	 */
188997403Sobrien	nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp, &dirfor, &diraft,
1890132720Skan	    &diraft_ret, NULL, NULL, p, exp);
1891132720Skan
1892132720Skan	if (nd->nd_flag & ND_NFSV3) {
1893132720Skan		if (!nd->nd_repstat) {
189497403Sobrien			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
1895132720Skan			nfsrv_postopattr(nd, 0, &nva);
1896132720Skan		}
1897132720Skan		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1898229551Spfg	} else if (!nd->nd_repstat) {
1899229551Spfg		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
1900132720Skan		nfsrv_fillattr(nd, &nva);
1901132720Skan	}
1902132720Skan
1903132720Skanout:
1904132720Skan	NFSEXITCODE2(0, nd);
1905117397Skan	return (0);
190697403Sobriennfsmout:
190797403Sobrien	vrele(dp);
1908132720Skan	nfsvno_relpathbuf(&named);
1909132720Skan	NFSEXITCODE2(error, nd);
1910132720Skan	return (error);
1911132720Skan}
1912132720Skan
1913132720Skan/*
1914132720Skan * Code common to mkdir for V2,3 and 4.
1915132720Skan */
1916132720Skanstatic void
1917132720Skannfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
1918132720Skan    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
1919132720Skan    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
1920117397Skan    int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
192197403Sobrien    NFSPROC_T *p, struct nfsexstuff *exp)
1922132720Skan{
1923132720Skan	vnode_t vp;
192497403Sobrien	u_int32_t *tl;
1925132720Skan
1926132720Skan	NFSVNO_SETATTRVAL(nvap, type, VDIR);
1927132720Skan	nd->nd_repstat = nfsvno_mkdir(ndp, nvap, nd->nd_saveduid,
1928132720Skan	    nd->nd_cred, p, exp);
1929132720Skan	if (!nd->nd_repstat) {
1930132720Skan		vp = ndp->ni_vp;
1931132720Skan		nfsrv_fixattr(nd, vp, nvap, aclp, p, attrbitp, exp);
1932146897Skan		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
1933146897Skan		if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
1934146897Skan			nd->nd_repstat = nfsvno_getattr(vp, nvap, nd->nd_cred,
1935146897Skan			    p, 1);
1936146897Skan		if (vpp && !nd->nd_repstat) {
1937132720Skan			NFSVOPUNLOCK(vp, 0);
1938117397Skan			*vpp = vp;
193997403Sobrien		} else {
194097403Sobrien			vput(vp);
1941132720Skan		}
1942132720Skan	}
1943132720Skan	if (dirp) {
1944117397Skan		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p, 0);
194597403Sobrien		vrele(dirp);
194697403Sobrien	}
1947259705Spfg	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
194897403Sobrien		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
194997403Sobrien		*tl++ = newnfs_false;
195097403Sobrien		txdr_hyper(dirforp->na_filerev, tl);
1951132720Skan		tl += 2;
1952132720Skan		txdr_hyper(diraftp->na_filerev, tl);
1953132720Skan		(void) nfsrv_putattrbit(nd, attrbitp);
1954132720Skan	}
1955132720Skan
1956132720Skan	NFSEXITCODE2(0, nd);
1957132720Skan}
1958132720Skan
1959132720Skan/*
1960132720Skan * nfs commit service
1961146897Skan */
1962146897SkanAPPLESTATIC int
1963146897Skannfsrvd_commit(struct nfsrv_descript *nd, __unused int isdgram,
1964146897Skan    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
1965146897Skan{
1966146897Skan	struct nfsvattr bfor, aft;
1967132720Skan	u_int32_t *tl;
1968117397Skan	int error = 0, for_ret = 1, aft_ret = 1, cnt;
196997403Sobrien	u_int64_t off;
197097403Sobrien
1971132720Skan	if (nd->nd_repstat) {
1972132720Skan		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
1973132720Skan		goto out;
1974132720Skan	}
1975132720Skan	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
1976132720Skan	/*
1977132720Skan	 * XXX At this time VOP_FSYNC() does not accept offset and byte
1978132720Skan	 * count parameters, so these arguments are useless (someday maybe).
1979132720Skan	 */
1980132720Skan	off = fxdr_hyper(tl);
1981132720Skan	tl += 2;
1982132720Skan	cnt = fxdr_unsigned(int, *tl);
1983132720Skan	if (nd->nd_flag & ND_NFSV3)
1984132720Skan		for_ret = nfsvno_getattr(vp, &bfor, nd->nd_cred, p, 1);
1985146897Skan	nd->nd_repstat = nfsvno_fsync(vp, off, cnt, nd->nd_cred, p);
1986146897Skan	if (nd->nd_flag & ND_NFSV3) {
1987146897Skan		aft_ret = nfsvno_getattr(vp, &aft, nd->nd_cred, p, 1);
1988146897Skan		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
1989146897Skan	}
1990146897Skan	vput(vp);
1991132720Skan	if (!nd->nd_repstat) {
1992117397Skan		NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
199397403Sobrien		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
199497403Sobrien		*tl = txdr_unsigned(nfsboottime.tv_usec);
199597403Sobrien	}
1996132720Skan
1997132720Skanout:
1998132720Skan	NFSEXITCODE2(0, nd);
1999132720Skan	return (0);
2000132720Skannfsmout:
2001132720Skan	vput(vp);
2002132720Skan	NFSEXITCODE2(error, nd);
2003146897Skan	return (error);
2004146897Skan}
2005146897Skan
2006146897Skan/*
2007146897Skan * nfs statfs service
2008146897Skan */
2009132720SkanAPPLESTATIC int
2010117397Skannfsrvd_statfs(struct nfsrv_descript *nd, __unused int isdgram,
201197403Sobrien    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
201297403Sobrien{
2013132720Skan	struct statfs *sf;
2014117397Skan	u_int32_t *tl;
2015132720Skan	int getret = 1;
2016132720Skan	struct nfsvattr at;
2017132720Skan	struct statfs sfs;
2018132720Skan	u_quad_t tval;
2019132720Skan
2020132720Skan	if (nd->nd_repstat) {
2021132720Skan		nfsrv_postopattr(nd, getret, &at);
2022132720Skan		goto out;
2023132720Skan	}
2024132720Skan	sf = &sfs;
2025146897Skan	nd->nd_repstat = nfsvno_statfs(vp, sf);
2026146897Skan	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
2027146897Skan	vput(vp);
2028146897Skan	if (nd->nd_flag & ND_NFSV3)
2029146897Skan		nfsrv_postopattr(nd, getret, &at);
2030146897Skan	if (nd->nd_repstat)
2031146897Skan		goto out;
2032132720Skan	if (nd->nd_flag & ND_NFSV2) {
2033117397Skan		NFSM_BUILD(tl, u_int32_t *, NFSX_V2STATFS);
203497403Sobrien		*tl++ = txdr_unsigned(NFS_V2MAXDATA);
203597403Sobrien		*tl++ = txdr_unsigned(sf->f_bsize);
2036132720Skan		*tl++ = txdr_unsigned(sf->f_blocks);
2037132720Skan		*tl++ = txdr_unsigned(sf->f_bfree);
2038132720Skan		*tl = txdr_unsigned(sf->f_bavail);
2039132720Skan	} else {
2040132720Skan		NFSM_BUILD(tl, u_int32_t *, NFSX_V3STATFS);
2041132720Skan		tval = (u_quad_t)sf->f_blocks;
2042132720Skan		tval *= (u_quad_t)sf->f_bsize;
2043132720Skan		txdr_hyper(tval, tl); tl += 2;
2044132720Skan		tval = (u_quad_t)sf->f_bfree;
2045132720Skan		tval *= (u_quad_t)sf->f_bsize;
2046132720Skan		txdr_hyper(tval, tl); tl += 2;
2047132720Skan		tval = (u_quad_t)sf->f_bavail;
2048146897Skan		tval *= (u_quad_t)sf->f_bsize;
2049146897Skan		txdr_hyper(tval, tl); tl += 2;
2050146897Skan		tval = (u_quad_t)sf->f_files;
2051146897Skan		txdr_hyper(tval, tl); tl += 2;
2052146897Skan		tval = (u_quad_t)sf->f_ffree;
2053146897Skan		txdr_hyper(tval, tl); tl += 2;
2054132720Skan		tval = (u_quad_t)sf->f_ffree;
2055132720Skan		txdr_hyper(tval, tl); tl += 2;
2056132720Skan		*tl = 0;
2057132720Skan	}
2058117397Skan
2059117397Skanout:
206097403Sobrien	NFSEXITCODE2(0, nd);
206197403Sobrien	return (0);
206297403Sobrien}
206397403Sobrien
206497403Sobrien/*
206597403Sobrien * nfs fsinfo service
2066146897Skan */
2067132720SkanAPPLESTATIC int
2068146897Skannfsrvd_fsinfo(struct nfsrv_descript *nd, int isdgram,
2069146897Skan    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2070146897Skan{
207197403Sobrien	u_int32_t *tl;
207297403Sobrien	struct nfsfsinfo fs;
2073132720Skan	int getret = 1;
2074132720Skan	struct nfsvattr at;
2075132720Skan
2076132720Skan	if (nd->nd_repstat) {
2077132720Skan		nfsrv_postopattr(nd, getret, &at);
2078132720Skan		goto out;
207997403Sobrien	}
208097403Sobrien	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
208197403Sobrien	nfsvno_getfs(&fs, isdgram);
208297403Sobrien	vput(vp);
208397403Sobrien	nfsrv_postopattr(nd, getret, &at);
208497403Sobrien	NFSM_BUILD(tl, u_int32_t *, NFSX_V3FSINFO);
208597403Sobrien	*tl++ = txdr_unsigned(fs.fs_rtmax);
208697403Sobrien	*tl++ = txdr_unsigned(fs.fs_rtpref);
208797403Sobrien	*tl++ = txdr_unsigned(fs.fs_rtmult);
208897403Sobrien	*tl++ = txdr_unsigned(fs.fs_wtmax);
2089132720Skan	*tl++ = txdr_unsigned(fs.fs_wtpref);
2090132720Skan	*tl++ = txdr_unsigned(fs.fs_wtmult);
2091132720Skan	*tl++ = txdr_unsigned(fs.fs_dtpref);
2092132720Skan	txdr_hyper(fs.fs_maxfilesize, tl);
2093132720Skan	tl += 2;
2094132720Skan	txdr_nfsv3time(&fs.fs_timedelta, tl);
209597403Sobrien	tl += 2;
209697403Sobrien	*tl = txdr_unsigned(fs.fs_properties);
209797403Sobrien
209897403Sobrienout:
209997403Sobrien	NFSEXITCODE2(0, nd);
2100132720Skan	return (0);
2101132720Skan}
2102132720Skan
2103132720Skan/*
2104132720Skan * nfs pathconf service
2105132720Skan */
210697403SobrienAPPLESTATIC int
210797403Sobriennfsrvd_pathconf(struct nfsrv_descript *nd, __unused int isdgram,
210897403Sobrien    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
210997403Sobrien{
2110132720Skan	struct nfsv3_pathconf *pc;
2111132720Skan	int getret = 1;
2112132720Skan	register_t linkmax, namemax, chownres, notrunc;
2113132720Skan	struct nfsvattr at;
2114132720Skan
2115132720Skan	if (nd->nd_repstat) {
211697403Sobrien		nfsrv_postopattr(nd, getret, &at);
211797403Sobrien		goto out;
211897403Sobrien	}
211997403Sobrien	nd->nd_repstat = nfsvno_pathconf(vp, _PC_LINK_MAX, &linkmax,
212097403Sobrien	    nd->nd_cred, p);
212197403Sobrien	if (!nd->nd_repstat)
212297403Sobrien		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NAME_MAX, &namemax,
212397403Sobrien		    nd->nd_cred, p);
212497403Sobrien	if (!nd->nd_repstat)
212597403Sobrien		nd->nd_repstat=nfsvno_pathconf(vp, _PC_CHOWN_RESTRICTED,
2126132720Skan		    &chownres, nd->nd_cred, p);
2127132720Skan	if (!nd->nd_repstat)
2128132720Skan		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NO_TRUNC, &notrunc,
2129132720Skan		    nd->nd_cred, p);
2130132720Skan	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
2131132720Skan	vput(vp);
213297403Sobrien	nfsrv_postopattr(nd, getret, &at);
213397403Sobrien	if (!nd->nd_repstat) {
213497403Sobrien		NFSM_BUILD(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
213597403Sobrien		pc->pc_linkmax = txdr_unsigned(linkmax);
2136132720Skan		pc->pc_namemax = txdr_unsigned(namemax);
213797403Sobrien		pc->pc_notrunc = txdr_unsigned(notrunc);
213897403Sobrien		pc->pc_chownrestricted = txdr_unsigned(chownres);
213997403Sobrien
214097403Sobrien		/*
214197403Sobrien		 * These should probably be supported by VOP_PATHCONF(), but
214297403Sobrien		 * until msdosfs is exportable (why would you want to?), the
214397403Sobrien		 * Unix defaults should be ok.
2144132720Skan		 */
2145132720Skan		pc->pc_caseinsensitive = newnfs_false;
2146132720Skan		pc->pc_casepreserving = newnfs_true;
2147132720Skan	}
2148132720Skan
2149132720Skanout:
215097403Sobrien	NFSEXITCODE2(0, nd);
215197403Sobrien	return (0);
215297403Sobrien}
215397403Sobrien
215497403Sobrien/*
215597403Sobrien * nfsv4 lock service
2156132720Skan */
2157132720SkanAPPLESTATIC int
2158132720Skannfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram,
2159132720Skan    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2160132720Skan{
2161132720Skan	u_int32_t *tl;
216297403Sobrien	int i;
216397403Sobrien	struct nfsstate *stp = NULL;
216497403Sobrien	struct nfslock *lop;
216597403Sobrien	struct nfslockconflict cf;
216697403Sobrien	int error = 0;
216797403Sobrien	u_short flags = NFSLCK_LOCK, lflags;
2168132720Skan	u_int64_t offset, len;
2169132720Skan	nfsv4stateid_t stateid;
2170132720Skan	nfsquad_t clientid;
2171132720Skan
2172132720Skan	NFSM_DISSECT(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2173132720Skan	i = fxdr_unsigned(int, *tl++);
217497403Sobrien	switch (i) {
217597403Sobrien	case NFSV4LOCKT_READW:
217697403Sobrien		flags |= NFSLCK_BLOCKING;
217797403Sobrien	case NFSV4LOCKT_READ:
217897403Sobrien		lflags = NFSLCK_READ;
217997403Sobrien		break;
218097403Sobrien	case NFSV4LOCKT_WRITEW:
2181132720Skan		flags |= NFSLCK_BLOCKING;
2182132720Skan	case NFSV4LOCKT_WRITE:
2183132720Skan		lflags = NFSLCK_WRITE;
2184132720Skan		break;
2185132720Skan	default:
2186132720Skan		nd->nd_repstat = NFSERR_BADXDR;
218797403Sobrien		goto nfsmout;
218897403Sobrien	};
218997403Sobrien	if (*tl++ == newnfs_true)
219097403Sobrien		flags |= NFSLCK_RECLAIM;
219197403Sobrien	offset = fxdr_hyper(tl);
219297403Sobrien	tl += 2;
2193132720Skan	len = fxdr_hyper(tl);
2194132720Skan	tl += 2;
2195132720Skan	if (*tl == newnfs_true)
2196132720Skan		flags |= NFSLCK_OPENTOLOCK;
2197132720Skan	if (flags & NFSLCK_OPENTOLOCK) {
2198132720Skan		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID);
219997403Sobrien		i = fxdr_unsigned(int, *(tl+4+(NFSX_STATEID / NFSX_UNSIGNED)));
220097403Sobrien		if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
220197403Sobrien			nd->nd_repstat = NFSERR_BADXDR;
220297403Sobrien			goto nfsmout;
220397403Sobrien		}
220497403Sobrien		MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2205132720Skan			M_NFSDSTATE, M_WAITOK);
2206132720Skan		stp->ls_ownerlen = i;
2207132720Skan		stp->ls_op = nd->nd_rp;
2208132720Skan		stp->ls_seq = fxdr_unsigned(int, *tl++);
2209132720Skan		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2210132720Skan		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
221197403Sobrien			NFSX_STATEIDOTHER);
221297403Sobrien		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
221397403Sobrien		stp->ls_opentolockseq = fxdr_unsigned(int, *tl++);
221497403Sobrien		clientid.lval[0] = *tl++;
221597403Sobrien		clientid.lval[1] = *tl++;
221697403Sobrien		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
221797403Sobrien			if ((nd->nd_flag & ND_NFSV41) != 0)
2218132720Skan				clientid.qval = nd->nd_clientid.qval;
2219132720Skan			else if (nd->nd_clientid.qval != clientid.qval)
2220132720Skan				printf("EEK3 multiple clids\n");
2221132720Skan		} else {
2222132720Skan			if ((nd->nd_flag & ND_NFSV41) != 0)
2223132720Skan				printf("EEK! no clientid from session\n");
222497403Sobrien			nd->nd_flag |= ND_IMPLIEDCLID;
222597403Sobrien			nd->nd_clientid.qval = clientid.qval;
222697403Sobrien		}
222797403Sobrien		error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
222897403Sobrien		if (error)
222997403Sobrien			goto nfsmout;
2230132720Skan	} else {
2231132720Skan		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
2232132720Skan		MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate),
2233132720Skan			M_NFSDSTATE, M_WAITOK);
2234132720Skan		stp->ls_ownerlen = 0;
2235132720Skan		stp->ls_op = nd->nd_rp;
223697403Sobrien		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
223797403Sobrien		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
223897403Sobrien			NFSX_STATEIDOTHER);
223997403Sobrien		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
224097403Sobrien		stp->ls_seq = fxdr_unsigned(int, *tl);
224197403Sobrien		clientid.lval[0] = stp->ls_stateid.other[0];
2242132720Skan		clientid.lval[1] = stp->ls_stateid.other[1];
2243132720Skan		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2244132720Skan			if ((nd->nd_flag & ND_NFSV41) != 0)
2245132720Skan				clientid.qval = nd->nd_clientid.qval;
2246132720Skan			else if (nd->nd_clientid.qval != clientid.qval)
2247132720Skan				printf("EEK4 multiple clids\n");
224897403Sobrien		} else {
224997403Sobrien			if ((nd->nd_flag & ND_NFSV41) != 0)
225097403Sobrien				printf("EEK! no clientid from session\n");
225197403Sobrien			nd->nd_flag |= ND_IMPLIEDCLID;
225297403Sobrien			nd->nd_clientid.qval = clientid.qval;
225397403Sobrien		}
225497403Sobrien	}
2255132720Skan	MALLOC(lop, struct nfslock *, sizeof (struct nfslock),
2256132720Skan		M_NFSDLOCK, M_WAITOK);
2257132720Skan	lop->lo_first = offset;
2258132720Skan	if (len == NFS64BITSSET) {
2259132720Skan		lop->lo_end = NFS64BITSSET;
2260132720Skan	} else {
226197403Sobrien		lop->lo_end = offset + len;
226297403Sobrien		if (lop->lo_end <= lop->lo_first)
226397403Sobrien			nd->nd_repstat = NFSERR_INVAL;
226497403Sobrien	}
226597403Sobrien	lop->lo_flags = lflags;
226697403Sobrien	stp->ls_flags = flags;
2267132720Skan	stp->ls_uid = nd->nd_cred->cr_uid;
2268132720Skan
2269132720Skan	/*
2270132720Skan	 * Do basic access checking.
2271132720Skan	 */
2272132720Skan	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
227397403Sobrien	    if (vnode_vtype(vp) == VDIR)
227497403Sobrien		nd->nd_repstat = NFSERR_ISDIR;
227597403Sobrien	    else
227697403Sobrien		nd->nd_repstat = NFSERR_INVAL;
227797403Sobrien	}
227897403Sobrien	if (!nd->nd_repstat) {
2279132720Skan	    if (lflags & NFSLCK_WRITE) {
2280132720Skan		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
2281132720Skan		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2282132720Skan		    NFSACCCHK_VPISLOCKED, NULL);
2283132720Skan	    } else {
2284132720Skan		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
228597403Sobrien		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
228697403Sobrien		    NFSACCCHK_VPISLOCKED, NULL);
228797403Sobrien		if (nd->nd_repstat)
228897403Sobrien		    nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
228997403Sobrien			nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
229097403Sobrien			NFSACCCHK_VPISLOCKED, NULL);
229197403Sobrien	    }
2292132720Skan	}
2293132720Skan
2294132720Skan	/*
2295132720Skan	 * We call nfsrv_lockctrl() even if nd_repstat set, so that the
2296132720Skan	 * seqid# gets updated. nfsrv_lockctrl() will return the value
2297132720Skan	 * of nd_repstat, if it gets that far.
229897403Sobrien	 */
229997403Sobrien	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
230097403Sobrien		&stateid, exp, nd, p);
230197403Sobrien	if (lop)
230297403Sobrien		FREE((caddr_t)lop, M_NFSDLOCK);
230397403Sobrien	if (stp)
2304132720Skan		FREE((caddr_t)stp, M_NFSDSTATE);
2305132720Skan	if (!nd->nd_repstat) {
2306132720Skan		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2307132720Skan		*tl++ = txdr_unsigned(stateid.seqid);
2308132720Skan		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2309132720Skan	} else if (nd->nd_repstat == NFSERR_DENIED) {
231097403Sobrien		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
231197403Sobrien		txdr_hyper(cf.cl_first, tl);
231297403Sobrien		tl += 2;
231397403Sobrien		if (cf.cl_end == NFS64BITSSET)
231497403Sobrien			len = NFS64BITSSET;
231597403Sobrien		else
2316132720Skan			len = cf.cl_end - cf.cl_first;
2317132720Skan		txdr_hyper(len, tl);
2318132720Skan		tl += 2;
2319132720Skan		if (cf.cl_flags == NFSLCK_WRITE)
2320132720Skan			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
2321132720Skan		else
232297403Sobrien			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
232397403Sobrien		*tl++ = stateid.other[0];
232497403Sobrien		*tl = stateid.other[1];
232597403Sobrien		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
2326169691Skan	}
232797403Sobrien	vput(vp);
232897403Sobrien	NFSEXITCODE2(0, nd);
2329132720Skan	return (0);
2330132720Skannfsmout:
2331132720Skan	vput(vp);
2332132720Skan	if (stp)
2333132720Skan		free((caddr_t)stp, M_NFSDSTATE);
2334132720Skan	NFSEXITCODE2(error, nd);
233597403Sobrien	return (error);
233697403Sobrien}
233797403Sobrien
233897403Sobrien/*
233997403Sobrien * nfsv4 lock test service
234097403Sobrien */
2341132720SkanAPPLESTATIC int
2342132720Skannfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram,
2343132720Skan    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2344132720Skan{
2345132720Skan	u_int32_t *tl;
2346132720Skan	int i;
234797403Sobrien	struct nfsstate *stp = NULL;
234897403Sobrien	struct nfslock lo, *lop = &lo;
234997403Sobrien	struct nfslockconflict cf;
235097403Sobrien	int error = 0;
235197403Sobrien	nfsv4stateid_t stateid;
235297403Sobrien	nfsquad_t clientid;
2353132720Skan	u_int64_t len;
2354132720Skan
2355132720Skan	NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
2356132720Skan	i = fxdr_unsigned(int, *(tl + 7));
2357132720Skan	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2358132720Skan		nd->nd_repstat = NFSERR_BADXDR;
235997403Sobrien		goto nfsmout;
236097403Sobrien	}
236197403Sobrien	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
236297403Sobrien	    M_NFSDSTATE, M_WAITOK);
236397403Sobrien	stp->ls_ownerlen = i;
236497403Sobrien	stp->ls_op = NULL;
2365132720Skan	stp->ls_flags = NFSLCK_TEST;
2366132720Skan	stp->ls_uid = nd->nd_cred->cr_uid;
2367132720Skan	i = fxdr_unsigned(int, *tl++);
2368132720Skan	switch (i) {
2369132720Skan	case NFSV4LOCKT_READW:
2370132720Skan		stp->ls_flags |= NFSLCK_BLOCKING;
2371132720Skan	case NFSV4LOCKT_READ:
237297403Sobrien		lo.lo_flags = NFSLCK_READ;
237397403Sobrien		break;
237497403Sobrien	case NFSV4LOCKT_WRITEW:
237597403Sobrien		stp->ls_flags |= NFSLCK_BLOCKING;
237697403Sobrien	case NFSV4LOCKT_WRITE:
237797403Sobrien		lo.lo_flags = NFSLCK_WRITE;
2378132720Skan		break;
2379132720Skan	default:
2380132720Skan		nd->nd_repstat = NFSERR_BADXDR;
2381132720Skan		goto nfsmout;
2382132720Skan	};
2383132720Skan	lo.lo_first = fxdr_hyper(tl);
2384132720Skan	tl += 2;
2385132720Skan	len = fxdr_hyper(tl);
2386132720Skan	if (len == NFS64BITSSET) {
2387132720Skan		lo.lo_end = NFS64BITSSET;
2388132720Skan	} else {
238997403Sobrien		lo.lo_end = lo.lo_first + len;
239097403Sobrien		if (lo.lo_end <= lo.lo_first)
239197403Sobrien			nd->nd_repstat = NFSERR_INVAL;
239297403Sobrien	}
239397403Sobrien	tl += 2;
2394169691Skan	clientid.lval[0] = *tl++;
2395169691Skan	clientid.lval[1] = *tl;
2396169691Skan	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2397169691Skan		if ((nd->nd_flag & ND_NFSV41) != 0)
2398132720Skan			clientid.qval = nd->nd_clientid.qval;
2399132720Skan		else if (nd->nd_clientid.qval != clientid.qval)
2400132720Skan			printf("EEK5 multiple clids\n");
2401132720Skan	} else {
2402132720Skan		if ((nd->nd_flag & ND_NFSV41) != 0)
2403132720Skan			printf("EEK! no clientid from session\n");
2404132720Skan		nd->nd_flag |= ND_IMPLIEDCLID;
2405132720Skan		nd->nd_clientid.qval = clientid.qval;
2406132720Skan	}
240797403Sobrien	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2408169691Skan	if (error)
240997403Sobrien		goto nfsmout;
2410169691Skan	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2411169691Skan	    if (vnode_vtype(vp) == VDIR)
2412169691Skan		nd->nd_repstat = NFSERR_ISDIR;
2413169691Skan	    else
2414169691Skan		nd->nd_repstat = NFSERR_INVAL;
2415169691Skan	}
241697403Sobrien	if (!nd->nd_repstat)
2417132720Skan	  nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
2418132720Skan	    &stateid, exp, nd, p);
2419132720Skan	if (nd->nd_repstat) {
2420132720Skan	    if (nd->nd_repstat == NFSERR_DENIED) {
2421132720Skan		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2422132720Skan		txdr_hyper(cf.cl_first, tl);
2423132720Skan		tl += 2;
2424132720Skan		if (cf.cl_end == NFS64BITSSET)
2425132720Skan			len = NFS64BITSSET;
2426132720Skan		else
2427132720Skan			len = cf.cl_end - cf.cl_first;
2428132720Skan		txdr_hyper(len, tl);
2429132720Skan		tl += 2;
243097403Sobrien		if (cf.cl_flags == NFSLCK_WRITE)
2431169691Skan			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
243297403Sobrien		else
243397403Sobrien			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
243497403Sobrien		*tl++ = stp->ls_stateid.other[0];
2435132720Skan		*tl = stp->ls_stateid.other[1];
2436132720Skan		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
2437132720Skan	    }
2438132720Skan	}
2439132720Skan	vput(vp);
2440132720Skan	if (stp)
2441132720Skan		FREE((caddr_t)stp, M_NFSDSTATE);
2442132720Skan	NFSEXITCODE2(0, nd);
2443132720Skan	return (0);
2444132720Skannfsmout:
2445132720Skan	vput(vp);
2446132720Skan	if (stp)
244797403Sobrien		free((caddr_t)stp, M_NFSDSTATE);
2448169691Skan	NFSEXITCODE2(error, nd);
244997403Sobrien	return (error);
2450169691Skan}
2451169691Skan
245297403Sobrien/*
2453169691Skan * nfsv4 unlock service
2454169691Skan */
2455169691SkanAPPLESTATIC int
2456169691Skannfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram,
2457169691Skan    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2458169691Skan{
2459169691Skan	u_int32_t *tl;
2460169691Skan	int i;
2461169691Skan	struct nfsstate *stp;
2462169691Skan	struct nfslock *lop;
2463169691Skan	int error = 0;
2464169691Skan	nfsv4stateid_t stateid;
2465169691Skan	nfsquad_t clientid;
2466169691Skan	u_int64_t len;
2467132720Skan
2468	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED + NFSX_STATEID);
2469	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate),
2470	    M_NFSDSTATE, M_WAITOK);
2471	MALLOC(lop, struct nfslock *, sizeof (struct nfslock),
2472	    M_NFSDLOCK, M_WAITOK);
2473	stp->ls_flags = NFSLCK_UNLOCK;
2474	lop->lo_flags = NFSLCK_UNLOCK;
2475	stp->ls_op = nd->nd_rp;
2476	i = fxdr_unsigned(int, *tl++);
2477	switch (i) {
2478	case NFSV4LOCKT_READW:
2479		stp->ls_flags |= NFSLCK_BLOCKING;
2480	case NFSV4LOCKT_READ:
2481		break;
2482	case NFSV4LOCKT_WRITEW:
2483		stp->ls_flags |= NFSLCK_BLOCKING;
2484	case NFSV4LOCKT_WRITE:
2485		break;
2486	default:
2487		nd->nd_repstat = NFSERR_BADXDR;
2488		free(stp, M_NFSDSTATE);
2489		free(lop, M_NFSDLOCK);
2490		goto nfsmout;
2491	};
2492	stp->ls_ownerlen = 0;
2493	stp->ls_uid = nd->nd_cred->cr_uid;
2494	stp->ls_seq = fxdr_unsigned(int, *tl++);
2495	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2496	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2497	    NFSX_STATEIDOTHER);
2498	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2499	lop->lo_first = fxdr_hyper(tl);
2500	tl += 2;
2501	len = fxdr_hyper(tl);
2502	if (len == NFS64BITSSET) {
2503		lop->lo_end = NFS64BITSSET;
2504	} else {
2505		lop->lo_end = lop->lo_first + len;
2506		if (lop->lo_end <= lop->lo_first)
2507			nd->nd_repstat = NFSERR_INVAL;
2508	}
2509	clientid.lval[0] = stp->ls_stateid.other[0];
2510	clientid.lval[1] = stp->ls_stateid.other[1];
2511	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2512		if ((nd->nd_flag & ND_NFSV41) != 0)
2513			clientid.qval = nd->nd_clientid.qval;
2514		else if (nd->nd_clientid.qval != clientid.qval)
2515			printf("EEK6 multiple clids\n");
2516	} else {
2517		if ((nd->nd_flag & ND_NFSV41) != 0)
2518			printf("EEK! no clientid from session\n");
2519		nd->nd_flag |= ND_IMPLIEDCLID;
2520		nd->nd_clientid.qval = clientid.qval;
2521	}
2522	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2523	    if (vnode_vtype(vp) == VDIR)
2524		nd->nd_repstat = NFSERR_ISDIR;
2525	    else
2526		nd->nd_repstat = NFSERR_INVAL;
2527	}
2528	/*
2529	 * Call nfsrv_lockctrl() even if nd_repstat is set, so that the
2530	 * seqid# gets incremented. nfsrv_lockctrl() will return the
2531	 * value of nd_repstat, if it gets that far.
2532	 */
2533	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
2534	    &stateid, exp, nd, p);
2535	if (stp)
2536		FREE((caddr_t)stp, M_NFSDSTATE);
2537	if (lop)
2538		free((caddr_t)lop, M_NFSDLOCK);
2539	if (!nd->nd_repstat) {
2540		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2541		*tl++ = txdr_unsigned(stateid.seqid);
2542		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2543	}
2544nfsmout:
2545	vput(vp);
2546	NFSEXITCODE2(error, nd);
2547	return (error);
2548}
2549
2550/*
2551 * nfsv4 open service
2552 */
2553APPLESTATIC int
2554nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
2555    vnode_t dp, vnode_t *vpp, __unused fhandle_t *fhp, NFSPROC_T *p,
2556    struct nfsexstuff *exp)
2557{
2558	u_int32_t *tl;
2559	int i, retext;
2560	struct nfsstate *stp = NULL;
2561	int error = 0, create, claim, exclusive_flag = 0;
2562	u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask;
2563	int how = NFSCREATE_UNCHECKED;
2564	int32_t cverf[2], tverf[2] = { 0, 0 };
2565	vnode_t vp = NULL, dirp = NULL;
2566	struct nfsvattr nva, dirfor, diraft;
2567	struct nameidata named;
2568	nfsv4stateid_t stateid, delegstateid;
2569	nfsattrbit_t attrbits;
2570	nfsquad_t clientid;
2571	char *bufp = NULL;
2572	u_long *hashp;
2573	NFSACL_T *aclp = NULL;
2574
2575#ifdef NFS4_ACL_EXTATTR_NAME
2576	aclp = acl_alloc(M_WAITOK);
2577	aclp->acl_cnt = 0;
2578#endif
2579	NFSZERO_ATTRBIT(&attrbits);
2580	named.ni_startdir = NULL;
2581	named.ni_cnd.cn_nameiop = 0;
2582	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
2583	i = fxdr_unsigned(int, *(tl + 5));
2584	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2585		nd->nd_repstat = NFSERR_BADXDR;
2586		goto nfsmout;
2587	}
2588	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2589	    M_NFSDSTATE, M_WAITOK);
2590	stp->ls_ownerlen = i;
2591	stp->ls_op = nd->nd_rp;
2592	stp->ls_flags = NFSLCK_OPEN;
2593	stp->ls_uid = nd->nd_cred->cr_uid;
2594	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
2595	i = fxdr_unsigned(int, *tl++);
2596	retext = 0;
2597	if ((i & (NFSV4OPEN_WANTDELEGMASK | NFSV4OPEN_WANTSIGNALDELEG |
2598	    NFSV4OPEN_WANTPUSHDELEG)) != 0 && (nd->nd_flag & ND_NFSV41) != 0) {
2599		retext = 1;
2600		/* For now, ignore these. */
2601		i &= ~(NFSV4OPEN_WANTPUSHDELEG | NFSV4OPEN_WANTSIGNALDELEG);
2602		switch (i & NFSV4OPEN_WANTDELEGMASK) {
2603		case NFSV4OPEN_WANTANYDELEG:
2604			stp->ls_flags |= (NFSLCK_WANTRDELEG |
2605			    NFSLCK_WANTWDELEG);
2606			i &= ~NFSV4OPEN_WANTDELEGMASK;
2607			break;
2608		case NFSV4OPEN_WANTREADDELEG:
2609			stp->ls_flags |= NFSLCK_WANTRDELEG;
2610			i &= ~NFSV4OPEN_WANTDELEGMASK;
2611			break;
2612		case NFSV4OPEN_WANTWRITEDELEG:
2613			stp->ls_flags |= NFSLCK_WANTWDELEG;
2614			i &= ~NFSV4OPEN_WANTDELEGMASK;
2615			break;
2616		case NFSV4OPEN_WANTNODELEG:
2617			stp->ls_flags |= NFSLCK_WANTNODELEG;
2618			i &= ~NFSV4OPEN_WANTDELEGMASK;
2619			break;
2620		case NFSV4OPEN_WANTCANCEL:
2621			printf("NFSv4: ignore Open WantCancel\n");
2622			i &= ~NFSV4OPEN_WANTDELEGMASK;
2623			break;
2624		default:
2625			/* nd_repstat will be set to NFSERR_INVAL below. */
2626			break;
2627		};
2628	}
2629	switch (i) {
2630	case NFSV4OPEN_ACCESSREAD:
2631		stp->ls_flags |= NFSLCK_READACCESS;
2632		break;
2633	case NFSV4OPEN_ACCESSWRITE:
2634		stp->ls_flags |= NFSLCK_WRITEACCESS;
2635		break;
2636	case NFSV4OPEN_ACCESSBOTH:
2637		stp->ls_flags |= (NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
2638		break;
2639	default:
2640		nd->nd_repstat = NFSERR_INVAL;
2641	};
2642	i = fxdr_unsigned(int, *tl++);
2643	switch (i) {
2644	case NFSV4OPEN_DENYNONE:
2645		break;
2646	case NFSV4OPEN_DENYREAD:
2647		stp->ls_flags |= NFSLCK_READDENY;
2648		break;
2649	case NFSV4OPEN_DENYWRITE:
2650		stp->ls_flags |= NFSLCK_WRITEDENY;
2651		break;
2652	case NFSV4OPEN_DENYBOTH:
2653		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
2654		break;
2655	default:
2656		nd->nd_repstat = NFSERR_INVAL;
2657	};
2658	clientid.lval[0] = *tl++;
2659	clientid.lval[1] = *tl;
2660	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2661		if ((nd->nd_flag & ND_NFSV41) != 0)
2662			clientid.qval = nd->nd_clientid.qval;
2663		else if (nd->nd_clientid.qval != clientid.qval)
2664			printf("EEK7 multiple clids\n");
2665	} else {
2666		if ((nd->nd_flag & ND_NFSV41) != 0)
2667			printf("EEK! no clientid from session\n");
2668		nd->nd_flag |= ND_IMPLIEDCLID;
2669		nd->nd_clientid.qval = clientid.qval;
2670	}
2671	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2672	if (error)
2673		goto nfsmout;
2674	NFSVNO_ATTRINIT(&nva);
2675	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2676	create = fxdr_unsigned(int, *tl);
2677	if (!nd->nd_repstat)
2678		nd->nd_repstat = nfsvno_getattr(dp, &dirfor, nd->nd_cred, p, 0);
2679	if (create == NFSV4OPEN_CREATE) {
2680		nva.na_type = VREG;
2681		nva.na_mode = 0;
2682		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2683		how = fxdr_unsigned(int, *tl);
2684		switch (how) {
2685		case NFSCREATE_UNCHECKED:
2686		case NFSCREATE_GUARDED:
2687			error = nfsv4_sattr(nd, &nva, &attrbits, aclp, p);
2688			if (error)
2689				goto nfsmout;
2690			/*
2691			 * If the na_gid being set is the same as that of
2692			 * the directory it is going in, clear it, since
2693			 * that is what will be set by default. This allows
2694			 * a user that isn't in that group to do the create.
2695			 */
2696			if (!nd->nd_repstat && NFSVNO_ISSETGID(&nva) &&
2697			    nva.na_gid == dirfor.na_gid)
2698				NFSVNO_UNSET(&nva, gid);
2699			if (!nd->nd_repstat)
2700				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
2701			break;
2702		case NFSCREATE_EXCLUSIVE:
2703			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2704			cverf[0] = *tl++;
2705			cverf[1] = *tl;
2706			break;
2707		case NFSCREATE_EXCLUSIVE41:
2708			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2709			cverf[0] = *tl++;
2710			cverf[1] = *tl;
2711			error = nfsv4_sattr(nd, &nva, &attrbits, aclp, p);
2712			if (error != 0)
2713				goto nfsmout;
2714			if (NFSISSET_ATTRBIT(&attrbits,
2715			    NFSATTRBIT_TIMEACCESSSET))
2716				nd->nd_repstat = NFSERR_INVAL;
2717			/*
2718			 * If the na_gid being set is the same as that of
2719			 * the directory it is going in, clear it, since
2720			 * that is what will be set by default. This allows
2721			 * a user that isn't in that group to do the create.
2722			 */
2723			if (nd->nd_repstat == 0 && NFSVNO_ISSETGID(&nva) &&
2724			    nva.na_gid == dirfor.na_gid)
2725				NFSVNO_UNSET(&nva, gid);
2726			if (nd->nd_repstat == 0)
2727				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
2728			break;
2729		default:
2730			nd->nd_repstat = NFSERR_BADXDR;
2731			goto nfsmout;
2732		};
2733	} else if (create != NFSV4OPEN_NOCREATE) {
2734		nd->nd_repstat = NFSERR_BADXDR;
2735		goto nfsmout;
2736	}
2737
2738	/*
2739	 * Now, handle the claim, which usually includes looking up a
2740	 * name in the directory referenced by dp. The exception is
2741	 * NFSV4OPEN_CLAIMPREVIOUS.
2742	 */
2743	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2744	claim = fxdr_unsigned(int, *tl);
2745	if (claim == NFSV4OPEN_CLAIMDELEGATECUR) {
2746		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
2747		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2748		NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
2749		stp->ls_flags |= NFSLCK_DELEGCUR;
2750	} else if (claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
2751		stp->ls_flags |= NFSLCK_DELEGPREV;
2752	}
2753	if (claim == NFSV4OPEN_CLAIMNULL || claim == NFSV4OPEN_CLAIMDELEGATECUR
2754	    || claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
2755		if (!nd->nd_repstat && create == NFSV4OPEN_CREATE &&
2756		    claim != NFSV4OPEN_CLAIMNULL)
2757			nd->nd_repstat = NFSERR_INVAL;
2758		if (nd->nd_repstat) {
2759			nd->nd_repstat = nfsrv_opencheck(clientid,
2760			    &stateid, stp, NULL, nd, p, nd->nd_repstat);
2761			goto nfsmout;
2762		}
2763		if (create == NFSV4OPEN_CREATE)
2764		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
2765			LOCKPARENT | LOCKLEAF | SAVESTART | NOCACHE);
2766		else
2767		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
2768			LOCKLEAF | SAVESTART);
2769		nfsvno_setpathbuf(&named, &bufp, &hashp);
2770		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
2771		if (error) {
2772			vrele(dp);
2773#ifdef NFS4_ACL_EXTATTR_NAME
2774			acl_free(aclp);
2775#endif
2776			FREE((caddr_t)stp, M_NFSDSTATE);
2777			nfsvno_relpathbuf(&named);
2778			NFSEXITCODE2(error, nd);
2779			return (error);
2780		}
2781		if (!nd->nd_repstat) {
2782			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp,
2783			    p, &dirp);
2784		} else {
2785			vrele(dp);
2786			nfsvno_relpathbuf(&named);
2787		}
2788		if (create == NFSV4OPEN_CREATE) {
2789		    switch (how) {
2790		    case NFSCREATE_UNCHECKED:
2791			if (named.ni_vp) {
2792				/*
2793				 * Clear the setable attribute bits, except
2794				 * for Size, if it is being truncated.
2795				 */
2796				NFSZERO_ATTRBIT(&attrbits);
2797				if (NFSVNO_ISSETSIZE(&nva))
2798					NFSSETBIT_ATTRBIT(&attrbits,
2799					    NFSATTRBIT_SIZE);
2800			}
2801			break;
2802		    case NFSCREATE_GUARDED:
2803			if (named.ni_vp && !nd->nd_repstat)
2804				nd->nd_repstat = EEXIST;
2805			break;
2806		    case NFSCREATE_EXCLUSIVE:
2807			exclusive_flag = 1;
2808			if (!named.ni_vp)
2809				nva.na_mode = 0;
2810			break;
2811		    case NFSCREATE_EXCLUSIVE41:
2812			exclusive_flag = 1;
2813			break;
2814		    };
2815		}
2816		nfsvno_open(nd, &named, clientid, &stateid, stp,
2817		    &exclusive_flag, &nva, cverf, create, aclp, &attrbits,
2818		    nd->nd_cred, p, exp, &vp);
2819	} else if (claim == NFSV4OPEN_CLAIMPREVIOUS || claim ==
2820	    NFSV4OPEN_CLAIMFH) {
2821		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
2822			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2823			i = fxdr_unsigned(int, *tl);
2824			switch (i) {
2825			case NFSV4OPEN_DELEGATEREAD:
2826				stp->ls_flags |= NFSLCK_DELEGREAD;
2827				break;
2828			case NFSV4OPEN_DELEGATEWRITE:
2829				stp->ls_flags |= NFSLCK_DELEGWRITE;
2830			case NFSV4OPEN_DELEGATENONE:
2831				break;
2832			default:
2833				nd->nd_repstat = NFSERR_BADXDR;
2834				goto nfsmout;
2835			};
2836			stp->ls_flags |= NFSLCK_RECLAIM;
2837		} else {
2838			/* CLAIM_NULL_FH */
2839			if (nd->nd_repstat == 0 && create == NFSV4OPEN_CREATE)
2840				nd->nd_repstat = NFSERR_INVAL;
2841		}
2842		vp = dp;
2843		NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
2844		if ((vp->v_iflag & VI_DOOMED) == 0)
2845			nd->nd_repstat = nfsrv_opencheck(clientid, &stateid,
2846			    stp, vp, nd, p, nd->nd_repstat);
2847		else
2848			nd->nd_repstat = NFSERR_PERM;
2849	} else {
2850		nd->nd_repstat = NFSERR_BADXDR;
2851		goto nfsmout;
2852	}
2853
2854	/*
2855	 * Do basic access checking.
2856	 */
2857	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2858		/*
2859		 * The IETF working group decided that this is the correct
2860		 * error return for all non-regular files.
2861		 */
2862		nd->nd_repstat = NFSERR_SYMLINK;
2863	}
2864	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_WRITEACCESS))
2865	    nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred,
2866	        exp, p, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
2867	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_READACCESS)) {
2868	    nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred,
2869	        exp, p, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
2870	    if (nd->nd_repstat)
2871		nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
2872		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2873		    NFSACCCHK_VPISLOCKED, NULL);
2874	}
2875
2876	if (!nd->nd_repstat) {
2877		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
2878		if (!nd->nd_repstat) {
2879			tverf[0] = nva.na_atime.tv_sec;
2880			tverf[1] = nva.na_atime.tv_nsec;
2881		}
2882	}
2883	if (!nd->nd_repstat && exclusive_flag && (cverf[0] != tverf[0] ||
2884	    cverf[1] != tverf[1]))
2885		nd->nd_repstat = EEXIST;
2886	/*
2887	 * Do the open locking/delegation stuff.
2888	 */
2889	if (!nd->nd_repstat)
2890	    nd->nd_repstat = nfsrv_openctrl(nd, vp, &stp, clientid, &stateid,
2891		&delegstateid, &rflags, exp, p, nva.na_filerev);
2892
2893	/*
2894	 * vp must be unlocked before the call to nfsvno_getattr(dirp,...)
2895	 * below, to avoid a deadlock with the lookup in nfsvno_namei() above.
2896	 * (ie: Leave the NFSVOPUNLOCK() about here.)
2897	 */
2898	if (vp)
2899		NFSVOPUNLOCK(vp, 0);
2900	if (stp)
2901		FREE((caddr_t)stp, M_NFSDSTATE);
2902	if (!nd->nd_repstat && dirp)
2903		nd->nd_repstat = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p,
2904		    0);
2905	if (!nd->nd_repstat) {
2906		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
2907		*tl++ = txdr_unsigned(stateid.seqid);
2908		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2909		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2910		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
2911			*tl++ = newnfs_true;
2912			*tl++ = 0;
2913			*tl++ = 0;
2914			*tl++ = 0;
2915			*tl++ = 0;
2916		} else {
2917			*tl++ = newnfs_false;	/* Since dirp is not locked */
2918			txdr_hyper(dirfor.na_filerev, tl);
2919			tl += 2;
2920			txdr_hyper(diraft.na_filerev, tl);
2921			tl += 2;
2922		}
2923		*tl = txdr_unsigned(rflags & NFSV4OPEN_RFLAGS);
2924		(void) nfsrv_putattrbit(nd, &attrbits);
2925		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2926		if (rflags & NFSV4OPEN_READDELEGATE)
2927			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEREAD);
2928		else if (rflags & NFSV4OPEN_WRITEDELEGATE)
2929			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEWRITE);
2930		else if (retext != 0) {
2931			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONEEXT);
2932			if ((rflags & NFSV4OPEN_WDCONTENTION) != 0) {
2933				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2934				*tl++ = txdr_unsigned(NFSV4OPEN_CONTENTION);
2935				*tl = newnfs_false;
2936			} else if ((rflags & NFSV4OPEN_WDRESOURCE) != 0) {
2937				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2938				*tl++ = txdr_unsigned(NFSV4OPEN_RESOURCE);
2939				*tl = newnfs_false;
2940			} else {
2941				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2942				*tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
2943			}
2944		} else
2945			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONE);
2946		if (rflags & (NFSV4OPEN_READDELEGATE|NFSV4OPEN_WRITEDELEGATE)) {
2947			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID+NFSX_UNSIGNED);
2948			*tl++ = txdr_unsigned(delegstateid.seqid);
2949			NFSBCOPY((caddr_t)delegstateid.other, (caddr_t)tl,
2950			    NFSX_STATEIDOTHER);
2951			tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2952			if (rflags & NFSV4OPEN_RECALL)
2953				*tl = newnfs_true;
2954			else
2955				*tl = newnfs_false;
2956			if (rflags & NFSV4OPEN_WRITEDELEGATE) {
2957				NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
2958				*tl++ = txdr_unsigned(NFSV4OPEN_LIMITSIZE);
2959				txdr_hyper(nva.na_size, tl);
2960			}
2961			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
2962			*tl++ = txdr_unsigned(NFSV4ACE_ALLOWEDTYPE);
2963			*tl++ = txdr_unsigned(0x0);
2964			acemask = NFSV4ACE_ALLFILESMASK;
2965			if (nva.na_mode & S_IRUSR)
2966			    acemask |= NFSV4ACE_READMASK;
2967			if (nva.na_mode & S_IWUSR)
2968			    acemask |= NFSV4ACE_WRITEMASK;
2969			if (nva.na_mode & S_IXUSR)
2970			    acemask |= NFSV4ACE_EXECUTEMASK;
2971			*tl = txdr_unsigned(acemask);
2972			(void) nfsm_strtom(nd, "OWNER@", 6);
2973		}
2974		*vpp = vp;
2975	} else if (vp) {
2976		vrele(vp);
2977	}
2978	if (dirp)
2979		vrele(dirp);
2980#ifdef NFS4_ACL_EXTATTR_NAME
2981	acl_free(aclp);
2982#endif
2983	NFSEXITCODE2(0, nd);
2984	return (0);
2985nfsmout:
2986	vrele(dp);
2987#ifdef NFS4_ACL_EXTATTR_NAME
2988	acl_free(aclp);
2989#endif
2990	if (stp)
2991		FREE((caddr_t)stp, M_NFSDSTATE);
2992	NFSEXITCODE2(error, nd);
2993	return (error);
2994}
2995
2996/*
2997 * nfsv4 close service
2998 */
2999APPLESTATIC int
3000nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram,
3001    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3002{
3003	u_int32_t *tl;
3004	struct nfsstate st, *stp = &st;
3005	int error = 0;
3006	nfsv4stateid_t stateid;
3007	nfsquad_t clientid;
3008
3009	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
3010	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
3011	stp->ls_ownerlen = 0;
3012	stp->ls_op = nd->nd_rp;
3013	stp->ls_uid = nd->nd_cred->cr_uid;
3014	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3015	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
3016	    NFSX_STATEIDOTHER);
3017	stp->ls_flags = NFSLCK_CLOSE;
3018	clientid.lval[0] = stp->ls_stateid.other[0];
3019	clientid.lval[1] = stp->ls_stateid.other[1];
3020	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3021		if ((nd->nd_flag & ND_NFSV41) != 0)
3022			clientid.qval = nd->nd_clientid.qval;
3023		else if (nd->nd_clientid.qval != clientid.qval)
3024			printf("EEK8 multiple clids\n");
3025	} else {
3026		if ((nd->nd_flag & ND_NFSV41) != 0)
3027			printf("EEK! no clientid from session\n");
3028		nd->nd_flag |= ND_IMPLIEDCLID;
3029		nd->nd_clientid.qval = clientid.qval;
3030	}
3031	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
3032	vput(vp);
3033	if (!nd->nd_repstat) {
3034		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
3035		*tl++ = txdr_unsigned(stateid.seqid);
3036		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
3037	}
3038	NFSEXITCODE2(0, nd);
3039	return (0);
3040nfsmout:
3041	vput(vp);
3042	NFSEXITCODE2(error, nd);
3043	return (error);
3044}
3045
3046/*
3047 * nfsv4 delegpurge service
3048 */
3049APPLESTATIC int
3050nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram,
3051    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
3052{
3053	u_int32_t *tl;
3054	int error = 0;
3055	nfsquad_t clientid;
3056
3057	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3058		nd->nd_repstat = NFSERR_WRONGSEC;
3059		goto nfsmout;
3060	}
3061	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3062	clientid.lval[0] = *tl++;
3063	clientid.lval[1] = *tl;
3064	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3065		if ((nd->nd_flag & ND_NFSV41) != 0)
3066			clientid.qval = nd->nd_clientid.qval;
3067		else if (nd->nd_clientid.qval != clientid.qval)
3068			printf("EEK9 multiple clids\n");
3069	} else {
3070		if ((nd->nd_flag & ND_NFSV41) != 0)
3071			printf("EEK! no clientid from session\n");
3072		nd->nd_flag |= ND_IMPLIEDCLID;
3073		nd->nd_clientid.qval = clientid.qval;
3074	}
3075	nd->nd_repstat = nfsrv_delegupdate(nd, clientid, NULL, NULL,
3076	    NFSV4OP_DELEGPURGE, nd->nd_cred, p);
3077nfsmout:
3078	NFSEXITCODE2(error, nd);
3079	return (error);
3080}
3081
3082/*
3083 * nfsv4 delegreturn service
3084 */
3085APPLESTATIC int
3086nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram,
3087    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3088{
3089	u_int32_t *tl;
3090	int error = 0;
3091	nfsv4stateid_t stateid;
3092	nfsquad_t clientid;
3093
3094	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
3095	stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3096	NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, NFSX_STATEIDOTHER);
3097	clientid.lval[0] = stateid.other[0];
3098	clientid.lval[1] = stateid.other[1];
3099	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3100		if ((nd->nd_flag & ND_NFSV41) != 0)
3101			clientid.qval = nd->nd_clientid.qval;
3102		else if (nd->nd_clientid.qval != clientid.qval)
3103			printf("EEK10 multiple clids\n");
3104	} else {
3105		if ((nd->nd_flag & ND_NFSV41) != 0)
3106			printf("EEK! no clientid from session\n");
3107		nd->nd_flag |= ND_IMPLIEDCLID;
3108		nd->nd_clientid.qval = clientid.qval;
3109	}
3110	nd->nd_repstat = nfsrv_delegupdate(nd, clientid, &stateid, vp,
3111	    NFSV4OP_DELEGRETURN, nd->nd_cred, p);
3112nfsmout:
3113	vput(vp);
3114	NFSEXITCODE2(error, nd);
3115	return (error);
3116}
3117
3118/*
3119 * nfsv4 get file handle service
3120 */
3121APPLESTATIC int
3122nfsrvd_getfh(struct nfsrv_descript *nd, __unused int isdgram,
3123    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3124{
3125	fhandle_t fh;
3126
3127	nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
3128	vput(vp);
3129	if (!nd->nd_repstat)
3130		(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
3131	NFSEXITCODE2(0, nd);
3132	return (0);
3133}
3134
3135/*
3136 * nfsv4 open confirm service
3137 */
3138APPLESTATIC int
3139nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram,
3140    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3141{
3142	u_int32_t *tl;
3143	struct nfsstate st, *stp = &st;
3144	int error = 0;
3145	nfsv4stateid_t stateid;
3146	nfsquad_t clientid;
3147
3148	if ((nd->nd_flag & ND_NFSV41) != 0) {
3149		nd->nd_repstat = NFSERR_NOTSUPP;
3150		goto nfsmout;
3151	}
3152	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
3153	stp->ls_ownerlen = 0;
3154	stp->ls_op = nd->nd_rp;
3155	stp->ls_uid = nd->nd_cred->cr_uid;
3156	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3157	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
3158	    NFSX_STATEIDOTHER);
3159	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3160	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl);
3161	stp->ls_flags = NFSLCK_CONFIRM;
3162	clientid.lval[0] = stp->ls_stateid.other[0];
3163	clientid.lval[1] = stp->ls_stateid.other[1];
3164	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3165		if ((nd->nd_flag & ND_NFSV41) != 0)
3166			clientid.qval = nd->nd_clientid.qval;
3167		else if (nd->nd_clientid.qval != clientid.qval)
3168			printf("EEK11 multiple clids\n");
3169	} else {
3170		if ((nd->nd_flag & ND_NFSV41) != 0)
3171			printf("EEK! no clientid from session\n");
3172		nd->nd_flag |= ND_IMPLIEDCLID;
3173		nd->nd_clientid.qval = clientid.qval;
3174	}
3175	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
3176	if (!nd->nd_repstat) {
3177		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
3178		*tl++ = txdr_unsigned(stateid.seqid);
3179		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
3180	}
3181nfsmout:
3182	vput(vp);
3183	NFSEXITCODE2(error, nd);
3184	return (error);
3185}
3186
3187/*
3188 * nfsv4 open downgrade service
3189 */
3190APPLESTATIC int
3191nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram,
3192    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3193{
3194	u_int32_t *tl;
3195	int i;
3196	struct nfsstate st, *stp = &st;
3197	int error = 0;
3198	nfsv4stateid_t stateid;
3199	nfsquad_t clientid;
3200
3201	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
3202	stp->ls_ownerlen = 0;
3203	stp->ls_op = nd->nd_rp;
3204	stp->ls_uid = nd->nd_cred->cr_uid;
3205	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3206	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
3207	    NFSX_STATEIDOTHER);
3208	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3209	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
3210	i = fxdr_unsigned(int, *tl++);
3211	switch (i) {
3212	case NFSV4OPEN_ACCESSREAD:
3213		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_DOWNGRADE);
3214		break;
3215	case NFSV4OPEN_ACCESSWRITE:
3216		stp->ls_flags = (NFSLCK_WRITEACCESS | NFSLCK_DOWNGRADE);
3217		break;
3218	case NFSV4OPEN_ACCESSBOTH:
3219		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_WRITEACCESS |
3220		    NFSLCK_DOWNGRADE);
3221		break;
3222	default:
3223		nd->nd_repstat = NFSERR_BADXDR;
3224	};
3225	i = fxdr_unsigned(int, *tl);
3226	switch (i) {
3227	case NFSV4OPEN_DENYNONE:
3228		break;
3229	case NFSV4OPEN_DENYREAD:
3230		stp->ls_flags |= NFSLCK_READDENY;
3231		break;
3232	case NFSV4OPEN_DENYWRITE:
3233		stp->ls_flags |= NFSLCK_WRITEDENY;
3234		break;
3235	case NFSV4OPEN_DENYBOTH:
3236		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
3237		break;
3238	default:
3239		nd->nd_repstat = NFSERR_BADXDR;
3240	};
3241
3242	clientid.lval[0] = stp->ls_stateid.other[0];
3243	clientid.lval[1] = stp->ls_stateid.other[1];
3244	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3245		if ((nd->nd_flag & ND_NFSV41) != 0)
3246			clientid.qval = nd->nd_clientid.qval;
3247		else if (nd->nd_clientid.qval != clientid.qval)
3248			printf("EEK12 multiple clids\n");
3249	} else {
3250		if ((nd->nd_flag & ND_NFSV41) != 0)
3251			printf("EEK! no clientid from session\n");
3252		nd->nd_flag |= ND_IMPLIEDCLID;
3253		nd->nd_clientid.qval = clientid.qval;
3254	}
3255	if (!nd->nd_repstat)
3256		nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid,
3257		    nd, p);
3258	if (!nd->nd_repstat) {
3259		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
3260		*tl++ = txdr_unsigned(stateid.seqid);
3261		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
3262	}
3263nfsmout:
3264	vput(vp);
3265	NFSEXITCODE2(error, nd);
3266	return (error);
3267}
3268
3269/*
3270 * nfsv4 renew lease service
3271 */
3272APPLESTATIC int
3273nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram,
3274    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3275{
3276	u_int32_t *tl;
3277	int error = 0;
3278	nfsquad_t clientid;
3279
3280	if ((nd->nd_flag & ND_NFSV41) != 0) {
3281		nd->nd_repstat = NFSERR_NOTSUPP;
3282		goto nfsmout;
3283	}
3284	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3285		nd->nd_repstat = NFSERR_WRONGSEC;
3286		goto nfsmout;
3287	}
3288	NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
3289	clientid.lval[0] = *tl++;
3290	clientid.lval[1] = *tl;
3291	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3292		if ((nd->nd_flag & ND_NFSV41) != 0)
3293			clientid.qval = nd->nd_clientid.qval;
3294		else if (nd->nd_clientid.qval != clientid.qval)
3295			printf("EEK13 multiple clids\n");
3296	} else {
3297		if ((nd->nd_flag & ND_NFSV41) != 0)
3298			printf("EEK! no clientid from session\n");
3299		nd->nd_flag |= ND_IMPLIEDCLID;
3300		nd->nd_clientid.qval = clientid.qval;
3301	}
3302	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW),
3303	    NULL, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p);
3304nfsmout:
3305	NFSEXITCODE2(error, nd);
3306	return (error);
3307}
3308
3309/*
3310 * nfsv4 security info service
3311 */
3312APPLESTATIC int
3313nfsrvd_secinfo(struct nfsrv_descript *nd, int isdgram,
3314    vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
3315{
3316	u_int32_t *tl;
3317	int len;
3318	struct nameidata named;
3319	vnode_t dirp = NULL, vp;
3320	struct nfsrvfh fh;
3321	struct nfsexstuff retnes;
3322	u_int32_t *sizp;
3323	int error = 0, savflag, i;
3324	char *bufp;
3325	u_long *hashp;
3326
3327	/*
3328	 * All this just to get the export flags for the name.
3329	 */
3330	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
3331	    LOCKLEAF | SAVESTART);
3332	nfsvno_setpathbuf(&named, &bufp, &hashp);
3333	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
3334	if (error) {
3335		vput(dp);
3336		nfsvno_relpathbuf(&named);
3337		goto out;
3338	}
3339	if (!nd->nd_repstat) {
3340		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
3341	} else {
3342		vput(dp);
3343		nfsvno_relpathbuf(&named);
3344	}
3345	if (dirp)
3346		vrele(dirp);
3347	if (nd->nd_repstat)
3348		goto out;
3349	vrele(named.ni_startdir);
3350	nfsvno_relpathbuf(&named);
3351	fh.nfsrvfh_len = NFSX_MYFH;
3352	vp = named.ni_vp;
3353	nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
3354	vput(vp);
3355	savflag = nd->nd_flag;
3356	if (!nd->nd_repstat) {
3357		nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0, p);
3358		if (vp)
3359			vput(vp);
3360	}
3361	nd->nd_flag = savflag;
3362	if (nd->nd_repstat)
3363		goto out;
3364
3365	/*
3366	 * Finally have the export flags for name, so we can create
3367	 * the security info.
3368	 */
3369	len = 0;
3370	NFSM_BUILD(sizp, u_int32_t *, NFSX_UNSIGNED);
3371	for (i = 0; i < retnes.nes_numsecflavor; i++) {
3372		if (retnes.nes_secflavors[i] == AUTH_SYS) {
3373			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3374			*tl = txdr_unsigned(RPCAUTH_UNIX);
3375			len++;
3376		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) {
3377			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3378			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3379			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3380			    nfsgss_mechlist[KERBV_MECH].len);
3381			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3382			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3383			*tl = txdr_unsigned(RPCAUTHGSS_SVCNONE);
3384			len++;
3385		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) {
3386			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3387			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3388			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3389			    nfsgss_mechlist[KERBV_MECH].len);
3390			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3391			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3392			*tl = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
3393			len++;
3394		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) {
3395			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3396			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3397			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3398			    nfsgss_mechlist[KERBV_MECH].len);
3399			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3400			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3401			*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
3402			len++;
3403		}
3404	}
3405	*sizp = txdr_unsigned(len);
3406
3407out:
3408	NFSEXITCODE2(error, nd);
3409	return (error);
3410}
3411
3412/*
3413 * nfsv4 set client id service
3414 */
3415APPLESTATIC int
3416nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram,
3417    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3418{
3419	u_int32_t *tl;
3420	int i;
3421	int error = 0, idlen;
3422	struct nfsclient *clp = NULL;
3423	struct sockaddr_in *rad;
3424	u_char *verf, *ucp, *ucp2, addrbuf[24];
3425	nfsquad_t clientid, confirm;
3426
3427	if ((nd->nd_flag & ND_NFSV41) != 0) {
3428		nd->nd_repstat = NFSERR_NOTSUPP;
3429		goto nfsmout;
3430	}
3431	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3432		nd->nd_repstat = NFSERR_WRONGSEC;
3433		goto out;
3434	}
3435	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
3436	verf = (u_char *)tl;
3437	tl += (NFSX_VERF / NFSX_UNSIGNED);
3438	i = fxdr_unsigned(int, *tl);
3439	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
3440		nd->nd_repstat = NFSERR_BADXDR;
3441		goto nfsmout;
3442	}
3443	idlen = i;
3444	if (nd->nd_flag & ND_GSS)
3445		i += nd->nd_princlen;
3446	clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
3447	    M_ZERO);
3448	clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
3449	    nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
3450	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
3451	NFSSOCKADDRALLOC(clp->lc_req.nr_nam);
3452	NFSSOCKADDRSIZE(clp->lc_req.nr_nam, sizeof (struct sockaddr_in));
3453	clp->lc_req.nr_cred = NULL;
3454	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
3455	clp->lc_idlen = idlen;
3456	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
3457	if (error)
3458		goto nfsmout;
3459	if (nd->nd_flag & ND_GSS) {
3460		clp->lc_flags = LCL_GSS;
3461		if (nd->nd_flag & ND_GSSINTEGRITY)
3462			clp->lc_flags |= LCL_GSSINTEGRITY;
3463		else if (nd->nd_flag & ND_GSSPRIVACY)
3464			clp->lc_flags |= LCL_GSSPRIVACY;
3465	} else {
3466		clp->lc_flags = 0;
3467	}
3468	if ((nd->nd_flag & ND_GSS) && nd->nd_princlen > 0) {
3469		clp->lc_flags |= LCL_NAME;
3470		clp->lc_namelen = nd->nd_princlen;
3471		clp->lc_name = &clp->lc_id[idlen];
3472		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
3473	} else {
3474		clp->lc_uid = nd->nd_cred->cr_uid;
3475		clp->lc_gid = nd->nd_cred->cr_gid;
3476	}
3477	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3478	clp->lc_program = fxdr_unsigned(u_int32_t, *tl);
3479	error = nfsrv_getclientipaddr(nd, clp);
3480	if (error)
3481		goto nfsmout;
3482	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3483	clp->lc_callback = fxdr_unsigned(u_int32_t, *tl);
3484
3485	/*
3486	 * nfsrv_setclient() does the actual work of adding it to the
3487	 * client list. If there is no error, the structure has been
3488	 * linked into the client list and clp should no longer be used
3489	 * here. When an error is returned, it has not been linked in,
3490	 * so it should be free'd.
3491	 */
3492	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
3493	if (nd->nd_repstat == NFSERR_CLIDINUSE) {
3494		if (clp->lc_flags & LCL_TCPCALLBACK)
3495			(void) nfsm_strtom(nd, "tcp", 3);
3496		else
3497			(void) nfsm_strtom(nd, "udp", 3);
3498		rad = NFSSOCKADDR(clp->lc_req.nr_nam, struct sockaddr_in *);
3499		ucp = (u_char *)&rad->sin_addr.s_addr;
3500		ucp2 = (u_char *)&rad->sin_port;
3501		sprintf(addrbuf, "%d.%d.%d.%d.%d.%d", ucp[0] & 0xff,
3502		    ucp[1] & 0xff, ucp[2] & 0xff, ucp[3] & 0xff,
3503		    ucp2[0] & 0xff, ucp2[1] & 0xff);
3504		(void) nfsm_strtom(nd, addrbuf, strlen(addrbuf));
3505	}
3506	if (clp) {
3507		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3508		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3509		free(clp->lc_stateid, M_NFSDCLIENT);
3510		free(clp, M_NFSDCLIENT);
3511	}
3512	if (!nd->nd_repstat) {
3513		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_HYPER);
3514		*tl++ = clientid.lval[0];
3515		*tl++ = clientid.lval[1];
3516		*tl++ = confirm.lval[0];
3517		*tl = confirm.lval[1];
3518	}
3519
3520out:
3521	NFSEXITCODE2(0, nd);
3522	return (0);
3523nfsmout:
3524	if (clp) {
3525		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3526		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3527		free(clp->lc_stateid, M_NFSDCLIENT);
3528		free(clp, M_NFSDCLIENT);
3529	}
3530	NFSEXITCODE2(error, nd);
3531	return (error);
3532}
3533
3534/*
3535 * nfsv4 set client id confirm service
3536 */
3537APPLESTATIC int
3538nfsrvd_setclientidcfrm(struct nfsrv_descript *nd,
3539    __unused int isdgram, __unused vnode_t vp, NFSPROC_T *p,
3540    __unused struct nfsexstuff *exp)
3541{
3542	u_int32_t *tl;
3543	int error = 0;
3544	nfsquad_t clientid, confirm;
3545
3546	if ((nd->nd_flag & ND_NFSV41) != 0) {
3547		nd->nd_repstat = NFSERR_NOTSUPP;
3548		goto nfsmout;
3549	}
3550	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3551		nd->nd_repstat = NFSERR_WRONGSEC;
3552		goto nfsmout;
3553	}
3554	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER);
3555	clientid.lval[0] = *tl++;
3556	clientid.lval[1] = *tl++;
3557	confirm.lval[0] = *tl++;
3558	confirm.lval[1] = *tl;
3559
3560	/*
3561	 * nfsrv_getclient() searches the client list for a match and
3562	 * returns the appropriate NFSERR status.
3563	 */
3564	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_CONFIRM|CLOPS_RENEW),
3565	    NULL, NULL, confirm, 0, nd, p);
3566nfsmout:
3567	NFSEXITCODE2(error, nd);
3568	return (error);
3569}
3570
3571/*
3572 * nfsv4 verify service
3573 */
3574APPLESTATIC int
3575nfsrvd_verify(struct nfsrv_descript *nd, int isdgram,
3576    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3577{
3578	int error = 0, ret, fhsize = NFSX_MYFH;
3579	struct nfsvattr nva;
3580	struct statfs sf;
3581	struct nfsfsinfo fs;
3582	fhandle_t fh;
3583
3584	nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
3585	if (!nd->nd_repstat)
3586		nd->nd_repstat = nfsvno_statfs(vp, &sf);
3587	if (!nd->nd_repstat)
3588		nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
3589	if (!nd->nd_repstat) {
3590		nfsvno_getfs(&fs, isdgram);
3591		error = nfsv4_loadattr(nd, vp, &nva, NULL, &fh, fhsize, NULL,
3592		    &sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, p, nd->nd_cred);
3593		if (!error) {
3594			if (nd->nd_procnum == NFSV4OP_NVERIFY) {
3595				if (ret == 0)
3596					nd->nd_repstat = NFSERR_SAME;
3597				else if (ret != NFSERR_NOTSAME)
3598					nd->nd_repstat = ret;
3599			} else if (ret)
3600				nd->nd_repstat = ret;
3601		}
3602	}
3603	vput(vp);
3604	NFSEXITCODE2(error, nd);
3605	return (error);
3606}
3607
3608/*
3609 * nfs openattr rpc
3610 */
3611APPLESTATIC int
3612nfsrvd_openattr(struct nfsrv_descript *nd, __unused int isdgram,
3613    vnode_t dp, __unused vnode_t *vpp, __unused fhandle_t *fhp,
3614    __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
3615{
3616	u_int32_t *tl;
3617	int error = 0, createdir;
3618
3619	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3620	createdir = fxdr_unsigned(int, *tl);
3621	nd->nd_repstat = NFSERR_NOTSUPP;
3622nfsmout:
3623	vrele(dp);
3624	NFSEXITCODE2(error, nd);
3625	return (error);
3626}
3627
3628/*
3629 * nfsv4 release lock owner service
3630 */
3631APPLESTATIC int
3632nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram,
3633    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3634{
3635	u_int32_t *tl;
3636	struct nfsstate *stp = NULL;
3637	int error = 0, len;
3638	nfsquad_t clientid;
3639
3640	if ((nd->nd_flag & ND_NFSV41) != 0) {
3641		nd->nd_repstat = NFSERR_NOTSUPP;
3642		goto nfsmout;
3643	}
3644	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3645		nd->nd_repstat = NFSERR_WRONGSEC;
3646		goto nfsmout;
3647	}
3648	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3649	len = fxdr_unsigned(int, *(tl + 2));
3650	if (len <= 0 || len > NFSV4_OPAQUELIMIT) {
3651		nd->nd_repstat = NFSERR_BADXDR;
3652		goto nfsmout;
3653	}
3654	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + len,
3655	    M_NFSDSTATE, M_WAITOK);
3656	stp->ls_ownerlen = len;
3657	stp->ls_op = NULL;
3658	stp->ls_flags = NFSLCK_RELEASE;
3659	stp->ls_uid = nd->nd_cred->cr_uid;
3660	clientid.lval[0] = *tl++;
3661	clientid.lval[1] = *tl;
3662	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3663		if ((nd->nd_flag & ND_NFSV41) != 0)
3664			clientid.qval = nd->nd_clientid.qval;
3665		else if (nd->nd_clientid.qval != clientid.qval)
3666			printf("EEK14 multiple clids\n");
3667	} else {
3668		if ((nd->nd_flag & ND_NFSV41) != 0)
3669			printf("EEK! no clientid from session\n");
3670		nd->nd_flag |= ND_IMPLIEDCLID;
3671		nd->nd_clientid.qval = clientid.qval;
3672	}
3673	error = nfsrv_mtostr(nd, stp->ls_owner, len);
3674	if (error)
3675		goto nfsmout;
3676	nd->nd_repstat = nfsrv_releaselckown(stp, clientid, p);
3677	FREE((caddr_t)stp, M_NFSDSTATE);
3678
3679	NFSEXITCODE2(0, nd);
3680	return (0);
3681nfsmout:
3682	if (stp)
3683		free((caddr_t)stp, M_NFSDSTATE);
3684	NFSEXITCODE2(error, nd);
3685	return (error);
3686}
3687
3688/*
3689 * nfsv4 exchange_id service
3690 */
3691APPLESTATIC int
3692nfsrvd_exchangeid(struct nfsrv_descript *nd, __unused int isdgram,
3693    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3694{
3695	uint32_t *tl;
3696	int error = 0, i, idlen;
3697	struct nfsclient *clp = NULL;
3698	nfsquad_t clientid, confirm;
3699	uint8_t *verf;
3700	uint32_t sp4type, v41flags;
3701	uint64_t owner_minor;
3702	struct timespec verstime;
3703
3704	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3705		nd->nd_repstat = NFSERR_WRONGSEC;
3706		goto nfsmout;
3707	}
3708	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
3709	verf = (uint8_t *)tl;
3710	tl += (NFSX_VERF / NFSX_UNSIGNED);
3711	i = fxdr_unsigned(int, *tl);
3712	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
3713		nd->nd_repstat = NFSERR_BADXDR;
3714		goto nfsmout;
3715	}
3716	idlen = i;
3717	if (nd->nd_flag & ND_GSS)
3718		i += nd->nd_princlen;
3719	clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
3720	    M_ZERO);
3721	clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
3722	    nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
3723	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
3724	NFSSOCKADDRALLOC(clp->lc_req.nr_nam);
3725	NFSSOCKADDRSIZE(clp->lc_req.nr_nam, sizeof (struct sockaddr_in));
3726	clp->lc_req.nr_cred = NULL;
3727	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
3728	clp->lc_idlen = idlen;
3729	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
3730	if (error != 0)
3731		goto nfsmout;
3732	if ((nd->nd_flag & ND_GSS) != 0) {
3733		clp->lc_flags = LCL_GSS | LCL_NFSV41;
3734		if ((nd->nd_flag & ND_GSSINTEGRITY) != 0)
3735			clp->lc_flags |= LCL_GSSINTEGRITY;
3736		else if ((nd->nd_flag & ND_GSSPRIVACY) != 0)
3737			clp->lc_flags |= LCL_GSSPRIVACY;
3738	} else
3739		clp->lc_flags = LCL_NFSV41;
3740	if ((nd->nd_flag & ND_GSS) != 0 && nd->nd_princlen > 0) {
3741		clp->lc_flags |= LCL_NAME;
3742		clp->lc_namelen = nd->nd_princlen;
3743		clp->lc_name = &clp->lc_id[idlen];
3744		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
3745	} else {
3746		clp->lc_uid = nd->nd_cred->cr_uid;
3747		clp->lc_gid = nd->nd_cred->cr_gid;
3748	}
3749	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3750	v41flags = fxdr_unsigned(uint32_t, *tl++);
3751	if ((v41flags & ~(NFSV4EXCH_SUPPMOVEDREFER | NFSV4EXCH_SUPPMOVEDMIGR |
3752	    NFSV4EXCH_BINDPRINCSTATEID | NFSV4EXCH_MASKPNFS |
3753	    NFSV4EXCH_UPDCONFIRMEDRECA)) != 0) {
3754		nd->nd_repstat = NFSERR_INVAL;
3755		goto nfsmout;
3756	}
3757	if ((v41flags & NFSV4EXCH_UPDCONFIRMEDRECA) != 0)
3758		confirm.lval[1] = 1;
3759	else
3760		confirm.lval[1] = 0;
3761	v41flags = NFSV4EXCH_USENONPNFS;
3762	sp4type = fxdr_unsigned(uint32_t, *tl);
3763	if (sp4type != NFSV4EXCH_SP4NONE) {
3764		nd->nd_repstat = NFSERR_NOTSUPP;
3765		goto nfsmout;
3766	}
3767
3768	/*
3769	 * nfsrv_setclient() does the actual work of adding it to the
3770	 * client list. If there is no error, the structure has been
3771	 * linked into the client list and clp should no longer be used
3772	 * here. When an error is returned, it has not been linked in,
3773	 * so it should be free'd.
3774	 */
3775	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
3776	if (clp != NULL) {
3777		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3778		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3779		free(clp->lc_stateid, M_NFSDCLIENT);
3780		free(clp, M_NFSDCLIENT);
3781	}
3782	if (nd->nd_repstat == 0) {
3783		if (confirm.lval[1] != 0)
3784			v41flags |= NFSV4EXCH_CONFIRMEDR;
3785		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + 3 * NFSX_UNSIGNED);
3786		*tl++ = clientid.lval[0];			/* ClientID */
3787		*tl++ = clientid.lval[1];
3788		*tl++ = txdr_unsigned(confirm.lval[0]);		/* SequenceID */
3789		*tl++ = txdr_unsigned(v41flags);		/* Exch flags */
3790		*tl++ = txdr_unsigned(NFSV4EXCH_SP4NONE);	/* No SSV */
3791		owner_minor = 0;				/* Owner */
3792		txdr_hyper(owner_minor, tl);			/* Minor */
3793		(void)nfsm_strtom(nd, nd->nd_cred->cr_prison->pr_hostuuid,
3794		    strlen(nd->nd_cred->cr_prison->pr_hostuuid)); /* Major */
3795		NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
3796		*tl++ = txdr_unsigned(NFSX_UNSIGNED);
3797		*tl++ = time_uptime;		/* Make scope a unique value. */
3798		*tl = txdr_unsigned(1);
3799		(void)nfsm_strtom(nd, "freebsd.org", strlen("freebsd.org"));
3800		(void)nfsm_strtom(nd, version, strlen(version));
3801		NFSM_BUILD(tl, uint32_t *, NFSX_V4TIME);
3802		verstime.tv_sec = 1293840000;		/* Jan 1, 2011 */
3803		verstime.tv_nsec = 0;
3804		txdr_nfsv4time(&verstime, tl);
3805	}
3806	NFSEXITCODE2(0, nd);
3807	return (0);
3808nfsmout:
3809	if (clp != NULL) {
3810		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3811		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3812		free(clp->lc_stateid, M_NFSDCLIENT);
3813		free(clp, M_NFSDCLIENT);
3814	}
3815	NFSEXITCODE2(error, nd);
3816	return (error);
3817}
3818
3819/*
3820 * nfsv4 create session service
3821 */
3822APPLESTATIC int
3823nfsrvd_createsession(struct nfsrv_descript *nd, __unused int isdgram,
3824    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3825{
3826	uint32_t *tl;
3827	int error = 0;
3828	nfsquad_t clientid, confirm;
3829	struct nfsdsession *sep = NULL;
3830	uint32_t rdmacnt;
3831
3832	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3833		nd->nd_repstat = NFSERR_WRONGSEC;
3834		goto nfsmout;
3835	}
3836	sep = (struct nfsdsession *)malloc(sizeof(struct nfsdsession),
3837	    M_NFSDSESSION, M_WAITOK | M_ZERO);
3838	sep->sess_refcnt = 1;
3839	mtx_init(&sep->sess_cbsess.nfsess_mtx, "nfscbsession", NULL, MTX_DEF);
3840	NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + 2 * NFSX_UNSIGNED);
3841	clientid.lval[0] = *tl++;
3842	clientid.lval[1] = *tl++;
3843	confirm.lval[0] = fxdr_unsigned(uint32_t, *tl++);
3844	sep->sess_crflags = fxdr_unsigned(uint32_t, *tl);
3845	/* Persistent sessions and RDMA are not supported. */
3846	sep->sess_crflags &= NFSV4CRSESS_CONNBACKCHAN;
3847
3848	/* Fore channel attributes. */
3849	NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
3850	tl++;					/* Header pad always 0. */
3851	sep->sess_maxreq = fxdr_unsigned(uint32_t, *tl++);
3852	sep->sess_maxresp = fxdr_unsigned(uint32_t, *tl++);
3853	sep->sess_maxrespcached = fxdr_unsigned(uint32_t, *tl++);
3854	sep->sess_maxops = fxdr_unsigned(uint32_t, *tl++);
3855	sep->sess_maxslots = fxdr_unsigned(uint32_t, *tl++);
3856	if (sep->sess_maxslots > NFSV4_SLOTS)
3857		sep->sess_maxslots = NFSV4_SLOTS;
3858	rdmacnt = fxdr_unsigned(uint32_t, *tl);
3859	if (rdmacnt > 1) {
3860		nd->nd_repstat = NFSERR_BADXDR;
3861		goto nfsmout;
3862	} else if (rdmacnt == 1)
3863		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
3864
3865	/* Back channel attributes. */
3866	NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
3867	tl++;					/* Header pad always 0. */
3868	sep->sess_cbmaxreq = fxdr_unsigned(uint32_t, *tl++);
3869	sep->sess_cbmaxresp = fxdr_unsigned(uint32_t, *tl++);
3870	sep->sess_cbmaxrespcached = fxdr_unsigned(uint32_t, *tl++);
3871	sep->sess_cbmaxops = fxdr_unsigned(uint32_t, *tl++);
3872	sep->sess_cbsess.nfsess_foreslots = fxdr_unsigned(uint32_t, *tl++);
3873	rdmacnt = fxdr_unsigned(uint32_t, *tl);
3874	if (rdmacnt > 1) {
3875		nd->nd_repstat = NFSERR_BADXDR;
3876		goto nfsmout;
3877	} else if (rdmacnt == 1)
3878		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
3879
3880	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
3881	sep->sess_cbprogram = fxdr_unsigned(uint32_t, *tl);
3882
3883	/*
3884	 * nfsrv_getclient() searches the client list for a match and
3885	 * returns the appropriate NFSERR status.
3886	 */
3887	nd->nd_repstat = nfsrv_getclient(clientid, CLOPS_CONFIRM | CLOPS_RENEW,
3888	    NULL, sep, confirm, sep->sess_cbprogram, nd, p);
3889	if (nd->nd_repstat == 0) {
3890		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
3891		NFSBCOPY(sep->sess_sessionid, tl, NFSX_V4SESSIONID);
3892		NFSM_BUILD(tl, uint32_t *, 18 * NFSX_UNSIGNED);
3893		*tl++ = txdr_unsigned(confirm.lval[0]);	/* sequenceid */
3894		*tl++ = txdr_unsigned(sep->sess_crflags);
3895
3896		/* Fore channel attributes. */
3897		*tl++ = 0;
3898		*tl++ = txdr_unsigned(sep->sess_maxreq);
3899		*tl++ = txdr_unsigned(sep->sess_maxresp);
3900		*tl++ = txdr_unsigned(sep->sess_maxrespcached);
3901		*tl++ = txdr_unsigned(sep->sess_maxops);
3902		*tl++ = txdr_unsigned(sep->sess_maxslots);
3903		*tl++ = txdr_unsigned(1);
3904		*tl++ = txdr_unsigned(0);			/* No RDMA. */
3905
3906		/* Back channel attributes. */
3907		*tl++ = 0;
3908		*tl++ = txdr_unsigned(sep->sess_cbmaxreq);
3909		*tl++ = txdr_unsigned(sep->sess_cbmaxresp);
3910		*tl++ = txdr_unsigned(sep->sess_cbmaxrespcached);
3911		*tl++ = txdr_unsigned(sep->sess_cbmaxops);
3912		*tl++ = txdr_unsigned(sep->sess_cbsess.nfsess_foreslots);
3913		*tl++ = txdr_unsigned(1);
3914		*tl = txdr_unsigned(0);			/* No RDMA. */
3915	}
3916nfsmout:
3917	if (nd->nd_repstat != 0 && sep != NULL)
3918		free(sep, M_NFSDSESSION);
3919	NFSEXITCODE2(error, nd);
3920	return (error);
3921}
3922
3923/*
3924 * nfsv4 sequence service
3925 */
3926APPLESTATIC int
3927nfsrvd_sequence(struct nfsrv_descript *nd, __unused int isdgram,
3928    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
3929{
3930	uint32_t *tl;
3931	uint32_t highest_slotid, sequenceid, sflags, target_highest_slotid;
3932	int cache_this, error = 0;
3933
3934	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3935		nd->nd_repstat = NFSERR_WRONGSEC;
3936		goto nfsmout;
3937	}
3938	NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID);
3939	NFSBCOPY(tl, nd->nd_sessionid, NFSX_V4SESSIONID);
3940	NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
3941	sequenceid = fxdr_unsigned(uint32_t, *tl++);
3942	nd->nd_slotid = fxdr_unsigned(uint32_t, *tl++);
3943	highest_slotid = fxdr_unsigned(uint32_t, *tl++);
3944	if (*tl == newnfs_true)
3945		cache_this = 1;
3946	else
3947		cache_this = 0;
3948	nd->nd_flag |= ND_HASSEQUENCE;
3949	nd->nd_repstat = nfsrv_checksequence(nd, sequenceid, &highest_slotid,
3950	    &target_highest_slotid, cache_this, &sflags, p);
3951	if (nd->nd_repstat == 0) {
3952		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
3953		NFSBCOPY(nd->nd_sessionid, tl, NFSX_V4SESSIONID);
3954		NFSM_BUILD(tl, uint32_t *, 5 * NFSX_UNSIGNED);
3955		*tl++ = txdr_unsigned(sequenceid);
3956		*tl++ = txdr_unsigned(nd->nd_slotid);
3957		*tl++ = txdr_unsigned(highest_slotid);
3958		*tl++ = txdr_unsigned(target_highest_slotid);
3959		*tl = txdr_unsigned(sflags);
3960	}
3961nfsmout:
3962	NFSEXITCODE2(error, nd);
3963	return (error);
3964}
3965
3966/*
3967 * nfsv4 reclaim complete service
3968 */
3969APPLESTATIC int
3970nfsrvd_reclaimcomplete(struct nfsrv_descript *nd, __unused int isdgram,
3971    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
3972{
3973	uint32_t *tl;
3974	int error = 0;
3975
3976	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3977		nd->nd_repstat = NFSERR_WRONGSEC;
3978		goto nfsmout;
3979	}
3980	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
3981	if (*tl == newnfs_true)
3982		nd->nd_repstat = NFSERR_NOTSUPP;
3983	else
3984		nd->nd_repstat = nfsrv_checkreclaimcomplete(nd);
3985nfsmout:
3986	NFSEXITCODE2(error, nd);
3987	return (error);
3988}
3989
3990/*
3991 * nfsv4 destroy clientid service
3992 */
3993APPLESTATIC int
3994nfsrvd_destroyclientid(struct nfsrv_descript *nd, __unused int isdgram,
3995    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3996{
3997	uint32_t *tl;
3998	nfsquad_t clientid;
3999	int error = 0;
4000
4001	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4002		nd->nd_repstat = NFSERR_WRONGSEC;
4003		goto nfsmout;
4004	}
4005	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
4006	clientid.lval[0] = *tl++;
4007	clientid.lval[1] = *tl;
4008	nd->nd_repstat = nfsrv_destroyclient(clientid, p);
4009nfsmout:
4010	NFSEXITCODE2(error, nd);
4011	return (error);
4012}
4013
4014/*
4015 * nfsv4 destroy session service
4016 */
4017APPLESTATIC int
4018nfsrvd_destroysession(struct nfsrv_descript *nd, __unused int isdgram,
4019    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
4020{
4021	uint8_t *cp, sessid[NFSX_V4SESSIONID];
4022	int error = 0;
4023
4024	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4025		nd->nd_repstat = NFSERR_WRONGSEC;
4026		goto nfsmout;
4027	}
4028	NFSM_DISSECT(cp, uint8_t *, NFSX_V4SESSIONID);
4029	NFSBCOPY(cp, sessid, NFSX_V4SESSIONID);
4030	nd->nd_repstat = nfsrv_destroysession(nd, sessid);
4031nfsmout:
4032	NFSEXITCODE2(error, nd);
4033	return (error);
4034}
4035
4036/*
4037 * nfsv4 free stateid service
4038 */
4039APPLESTATIC int
4040nfsrvd_freestateid(struct nfsrv_descript *nd, __unused int isdgram,
4041    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
4042{
4043	uint32_t *tl;
4044	nfsv4stateid_t stateid;
4045	int error = 0;
4046
4047	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4048		nd->nd_repstat = NFSERR_WRONGSEC;
4049		goto nfsmout;
4050	}
4051	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
4052	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
4053	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
4054	nd->nd_repstat = nfsrv_freestateid(nd, &stateid, p);
4055nfsmout:
4056	NFSEXITCODE2(error, nd);
4057	return (error);
4058}
4059
4060/*
4061 * nfsv4 service not supported
4062 */
4063APPLESTATIC int
4064nfsrvd_notsupp(struct nfsrv_descript *nd, __unused int isdgram,
4065    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
4066{
4067
4068	nd->nd_repstat = NFSERR_NOTSUPP;
4069	NFSEXITCODE2(0, nd);
4070	return (0);
4071}
4072
4073