nfs_nfsdserv.c revision 361070
1/*-
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: stable/11/sys/fs/nfsserver/nfs_nfsdserv.c 361070 2020-05-15 01:01:02Z freqlabs $");
36
37#include "opt_inet.h"
38#include "opt_inet6.h"
39/*
40 * nfs version 2, 3 and 4 server calls to vnode ops
41 * - these routines generally have 3 phases
42 *   1 - break down and validate rpc request in mbuf list
43 *   2 - do the vnode ops for the request, usually by calling a nfsvno_XXX()
44 *       function in nfsd_port.c
45 *   3 - build the rpc reply in an mbuf list
46 * For nfsv4, these functions are called for each Op within the Compound RPC.
47 */
48
49#include <fs/nfs/nfsport.h>
50
51/* Global vars */
52extern u_int32_t newnfs_false, newnfs_true;
53extern enum vtype nv34tov_type[8];
54extern struct timeval nfsboottime;
55extern int nfs_rootfhset;
56extern int nfsrv_enable_crossmntpt;
57extern int nfsrv_statehashsize;
58
59static int	nfs_async = 0;
60SYSCTL_DECL(_vfs_nfsd);
61SYSCTL_INT(_vfs_nfsd, OID_AUTO, async, CTLFLAG_RW, &nfs_async, 0,
62    "Tell client that writes were synced even though they were not");
63static bool	nfsrv_openaccess = true;
64SYSCTL_BOOL(_vfs_nfsd, OID_AUTO, v4openaccess, CTLFLAG_RW,
65    &nfsrv_openaccess, 0,
66    "Enable Linux style NFSv4 Open access check");
67
68/*
69 * This list defines the GSS mechanisms supported.
70 * (Don't ask me how you get these strings from the RFC stuff like
71 *  iso(1), org(3)... but someone did it, so I don't need to know.)
72 */
73static struct nfsgss_mechlist nfsgss_mechlist[] = {
74	{ 9, "\052\206\110\206\367\022\001\002\002", 11 },
75	{ 0, "", 0 },
76};
77
78/* local functions */
79static void nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
80    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
81    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
82    int *diraft_retp, nfsattrbit_t *attrbitp,
83    NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
84    int pathlen);
85static void nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
86    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
87    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
88    int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
89    NFSPROC_T *p, struct nfsexstuff *exp);
90
91/*
92 * nfs access service (not a part of NFS V2)
93 */
94APPLESTATIC int
95nfsrvd_access(struct nfsrv_descript *nd, __unused int isdgram,
96    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
97{
98	u_int32_t *tl;
99	int getret, error = 0;
100	struct nfsvattr nva;
101	u_int32_t testmode, nfsmode, supported = 0;
102	accmode_t deletebit;
103
104	if (nd->nd_repstat) {
105		nfsrv_postopattr(nd, 1, &nva);
106		goto out;
107	}
108	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
109	nfsmode = fxdr_unsigned(u_int32_t, *tl);
110	if ((nd->nd_flag & ND_NFSV4) &&
111	    (nfsmode & ~(NFSACCESS_READ | NFSACCESS_LOOKUP |
112	     NFSACCESS_MODIFY | NFSACCESS_EXTEND | NFSACCESS_DELETE |
113	     NFSACCESS_EXECUTE))) {
114		nd->nd_repstat = NFSERR_INVAL;
115		vput(vp);
116		goto out;
117	}
118	if (nfsmode & NFSACCESS_READ) {
119		supported |= NFSACCESS_READ;
120		if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
121		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
122			nfsmode &= ~NFSACCESS_READ;
123	}
124	if (nfsmode & NFSACCESS_MODIFY) {
125		supported |= NFSACCESS_MODIFY;
126		if (nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp, p,
127		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
128			nfsmode &= ~NFSACCESS_MODIFY;
129	}
130	if (nfsmode & NFSACCESS_EXTEND) {
131		supported |= NFSACCESS_EXTEND;
132		if (nfsvno_accchk(vp, VWRITE | VAPPEND, nd->nd_cred, exp, p,
133		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
134			nfsmode &= ~NFSACCESS_EXTEND;
135	}
136	if (nfsmode & NFSACCESS_DELETE) {
137		supported |= NFSACCESS_DELETE;
138		if (vp->v_type == VDIR)
139			deletebit = VDELETE_CHILD;
140		else
141			deletebit = VDELETE;
142		if (nfsvno_accchk(vp, deletebit, nd->nd_cred, exp, p,
143		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
144			nfsmode &= ~NFSACCESS_DELETE;
145	}
146	if (vnode_vtype(vp) == VDIR)
147		testmode = NFSACCESS_LOOKUP;
148	else
149		testmode = NFSACCESS_EXECUTE;
150	if (nfsmode & testmode) {
151		supported |= (nfsmode & testmode);
152		if (nfsvno_accchk(vp, VEXEC, nd->nd_cred, exp, p,
153		    NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
154			nfsmode &= ~testmode;
155	}
156	nfsmode &= supported;
157	if (nd->nd_flag & ND_NFSV3) {
158		getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
159		nfsrv_postopattr(nd, getret, &nva);
160	}
161	vput(vp);
162	if (nd->nd_flag & ND_NFSV4) {
163		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
164		*tl++ = txdr_unsigned(supported);
165	} else
166		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
167	*tl = txdr_unsigned(nfsmode);
168
169out:
170	NFSEXITCODE2(0, nd);
171	return (0);
172nfsmout:
173	vput(vp);
174	NFSEXITCODE2(error, nd);
175	return (error);
176}
177
178/*
179 * nfs getattr service
180 */
181APPLESTATIC int
182nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
183    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
184{
185	struct nfsvattr nva;
186	fhandle_t fh;
187	int at_root = 0, error = 0, supports_nfsv4acls;
188	struct nfsreferral *refp;
189	nfsattrbit_t attrbits, tmpbits;
190	struct mount *mp;
191	struct vnode *tvp = NULL;
192	struct vattr va;
193	uint64_t mounted_on_fileno = 0;
194	accmode_t accmode;
195
196	if (nd->nd_repstat)
197		goto out;
198	if (nd->nd_flag & ND_NFSV4) {
199		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
200		if (error) {
201			vput(vp);
202			goto out;
203		}
204
205		/*
206		 * Check for a referral.
207		 */
208		refp = nfsv4root_getreferral(vp, NULL, 0);
209		if (refp != NULL) {
210			(void) nfsrv_putreferralattr(nd, &attrbits, refp, 1,
211			    &nd->nd_repstat);
212			vput(vp);
213			goto out;
214		}
215		if (nd->nd_repstat == 0) {
216			accmode = 0;
217			NFSSET_ATTRBIT(&tmpbits, &attrbits);
218
219			/*
220			 * GETATTR with write-only attr time_access_set and time_modify_set
221			 * should return NFS4ERR_INVAL.
222			 */
223			if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_TIMEACCESSSET) ||
224					NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_TIMEMODIFYSET)){
225				error = NFSERR_INVAL;
226				vput(vp);
227				goto out;
228			}
229			if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_ACL)) {
230				NFSCLRBIT_ATTRBIT(&tmpbits, NFSATTRBIT_ACL);
231				accmode |= VREAD_ACL;
232			}
233			if (NFSNONZERO_ATTRBIT(&tmpbits))
234				accmode |= VREAD_ATTRIBUTES;
235			if (accmode != 0)
236				nd->nd_repstat = nfsvno_accchk(vp, accmode,
237				    nd->nd_cred, exp, p, NFSACCCHK_NOOVERRIDE,
238				    NFSACCCHK_VPISLOCKED, NULL);
239		}
240	}
241	if (!nd->nd_repstat)
242		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
243	if (!nd->nd_repstat) {
244		if (nd->nd_flag & ND_NFSV4) {
245			if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_FILEHANDLE))
246				nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
247			if (!nd->nd_repstat)
248				nd->nd_repstat = nfsrv_checkgetattr(nd, vp,
249				    &nva, &attrbits, nd->nd_cred, p);
250			if (nd->nd_repstat == 0) {
251				supports_nfsv4acls = nfs_supportsnfsv4acls(vp);
252				mp = vp->v_mount;
253				if (nfsrv_enable_crossmntpt != 0 &&
254				    vp->v_type == VDIR &&
255				    (vp->v_vflag & VV_ROOT) != 0 &&
256				    vp != rootvnode) {
257					tvp = mp->mnt_vnodecovered;
258					VREF(tvp);
259					at_root = 1;
260				} else
261					at_root = 0;
262				vfs_ref(mp);
263				NFSVOPUNLOCK(vp, 0);
264				if (at_root != 0) {
265					if ((nd->nd_repstat =
266					     NFSVOPLOCK(tvp, LK_SHARED)) == 0) {
267						nd->nd_repstat = VOP_GETATTR(
268						    tvp, &va, nd->nd_cred);
269						vput(tvp);
270					} else
271						vrele(tvp);
272					if (nd->nd_repstat == 0)
273						mounted_on_fileno = (uint64_t)
274						    va.va_fileid;
275					else
276						at_root = 0;
277				}
278				if (nd->nd_repstat == 0)
279					nd->nd_repstat = vfs_busy(mp, 0);
280				vfs_rel(mp);
281				if (nd->nd_repstat == 0) {
282					(void)nfsvno_fillattr(nd, mp, vp, &nva,
283					    &fh, 0, &attrbits, nd->nd_cred, p,
284					    isdgram, 1, supports_nfsv4acls,
285					    at_root, mounted_on_fileno);
286					vfs_unbusy(mp);
287				}
288				vrele(vp);
289			} else
290				vput(vp);
291		} else {
292			nfsrv_fillattr(nd, &nva);
293			vput(vp);
294		}
295	} else {
296		vput(vp);
297	}
298
299out:
300	NFSEXITCODE2(error, nd);
301	return (error);
302}
303
304/*
305 * nfs setattr service
306 */
307APPLESTATIC int
308nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
309    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
310{
311	struct nfsvattr nva, nva2;
312	u_int32_t *tl;
313	int preat_ret = 1, postat_ret = 1, gcheck = 0, error = 0;
314	struct timespec guard = { 0, 0 };
315	nfsattrbit_t attrbits, retbits;
316	nfsv4stateid_t stateid;
317	NFSACL_T *aclp = NULL;
318
319	if (nd->nd_repstat) {
320		nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
321		goto out;
322	}
323#ifdef NFS4_ACL_EXTATTR_NAME
324	aclp = acl_alloc(M_WAITOK);
325	aclp->acl_cnt = 0;
326#endif
327	NFSVNO_ATTRINIT(&nva);
328	NFSZERO_ATTRBIT(&retbits);
329	if (nd->nd_flag & ND_NFSV4) {
330		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
331		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
332		NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
333	}
334	error = nfsrv_sattr(nd, vp, &nva, &attrbits, aclp, p);
335	if (error)
336		goto nfsmout;
337	preat_ret = nfsvno_getattr(vp, &nva2, nd->nd_cred, p, 1);
338	if (!nd->nd_repstat)
339		nd->nd_repstat = preat_ret;
340	if (nd->nd_flag & ND_NFSV3) {
341		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
342		gcheck = fxdr_unsigned(int, *tl);
343		if (gcheck) {
344			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
345			fxdr_nfsv3time(tl, &guard);
346		}
347		if (!nd->nd_repstat && gcheck &&
348		    (nva2.na_ctime.tv_sec != guard.tv_sec ||
349		     nva2.na_ctime.tv_nsec != guard.tv_nsec))
350			nd->nd_repstat = NFSERR_NOT_SYNC;
351		if (nd->nd_repstat) {
352			vput(vp);
353#ifdef NFS4_ACL_EXTATTR_NAME
354			acl_free(aclp);
355#endif
356			nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
357			goto out;
358		}
359	} else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
360		nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
361
362	/*
363	 * Now that we have all the fields, lets do it.
364	 * If the size is being changed write access is required, otherwise
365	 * just check for a read only file system.
366	 */
367	if (!nd->nd_repstat) {
368		if (NFSVNO_NOTSETSIZE(&nva)) {
369			if (NFSVNO_EXRDONLY(exp) ||
370			    (vfs_flags(vnode_mount(vp)) & MNT_RDONLY))
371				nd->nd_repstat = EROFS;
372		} else {
373			if (vnode_vtype(vp) != VREG)
374				nd->nd_repstat = EINVAL;
375			else if (nva2.na_uid != nd->nd_cred->cr_uid ||
376			    NFSVNO_EXSTRICTACCESS(exp))
377				nd->nd_repstat = nfsvno_accchk(vp,
378				    VWRITE, nd->nd_cred, exp, p,
379				    NFSACCCHK_NOOVERRIDE,
380				    NFSACCCHK_VPISLOCKED, NULL);
381		}
382	}
383	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
384		nd->nd_repstat = nfsrv_checksetattr(vp, nd, &stateid,
385		    &nva, &attrbits, exp, p);
386
387	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
388	    /*
389	     * For V4, try setting the attrbutes in sets, so that the
390	     * reply bitmap will be correct for an error case.
391	     */
392	    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER) ||
393		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP)) {
394		NFSVNO_ATTRINIT(&nva2);
395		NFSVNO_SETATTRVAL(&nva2, uid, nva.na_uid);
396		NFSVNO_SETATTRVAL(&nva2, gid, nva.na_gid);
397		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
398		    exp);
399		if (!nd->nd_repstat) {
400		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER))
401			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
402		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP))
403			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNERGROUP);
404		}
405	    }
406	    if (!nd->nd_repstat &&
407		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SIZE)) {
408		NFSVNO_ATTRINIT(&nva2);
409		NFSVNO_SETATTRVAL(&nva2, size, nva.na_size);
410		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
411		    exp);
412		if (!nd->nd_repstat)
413		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SIZE);
414	    }
415	    if (!nd->nd_repstat &&
416		(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET) ||
417		 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))) {
418		NFSVNO_ATTRINIT(&nva2);
419		NFSVNO_SETATTRVAL(&nva2, atime, nva.na_atime);
420		NFSVNO_SETATTRVAL(&nva2, mtime, nva.na_mtime);
421		if (nva.na_vaflags & VA_UTIMES_NULL) {
422			nva2.na_vaflags |= VA_UTIMES_NULL;
423			NFSVNO_SETACTIVE(&nva2, vaflags);
424		}
425		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
426		    exp);
427		if (!nd->nd_repstat) {
428		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET))
429			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEACCESSSET);
430		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))
431			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEMODIFYSET);
432		}
433	    }
434	    if (!nd->nd_repstat &&
435		(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE) ||
436		 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODESETMASKED))) {
437		NFSVNO_ATTRINIT(&nva2);
438		NFSVNO_SETATTRVAL(&nva2, mode, nva.na_mode);
439		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
440		    exp);
441		if (!nd->nd_repstat) {
442		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE))
443			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODE);
444		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODESETMASKED))
445			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODESETMASKED);
446		}
447	    }
448
449#ifdef NFS4_ACL_EXTATTR_NAME
450	    if (!nd->nd_repstat && aclp->acl_cnt > 0 &&
451		NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ACL)) {
452		nd->nd_repstat = nfsrv_setacl(vp, aclp, nd->nd_cred, p);
453		if (!nd->nd_repstat)
454		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_ACL);
455	    }
456#endif
457	} else if (!nd->nd_repstat) {
458		nd->nd_repstat = nfsvno_setattr(vp, &nva, nd->nd_cred, p,
459		    exp);
460	}
461	if (nd->nd_flag & (ND_NFSV2 | ND_NFSV3)) {
462		postat_ret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
463		if (!nd->nd_repstat)
464			nd->nd_repstat = postat_ret;
465	}
466	vput(vp);
467#ifdef NFS4_ACL_EXTATTR_NAME
468	acl_free(aclp);
469#endif
470	if (nd->nd_flag & ND_NFSV3)
471		nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
472	else if (nd->nd_flag & ND_NFSV4)
473		(void) nfsrv_putattrbit(nd, &retbits);
474	else if (!nd->nd_repstat)
475		nfsrv_fillattr(nd, &nva);
476
477out:
478	NFSEXITCODE2(0, nd);
479	return (0);
480nfsmout:
481	vput(vp);
482#ifdef NFS4_ACL_EXTATTR_NAME
483	acl_free(aclp);
484#endif
485	if (nd->nd_flag & ND_NFSV4) {
486		/*
487		 * For all nd_repstat, the V4 reply includes a bitmap,
488		 * even NFSERR_BADXDR, which is what this will end up
489		 * returning.
490		 */
491		(void) nfsrv_putattrbit(nd, &retbits);
492	}
493	NFSEXITCODE2(error, nd);
494	return (error);
495}
496
497/*
498 * nfs lookup rpc
499 * (Also performs lookup parent for v4)
500 */
501APPLESTATIC int
502nfsrvd_lookup(struct nfsrv_descript *nd, __unused int isdgram,
503    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
504    struct nfsexstuff *exp)
505{
506	struct nameidata named;
507	vnode_t vp, dirp = NULL;
508	int error = 0, dattr_ret = 1;
509	struct nfsvattr nva, dattr;
510	char *bufp;
511	u_long *hashp;
512
513	if (nd->nd_repstat) {
514		nfsrv_postopattr(nd, dattr_ret, &dattr);
515		goto out;
516	}
517
518	/*
519	 * For some reason, if dp is a symlink, the error
520	 * returned is supposed to be NFSERR_SYMLINK and not NFSERR_NOTDIR.
521	 */
522	if (dp->v_type == VLNK && (nd->nd_flag & ND_NFSV4)) {
523		nd->nd_repstat = NFSERR_SYMLINK;
524		vrele(dp);
525		goto out;
526	}
527
528	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
529	    LOCKLEAF | SAVESTART);
530	nfsvno_setpathbuf(&named, &bufp, &hashp);
531	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
532	if (error) {
533		vrele(dp);
534		nfsvno_relpathbuf(&named);
535		goto out;
536	}
537	if (!nd->nd_repstat) {
538		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
539	} else {
540		vrele(dp);
541		nfsvno_relpathbuf(&named);
542	}
543	if (nd->nd_repstat) {
544		if (dirp) {
545			if (nd->nd_flag & ND_NFSV3)
546				dattr_ret = nfsvno_getattr(dirp, &dattr,
547				    nd->nd_cred, p, 0);
548			vrele(dirp);
549		}
550		if (nd->nd_flag & ND_NFSV3)
551			nfsrv_postopattr(nd, dattr_ret, &dattr);
552		goto out;
553	}
554	if (named.ni_startdir)
555		vrele(named.ni_startdir);
556	nfsvno_relpathbuf(&named);
557	vp = named.ni_vp;
558	if ((nd->nd_flag & ND_NFSV4) != 0 && !NFSVNO_EXPORTED(exp) &&
559	    vp->v_type != VDIR && vp->v_type != VLNK)
560		/*
561		 * Only allow lookup of VDIR and VLNK for traversal of
562		 * non-exported volumes during NFSv4 mounting.
563		 */
564		nd->nd_repstat = ENOENT;
565	if (nd->nd_repstat == 0)
566		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
567	if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
568		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
569	if (vpp != NULL && nd->nd_repstat == 0)
570		*vpp = vp;
571	else
572		vput(vp);
573	if (dirp) {
574		if (nd->nd_flag & ND_NFSV3)
575			dattr_ret = nfsvno_getattr(dirp, &dattr, nd->nd_cred,
576			    p, 0);
577		vrele(dirp);
578	}
579	if (nd->nd_repstat) {
580		if (nd->nd_flag & ND_NFSV3)
581			nfsrv_postopattr(nd, dattr_ret, &dattr);
582		goto out;
583	}
584	if (nd->nd_flag & ND_NFSV2) {
585		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
586		nfsrv_fillattr(nd, &nva);
587	} else if (nd->nd_flag & ND_NFSV3) {
588		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
589		nfsrv_postopattr(nd, 0, &nva);
590		nfsrv_postopattr(nd, dattr_ret, &dattr);
591	}
592
593out:
594	NFSEXITCODE2(error, nd);
595	return (error);
596}
597
598/*
599 * nfs readlink service
600 */
601APPLESTATIC int
602nfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram,
603    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
604{
605	u_int32_t *tl;
606	mbuf_t mp = NULL, mpend = NULL;
607	int getret = 1, len;
608	struct nfsvattr nva;
609
610	if (nd->nd_repstat) {
611		nfsrv_postopattr(nd, getret, &nva);
612		goto out;
613	}
614	if (vnode_vtype(vp) != VLNK) {
615		if (nd->nd_flag & ND_NFSV2)
616			nd->nd_repstat = ENXIO;
617		else
618			nd->nd_repstat = EINVAL;
619	}
620	if (!nd->nd_repstat)
621		nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred, p,
622		    &mp, &mpend, &len);
623	if (nd->nd_flag & ND_NFSV3)
624		getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
625	vput(vp);
626	if (nd->nd_flag & ND_NFSV3)
627		nfsrv_postopattr(nd, getret, &nva);
628	if (nd->nd_repstat)
629		goto out;
630	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
631	*tl = txdr_unsigned(len);
632	mbuf_setnext(nd->nd_mb, mp);
633	nd->nd_mb = mpend;
634	nd->nd_bpos = NFSMTOD(mpend, caddr_t) + mbuf_len(mpend);
635
636out:
637	NFSEXITCODE2(0, nd);
638	return (0);
639}
640
641/*
642 * nfs read service
643 */
644APPLESTATIC int
645nfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram,
646    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
647{
648	u_int32_t *tl;
649	int error = 0, cnt, getret = 1, reqlen, eof = 0;
650	mbuf_t m2, m3;
651	struct nfsvattr nva;
652	off_t off = 0x0;
653	struct nfsstate st, *stp = &st;
654	struct nfslock lo, *lop = &lo;
655	nfsv4stateid_t stateid;
656	nfsquad_t clientid;
657
658	if (nd->nd_repstat) {
659		nfsrv_postopattr(nd, getret, &nva);
660		goto out;
661	}
662	if (nd->nd_flag & ND_NFSV2) {
663		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
664		off = (off_t)fxdr_unsigned(u_int32_t, *tl++);
665		reqlen = fxdr_unsigned(int, *tl);
666	} else if (nd->nd_flag & ND_NFSV3) {
667		NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
668		off = fxdr_hyper(tl);
669		tl += 2;
670		reqlen = fxdr_unsigned(int, *tl);
671	} else {
672		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3*NFSX_UNSIGNED);
673		reqlen = fxdr_unsigned(int, *(tl + 6));
674	}
675	if (reqlen > NFS_SRVMAXDATA(nd)) {
676		reqlen = NFS_SRVMAXDATA(nd);
677	} else if (reqlen < 0) {
678		error = EBADRPC;
679		goto nfsmout;
680	}
681	if (nd->nd_flag & ND_NFSV4) {
682		stp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
683		lop->lo_flags = NFSLCK_READ;
684		stp->ls_ownerlen = 0;
685		stp->ls_op = NULL;
686		stp->ls_uid = nd->nd_cred->cr_uid;
687		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
688		clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
689		clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
690		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
691			if ((nd->nd_flag & ND_NFSV41) != 0)
692				clientid.qval = nd->nd_clientid.qval;
693			else if (nd->nd_clientid.qval != clientid.qval)
694				printf("EEK1 multiple clids\n");
695		} else {
696			if ((nd->nd_flag & ND_NFSV41) != 0)
697				printf("EEK! no clientid from session\n");
698			nd->nd_flag |= ND_IMPLIEDCLID;
699			nd->nd_clientid.qval = clientid.qval;
700		}
701		stp->ls_stateid.other[2] = *tl++;
702		off = fxdr_hyper(tl);
703		lop->lo_first = off;
704		tl += 2;
705		lop->lo_end = off + reqlen;
706		/*
707		 * Paranoia, just in case it wraps around.
708		 */
709		if (lop->lo_end < off)
710			lop->lo_end = NFS64BITSSET;
711	}
712	if (vnode_vtype(vp) != VREG) {
713		if (nd->nd_flag & ND_NFSV3)
714			nd->nd_repstat = EINVAL;
715		else
716			nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
717			    EINVAL;
718	}
719	getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
720	if (!nd->nd_repstat)
721		nd->nd_repstat = getret;
722	if (!nd->nd_repstat &&
723	    (nva.na_uid != nd->nd_cred->cr_uid ||
724	     NFSVNO_EXSTRICTACCESS(exp))) {
725		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
726		    nd->nd_cred, exp, p,
727		    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
728		if (nd->nd_repstat)
729			nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
730			    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
731			    NFSACCCHK_VPISLOCKED, NULL);
732	}
733	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
734		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
735		    &stateid, exp, nd, p);
736	if (nd->nd_repstat) {
737		vput(vp);
738		if (nd->nd_flag & ND_NFSV3)
739			nfsrv_postopattr(nd, getret, &nva);
740		goto out;
741	}
742	if (off >= nva.na_size) {
743		cnt = 0;
744		eof = 1;
745	} else if (reqlen == 0)
746		cnt = 0;
747	else if ((off + reqlen) >= nva.na_size) {
748		cnt = nva.na_size - off;
749		eof = 1;
750	} else
751		cnt = reqlen;
752	m3 = NULL;
753	if (cnt > 0) {
754		nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred, p,
755		    &m3, &m2);
756		if (!(nd->nd_flag & ND_NFSV4)) {
757			getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
758			if (!nd->nd_repstat)
759				nd->nd_repstat = getret;
760		}
761		if (nd->nd_repstat) {
762			vput(vp);
763			if (m3)
764				mbuf_freem(m3);
765			if (nd->nd_flag & ND_NFSV3)
766				nfsrv_postopattr(nd, getret, &nva);
767			goto out;
768		}
769	}
770	vput(vp);
771	if (nd->nd_flag & ND_NFSV2) {
772		nfsrv_fillattr(nd, &nva);
773		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
774	} else {
775		if (nd->nd_flag & ND_NFSV3) {
776			nfsrv_postopattr(nd, getret, &nva);
777			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
778			*tl++ = txdr_unsigned(cnt);
779		} else
780			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
781		if (eof)
782			*tl++ = newnfs_true;
783		else
784			*tl++ = newnfs_false;
785	}
786	*tl = txdr_unsigned(cnt);
787	if (m3) {
788		mbuf_setnext(nd->nd_mb, m3);
789		nd->nd_mb = m2;
790		nd->nd_bpos = NFSMTOD(m2, caddr_t) + mbuf_len(m2);
791	}
792
793out:
794	NFSEXITCODE2(0, nd);
795	return (0);
796nfsmout:
797	vput(vp);
798	NFSEXITCODE2(error, nd);
799	return (error);
800}
801
802/*
803 * nfs write service
804 */
805APPLESTATIC int
806nfsrvd_write(struct nfsrv_descript *nd, __unused int isdgram,
807    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
808{
809	int i, cnt;
810	u_int32_t *tl;
811	mbuf_t mp;
812	struct nfsvattr nva, forat;
813	int aftat_ret = 1, retlen, len, error = 0, forat_ret = 1;
814	int stable = NFSWRITE_FILESYNC;
815	off_t off;
816	struct nfsstate st, *stp = &st;
817	struct nfslock lo, *lop = &lo;
818	nfsv4stateid_t stateid;
819	nfsquad_t clientid;
820
821	if (nd->nd_repstat) {
822		nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
823		goto out;
824	}
825	if (nd->nd_flag & ND_NFSV2) {
826		NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
827		off = (off_t)fxdr_unsigned(u_int32_t, *++tl);
828		tl += 2;
829		retlen = len = fxdr_unsigned(int32_t, *tl);
830	} else if (nd->nd_flag & ND_NFSV3) {
831		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
832		off = fxdr_hyper(tl);
833		tl += 3;
834		stable = fxdr_unsigned(int, *tl++);
835		retlen = len = fxdr_unsigned(int32_t, *tl);
836	} else {
837		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 4 * NFSX_UNSIGNED);
838		stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
839		lop->lo_flags = NFSLCK_WRITE;
840		stp->ls_ownerlen = 0;
841		stp->ls_op = NULL;
842		stp->ls_uid = nd->nd_cred->cr_uid;
843		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
844		clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
845		clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
846		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
847			if ((nd->nd_flag & ND_NFSV41) != 0)
848				clientid.qval = nd->nd_clientid.qval;
849			else if (nd->nd_clientid.qval != clientid.qval)
850				printf("EEK2 multiple clids\n");
851		} else {
852			if ((nd->nd_flag & ND_NFSV41) != 0)
853				printf("EEK! no clientid from session\n");
854			nd->nd_flag |= ND_IMPLIEDCLID;
855			nd->nd_clientid.qval = clientid.qval;
856		}
857		stp->ls_stateid.other[2] = *tl++;
858		off = fxdr_hyper(tl);
859		lop->lo_first = off;
860		tl += 2;
861		stable = fxdr_unsigned(int, *tl++);
862		retlen = len = fxdr_unsigned(int32_t, *tl);
863		lop->lo_end = off + len;
864		/*
865		 * Paranoia, just in case it wraps around, which shouldn't
866		 * ever happen anyhow.
867		 */
868		if (lop->lo_end < lop->lo_first)
869			lop->lo_end = NFS64BITSSET;
870	}
871
872	/*
873	 * Loop through the mbuf chain, counting how many mbufs are a
874	 * part of this write operation, so the iovec size is known.
875	 */
876	cnt = 0;
877	mp = nd->nd_md;
878	i = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - nd->nd_dpos;
879	while (len > 0) {
880		if (i > 0) {
881			len -= i;
882			cnt++;
883		}
884		mp = mbuf_next(mp);
885		if (!mp) {
886			if (len > 0) {
887				error = EBADRPC;
888				goto nfsmout;
889			}
890		} else
891			i = mbuf_len(mp);
892	}
893
894	if (retlen > NFS_SRVMAXIO || retlen < 0)
895		nd->nd_repstat = EIO;
896	if (vnode_vtype(vp) != VREG && !nd->nd_repstat) {
897		if (nd->nd_flag & ND_NFSV3)
898			nd->nd_repstat = EINVAL;
899		else
900			nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
901			    EINVAL;
902	}
903	forat_ret = nfsvno_getattr(vp, &forat, nd->nd_cred, p, 1);
904	if (!nd->nd_repstat)
905		nd->nd_repstat = forat_ret;
906	if (!nd->nd_repstat &&
907	    (forat.na_uid != nd->nd_cred->cr_uid ||
908	     NFSVNO_EXSTRICTACCESS(exp)))
909		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
910		    nd->nd_cred, exp, p,
911		    NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
912	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
913		nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
914		    &stateid, exp, nd, p);
915	}
916	if (nd->nd_repstat) {
917		vput(vp);
918		if (nd->nd_flag & ND_NFSV3)
919			nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
920		goto out;
921	}
922
923	/*
924	 * For NFS Version 2, it is not obvious what a write of zero length
925	 * should do, but I might as well be consistent with Version 3,
926	 * which is to return ok so long as there are no permission problems.
927	 */
928	if (retlen > 0) {
929		nd->nd_repstat = nfsvno_write(vp, off, retlen, cnt, stable,
930		    nd->nd_md, nd->nd_dpos, nd->nd_cred, p);
931		error = nfsm_advance(nd, NFSM_RNDUP(retlen), -1);
932		if (error)
933			goto nfsmout;
934	}
935	if (nd->nd_flag & ND_NFSV4)
936		aftat_ret = 0;
937	else
938		aftat_ret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
939	vput(vp);
940	if (!nd->nd_repstat)
941		nd->nd_repstat = aftat_ret;
942	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
943		if (nd->nd_flag & ND_NFSV3)
944			nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
945		if (nd->nd_repstat)
946			goto out;
947		NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
948		*tl++ = txdr_unsigned(retlen);
949		/*
950		 * If nfs_async is set, then pretend the write was FILESYNC.
951		 * Warning: Doing this violates RFC1813 and runs a risk
952		 * of data written by a client being lost when the server
953		 * crashes/reboots.
954		 */
955		if (stable == NFSWRITE_UNSTABLE && nfs_async == 0)
956			*tl++ = txdr_unsigned(stable);
957		else
958			*tl++ = txdr_unsigned(NFSWRITE_FILESYNC);
959		/*
960		 * Actually, there is no need to txdr these fields,
961		 * but it may make the values more human readable,
962		 * for debugging purposes.
963		 */
964		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
965		*tl = txdr_unsigned(nfsboottime.tv_usec);
966	} else if (!nd->nd_repstat)
967		nfsrv_fillattr(nd, &nva);
968
969out:
970	NFSEXITCODE2(0, nd);
971	return (0);
972nfsmout:
973	vput(vp);
974	NFSEXITCODE2(error, nd);
975	return (error);
976}
977
978/*
979 * nfs create service (creates regular files for V2 and V3. Spec. files for V2.)
980 * now does a truncate to 0 length via. setattr if it already exists
981 * The core creation routine has been extracted out into nfsrv_creatsub(),
982 * so it can also be used by nfsrv_open() for V4.
983 */
984APPLESTATIC int
985nfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram,
986    vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
987{
988	struct nfsvattr nva, dirfor, diraft;
989	struct nfsv2_sattr *sp;
990	struct nameidata named;
991	u_int32_t *tl;
992	int error = 0, tsize, dirfor_ret = 1, diraft_ret = 1;
993	int how = NFSCREATE_UNCHECKED, exclusive_flag = 0;
994	NFSDEV_T rdev = 0;
995	vnode_t vp = NULL, dirp = NULL;
996	fhandle_t fh;
997	char *bufp;
998	u_long *hashp;
999	enum vtype vtyp;
1000	int32_t cverf[2], tverf[2] = { 0, 0 };
1001
1002	if (nd->nd_repstat) {
1003		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1004		goto out;
1005	}
1006	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
1007	    LOCKPARENT | LOCKLEAF | SAVESTART | NOCACHE);
1008	nfsvno_setpathbuf(&named, &bufp, &hashp);
1009	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1010	if (error)
1011		goto nfsmout;
1012	if (!nd->nd_repstat) {
1013		NFSVNO_ATTRINIT(&nva);
1014		if (nd->nd_flag & ND_NFSV2) {
1015			NFSM_DISSECT(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
1016			vtyp = IFTOVT(fxdr_unsigned(u_int32_t, sp->sa_mode));
1017			if (vtyp == VNON)
1018				vtyp = VREG;
1019			NFSVNO_SETATTRVAL(&nva, type, vtyp);
1020			NFSVNO_SETATTRVAL(&nva, mode,
1021			    nfstov_mode(sp->sa_mode));
1022			switch (nva.na_type) {
1023			case VREG:
1024				tsize = fxdr_unsigned(int32_t, sp->sa_size);
1025				if (tsize != -1)
1026					NFSVNO_SETATTRVAL(&nva, size,
1027					    (u_quad_t)tsize);
1028				break;
1029			case VCHR:
1030			case VBLK:
1031			case VFIFO:
1032				rdev = fxdr_unsigned(NFSDEV_T, sp->sa_size);
1033				break;
1034			default:
1035				break;
1036			}
1037		} else {
1038			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1039			how = fxdr_unsigned(int, *tl);
1040			switch (how) {
1041			case NFSCREATE_GUARDED:
1042			case NFSCREATE_UNCHECKED:
1043				error = nfsrv_sattr(nd, NULL, &nva, NULL, NULL, p);
1044				if (error)
1045					goto nfsmout;
1046				break;
1047			case NFSCREATE_EXCLUSIVE:
1048				NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
1049				cverf[0] = *tl++;
1050				cverf[1] = *tl;
1051				exclusive_flag = 1;
1052				break;
1053			}
1054			NFSVNO_SETATTRVAL(&nva, type, VREG);
1055		}
1056	}
1057	if (nd->nd_repstat) {
1058		nfsvno_relpathbuf(&named);
1059		if (nd->nd_flag & ND_NFSV3) {
1060			dirfor_ret = nfsvno_getattr(dp, &dirfor, nd->nd_cred,
1061			    p, 1);
1062			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1063			    &diraft);
1064		}
1065		vput(dp);
1066		goto out;
1067	}
1068
1069	nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
1070	if (dirp) {
1071		if (nd->nd_flag & ND_NFSV2) {
1072			vrele(dirp);
1073			dirp = NULL;
1074		} else {
1075			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
1076			    p, 0);
1077		}
1078	}
1079	if (nd->nd_repstat) {
1080		if (nd->nd_flag & ND_NFSV3)
1081			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1082			    &diraft);
1083		if (dirp)
1084			vrele(dirp);
1085		goto out;
1086	}
1087
1088	if (!(nd->nd_flag & ND_NFSV2)) {
1089		switch (how) {
1090		case NFSCREATE_GUARDED:
1091			if (named.ni_vp)
1092				nd->nd_repstat = EEXIST;
1093			break;
1094		case NFSCREATE_UNCHECKED:
1095			break;
1096		case NFSCREATE_EXCLUSIVE:
1097			if (named.ni_vp == NULL)
1098				NFSVNO_SETATTRVAL(&nva, mode, 0);
1099			break;
1100		}
1101	}
1102
1103	/*
1104	 * Iff doesn't exist, create it
1105	 * otherwise just truncate to 0 length
1106	 *   should I set the mode too ?
1107	 */
1108	nd->nd_repstat = nfsvno_createsub(nd, &named, &vp, &nva,
1109	    &exclusive_flag, cverf, rdev, p, exp);
1110
1111	if (!nd->nd_repstat) {
1112		nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
1113		if (!nd->nd_repstat)
1114			nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred,
1115			    p, 1);
1116		vput(vp);
1117		if (!nd->nd_repstat) {
1118			tverf[0] = nva.na_atime.tv_sec;
1119			tverf[1] = nva.na_atime.tv_nsec;
1120		}
1121	}
1122	if (nd->nd_flag & ND_NFSV2) {
1123		if (!nd->nd_repstat) {
1124			(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
1125			nfsrv_fillattr(nd, &nva);
1126		}
1127	} else {
1128		if (exclusive_flag && !nd->nd_repstat && (cverf[0] != tverf[0]
1129		    || cverf[1] != tverf[1]))
1130			nd->nd_repstat = EEXIST;
1131		diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0);
1132		vrele(dirp);
1133		if (!nd->nd_repstat) {
1134			(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 1);
1135			nfsrv_postopattr(nd, 0, &nva);
1136		}
1137		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1138	}
1139
1140out:
1141	NFSEXITCODE2(0, nd);
1142	return (0);
1143nfsmout:
1144	vput(dp);
1145	nfsvno_relpathbuf(&named);
1146	NFSEXITCODE2(error, nd);
1147	return (error);
1148}
1149
1150/*
1151 * nfs v3 mknod service (and v4 create)
1152 */
1153APPLESTATIC int
1154nfsrvd_mknod(struct nfsrv_descript *nd, __unused int isdgram,
1155    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
1156    struct nfsexstuff *exp)
1157{
1158	struct nfsvattr nva, dirfor, diraft;
1159	u_int32_t *tl;
1160	struct nameidata named;
1161	int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
1162	u_int32_t major, minor;
1163	enum vtype vtyp = VNON;
1164	nfstype nfs4type = NFNON;
1165	vnode_t vp, dirp = NULL;
1166	nfsattrbit_t attrbits;
1167	char *bufp = NULL, *pathcp = NULL;
1168	u_long *hashp, cnflags;
1169	NFSACL_T *aclp = NULL;
1170
1171	NFSVNO_ATTRINIT(&nva);
1172	cnflags = (LOCKPARENT | SAVESTART);
1173	if (nd->nd_repstat) {
1174		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1175		goto out;
1176	}
1177#ifdef NFS4_ACL_EXTATTR_NAME
1178	aclp = acl_alloc(M_WAITOK);
1179	aclp->acl_cnt = 0;
1180#endif
1181
1182	/*
1183	 * For V4, the creation stuff is here, Yuck!
1184	 */
1185	if (nd->nd_flag & ND_NFSV4) {
1186		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1187		vtyp = nfsv34tov_type(*tl);
1188		nfs4type = fxdr_unsigned(nfstype, *tl);
1189		switch (nfs4type) {
1190		case NFLNK:
1191			error = nfsvno_getsymlink(nd, &nva, p, &pathcp,
1192			    &pathlen);
1193			if (error)
1194				goto nfsmout;
1195			break;
1196		case NFCHR:
1197		case NFBLK:
1198			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1199			major = fxdr_unsigned(u_int32_t, *tl++);
1200			minor = fxdr_unsigned(u_int32_t, *tl);
1201			nva.na_rdev = NFSMAKEDEV(major, minor);
1202			break;
1203		case NFSOCK:
1204		case NFFIFO:
1205			break;
1206		case NFDIR:
1207			cnflags = (LOCKPARENT | SAVENAME);
1208			break;
1209		default:
1210			nd->nd_repstat = NFSERR_BADTYPE;
1211			vrele(dp);
1212#ifdef NFS4_ACL_EXTATTR_NAME
1213			acl_free(aclp);
1214#endif
1215			goto out;
1216		}
1217	}
1218	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, cnflags | NOCACHE);
1219	nfsvno_setpathbuf(&named, &bufp, &hashp);
1220	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1221	if (error)
1222		goto nfsmout;
1223	if (!nd->nd_repstat) {
1224		if (nd->nd_flag & ND_NFSV3) {
1225			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1226			vtyp = nfsv34tov_type(*tl);
1227		}
1228		error = nfsrv_sattr(nd, NULL, &nva, &attrbits, aclp, p);
1229		if (error)
1230			goto nfsmout;
1231		nva.na_type = vtyp;
1232		if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV3) &&
1233		    (vtyp == VCHR || vtyp == VBLK)) {
1234			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1235			major = fxdr_unsigned(u_int32_t, *tl++);
1236			minor = fxdr_unsigned(u_int32_t, *tl);
1237			nva.na_rdev = NFSMAKEDEV(major, minor);
1238		}
1239	}
1240
1241	dirfor_ret = nfsvno_getattr(dp, &dirfor, nd->nd_cred, p, 0);
1242	if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
1243		if (!dirfor_ret && NFSVNO_ISSETGID(&nva) &&
1244		    dirfor.na_gid == nva.na_gid)
1245			NFSVNO_UNSET(&nva, gid);
1246		nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
1247	}
1248	if (nd->nd_repstat) {
1249		vrele(dp);
1250#ifdef NFS4_ACL_EXTATTR_NAME
1251		acl_free(aclp);
1252#endif
1253		nfsvno_relpathbuf(&named);
1254		if (pathcp)
1255			FREE(pathcp, M_TEMP);
1256		if (nd->nd_flag & ND_NFSV3)
1257			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1258			    &diraft);
1259		goto out;
1260	}
1261
1262	/*
1263	 * Yuck! For V4, mkdir and link are here and some V4 clients don't fill
1264	 * in va_mode, so we'll have to set a default here.
1265	 */
1266	if (NFSVNO_NOTSETMODE(&nva)) {
1267		if (vtyp == VLNK)
1268			nva.na_mode = 0755;
1269		else
1270			nva.na_mode = 0400;
1271	}
1272
1273	if (vtyp == VDIR)
1274		named.ni_cnd.cn_flags |= WILLBEDIR;
1275	nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
1276	if (nd->nd_repstat) {
1277		if (dirp) {
1278			if (nd->nd_flag & ND_NFSV3)
1279				dirfor_ret = nfsvno_getattr(dirp, &dirfor,
1280				    nd->nd_cred, p, 0);
1281			vrele(dirp);
1282		}
1283#ifdef NFS4_ACL_EXTATTR_NAME
1284		acl_free(aclp);
1285#endif
1286		if (nd->nd_flag & ND_NFSV3)
1287			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1288			    &diraft);
1289		goto out;
1290	}
1291	if (dirp)
1292		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
1293
1294	if ((nd->nd_flag & ND_NFSV4) && (vtyp == VDIR || vtyp == VLNK)) {
1295		if (vtyp == VDIR) {
1296			nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp,
1297			    &dirfor, &diraft, &diraft_ret, &attrbits, aclp, p,
1298			    exp);
1299#ifdef NFS4_ACL_EXTATTR_NAME
1300			acl_free(aclp);
1301#endif
1302			goto out;
1303		} else if (vtyp == VLNK) {
1304			nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
1305			    &dirfor, &diraft, &diraft_ret, &attrbits,
1306			    aclp, p, exp, pathcp, pathlen);
1307#ifdef NFS4_ACL_EXTATTR_NAME
1308			acl_free(aclp);
1309#endif
1310			FREE(pathcp, M_TEMP);
1311			goto out;
1312		}
1313	}
1314
1315	nd->nd_repstat = nfsvno_mknod(&named, &nva, nd->nd_cred, p);
1316	if (!nd->nd_repstat) {
1317		vp = named.ni_vp;
1318		nfsrv_fixattr(nd, vp, &nva, aclp, p, &attrbits, exp);
1319		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
1320		if ((nd->nd_flag & ND_NFSV3) && !nd->nd_repstat)
1321			nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred,
1322			    p, 1);
1323		if (vpp != NULL && nd->nd_repstat == 0) {
1324			NFSVOPUNLOCK(vp, 0);
1325			*vpp = vp;
1326		} else
1327			vput(vp);
1328	}
1329
1330	diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0);
1331	vrele(dirp);
1332	if (!nd->nd_repstat) {
1333		if (nd->nd_flag & ND_NFSV3) {
1334			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
1335			nfsrv_postopattr(nd, 0, &nva);
1336		} else {
1337			NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1338			*tl++ = newnfs_false;
1339			txdr_hyper(dirfor.na_filerev, tl);
1340			tl += 2;
1341			txdr_hyper(diraft.na_filerev, tl);
1342			(void) nfsrv_putattrbit(nd, &attrbits);
1343		}
1344	}
1345	if (nd->nd_flag & ND_NFSV3)
1346		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1347#ifdef NFS4_ACL_EXTATTR_NAME
1348	acl_free(aclp);
1349#endif
1350
1351out:
1352	NFSEXITCODE2(0, nd);
1353	return (0);
1354nfsmout:
1355	vrele(dp);
1356#ifdef NFS4_ACL_EXTATTR_NAME
1357	acl_free(aclp);
1358#endif
1359	if (bufp)
1360		nfsvno_relpathbuf(&named);
1361	if (pathcp)
1362		FREE(pathcp, M_TEMP);
1363
1364	NFSEXITCODE2(error, nd);
1365	return (error);
1366}
1367
1368/*
1369 * nfs remove service
1370 */
1371APPLESTATIC int
1372nfsrvd_remove(struct nfsrv_descript *nd, __unused int isdgram,
1373    vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
1374{
1375	struct nameidata named;
1376	u_int32_t *tl;
1377	int error = 0, dirfor_ret = 1, diraft_ret = 1;
1378	vnode_t dirp = NULL;
1379	struct nfsvattr dirfor, diraft;
1380	char *bufp;
1381	u_long *hashp;
1382
1383	if (nd->nd_repstat) {
1384		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1385		goto out;
1386	}
1387	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, DELETE,
1388	    LOCKPARENT | LOCKLEAF);
1389	nfsvno_setpathbuf(&named, &bufp, &hashp);
1390	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1391	if (error) {
1392		vput(dp);
1393		nfsvno_relpathbuf(&named);
1394		goto out;
1395	}
1396	if (!nd->nd_repstat) {
1397		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
1398	} else {
1399		vput(dp);
1400		nfsvno_relpathbuf(&named);
1401	}
1402	if (dirp) {
1403		if (!(nd->nd_flag & ND_NFSV2)) {
1404			dirfor_ret = nfsvno_getattr(dirp, &dirfor,
1405			    nd->nd_cred, p, 0);
1406		} else {
1407			vrele(dirp);
1408			dirp = NULL;
1409		}
1410	}
1411	if (!nd->nd_repstat) {
1412		if (nd->nd_flag & ND_NFSV4) {
1413			if (vnode_vtype(named.ni_vp) == VDIR)
1414				nd->nd_repstat = nfsvno_rmdirsub(&named, 1,
1415				    nd->nd_cred, p, exp);
1416			else
1417				nd->nd_repstat = nfsvno_removesub(&named, 1,
1418				    nd->nd_cred, p, exp);
1419		} else if (nd->nd_procnum == NFSPROC_RMDIR) {
1420			nd->nd_repstat = nfsvno_rmdirsub(&named, 0,
1421			    nd->nd_cred, p, exp);
1422		} else {
1423			nd->nd_repstat = nfsvno_removesub(&named, 0,
1424			    nd->nd_cred, p, exp);
1425		}
1426	}
1427	if (!(nd->nd_flag & ND_NFSV2)) {
1428		if (dirp) {
1429			diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred,
1430			    p, 0);
1431			vrele(dirp);
1432		}
1433		if (nd->nd_flag & ND_NFSV3) {
1434			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1435			    &diraft);
1436		} else if (!nd->nd_repstat) {
1437			NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1438			*tl++ = newnfs_false;
1439			txdr_hyper(dirfor.na_filerev, tl);
1440			tl += 2;
1441			txdr_hyper(diraft.na_filerev, tl);
1442		}
1443	}
1444
1445out:
1446	NFSEXITCODE2(error, nd);
1447	return (error);
1448}
1449
1450/*
1451 * nfs rename service
1452 */
1453APPLESTATIC int
1454nfsrvd_rename(struct nfsrv_descript *nd, int isdgram,
1455    vnode_t dp, vnode_t todp, NFSPROC_T *p, struct nfsexstuff *exp,
1456    struct nfsexstuff *toexp)
1457{
1458	u_int32_t *tl;
1459	int error = 0, fdirfor_ret = 1, fdiraft_ret = 1;
1460	int tdirfor_ret = 1, tdiraft_ret = 1;
1461	struct nameidata fromnd, tond;
1462	vnode_t fdirp = NULL, tdirp = NULL, tdp = NULL;
1463	struct nfsvattr fdirfor, fdiraft, tdirfor, tdiraft;
1464	struct nfsexstuff tnes;
1465	struct nfsrvfh tfh;
1466	char *bufp, *tbufp = NULL;
1467	u_long *hashp;
1468	fhandle_t fh;
1469
1470	if (nd->nd_repstat) {
1471		nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
1472		nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
1473		goto out;
1474	}
1475	if (!(nd->nd_flag & ND_NFSV2))
1476		fdirfor_ret = nfsvno_getattr(dp, &fdirfor, nd->nd_cred, p, 1);
1477	tond.ni_cnd.cn_nameiop = 0;
1478	tond.ni_startdir = NULL;
1479	NFSNAMEICNDSET(&fromnd.ni_cnd, nd->nd_cred, DELETE, WANTPARENT | SAVESTART);
1480	nfsvno_setpathbuf(&fromnd, &bufp, &hashp);
1481	error = nfsrv_parsename(nd, bufp, hashp, &fromnd.ni_pathlen);
1482	if (error) {
1483		vput(dp);
1484		if (todp)
1485			vrele(todp);
1486		nfsvno_relpathbuf(&fromnd);
1487		goto out;
1488	}
1489	/*
1490	 * Unlock dp in this code section, so it is unlocked before
1491	 * tdp gets locked. This avoids a potential LOR if tdp is the
1492	 * parent directory of dp.
1493	 */
1494	if (nd->nd_flag & ND_NFSV4) {
1495		tdp = todp;
1496		tnes = *toexp;
1497		if (dp != tdp) {
1498			NFSVOPUNLOCK(dp, 0);
1499			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred,
1500			    p, 0);	/* Might lock tdp. */
1501		} else {
1502			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred,
1503			    p, 1);
1504			NFSVOPUNLOCK(dp, 0);
1505		}
1506	} else {
1507		tfh.nfsrvfh_len = 0;
1508		error = nfsrv_mtofh(nd, &tfh);
1509		if (error == 0)
1510			error = nfsvno_getfh(dp, &fh, p);
1511		if (error) {
1512			vput(dp);
1513			/* todp is always NULL except NFSv4 */
1514			nfsvno_relpathbuf(&fromnd);
1515			goto out;
1516		}
1517
1518		/* If this is the same file handle, just VREF() the vnode. */
1519		if (tfh.nfsrvfh_len == NFSX_MYFH &&
1520		    !NFSBCMP(tfh.nfsrvfh_data, &fh, NFSX_MYFH)) {
1521			VREF(dp);
1522			tdp = dp;
1523			tnes = *exp;
1524			tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred,
1525			    p, 1);
1526			NFSVOPUNLOCK(dp, 0);
1527		} else {
1528			NFSVOPUNLOCK(dp, 0);
1529			nd->nd_cred->cr_uid = nd->nd_saveduid;
1530			nfsd_fhtovp(nd, &tfh, LK_EXCLUSIVE, &tdp, &tnes, NULL,
1531			    0, p);	/* Locks tdp. */
1532			if (tdp) {
1533				tdirfor_ret = nfsvno_getattr(tdp, &tdirfor,
1534				    nd->nd_cred, p, 1);
1535				NFSVOPUNLOCK(tdp, 0);
1536			}
1537		}
1538	}
1539	NFSNAMEICNDSET(&tond.ni_cnd, nd->nd_cred, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART);
1540	nfsvno_setpathbuf(&tond, &tbufp, &hashp);
1541	if (!nd->nd_repstat) {
1542		error = nfsrv_parsename(nd, tbufp, hashp, &tond.ni_pathlen);
1543		if (error) {
1544			if (tdp)
1545				vrele(tdp);
1546			vrele(dp);
1547			nfsvno_relpathbuf(&fromnd);
1548			nfsvno_relpathbuf(&tond);
1549			goto out;
1550		}
1551	}
1552	if (nd->nd_repstat) {
1553		if (nd->nd_flag & ND_NFSV3) {
1554			nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
1555			    &fdiraft);
1556			nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
1557			    &tdiraft);
1558		}
1559		if (tdp)
1560			vrele(tdp);
1561		vrele(dp);
1562		nfsvno_relpathbuf(&fromnd);
1563		nfsvno_relpathbuf(&tond);
1564		goto out;
1565	}
1566
1567	/*
1568	 * Done parsing, now down to business.
1569	 */
1570	nd->nd_repstat = nfsvno_namei(nd, &fromnd, dp, 0, exp, p, &fdirp);
1571	if (nd->nd_repstat) {
1572		if (nd->nd_flag & ND_NFSV3) {
1573			nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
1574			    &fdiraft);
1575			nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
1576			    &tdiraft);
1577		}
1578		if (fdirp)
1579			vrele(fdirp);
1580		if (tdp)
1581			vrele(tdp);
1582		nfsvno_relpathbuf(&tond);
1583		goto out;
1584	}
1585	if (vnode_vtype(fromnd.ni_vp) == VDIR)
1586		tond.ni_cnd.cn_flags |= WILLBEDIR;
1587	nd->nd_repstat = nfsvno_namei(nd, &tond, tdp, 0, &tnes, p, &tdirp);
1588	nd->nd_repstat = nfsvno_rename(&fromnd, &tond, nd->nd_repstat,
1589	    nd->nd_flag, nd->nd_cred, p);
1590	if (fdirp)
1591		fdiraft_ret = nfsvno_getattr(fdirp, &fdiraft, nd->nd_cred, p,
1592		    0);
1593	if (tdirp)
1594		tdiraft_ret = nfsvno_getattr(tdirp, &tdiraft, nd->nd_cred, p,
1595		    0);
1596	if (fdirp)
1597		vrele(fdirp);
1598	if (tdirp)
1599		vrele(tdirp);
1600	if (nd->nd_flag & ND_NFSV3) {
1601		nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
1602		nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
1603	} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1604		NFSM_BUILD(tl, u_int32_t *, 10 * NFSX_UNSIGNED);
1605		*tl++ = newnfs_false;
1606		txdr_hyper(fdirfor.na_filerev, tl);
1607		tl += 2;
1608		txdr_hyper(fdiraft.na_filerev, tl);
1609		tl += 2;
1610		*tl++ = newnfs_false;
1611		txdr_hyper(tdirfor.na_filerev, tl);
1612		tl += 2;
1613		txdr_hyper(tdiraft.na_filerev, tl);
1614	}
1615
1616out:
1617	NFSEXITCODE2(error, nd);
1618	return (error);
1619}
1620
1621/*
1622 * nfs link service
1623 */
1624APPLESTATIC int
1625nfsrvd_link(struct nfsrv_descript *nd, int isdgram,
1626    vnode_t vp, vnode_t tovp, NFSPROC_T *p, struct nfsexstuff *exp,
1627    struct nfsexstuff *toexp)
1628{
1629	struct nameidata named;
1630	u_int32_t *tl;
1631	int error = 0, dirfor_ret = 1, diraft_ret = 1, getret = 1;
1632	vnode_t dirp = NULL, dp = NULL;
1633	struct nfsvattr dirfor, diraft, at;
1634	struct nfsexstuff tnes;
1635	struct nfsrvfh dfh;
1636	char *bufp;
1637	u_long *hashp;
1638
1639	if (nd->nd_repstat) {
1640		nfsrv_postopattr(nd, getret, &at);
1641		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1642		goto out;
1643	}
1644	NFSVOPUNLOCK(vp, 0);
1645	if (vnode_vtype(vp) == VDIR) {
1646		if (nd->nd_flag & ND_NFSV4)
1647			nd->nd_repstat = NFSERR_ISDIR;
1648		else
1649			nd->nd_repstat = NFSERR_INVAL;
1650		if (tovp)
1651			vrele(tovp);
1652	}
1653	if (!nd->nd_repstat) {
1654		if (nd->nd_flag & ND_NFSV4) {
1655			dp = tovp;
1656			tnes = *toexp;
1657		} else {
1658			error = nfsrv_mtofh(nd, &dfh);
1659			if (error) {
1660				vrele(vp);
1661				/* tovp is always NULL unless NFSv4 */
1662				goto out;
1663			}
1664			nfsd_fhtovp(nd, &dfh, LK_EXCLUSIVE, &dp, &tnes, NULL, 0,
1665			    p);
1666			if (dp)
1667				NFSVOPUNLOCK(dp, 0);
1668		}
1669	}
1670	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
1671	    LOCKPARENT | SAVENAME | NOCACHE);
1672	if (!nd->nd_repstat) {
1673		nfsvno_setpathbuf(&named, &bufp, &hashp);
1674		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1675		if (error) {
1676			vrele(vp);
1677			if (dp)
1678				vrele(dp);
1679			nfsvno_relpathbuf(&named);
1680			goto out;
1681		}
1682		if (!nd->nd_repstat) {
1683			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, &tnes,
1684			    p, &dirp);
1685		} else {
1686			if (dp)
1687				vrele(dp);
1688			nfsvno_relpathbuf(&named);
1689		}
1690	}
1691	if (dirp) {
1692		if (nd->nd_flag & ND_NFSV2) {
1693			vrele(dirp);
1694			dirp = NULL;
1695		} else {
1696			dirfor_ret = nfsvno_getattr(dirp, &dirfor,
1697			    nd->nd_cred, p, 0);
1698		}
1699	}
1700	if (!nd->nd_repstat)
1701		nd->nd_repstat = nfsvno_link(&named, vp, nd->nd_cred, p, exp);
1702	if (nd->nd_flag & ND_NFSV3)
1703		getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 0);
1704	if (dirp) {
1705		diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0);
1706		vrele(dirp);
1707	}
1708	vrele(vp);
1709	if (nd->nd_flag & ND_NFSV3) {
1710		nfsrv_postopattr(nd, getret, &at);
1711		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1712	} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1713		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1714		*tl++ = newnfs_false;
1715		txdr_hyper(dirfor.na_filerev, tl);
1716		tl += 2;
1717		txdr_hyper(diraft.na_filerev, tl);
1718	}
1719
1720out:
1721	NFSEXITCODE2(error, nd);
1722	return (error);
1723}
1724
1725/*
1726 * nfs symbolic link service
1727 */
1728APPLESTATIC int
1729nfsrvd_symlink(struct nfsrv_descript *nd, __unused int isdgram,
1730    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
1731    struct nfsexstuff *exp)
1732{
1733	struct nfsvattr nva, dirfor, diraft;
1734	struct nameidata named;
1735	int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
1736	vnode_t dirp = NULL;
1737	char *bufp, *pathcp = NULL;
1738	u_long *hashp;
1739
1740	if (nd->nd_repstat) {
1741		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1742		goto out;
1743	}
1744	if (vpp)
1745		*vpp = NULL;
1746	NFSVNO_ATTRINIT(&nva);
1747	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
1748	    LOCKPARENT | SAVESTART | NOCACHE);
1749	nfsvno_setpathbuf(&named, &bufp, &hashp);
1750	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1751	if (!error && !nd->nd_repstat)
1752		error = nfsvno_getsymlink(nd, &nva, p, &pathcp, &pathlen);
1753	if (error) {
1754		vrele(dp);
1755		nfsvno_relpathbuf(&named);
1756		goto out;
1757	}
1758	if (!nd->nd_repstat) {
1759		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
1760	} else {
1761		vrele(dp);
1762		nfsvno_relpathbuf(&named);
1763	}
1764	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
1765		vrele(dirp);
1766		dirp = NULL;
1767	}
1768
1769	/*
1770	 * And call nfsrvd_symlinksub() to do the common code. It will
1771	 * return EBADRPC upon a parsing error, 0 otherwise.
1772	 */
1773	if (!nd->nd_repstat) {
1774		if (dirp != NULL)
1775			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
1776			    p, 0);
1777		nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
1778		    &dirfor, &diraft, &diraft_ret, NULL, NULL, p, exp,
1779		    pathcp, pathlen);
1780	} else if (dirp != NULL) {
1781		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
1782		vrele(dirp);
1783	}
1784	if (pathcp)
1785		FREE(pathcp, M_TEMP);
1786
1787	if (nd->nd_flag & ND_NFSV3) {
1788		if (!nd->nd_repstat) {
1789			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
1790			nfsrv_postopattr(nd, 0, &nva);
1791		}
1792		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1793	}
1794
1795out:
1796	NFSEXITCODE2(error, nd);
1797	return (error);
1798}
1799
1800/*
1801 * Common code for creating a symbolic link.
1802 */
1803static void
1804nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
1805    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
1806    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
1807    int *diraft_retp, nfsattrbit_t *attrbitp,
1808    NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
1809    int pathlen)
1810{
1811	u_int32_t *tl;
1812
1813	nd->nd_repstat = nfsvno_symlink(ndp, nvap, pathcp, pathlen,
1814	    !(nd->nd_flag & ND_NFSV2), nd->nd_saveduid, nd->nd_cred, p, exp);
1815	if (!nd->nd_repstat && !(nd->nd_flag & ND_NFSV2)) {
1816		nfsrv_fixattr(nd, ndp->ni_vp, nvap, aclp, p, attrbitp, exp);
1817		if (nd->nd_flag & ND_NFSV3) {
1818			nd->nd_repstat = nfsvno_getfh(ndp->ni_vp, fhp, p);
1819			if (!nd->nd_repstat)
1820				nd->nd_repstat = nfsvno_getattr(ndp->ni_vp,
1821				    nvap, nd->nd_cred, p, 1);
1822		}
1823		if (vpp != NULL && nd->nd_repstat == 0) {
1824			NFSVOPUNLOCK(ndp->ni_vp, 0);
1825			*vpp = ndp->ni_vp;
1826		} else
1827			vput(ndp->ni_vp);
1828	}
1829	if (dirp) {
1830		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p, 0);
1831		vrele(dirp);
1832	}
1833	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1834		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1835		*tl++ = newnfs_false;
1836		txdr_hyper(dirforp->na_filerev, tl);
1837		tl += 2;
1838		txdr_hyper(diraftp->na_filerev, tl);
1839		(void) nfsrv_putattrbit(nd, attrbitp);
1840	}
1841
1842	NFSEXITCODE2(0, nd);
1843}
1844
1845/*
1846 * nfs mkdir service
1847 */
1848APPLESTATIC int
1849nfsrvd_mkdir(struct nfsrv_descript *nd, __unused int isdgram,
1850    vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
1851    struct nfsexstuff *exp)
1852{
1853	struct nfsvattr nva, dirfor, diraft;
1854	struct nameidata named;
1855	u_int32_t *tl;
1856	int error = 0, dirfor_ret = 1, diraft_ret = 1;
1857	vnode_t dirp = NULL;
1858	char *bufp;
1859	u_long *hashp;
1860
1861	if (nd->nd_repstat) {
1862		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1863		goto out;
1864	}
1865	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
1866	    LOCKPARENT | SAVENAME | NOCACHE);
1867	nfsvno_setpathbuf(&named, &bufp, &hashp);
1868	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
1869	if (error)
1870		goto nfsmout;
1871	if (!nd->nd_repstat) {
1872		NFSVNO_ATTRINIT(&nva);
1873		if (nd->nd_flag & ND_NFSV3) {
1874			error = nfsrv_sattr(nd, NULL, &nva, NULL, NULL, p);
1875			if (error)
1876				goto nfsmout;
1877		} else {
1878			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1879			nva.na_mode = nfstov_mode(*tl++);
1880		}
1881	}
1882	if (!nd->nd_repstat) {
1883		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
1884	} else {
1885		vrele(dp);
1886		nfsvno_relpathbuf(&named);
1887	}
1888	if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
1889		vrele(dirp);
1890		dirp = NULL;
1891	}
1892	if (nd->nd_repstat) {
1893		if (dirp != NULL) {
1894			dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
1895			    p, 0);
1896			vrele(dirp);
1897		}
1898		if (nd->nd_flag & ND_NFSV3)
1899			nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
1900			    &diraft);
1901		goto out;
1902	}
1903	if (dirp != NULL)
1904		dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0);
1905
1906	/*
1907	 * Call nfsrvd_mkdirsub() for the code common to V4 as well.
1908	 */
1909	nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp, &dirfor, &diraft,
1910	    &diraft_ret, NULL, NULL, p, exp);
1911
1912	if (nd->nd_flag & ND_NFSV3) {
1913		if (!nd->nd_repstat) {
1914			(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
1915			nfsrv_postopattr(nd, 0, &nva);
1916		}
1917		nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
1918	} else if (!nd->nd_repstat) {
1919		(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
1920		nfsrv_fillattr(nd, &nva);
1921	}
1922
1923out:
1924	NFSEXITCODE2(0, nd);
1925	return (0);
1926nfsmout:
1927	vrele(dp);
1928	nfsvno_relpathbuf(&named);
1929	NFSEXITCODE2(error, nd);
1930	return (error);
1931}
1932
1933/*
1934 * Code common to mkdir for V2,3 and 4.
1935 */
1936static void
1937nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
1938    struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
1939    vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
1940    int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
1941    NFSPROC_T *p, struct nfsexstuff *exp)
1942{
1943	vnode_t vp;
1944	u_int32_t *tl;
1945
1946	NFSVNO_SETATTRVAL(nvap, type, VDIR);
1947	nd->nd_repstat = nfsvno_mkdir(ndp, nvap, nd->nd_saveduid,
1948	    nd->nd_cred, p, exp);
1949	if (!nd->nd_repstat) {
1950		vp = ndp->ni_vp;
1951		nfsrv_fixattr(nd, vp, nvap, aclp, p, attrbitp, exp);
1952		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
1953		if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
1954			nd->nd_repstat = nfsvno_getattr(vp, nvap, nd->nd_cred,
1955			    p, 1);
1956		if (vpp && !nd->nd_repstat) {
1957			NFSVOPUNLOCK(vp, 0);
1958			*vpp = vp;
1959		} else {
1960			vput(vp);
1961		}
1962	}
1963	if (dirp) {
1964		*diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p, 0);
1965		vrele(dirp);
1966	}
1967	if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
1968		NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1969		*tl++ = newnfs_false;
1970		txdr_hyper(dirforp->na_filerev, tl);
1971		tl += 2;
1972		txdr_hyper(diraftp->na_filerev, tl);
1973		(void) nfsrv_putattrbit(nd, attrbitp);
1974	}
1975
1976	NFSEXITCODE2(0, nd);
1977}
1978
1979/*
1980 * nfs commit service
1981 */
1982APPLESTATIC int
1983nfsrvd_commit(struct nfsrv_descript *nd, __unused int isdgram,
1984    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
1985{
1986	struct nfsvattr bfor, aft;
1987	u_int32_t *tl;
1988	int error = 0, for_ret = 1, aft_ret = 1, cnt;
1989	u_int64_t off;
1990
1991       if (nd->nd_repstat) {
1992		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
1993		goto out;
1994	}
1995
1996	/* Return NFSERR_ISDIR in NFSv4 when commit on a directory. */
1997	if (vp->v_type != VREG) {
1998		if (nd->nd_flag & ND_NFSV3)
1999			error = NFSERR_NOTSUPP;
2000		else
2001			error = (vp->v_type == VDIR) ? NFSERR_ISDIR : NFSERR_INVAL;
2002		goto nfsmout;
2003	}
2004	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
2005
2006	/*
2007	 * XXX At this time VOP_FSYNC() does not accept offset and byte
2008	 * count parameters, so these arguments are useless (someday maybe).
2009	 */
2010	off = fxdr_hyper(tl);
2011	tl += 2;
2012	cnt = fxdr_unsigned(int, *tl);
2013	if (nd->nd_flag & ND_NFSV3)
2014		for_ret = nfsvno_getattr(vp, &bfor, nd->nd_cred, p, 1);
2015	nd->nd_repstat = nfsvno_fsync(vp, off, cnt, nd->nd_cred, p);
2016	if (nd->nd_flag & ND_NFSV3) {
2017		aft_ret = nfsvno_getattr(vp, &aft, nd->nd_cred, p, 1);
2018		nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
2019	}
2020	vput(vp);
2021	if (!nd->nd_repstat) {
2022		NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
2023		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
2024		*tl = txdr_unsigned(nfsboottime.tv_usec);
2025	}
2026
2027out:
2028	NFSEXITCODE2(0, nd);
2029	return (0);
2030nfsmout:
2031	vput(vp);
2032	NFSEXITCODE2(error, nd);
2033	return (error);
2034}
2035
2036/*
2037 * nfs statfs service
2038 */
2039APPLESTATIC int
2040nfsrvd_statfs(struct nfsrv_descript *nd, __unused int isdgram,
2041    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2042{
2043	struct statfs *sf;
2044	u_int32_t *tl;
2045	int getret = 1;
2046	struct nfsvattr at;
2047	u_quad_t tval;
2048
2049	sf = NULL;
2050	if (nd->nd_repstat) {
2051		nfsrv_postopattr(nd, getret, &at);
2052		goto out;
2053	}
2054	sf = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
2055	nd->nd_repstat = nfsvno_statfs(vp, sf);
2056	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
2057	vput(vp);
2058	if (nd->nd_flag & ND_NFSV3)
2059		nfsrv_postopattr(nd, getret, &at);
2060	if (nd->nd_repstat)
2061		goto out;
2062	if (nd->nd_flag & ND_NFSV2) {
2063		NFSM_BUILD(tl, u_int32_t *, NFSX_V2STATFS);
2064		*tl++ = txdr_unsigned(NFS_V2MAXDATA);
2065		*tl++ = txdr_unsigned(sf->f_bsize);
2066		*tl++ = txdr_unsigned(sf->f_blocks);
2067		*tl++ = txdr_unsigned(sf->f_bfree);
2068		*tl = txdr_unsigned(sf->f_bavail);
2069	} else {
2070		NFSM_BUILD(tl, u_int32_t *, NFSX_V3STATFS);
2071		tval = (u_quad_t)sf->f_blocks;
2072		tval *= (u_quad_t)sf->f_bsize;
2073		txdr_hyper(tval, tl); tl += 2;
2074		tval = (u_quad_t)sf->f_bfree;
2075		tval *= (u_quad_t)sf->f_bsize;
2076		txdr_hyper(tval, tl); tl += 2;
2077		tval = (u_quad_t)sf->f_bavail;
2078		tval *= (u_quad_t)sf->f_bsize;
2079		txdr_hyper(tval, tl); tl += 2;
2080		tval = (u_quad_t)sf->f_files;
2081		txdr_hyper(tval, tl); tl += 2;
2082		tval = (u_quad_t)sf->f_ffree;
2083		txdr_hyper(tval, tl); tl += 2;
2084		tval = (u_quad_t)sf->f_ffree;
2085		txdr_hyper(tval, tl); tl += 2;
2086		*tl = 0;
2087	}
2088
2089out:
2090	free(sf, M_STATFS);
2091	NFSEXITCODE2(0, nd);
2092	return (0);
2093}
2094
2095/*
2096 * nfs fsinfo service
2097 */
2098APPLESTATIC int
2099nfsrvd_fsinfo(struct nfsrv_descript *nd, int isdgram,
2100    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2101{
2102	u_int32_t *tl;
2103	struct nfsfsinfo fs;
2104	int getret = 1;
2105	struct nfsvattr at;
2106
2107	if (nd->nd_repstat) {
2108		nfsrv_postopattr(nd, getret, &at);
2109		goto out;
2110	}
2111	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
2112	nfsvno_getfs(&fs, isdgram);
2113	vput(vp);
2114	nfsrv_postopattr(nd, getret, &at);
2115	NFSM_BUILD(tl, u_int32_t *, NFSX_V3FSINFO);
2116	*tl++ = txdr_unsigned(fs.fs_rtmax);
2117	*tl++ = txdr_unsigned(fs.fs_rtpref);
2118	*tl++ = txdr_unsigned(fs.fs_rtmult);
2119	*tl++ = txdr_unsigned(fs.fs_wtmax);
2120	*tl++ = txdr_unsigned(fs.fs_wtpref);
2121	*tl++ = txdr_unsigned(fs.fs_wtmult);
2122	*tl++ = txdr_unsigned(fs.fs_dtpref);
2123	txdr_hyper(fs.fs_maxfilesize, tl);
2124	tl += 2;
2125	txdr_nfsv3time(&fs.fs_timedelta, tl);
2126	tl += 2;
2127	*tl = txdr_unsigned(fs.fs_properties);
2128
2129out:
2130	NFSEXITCODE2(0, nd);
2131	return (0);
2132}
2133
2134/*
2135 * nfs pathconf service
2136 */
2137APPLESTATIC int
2138nfsrvd_pathconf(struct nfsrv_descript *nd, __unused int isdgram,
2139    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
2140{
2141	struct nfsv3_pathconf *pc;
2142	int getret = 1;
2143	register_t linkmax, namemax, chownres, notrunc;
2144	struct nfsvattr at;
2145
2146	if (nd->nd_repstat) {
2147		nfsrv_postopattr(nd, getret, &at);
2148		goto out;
2149	}
2150	nd->nd_repstat = nfsvno_pathconf(vp, _PC_LINK_MAX, &linkmax,
2151	    nd->nd_cred, p);
2152	if (!nd->nd_repstat)
2153		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NAME_MAX, &namemax,
2154		    nd->nd_cred, p);
2155	if (!nd->nd_repstat)
2156		nd->nd_repstat=nfsvno_pathconf(vp, _PC_CHOWN_RESTRICTED,
2157		    &chownres, nd->nd_cred, p);
2158	if (!nd->nd_repstat)
2159		nd->nd_repstat = nfsvno_pathconf(vp, _PC_NO_TRUNC, &notrunc,
2160		    nd->nd_cred, p);
2161	getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1);
2162	vput(vp);
2163	nfsrv_postopattr(nd, getret, &at);
2164	if (!nd->nd_repstat) {
2165		NFSM_BUILD(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
2166		pc->pc_linkmax = txdr_unsigned(linkmax);
2167		pc->pc_namemax = txdr_unsigned(namemax);
2168		pc->pc_notrunc = txdr_unsigned(notrunc);
2169		pc->pc_chownrestricted = txdr_unsigned(chownres);
2170
2171		/*
2172		 * These should probably be supported by VOP_PATHCONF(), but
2173		 * until msdosfs is exportable (why would you want to?), the
2174		 * Unix defaults should be ok.
2175		 */
2176		pc->pc_caseinsensitive = newnfs_false;
2177		pc->pc_casepreserving = newnfs_true;
2178	}
2179
2180out:
2181	NFSEXITCODE2(0, nd);
2182	return (0);
2183}
2184
2185/*
2186 * nfsv4 lock service
2187 */
2188APPLESTATIC int
2189nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram,
2190    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2191{
2192	u_int32_t *tl;
2193	int i;
2194	struct nfsstate *stp = NULL;
2195	struct nfslock *lop;
2196	struct nfslockconflict cf;
2197	int error = 0;
2198	u_short flags = NFSLCK_LOCK, lflags;
2199	u_int64_t offset, len;
2200	nfsv4stateid_t stateid;
2201	nfsquad_t clientid;
2202
2203	NFSM_DISSECT(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2204	i = fxdr_unsigned(int, *tl++);
2205	switch (i) {
2206	case NFSV4LOCKT_READW:
2207		flags |= NFSLCK_BLOCKING;
2208	case NFSV4LOCKT_READ:
2209		lflags = NFSLCK_READ;
2210		break;
2211	case NFSV4LOCKT_WRITEW:
2212		flags |= NFSLCK_BLOCKING;
2213	case NFSV4LOCKT_WRITE:
2214		lflags = NFSLCK_WRITE;
2215		break;
2216	default:
2217		nd->nd_repstat = NFSERR_BADXDR;
2218		goto nfsmout;
2219	}
2220	if (*tl++ == newnfs_true)
2221		flags |= NFSLCK_RECLAIM;
2222	offset = fxdr_hyper(tl);
2223	tl += 2;
2224	len = fxdr_hyper(tl);
2225	tl += 2;
2226	if (*tl == newnfs_true)
2227		flags |= NFSLCK_OPENTOLOCK;
2228	if (flags & NFSLCK_OPENTOLOCK) {
2229		NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID);
2230		i = fxdr_unsigned(int, *(tl+4+(NFSX_STATEID / NFSX_UNSIGNED)));
2231		if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2232			nd->nd_repstat = NFSERR_BADXDR;
2233			goto nfsmout;
2234		}
2235		MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2236			M_NFSDSTATE, M_WAITOK);
2237		stp->ls_ownerlen = i;
2238		stp->ls_op = nd->nd_rp;
2239		stp->ls_seq = fxdr_unsigned(int, *tl++);
2240		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2241		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2242			NFSX_STATEIDOTHER);
2243		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2244		stp->ls_opentolockseq = fxdr_unsigned(int, *tl++);
2245		clientid.lval[0] = *tl++;
2246		clientid.lval[1] = *tl++;
2247		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2248			if ((nd->nd_flag & ND_NFSV41) != 0)
2249				clientid.qval = nd->nd_clientid.qval;
2250			else if (nd->nd_clientid.qval != clientid.qval)
2251				printf("EEK3 multiple clids\n");
2252		} else {
2253			if ((nd->nd_flag & ND_NFSV41) != 0)
2254				printf("EEK! no clientid from session\n");
2255			nd->nd_flag |= ND_IMPLIEDCLID;
2256			nd->nd_clientid.qval = clientid.qval;
2257		}
2258		error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2259		if (error)
2260			goto nfsmout;
2261	} else {
2262		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
2263		MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate),
2264			M_NFSDSTATE, M_WAITOK);
2265		stp->ls_ownerlen = 0;
2266		stp->ls_op = nd->nd_rp;
2267		stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2268		NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2269			NFSX_STATEIDOTHER);
2270		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2271		stp->ls_seq = fxdr_unsigned(int, *tl);
2272		clientid.lval[0] = stp->ls_stateid.other[0];
2273		clientid.lval[1] = stp->ls_stateid.other[1];
2274		if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2275			if ((nd->nd_flag & ND_NFSV41) != 0)
2276				clientid.qval = nd->nd_clientid.qval;
2277			else if (nd->nd_clientid.qval != clientid.qval)
2278				printf("EEK4 multiple clids\n");
2279		} else {
2280			if ((nd->nd_flag & ND_NFSV41) != 0)
2281				printf("EEK! no clientid from session\n");
2282			nd->nd_flag |= ND_IMPLIEDCLID;
2283			nd->nd_clientid.qval = clientid.qval;
2284		}
2285	}
2286	MALLOC(lop, struct nfslock *, sizeof (struct nfslock),
2287		M_NFSDLOCK, M_WAITOK);
2288	lop->lo_first = offset;
2289	if (len == NFS64BITSSET) {
2290		lop->lo_end = NFS64BITSSET;
2291	} else {
2292		lop->lo_end = offset + len;
2293		if (lop->lo_end <= lop->lo_first)
2294			nd->nd_repstat = NFSERR_INVAL;
2295	}
2296	lop->lo_flags = lflags;
2297	stp->ls_flags = flags;
2298	stp->ls_uid = nd->nd_cred->cr_uid;
2299
2300	/*
2301	 * Do basic access checking.
2302	 */
2303	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2304	    if (vnode_vtype(vp) == VDIR)
2305		nd->nd_repstat = NFSERR_ISDIR;
2306	    else
2307		nd->nd_repstat = NFSERR_INVAL;
2308	}
2309	if (!nd->nd_repstat) {
2310	    if (lflags & NFSLCK_WRITE) {
2311		nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
2312		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2313		    NFSACCCHK_VPISLOCKED, NULL);
2314	    } else {
2315		nd->nd_repstat = nfsvno_accchk(vp, VREAD,
2316		    nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2317		    NFSACCCHK_VPISLOCKED, NULL);
2318		if (nd->nd_repstat)
2319		    nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
2320			nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
2321			NFSACCCHK_VPISLOCKED, NULL);
2322	    }
2323	}
2324
2325	/*
2326	 * We call nfsrv_lockctrl() even if nd_repstat set, so that the
2327	 * seqid# gets updated. nfsrv_lockctrl() will return the value
2328	 * of nd_repstat, if it gets that far.
2329	 */
2330	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
2331		&stateid, exp, nd, p);
2332	if (lop)
2333		FREE((caddr_t)lop, M_NFSDLOCK);
2334	if (stp)
2335		FREE((caddr_t)stp, M_NFSDSTATE);
2336	if (!nd->nd_repstat) {
2337		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2338		*tl++ = txdr_unsigned(stateid.seqid);
2339		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2340	} else if (nd->nd_repstat == NFSERR_DENIED) {
2341		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2342		txdr_hyper(cf.cl_first, tl);
2343		tl += 2;
2344		if (cf.cl_end == NFS64BITSSET)
2345			len = NFS64BITSSET;
2346		else
2347			len = cf.cl_end - cf.cl_first;
2348		txdr_hyper(len, tl);
2349		tl += 2;
2350		if (cf.cl_flags == NFSLCK_WRITE)
2351			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
2352		else
2353			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
2354		*tl++ = stateid.other[0];
2355		*tl = stateid.other[1];
2356		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
2357	}
2358	vput(vp);
2359	NFSEXITCODE2(0, nd);
2360	return (0);
2361nfsmout:
2362	vput(vp);
2363	if (stp)
2364		free((caddr_t)stp, M_NFSDSTATE);
2365	NFSEXITCODE2(error, nd);
2366	return (error);
2367}
2368
2369/*
2370 * nfsv4 lock test service
2371 */
2372APPLESTATIC int
2373nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram,
2374    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2375{
2376	u_int32_t *tl;
2377	int i;
2378	struct nfsstate *stp = NULL;
2379	struct nfslock lo, *lop = &lo;
2380	struct nfslockconflict cf;
2381	int error = 0;
2382	nfsv4stateid_t stateid;
2383	nfsquad_t clientid;
2384	u_int64_t len;
2385
2386	NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
2387	i = fxdr_unsigned(int, *(tl + 7));
2388	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2389		nd->nd_repstat = NFSERR_BADXDR;
2390		goto nfsmout;
2391	}
2392	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2393	    M_NFSDSTATE, M_WAITOK);
2394	stp->ls_ownerlen = i;
2395	stp->ls_op = NULL;
2396	stp->ls_flags = NFSLCK_TEST;
2397	stp->ls_uid = nd->nd_cred->cr_uid;
2398	i = fxdr_unsigned(int, *tl++);
2399	switch (i) {
2400	case NFSV4LOCKT_READW:
2401		stp->ls_flags |= NFSLCK_BLOCKING;
2402	case NFSV4LOCKT_READ:
2403		lo.lo_flags = NFSLCK_READ;
2404		break;
2405	case NFSV4LOCKT_WRITEW:
2406		stp->ls_flags |= NFSLCK_BLOCKING;
2407	case NFSV4LOCKT_WRITE:
2408		lo.lo_flags = NFSLCK_WRITE;
2409		break;
2410	default:
2411		nd->nd_repstat = NFSERR_BADXDR;
2412		goto nfsmout;
2413	}
2414	lo.lo_first = fxdr_hyper(tl);
2415	tl += 2;
2416	len = fxdr_hyper(tl);
2417	if (len == NFS64BITSSET) {
2418		lo.lo_end = NFS64BITSSET;
2419	} else {
2420		lo.lo_end = lo.lo_first + len;
2421		if (lo.lo_end <= lo.lo_first)
2422			nd->nd_repstat = NFSERR_INVAL;
2423	}
2424	tl += 2;
2425	clientid.lval[0] = *tl++;
2426	clientid.lval[1] = *tl;
2427	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2428		if ((nd->nd_flag & ND_NFSV41) != 0)
2429			clientid.qval = nd->nd_clientid.qval;
2430		else if (nd->nd_clientid.qval != clientid.qval)
2431			printf("EEK5 multiple clids\n");
2432	} else {
2433		if ((nd->nd_flag & ND_NFSV41) != 0)
2434			printf("EEK! no clientid from session\n");
2435		nd->nd_flag |= ND_IMPLIEDCLID;
2436		nd->nd_clientid.qval = clientid.qval;
2437	}
2438	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2439	if (error)
2440		goto nfsmout;
2441	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2442	    if (vnode_vtype(vp) == VDIR)
2443		nd->nd_repstat = NFSERR_ISDIR;
2444	    else
2445		nd->nd_repstat = NFSERR_INVAL;
2446	}
2447	if (!nd->nd_repstat)
2448	  nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
2449	    &stateid, exp, nd, p);
2450	if (nd->nd_repstat) {
2451	    if (nd->nd_repstat == NFSERR_DENIED) {
2452		NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
2453		txdr_hyper(cf.cl_first, tl);
2454		tl += 2;
2455		if (cf.cl_end == NFS64BITSSET)
2456			len = NFS64BITSSET;
2457		else
2458			len = cf.cl_end - cf.cl_first;
2459		txdr_hyper(len, tl);
2460		tl += 2;
2461		if (cf.cl_flags == NFSLCK_WRITE)
2462			*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
2463		else
2464			*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
2465		*tl++ = stp->ls_stateid.other[0];
2466		*tl = stp->ls_stateid.other[1];
2467		(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
2468	    }
2469	}
2470	vput(vp);
2471	if (stp)
2472		FREE((caddr_t)stp, M_NFSDSTATE);
2473	NFSEXITCODE2(0, nd);
2474	return (0);
2475nfsmout:
2476	vput(vp);
2477	if (stp)
2478		free((caddr_t)stp, M_NFSDSTATE);
2479	NFSEXITCODE2(error, nd);
2480	return (error);
2481}
2482
2483/*
2484 * nfsv4 unlock service
2485 */
2486APPLESTATIC int
2487nfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram,
2488    vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
2489{
2490	u_int32_t *tl;
2491	int i;
2492	struct nfsstate *stp;
2493	struct nfslock *lop;
2494	int error = 0;
2495	nfsv4stateid_t stateid;
2496	nfsquad_t clientid;
2497	u_int64_t len;
2498
2499	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED + NFSX_STATEID);
2500	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate),
2501	    M_NFSDSTATE, M_WAITOK);
2502	MALLOC(lop, struct nfslock *, sizeof (struct nfslock),
2503	    M_NFSDLOCK, M_WAITOK);
2504	stp->ls_flags = NFSLCK_UNLOCK;
2505	lop->lo_flags = NFSLCK_UNLOCK;
2506	stp->ls_op = nd->nd_rp;
2507	i = fxdr_unsigned(int, *tl++);
2508	switch (i) {
2509	case NFSV4LOCKT_READW:
2510		stp->ls_flags |= NFSLCK_BLOCKING;
2511	case NFSV4LOCKT_READ:
2512		break;
2513	case NFSV4LOCKT_WRITEW:
2514		stp->ls_flags |= NFSLCK_BLOCKING;
2515	case NFSV4LOCKT_WRITE:
2516		break;
2517	default:
2518		nd->nd_repstat = NFSERR_BADXDR;
2519		free(stp, M_NFSDSTATE);
2520		free(lop, M_NFSDLOCK);
2521		goto nfsmout;
2522	}
2523	stp->ls_ownerlen = 0;
2524	stp->ls_uid = nd->nd_cred->cr_uid;
2525	stp->ls_seq = fxdr_unsigned(int, *tl++);
2526	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2527	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
2528	    NFSX_STATEIDOTHER);
2529	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2530	lop->lo_first = fxdr_hyper(tl);
2531	tl += 2;
2532	len = fxdr_hyper(tl);
2533	if (len == NFS64BITSSET) {
2534		lop->lo_end = NFS64BITSSET;
2535	} else {
2536		lop->lo_end = lop->lo_first + len;
2537		if (lop->lo_end <= lop->lo_first)
2538			nd->nd_repstat = NFSERR_INVAL;
2539	}
2540	clientid.lval[0] = stp->ls_stateid.other[0];
2541	clientid.lval[1] = stp->ls_stateid.other[1];
2542	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2543		if ((nd->nd_flag & ND_NFSV41) != 0)
2544			clientid.qval = nd->nd_clientid.qval;
2545		else if (nd->nd_clientid.qval != clientid.qval)
2546			printf("EEK6 multiple clids\n");
2547	} else {
2548		if ((nd->nd_flag & ND_NFSV41) != 0)
2549			printf("EEK! no clientid from session\n");
2550		nd->nd_flag |= ND_IMPLIEDCLID;
2551		nd->nd_clientid.qval = clientid.qval;
2552	}
2553	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2554	    if (vnode_vtype(vp) == VDIR)
2555		nd->nd_repstat = NFSERR_ISDIR;
2556	    else
2557		nd->nd_repstat = NFSERR_INVAL;
2558	}
2559	/*
2560	 * Call nfsrv_lockctrl() even if nd_repstat is set, so that the
2561	 * seqid# gets incremented. nfsrv_lockctrl() will return the
2562	 * value of nd_repstat, if it gets that far.
2563	 */
2564	nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
2565	    &stateid, exp, nd, p);
2566	if (stp)
2567		FREE((caddr_t)stp, M_NFSDSTATE);
2568	if (lop)
2569		free((caddr_t)lop, M_NFSDLOCK);
2570	if (!nd->nd_repstat) {
2571		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2572		*tl++ = txdr_unsigned(stateid.seqid);
2573		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2574	}
2575nfsmout:
2576	vput(vp);
2577	NFSEXITCODE2(error, nd);
2578	return (error);
2579}
2580
2581/*
2582 * nfsv4 open service
2583 */
2584APPLESTATIC int
2585nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
2586    vnode_t dp, vnode_t *vpp, __unused fhandle_t *fhp, NFSPROC_T *p,
2587    struct nfsexstuff *exp)
2588{
2589	u_int32_t *tl;
2590	int i, retext;
2591	struct nfsstate *stp = NULL;
2592	int error = 0, create, claim, exclusive_flag = 0, override;
2593	u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask;
2594	int how = NFSCREATE_UNCHECKED;
2595	int32_t cverf[2], tverf[2] = { 0, 0 };
2596	vnode_t vp = NULL, dirp = NULL;
2597	struct nfsvattr nva, dirfor, diraft;
2598	struct nameidata named;
2599	nfsv4stateid_t stateid, delegstateid;
2600	nfsattrbit_t attrbits;
2601	nfsquad_t clientid;
2602	char *bufp = NULL;
2603	u_long *hashp;
2604	NFSACL_T *aclp = NULL;
2605
2606#ifdef NFS4_ACL_EXTATTR_NAME
2607	aclp = acl_alloc(M_WAITOK);
2608	aclp->acl_cnt = 0;
2609#endif
2610	NFSZERO_ATTRBIT(&attrbits);
2611	named.ni_startdir = NULL;
2612	named.ni_cnd.cn_nameiop = 0;
2613	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
2614	i = fxdr_unsigned(int, *(tl + 5));
2615	if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
2616		nd->nd_repstat = NFSERR_BADXDR;
2617		goto nfsmout;
2618	}
2619	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
2620	    M_NFSDSTATE, M_WAITOK);
2621	stp->ls_ownerlen = i;
2622	stp->ls_op = nd->nd_rp;
2623	stp->ls_flags = NFSLCK_OPEN;
2624	stp->ls_uid = nd->nd_cred->cr_uid;
2625	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
2626	i = fxdr_unsigned(int, *tl++);
2627	retext = 0;
2628	if ((i & (NFSV4OPEN_WANTDELEGMASK | NFSV4OPEN_WANTSIGNALDELEG |
2629	    NFSV4OPEN_WANTPUSHDELEG)) != 0 && (nd->nd_flag & ND_NFSV41) != 0) {
2630		retext = 1;
2631		/* For now, ignore these. */
2632		i &= ~(NFSV4OPEN_WANTPUSHDELEG | NFSV4OPEN_WANTSIGNALDELEG);
2633		switch (i & NFSV4OPEN_WANTDELEGMASK) {
2634		case NFSV4OPEN_WANTANYDELEG:
2635			stp->ls_flags |= (NFSLCK_WANTRDELEG |
2636			    NFSLCK_WANTWDELEG);
2637			i &= ~NFSV4OPEN_WANTDELEGMASK;
2638			break;
2639		case NFSV4OPEN_WANTREADDELEG:
2640			stp->ls_flags |= NFSLCK_WANTRDELEG;
2641			i &= ~NFSV4OPEN_WANTDELEGMASK;
2642			break;
2643		case NFSV4OPEN_WANTWRITEDELEG:
2644			stp->ls_flags |= NFSLCK_WANTWDELEG;
2645			i &= ~NFSV4OPEN_WANTDELEGMASK;
2646			break;
2647		case NFSV4OPEN_WANTNODELEG:
2648			stp->ls_flags |= NFSLCK_WANTNODELEG;
2649			i &= ~NFSV4OPEN_WANTDELEGMASK;
2650			break;
2651		case NFSV4OPEN_WANTCANCEL:
2652			printf("NFSv4: ignore Open WantCancel\n");
2653			i &= ~NFSV4OPEN_WANTDELEGMASK;
2654			break;
2655		default:
2656			/* nd_repstat will be set to NFSERR_INVAL below. */
2657			break;
2658		}
2659	}
2660	switch (i) {
2661	case NFSV4OPEN_ACCESSREAD:
2662		stp->ls_flags |= NFSLCK_READACCESS;
2663		break;
2664	case NFSV4OPEN_ACCESSWRITE:
2665		stp->ls_flags |= NFSLCK_WRITEACCESS;
2666		break;
2667	case NFSV4OPEN_ACCESSBOTH:
2668		stp->ls_flags |= (NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
2669		break;
2670	default:
2671		nd->nd_repstat = NFSERR_INVAL;
2672	}
2673	i = fxdr_unsigned(int, *tl++);
2674	switch (i) {
2675	case NFSV4OPEN_DENYNONE:
2676		break;
2677	case NFSV4OPEN_DENYREAD:
2678		stp->ls_flags |= NFSLCK_READDENY;
2679		break;
2680	case NFSV4OPEN_DENYWRITE:
2681		stp->ls_flags |= NFSLCK_WRITEDENY;
2682		break;
2683	case NFSV4OPEN_DENYBOTH:
2684		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
2685		break;
2686	default:
2687		nd->nd_repstat = NFSERR_INVAL;
2688	}
2689	clientid.lval[0] = *tl++;
2690	clientid.lval[1] = *tl;
2691	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
2692		if ((nd->nd_flag & ND_NFSV41) != 0)
2693			clientid.qval = nd->nd_clientid.qval;
2694		else if (nd->nd_clientid.qval != clientid.qval)
2695			printf("EEK7 multiple clids\n");
2696	} else {
2697		if ((nd->nd_flag & ND_NFSV41) != 0)
2698			printf("EEK! no clientid from session\n");
2699		nd->nd_flag |= ND_IMPLIEDCLID;
2700		nd->nd_clientid.qval = clientid.qval;
2701	}
2702	error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
2703	if (error)
2704		goto nfsmout;
2705	NFSVNO_ATTRINIT(&nva);
2706	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2707	create = fxdr_unsigned(int, *tl);
2708	if (!nd->nd_repstat)
2709		nd->nd_repstat = nfsvno_getattr(dp, &dirfor, nd->nd_cred, p, 0);
2710	if (create == NFSV4OPEN_CREATE) {
2711		nva.na_type = VREG;
2712		nva.na_mode = 0;
2713		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2714		how = fxdr_unsigned(int, *tl);
2715		switch (how) {
2716		case NFSCREATE_UNCHECKED:
2717		case NFSCREATE_GUARDED:
2718			error = nfsv4_sattr(nd, NULL, &nva, &attrbits, aclp, p);
2719			if (error)
2720				goto nfsmout;
2721			/*
2722			 * If the na_gid being set is the same as that of
2723			 * the directory it is going in, clear it, since
2724			 * that is what will be set by default. This allows
2725			 * a user that isn't in that group to do the create.
2726			 */
2727			if (!nd->nd_repstat && NFSVNO_ISSETGID(&nva) &&
2728			    nva.na_gid == dirfor.na_gid)
2729				NFSVNO_UNSET(&nva, gid);
2730			if (!nd->nd_repstat)
2731				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
2732			break;
2733		case NFSCREATE_EXCLUSIVE:
2734			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2735			cverf[0] = *tl++;
2736			cverf[1] = *tl;
2737			break;
2738		case NFSCREATE_EXCLUSIVE41:
2739			NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
2740			cverf[0] = *tl++;
2741			cverf[1] = *tl;
2742			error = nfsv4_sattr(nd, NULL, &nva, &attrbits, aclp, p);
2743			if (error != 0)
2744				goto nfsmout;
2745			if (NFSISSET_ATTRBIT(&attrbits,
2746			    NFSATTRBIT_TIMEACCESSSET))
2747				nd->nd_repstat = NFSERR_INVAL;
2748			/*
2749			 * If the na_gid being set is the same as that of
2750			 * the directory it is going in, clear it, since
2751			 * that is what will be set by default. This allows
2752			 * a user that isn't in that group to do the create.
2753			 */
2754			if (nd->nd_repstat == 0 && NFSVNO_ISSETGID(&nva) &&
2755			    nva.na_gid == dirfor.na_gid)
2756				NFSVNO_UNSET(&nva, gid);
2757			if (nd->nd_repstat == 0)
2758				nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
2759			break;
2760		default:
2761			nd->nd_repstat = NFSERR_BADXDR;
2762			goto nfsmout;
2763		}
2764	} else if (create != NFSV4OPEN_NOCREATE) {
2765		nd->nd_repstat = NFSERR_BADXDR;
2766		goto nfsmout;
2767	}
2768
2769	/*
2770	 * Now, handle the claim, which usually includes looking up a
2771	 * name in the directory referenced by dp. The exception is
2772	 * NFSV4OPEN_CLAIMPREVIOUS.
2773	 */
2774	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2775	claim = fxdr_unsigned(int, *tl);
2776	if (claim == NFSV4OPEN_CLAIMDELEGATECUR) {
2777		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
2778		stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
2779		NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
2780		stp->ls_flags |= NFSLCK_DELEGCUR;
2781	} else if (claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
2782		stp->ls_flags |= NFSLCK_DELEGPREV;
2783	}
2784	if (claim == NFSV4OPEN_CLAIMNULL || claim == NFSV4OPEN_CLAIMDELEGATECUR
2785	    || claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
2786		if (!nd->nd_repstat && create == NFSV4OPEN_CREATE &&
2787		    claim != NFSV4OPEN_CLAIMNULL)
2788			nd->nd_repstat = NFSERR_INVAL;
2789		if (nd->nd_repstat) {
2790			nd->nd_repstat = nfsrv_opencheck(clientid,
2791			    &stateid, stp, NULL, nd, p, nd->nd_repstat);
2792			goto nfsmout;
2793		}
2794		if (create == NFSV4OPEN_CREATE)
2795		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
2796			LOCKPARENT | LOCKLEAF | SAVESTART | NOCACHE);
2797		else
2798		    NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
2799			LOCKLEAF | SAVESTART);
2800		nfsvno_setpathbuf(&named, &bufp, &hashp);
2801		error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
2802		if (error) {
2803			vrele(dp);
2804#ifdef NFS4_ACL_EXTATTR_NAME
2805			acl_free(aclp);
2806#endif
2807			FREE((caddr_t)stp, M_NFSDSTATE);
2808			nfsvno_relpathbuf(&named);
2809			NFSEXITCODE2(error, nd);
2810			return (error);
2811		}
2812		if (!nd->nd_repstat) {
2813			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp,
2814			    p, &dirp);
2815		} else {
2816			vrele(dp);
2817			nfsvno_relpathbuf(&named);
2818		}
2819		if (create == NFSV4OPEN_CREATE) {
2820		    switch (how) {
2821		    case NFSCREATE_UNCHECKED:
2822			if (named.ni_vp) {
2823				/*
2824				 * Clear the setable attribute bits, except
2825				 * for Size, if it is being truncated.
2826				 */
2827				NFSZERO_ATTRBIT(&attrbits);
2828				if (NFSVNO_ISSETSIZE(&nva))
2829					NFSSETBIT_ATTRBIT(&attrbits,
2830					    NFSATTRBIT_SIZE);
2831			}
2832			break;
2833		    case NFSCREATE_GUARDED:
2834			if (named.ni_vp && !nd->nd_repstat)
2835				nd->nd_repstat = EEXIST;
2836			break;
2837		    case NFSCREATE_EXCLUSIVE:
2838			exclusive_flag = 1;
2839			if (!named.ni_vp)
2840				nva.na_mode = 0;
2841			break;
2842		    case NFSCREATE_EXCLUSIVE41:
2843			exclusive_flag = 1;
2844			break;
2845		    }
2846		}
2847		nfsvno_open(nd, &named, clientid, &stateid, stp,
2848		    &exclusive_flag, &nva, cverf, create, aclp, &attrbits,
2849		    nd->nd_cred, p, exp, &vp);
2850	} else if (claim == NFSV4OPEN_CLAIMPREVIOUS || claim ==
2851	    NFSV4OPEN_CLAIMFH) {
2852		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
2853			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2854			i = fxdr_unsigned(int, *tl);
2855			switch (i) {
2856			case NFSV4OPEN_DELEGATEREAD:
2857				stp->ls_flags |= NFSLCK_DELEGREAD;
2858				break;
2859			case NFSV4OPEN_DELEGATEWRITE:
2860				stp->ls_flags |= NFSLCK_DELEGWRITE;
2861			case NFSV4OPEN_DELEGATENONE:
2862				break;
2863			default:
2864				nd->nd_repstat = NFSERR_BADXDR;
2865				goto nfsmout;
2866			}
2867			stp->ls_flags |= NFSLCK_RECLAIM;
2868		} else {
2869			/* CLAIM_NULL_FH */
2870			if (nd->nd_repstat == 0 && create == NFSV4OPEN_CREATE)
2871				nd->nd_repstat = NFSERR_INVAL;
2872		}
2873		vp = dp;
2874		NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
2875		if ((vp->v_iflag & VI_DOOMED) == 0)
2876			nd->nd_repstat = nfsrv_opencheck(clientid, &stateid,
2877			    stp, vp, nd, p, nd->nd_repstat);
2878		else
2879			nd->nd_repstat = NFSERR_PERM;
2880	} else {
2881		nd->nd_repstat = NFSERR_BADXDR;
2882		goto nfsmout;
2883	}
2884
2885	/*
2886	 * Do basic access checking.
2887	 */
2888	if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
2889		/*
2890		 * The IETF working group decided that this is the correct
2891		 * error return for all non-regular files.
2892		 */
2893		nd->nd_repstat = (vp->v_type == VDIR) ? NFSERR_ISDIR : NFSERR_SYMLINK;
2894	}
2895
2896	/*
2897	 * If the Open is being done for a file that already exists, apply
2898	 * normal permission checking including for the file owner, if
2899	 * vfs.nfsd.v4openaccess is set.
2900	 * Previously, the owner was always allowed to open the file to
2901	 * be consistent with the NFS tradition of always allowing the
2902	 * owner of the file to write to the file regardless of permissions.
2903	 * It now appears that the Linux client expects the owner
2904	 * permissions to be checked for opens that are not creating the
2905	 * file.  I believe the correct approach is to use the Access
2906	 * operation's results to be consistent with NFSv3, but that is
2907	 * not what the current Linux client appears to be doing.
2908	 * Since both the Linux and OpenSolaris NFSv4 servers do this check,
2909	 * I have enabled it by default.
2910	 * If this semantic change causes a problem, it can be disabled by
2911	 * setting the sysctl vfs.nfsd.v4openaccess to 0 to re-enable the
2912	 * previous semantics.
2913	 */
2914	if (nfsrv_openaccess && create == NFSV4OPEN_NOCREATE)
2915		override = NFSACCCHK_NOOVERRIDE;
2916	else
2917		override = NFSACCCHK_ALLOWOWNER;
2918	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_WRITEACCESS))
2919	    nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred,
2920	        exp, p, override, NFSACCCHK_VPISLOCKED, NULL);
2921	if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_READACCESS)) {
2922	    nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred,
2923	        exp, p, override, NFSACCCHK_VPISLOCKED, NULL);
2924	    if (nd->nd_repstat)
2925		nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
2926		    nd->nd_cred, exp, p, override,
2927		    NFSACCCHK_VPISLOCKED, NULL);
2928	}
2929
2930	if (!nd->nd_repstat) {
2931		nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
2932		if (!nd->nd_repstat) {
2933			tverf[0] = nva.na_atime.tv_sec;
2934			tverf[1] = nva.na_atime.tv_nsec;
2935		}
2936	}
2937	if (!nd->nd_repstat && exclusive_flag && (cverf[0] != tverf[0] ||
2938	    cverf[1] != tverf[1]))
2939		nd->nd_repstat = EEXIST;
2940	/*
2941	 * Do the open locking/delegation stuff.
2942	 */
2943	if (!nd->nd_repstat)
2944	    nd->nd_repstat = nfsrv_openctrl(nd, vp, &stp, clientid, &stateid,
2945		&delegstateid, &rflags, exp, p, nva.na_filerev);
2946
2947	/*
2948	 * vp must be unlocked before the call to nfsvno_getattr(dirp,...)
2949	 * below, to avoid a deadlock with the lookup in nfsvno_namei() above.
2950	 * (ie: Leave the NFSVOPUNLOCK() about here.)
2951	 */
2952	if (vp)
2953		NFSVOPUNLOCK(vp, 0);
2954	if (stp)
2955		FREE((caddr_t)stp, M_NFSDSTATE);
2956	if (!nd->nd_repstat && dirp)
2957		nd->nd_repstat = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p,
2958		    0);
2959	if (!nd->nd_repstat) {
2960		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
2961		*tl++ = txdr_unsigned(stateid.seqid);
2962		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
2963		tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
2964		if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
2965			*tl++ = newnfs_true;
2966			*tl++ = 0;
2967			*tl++ = 0;
2968			*tl++ = 0;
2969			*tl++ = 0;
2970		} else {
2971			*tl++ = newnfs_false;	/* Since dirp is not locked */
2972			txdr_hyper(dirfor.na_filerev, tl);
2973			tl += 2;
2974			txdr_hyper(diraft.na_filerev, tl);
2975			tl += 2;
2976		}
2977		*tl = txdr_unsigned(rflags & NFSV4OPEN_RFLAGS);
2978		(void) nfsrv_putattrbit(nd, &attrbits);
2979		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2980		if (rflags & NFSV4OPEN_READDELEGATE)
2981			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEREAD);
2982		else if (rflags & NFSV4OPEN_WRITEDELEGATE)
2983			*tl = txdr_unsigned(NFSV4OPEN_DELEGATEWRITE);
2984		else if (retext != 0) {
2985			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONEEXT);
2986			if ((rflags & NFSV4OPEN_WDNOTWANTED) != 0) {
2987				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2988				*tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
2989			} else if ((rflags & NFSV4OPEN_WDSUPPFTYPE) != 0) {
2990				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2991				*tl = txdr_unsigned(NFSV4OPEN_NOTSUPPFTYPE);
2992			} else if ((rflags & NFSV4OPEN_WDCONTENTION) != 0) {
2993				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2994				*tl++ = txdr_unsigned(NFSV4OPEN_CONTENTION);
2995				*tl = newnfs_false;
2996			} else if ((rflags & NFSV4OPEN_WDRESOURCE) != 0) {
2997				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2998				*tl++ = txdr_unsigned(NFSV4OPEN_RESOURCE);
2999				*tl = newnfs_false;
3000			} else {
3001				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3002				*tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
3003			}
3004		} else
3005			*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONE);
3006		if (rflags & (NFSV4OPEN_READDELEGATE|NFSV4OPEN_WRITEDELEGATE)) {
3007			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID+NFSX_UNSIGNED);
3008			*tl++ = txdr_unsigned(delegstateid.seqid);
3009			NFSBCOPY((caddr_t)delegstateid.other, (caddr_t)tl,
3010			    NFSX_STATEIDOTHER);
3011			tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3012			if (rflags & NFSV4OPEN_RECALL)
3013				*tl = newnfs_true;
3014			else
3015				*tl = newnfs_false;
3016			if (rflags & NFSV4OPEN_WRITEDELEGATE) {
3017				NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3018				*tl++ = txdr_unsigned(NFSV4OPEN_LIMITSIZE);
3019				txdr_hyper(nva.na_size, tl);
3020			}
3021			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3022			*tl++ = txdr_unsigned(NFSV4ACE_ALLOWEDTYPE);
3023			*tl++ = txdr_unsigned(0x0);
3024			acemask = NFSV4ACE_ALLFILESMASK;
3025			if (nva.na_mode & S_IRUSR)
3026			    acemask |= NFSV4ACE_READMASK;
3027			if (nva.na_mode & S_IWUSR)
3028			    acemask |= NFSV4ACE_WRITEMASK;
3029			if (nva.na_mode & S_IXUSR)
3030			    acemask |= NFSV4ACE_EXECUTEMASK;
3031			*tl = txdr_unsigned(acemask);
3032			(void) nfsm_strtom(nd, "OWNER@", 6);
3033		}
3034		*vpp = vp;
3035	} else if (vp) {
3036		vrele(vp);
3037	}
3038	if (dirp)
3039		vrele(dirp);
3040#ifdef NFS4_ACL_EXTATTR_NAME
3041	acl_free(aclp);
3042#endif
3043	NFSEXITCODE2(0, nd);
3044	return (0);
3045nfsmout:
3046	vrele(dp);
3047#ifdef NFS4_ACL_EXTATTR_NAME
3048	acl_free(aclp);
3049#endif
3050	if (stp)
3051		FREE((caddr_t)stp, M_NFSDSTATE);
3052	NFSEXITCODE2(error, nd);
3053	return (error);
3054}
3055
3056/*
3057 * nfsv4 close service
3058 */
3059APPLESTATIC int
3060nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram,
3061    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3062{
3063	u_int32_t *tl;
3064	struct nfsstate st, *stp = &st;
3065	int error = 0;
3066	nfsv4stateid_t stateid;
3067	nfsquad_t clientid;
3068
3069	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
3070	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
3071	stp->ls_ownerlen = 0;
3072	stp->ls_op = nd->nd_rp;
3073	stp->ls_uid = nd->nd_cred->cr_uid;
3074	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3075	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
3076	    NFSX_STATEIDOTHER);
3077	stp->ls_flags = NFSLCK_CLOSE;
3078	clientid.lval[0] = stp->ls_stateid.other[0];
3079	clientid.lval[1] = stp->ls_stateid.other[1];
3080	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3081		if ((nd->nd_flag & ND_NFSV41) != 0)
3082			clientid.qval = nd->nd_clientid.qval;
3083		else if (nd->nd_clientid.qval != clientid.qval)
3084			printf("EEK8 multiple clids\n");
3085	} else {
3086		if ((nd->nd_flag & ND_NFSV41) != 0)
3087			printf("EEK! no clientid from session\n");
3088		nd->nd_flag |= ND_IMPLIEDCLID;
3089		nd->nd_clientid.qval = clientid.qval;
3090	}
3091	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
3092	vput(vp);
3093	if (!nd->nd_repstat) {
3094		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
3095		*tl++ = txdr_unsigned(stateid.seqid);
3096		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
3097	}
3098	NFSEXITCODE2(0, nd);
3099	return (0);
3100nfsmout:
3101	vput(vp);
3102	NFSEXITCODE2(error, nd);
3103	return (error);
3104}
3105
3106/*
3107 * nfsv4 delegpurge service
3108 */
3109APPLESTATIC int
3110nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram,
3111    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
3112{
3113	u_int32_t *tl;
3114	int error = 0;
3115	nfsquad_t clientid;
3116
3117	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3118		nd->nd_repstat = NFSERR_WRONGSEC;
3119		goto nfsmout;
3120	}
3121	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3122	clientid.lval[0] = *tl++;
3123	clientid.lval[1] = *tl;
3124	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3125		if ((nd->nd_flag & ND_NFSV41) != 0)
3126			clientid.qval = nd->nd_clientid.qval;
3127		else if (nd->nd_clientid.qval != clientid.qval)
3128			printf("EEK9 multiple clids\n");
3129	} else {
3130		if ((nd->nd_flag & ND_NFSV41) != 0)
3131			printf("EEK! no clientid from session\n");
3132		nd->nd_flag |= ND_IMPLIEDCLID;
3133		nd->nd_clientid.qval = clientid.qval;
3134	}
3135	nd->nd_repstat = nfsrv_delegupdate(nd, clientid, NULL, NULL,
3136	    NFSV4OP_DELEGPURGE, nd->nd_cred, p);
3137nfsmout:
3138	NFSEXITCODE2(error, nd);
3139	return (error);
3140}
3141
3142/*
3143 * nfsv4 delegreturn service
3144 */
3145APPLESTATIC int
3146nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram,
3147    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3148{
3149	u_int32_t *tl;
3150	int error = 0;
3151	nfsv4stateid_t stateid;
3152	nfsquad_t clientid;
3153
3154	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
3155	stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3156	NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, NFSX_STATEIDOTHER);
3157	clientid.lval[0] = stateid.other[0];
3158	clientid.lval[1] = stateid.other[1];
3159	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3160		if ((nd->nd_flag & ND_NFSV41) != 0)
3161			clientid.qval = nd->nd_clientid.qval;
3162		else if (nd->nd_clientid.qval != clientid.qval)
3163			printf("EEK10 multiple clids\n");
3164	} else {
3165		if ((nd->nd_flag & ND_NFSV41) != 0)
3166			printf("EEK! no clientid from session\n");
3167		nd->nd_flag |= ND_IMPLIEDCLID;
3168		nd->nd_clientid.qval = clientid.qval;
3169	}
3170	nd->nd_repstat = nfsrv_delegupdate(nd, clientid, &stateid, vp,
3171	    NFSV4OP_DELEGRETURN, nd->nd_cred, p);
3172nfsmout:
3173	vput(vp);
3174	NFSEXITCODE2(error, nd);
3175	return (error);
3176}
3177
3178/*
3179 * nfsv4 get file handle service
3180 */
3181APPLESTATIC int
3182nfsrvd_getfh(struct nfsrv_descript *nd, __unused int isdgram,
3183    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3184{
3185	fhandle_t fh;
3186
3187	nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
3188	vput(vp);
3189	if (!nd->nd_repstat)
3190		(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
3191	NFSEXITCODE2(0, nd);
3192	return (0);
3193}
3194
3195/*
3196 * nfsv4 open confirm service
3197 */
3198APPLESTATIC int
3199nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram,
3200    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3201{
3202	u_int32_t *tl;
3203	struct nfsstate st, *stp = &st;
3204	int error = 0;
3205	nfsv4stateid_t stateid;
3206	nfsquad_t clientid;
3207
3208	if ((nd->nd_flag & ND_NFSV41) != 0) {
3209		nd->nd_repstat = NFSERR_NOTSUPP;
3210		goto nfsmout;
3211	}
3212	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
3213	stp->ls_ownerlen = 0;
3214	stp->ls_op = nd->nd_rp;
3215	stp->ls_uid = nd->nd_cred->cr_uid;
3216	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3217	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
3218	    NFSX_STATEIDOTHER);
3219	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3220	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl);
3221	stp->ls_flags = NFSLCK_CONFIRM;
3222	clientid.lval[0] = stp->ls_stateid.other[0];
3223	clientid.lval[1] = stp->ls_stateid.other[1];
3224	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3225		if ((nd->nd_flag & ND_NFSV41) != 0)
3226			clientid.qval = nd->nd_clientid.qval;
3227		else if (nd->nd_clientid.qval != clientid.qval)
3228			printf("EEK11 multiple clids\n");
3229	} else {
3230		if ((nd->nd_flag & ND_NFSV41) != 0)
3231			printf("EEK! no clientid from session\n");
3232		nd->nd_flag |= ND_IMPLIEDCLID;
3233		nd->nd_clientid.qval = clientid.qval;
3234	}
3235	nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
3236	if (!nd->nd_repstat) {
3237		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
3238		*tl++ = txdr_unsigned(stateid.seqid);
3239		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
3240	}
3241nfsmout:
3242	vput(vp);
3243	NFSEXITCODE2(error, nd);
3244	return (error);
3245}
3246
3247/*
3248 * nfsv4 open downgrade service
3249 */
3250APPLESTATIC int
3251nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram,
3252    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3253{
3254	u_int32_t *tl;
3255	int i;
3256	struct nfsstate st, *stp = &st;
3257	int error = 0;
3258	nfsv4stateid_t stateid;
3259	nfsquad_t clientid;
3260
3261	/* opendowngrade can only work on a file object.*/
3262	if (vp->v_type != VREG) {
3263		error = NFSERR_INVAL;
3264		goto nfsmout;
3265	}
3266	NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
3267	stp->ls_ownerlen = 0;
3268	stp->ls_op = nd->nd_rp;
3269	stp->ls_uid = nd->nd_cred->cr_uid;
3270	stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
3271	NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
3272	    NFSX_STATEIDOTHER);
3273	tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3274	stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
3275	i = fxdr_unsigned(int, *tl++);
3276	if ((nd->nd_flag & ND_NFSV41) != 0)
3277		i &= ~NFSV4OPEN_WANTDELEGMASK;
3278	switch (i) {
3279	case NFSV4OPEN_ACCESSREAD:
3280		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_DOWNGRADE);
3281		break;
3282	case NFSV4OPEN_ACCESSWRITE:
3283		stp->ls_flags = (NFSLCK_WRITEACCESS | NFSLCK_DOWNGRADE);
3284		break;
3285	case NFSV4OPEN_ACCESSBOTH:
3286		stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_WRITEACCESS |
3287		    NFSLCK_DOWNGRADE);
3288		break;
3289	default:
3290		nd->nd_repstat = NFSERR_INVAL;
3291	}
3292	i = fxdr_unsigned(int, *tl);
3293	switch (i) {
3294	case NFSV4OPEN_DENYNONE:
3295		break;
3296	case NFSV4OPEN_DENYREAD:
3297		stp->ls_flags |= NFSLCK_READDENY;
3298		break;
3299	case NFSV4OPEN_DENYWRITE:
3300		stp->ls_flags |= NFSLCK_WRITEDENY;
3301		break;
3302	case NFSV4OPEN_DENYBOTH:
3303		stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
3304		break;
3305	default:
3306		nd->nd_repstat = NFSERR_INVAL;
3307	}
3308
3309	clientid.lval[0] = stp->ls_stateid.other[0];
3310	clientid.lval[1] = stp->ls_stateid.other[1];
3311	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3312		if ((nd->nd_flag & ND_NFSV41) != 0)
3313			clientid.qval = nd->nd_clientid.qval;
3314		else if (nd->nd_clientid.qval != clientid.qval)
3315			printf("EEK12 multiple clids\n");
3316	} else {
3317		if ((nd->nd_flag & ND_NFSV41) != 0)
3318			printf("EEK! no clientid from session\n");
3319		nd->nd_flag |= ND_IMPLIEDCLID;
3320		nd->nd_clientid.qval = clientid.qval;
3321	}
3322	if (!nd->nd_repstat)
3323		nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid,
3324		    nd, p);
3325	if (!nd->nd_repstat) {
3326		NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
3327		*tl++ = txdr_unsigned(stateid.seqid);
3328		NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
3329	}
3330nfsmout:
3331	vput(vp);
3332	NFSEXITCODE2(error, nd);
3333	return (error);
3334}
3335
3336/*
3337 * nfsv4 renew lease service
3338 */
3339APPLESTATIC int
3340nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram,
3341    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3342{
3343	u_int32_t *tl;
3344	int error = 0;
3345	nfsquad_t clientid;
3346
3347	if ((nd->nd_flag & ND_NFSV41) != 0) {
3348		nd->nd_repstat = NFSERR_NOTSUPP;
3349		goto nfsmout;
3350	}
3351	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3352		nd->nd_repstat = NFSERR_WRONGSEC;
3353		goto nfsmout;
3354	}
3355	NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
3356	clientid.lval[0] = *tl++;
3357	clientid.lval[1] = *tl;
3358	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3359		if ((nd->nd_flag & ND_NFSV41) != 0)
3360			clientid.qval = nd->nd_clientid.qval;
3361		else if (nd->nd_clientid.qval != clientid.qval)
3362			printf("EEK13 multiple clids\n");
3363	} else {
3364		if ((nd->nd_flag & ND_NFSV41) != 0)
3365			printf("EEK! no clientid from session\n");
3366		nd->nd_flag |= ND_IMPLIEDCLID;
3367		nd->nd_clientid.qval = clientid.qval;
3368	}
3369	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW),
3370	    NULL, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p);
3371nfsmout:
3372	NFSEXITCODE2(error, nd);
3373	return (error);
3374}
3375
3376/*
3377 * nfsv4 security info service
3378 */
3379APPLESTATIC int
3380nfsrvd_secinfo(struct nfsrv_descript *nd, int isdgram,
3381    vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
3382{
3383	u_int32_t *tl;
3384	int len;
3385	struct nameidata named;
3386	vnode_t dirp = NULL, vp;
3387	struct nfsrvfh fh;
3388	struct nfsexstuff retnes;
3389	u_int32_t *sizp;
3390	int error = 0, savflag, i;
3391	char *bufp;
3392	u_long *hashp;
3393
3394	/*
3395	 * All this just to get the export flags for the name.
3396	 */
3397	NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
3398	    LOCKLEAF | SAVESTART);
3399	nfsvno_setpathbuf(&named, &bufp, &hashp);
3400	error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
3401	if (error) {
3402		vput(dp);
3403		nfsvno_relpathbuf(&named);
3404		goto out;
3405	}
3406	if (!nd->nd_repstat) {
3407		nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
3408	} else {
3409		vput(dp);
3410		nfsvno_relpathbuf(&named);
3411	}
3412	if (dirp)
3413		vrele(dirp);
3414	if (nd->nd_repstat)
3415		goto out;
3416	vrele(named.ni_startdir);
3417	nfsvno_relpathbuf(&named);
3418	fh.nfsrvfh_len = NFSX_MYFH;
3419	vp = named.ni_vp;
3420	nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
3421	vput(vp);
3422	savflag = nd->nd_flag;
3423	if (!nd->nd_repstat) {
3424		nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0, p);
3425		if (vp)
3426			vput(vp);
3427	}
3428	nd->nd_flag = savflag;
3429	if (nd->nd_repstat)
3430		goto out;
3431
3432	/*
3433	 * Finally have the export flags for name, so we can create
3434	 * the security info.
3435	 */
3436	len = 0;
3437	NFSM_BUILD(sizp, u_int32_t *, NFSX_UNSIGNED);
3438	for (i = 0; i < retnes.nes_numsecflavor; i++) {
3439		if (retnes.nes_secflavors[i] == AUTH_SYS) {
3440			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3441			*tl = txdr_unsigned(RPCAUTH_UNIX);
3442			len++;
3443		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) {
3444			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3445			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3446			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3447			    nfsgss_mechlist[KERBV_MECH].len);
3448			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3449			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3450			*tl = txdr_unsigned(RPCAUTHGSS_SVCNONE);
3451			len++;
3452		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) {
3453			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3454			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3455			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3456			    nfsgss_mechlist[KERBV_MECH].len);
3457			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3458			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3459			*tl = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
3460			len++;
3461		} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) {
3462			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3463			*tl++ = txdr_unsigned(RPCAUTH_GSS);
3464			(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
3465			    nfsgss_mechlist[KERBV_MECH].len);
3466			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3467			*tl++ = txdr_unsigned(GSS_KERBV_QOP);
3468			*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
3469			len++;
3470		}
3471	}
3472	*sizp = txdr_unsigned(len);
3473
3474out:
3475	NFSEXITCODE2(error, nd);
3476	return (error);
3477}
3478
3479/*
3480 * nfsv4 set client id service
3481 */
3482APPLESTATIC int
3483nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram,
3484    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3485{
3486	u_int32_t *tl;
3487	int i;
3488	int error = 0, idlen;
3489	struct nfsclient *clp = NULL;
3490#ifdef INET
3491	struct sockaddr_in *rin;
3492#endif
3493#ifdef INET6
3494	struct sockaddr_in6 *rin6;
3495#endif
3496#if defined(INET) || defined(INET6)
3497	u_char *ucp, *ucp2;
3498#endif
3499	u_char *verf, *addrbuf;
3500	nfsquad_t clientid, confirm;
3501
3502	if ((nd->nd_flag & ND_NFSV41) != 0) {
3503		nd->nd_repstat = NFSERR_NOTSUPP;
3504		goto nfsmout;
3505	}
3506	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3507		nd->nd_repstat = NFSERR_WRONGSEC;
3508		goto out;
3509	}
3510	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
3511	verf = (u_char *)tl;
3512	tl += (NFSX_VERF / NFSX_UNSIGNED);
3513	i = fxdr_unsigned(int, *tl);
3514	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
3515		nd->nd_repstat = NFSERR_BADXDR;
3516		goto nfsmout;
3517	}
3518	idlen = i;
3519	if (nd->nd_flag & ND_GSS)
3520		i += nd->nd_princlen;
3521	clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
3522	    M_ZERO);
3523	clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
3524	    nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
3525	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
3526	/* Allocated large enough for an AF_INET or AF_INET6 socket. */
3527	clp->lc_req.nr_nam = malloc(sizeof(struct sockaddr_in6), M_SONAME,
3528	    M_WAITOK | M_ZERO);
3529	clp->lc_req.nr_cred = NULL;
3530	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
3531	clp->lc_idlen = idlen;
3532	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
3533	if (error)
3534		goto nfsmout;
3535	if (nd->nd_flag & ND_GSS) {
3536		clp->lc_flags = LCL_GSS;
3537		if (nd->nd_flag & ND_GSSINTEGRITY)
3538			clp->lc_flags |= LCL_GSSINTEGRITY;
3539		else if (nd->nd_flag & ND_GSSPRIVACY)
3540			clp->lc_flags |= LCL_GSSPRIVACY;
3541	} else {
3542		clp->lc_flags = 0;
3543	}
3544	if ((nd->nd_flag & ND_GSS) && nd->nd_princlen > 0) {
3545		clp->lc_flags |= LCL_NAME;
3546		clp->lc_namelen = nd->nd_princlen;
3547		clp->lc_name = &clp->lc_id[idlen];
3548		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
3549	} else {
3550		clp->lc_uid = nd->nd_cred->cr_uid;
3551		clp->lc_gid = nd->nd_cred->cr_gid;
3552	}
3553	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3554	clp->lc_program = fxdr_unsigned(u_int32_t, *tl);
3555	error = nfsrv_getclientipaddr(nd, clp);
3556	if (error)
3557		goto nfsmout;
3558	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3559	clp->lc_callback = fxdr_unsigned(u_int32_t, *tl);
3560
3561	/*
3562	 * nfsrv_setclient() does the actual work of adding it to the
3563	 * client list. If there is no error, the structure has been
3564	 * linked into the client list and clp should no longer be used
3565	 * here. When an error is returned, it has not been linked in,
3566	 * so it should be free'd.
3567	 */
3568	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
3569	if (nd->nd_repstat == NFSERR_CLIDINUSE) {
3570		/*
3571		 * 8 is the maximum length of the port# string.
3572		 */
3573		addrbuf = malloc(INET6_ADDRSTRLEN + 8, M_TEMP, M_WAITOK);
3574		switch (clp->lc_req.nr_nam->sa_family) {
3575#ifdef INET
3576		case AF_INET:
3577			if (clp->lc_flags & LCL_TCPCALLBACK)
3578				(void) nfsm_strtom(nd, "tcp", 3);
3579			else
3580				(void) nfsm_strtom(nd, "udp", 3);
3581			rin = (struct sockaddr_in *)clp->lc_req.nr_nam;
3582			ucp = (u_char *)&rin->sin_addr.s_addr;
3583			ucp2 = (u_char *)&rin->sin_port;
3584			sprintf(addrbuf, "%d.%d.%d.%d.%d.%d", ucp[0] & 0xff,
3585			    ucp[1] & 0xff, ucp[2] & 0xff, ucp[3] & 0xff,
3586			    ucp2[0] & 0xff, ucp2[1] & 0xff);
3587			break;
3588#endif
3589#ifdef INET6
3590		case AF_INET6:
3591			if (clp->lc_flags & LCL_TCPCALLBACK)
3592				(void) nfsm_strtom(nd, "tcp6", 4);
3593			else
3594				(void) nfsm_strtom(nd, "udp6", 4);
3595			rin6 = (struct sockaddr_in6 *)clp->lc_req.nr_nam;
3596			ucp = inet_ntop(AF_INET6, &rin6->sin6_addr, addrbuf,
3597			    INET6_ADDRSTRLEN);
3598			if (ucp != NULL)
3599				i = strlen(ucp);
3600			else
3601				i = 0;
3602			ucp2 = (u_char *)&rin6->sin6_port;
3603			sprintf(&addrbuf[i], ".%d.%d", ucp2[0] & 0xff,
3604			    ucp2[1] & 0xff);
3605			break;
3606#endif
3607		}
3608		(void) nfsm_strtom(nd, addrbuf, strlen(addrbuf));
3609		free(addrbuf, M_TEMP);
3610	}
3611	if (clp) {
3612		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3613		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3614		free(clp->lc_stateid, M_NFSDCLIENT);
3615		free(clp, M_NFSDCLIENT);
3616	}
3617	if (!nd->nd_repstat) {
3618		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_HYPER);
3619		*tl++ = clientid.lval[0];
3620		*tl++ = clientid.lval[1];
3621		*tl++ = confirm.lval[0];
3622		*tl = confirm.lval[1];
3623	}
3624
3625out:
3626	NFSEXITCODE2(0, nd);
3627	return (0);
3628nfsmout:
3629	if (clp) {
3630		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3631		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3632		free(clp->lc_stateid, M_NFSDCLIENT);
3633		free(clp, M_NFSDCLIENT);
3634	}
3635	NFSEXITCODE2(error, nd);
3636	return (error);
3637}
3638
3639/*
3640 * nfsv4 set client id confirm service
3641 */
3642APPLESTATIC int
3643nfsrvd_setclientidcfrm(struct nfsrv_descript *nd,
3644    __unused int isdgram, __unused vnode_t vp, NFSPROC_T *p,
3645    __unused struct nfsexstuff *exp)
3646{
3647	u_int32_t *tl;
3648	int error = 0;
3649	nfsquad_t clientid, confirm;
3650
3651	if ((nd->nd_flag & ND_NFSV41) != 0) {
3652		nd->nd_repstat = NFSERR_NOTSUPP;
3653		goto nfsmout;
3654	}
3655	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3656		nd->nd_repstat = NFSERR_WRONGSEC;
3657		goto nfsmout;
3658	}
3659	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER);
3660	clientid.lval[0] = *tl++;
3661	clientid.lval[1] = *tl++;
3662	confirm.lval[0] = *tl++;
3663	confirm.lval[1] = *tl;
3664
3665	/*
3666	 * nfsrv_getclient() searches the client list for a match and
3667	 * returns the appropriate NFSERR status.
3668	 */
3669	nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_CONFIRM|CLOPS_RENEW),
3670	    NULL, NULL, confirm, 0, nd, p);
3671nfsmout:
3672	NFSEXITCODE2(error, nd);
3673	return (error);
3674}
3675
3676/*
3677 * nfsv4 verify service
3678 */
3679APPLESTATIC int
3680nfsrvd_verify(struct nfsrv_descript *nd, int isdgram,
3681    vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3682{
3683	int error = 0, ret, fhsize = NFSX_MYFH;
3684	struct nfsvattr nva;
3685	struct statfs *sf;
3686	struct nfsfsinfo fs;
3687	fhandle_t fh;
3688
3689	sf = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
3690	nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1);
3691	if (!nd->nd_repstat)
3692		nd->nd_repstat = nfsvno_statfs(vp, sf);
3693	if (!nd->nd_repstat)
3694		nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
3695	if (!nd->nd_repstat) {
3696		nfsvno_getfs(&fs, isdgram);
3697		error = nfsv4_loadattr(nd, vp, &nva, NULL, &fh, fhsize, NULL,
3698		    sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, p, nd->nd_cred);
3699		if (!error) {
3700			if (nd->nd_procnum == NFSV4OP_NVERIFY) {
3701				if (ret == 0)
3702					nd->nd_repstat = NFSERR_SAME;
3703				else if (ret != NFSERR_NOTSAME)
3704					nd->nd_repstat = ret;
3705			} else if (ret)
3706				nd->nd_repstat = ret;
3707		}
3708	}
3709	vput(vp);
3710	free(sf, M_STATFS);
3711	NFSEXITCODE2(error, nd);
3712	return (error);
3713}
3714
3715/*
3716 * nfs openattr rpc
3717 */
3718APPLESTATIC int
3719nfsrvd_openattr(struct nfsrv_descript *nd, __unused int isdgram,
3720    vnode_t dp, __unused vnode_t *vpp, __unused fhandle_t *fhp,
3721    __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
3722{
3723	u_int32_t *tl;
3724	int error = 0, createdir;
3725
3726	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3727	createdir = fxdr_unsigned(int, *tl);
3728	nd->nd_repstat = NFSERR_NOTSUPP;
3729nfsmout:
3730	vrele(dp);
3731	NFSEXITCODE2(error, nd);
3732	return (error);
3733}
3734
3735/*
3736 * nfsv4 release lock owner service
3737 */
3738APPLESTATIC int
3739nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram,
3740    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3741{
3742	u_int32_t *tl;
3743	struct nfsstate *stp = NULL;
3744	int error = 0, len;
3745	nfsquad_t clientid;
3746
3747	if ((nd->nd_flag & ND_NFSV41) != 0) {
3748		nd->nd_repstat = NFSERR_NOTSUPP;
3749		goto nfsmout;
3750	}
3751	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3752		nd->nd_repstat = NFSERR_WRONGSEC;
3753		goto nfsmout;
3754	}
3755	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3756	len = fxdr_unsigned(int, *(tl + 2));
3757	if (len <= 0 || len > NFSV4_OPAQUELIMIT) {
3758		nd->nd_repstat = NFSERR_BADXDR;
3759		goto nfsmout;
3760	}
3761	MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + len,
3762	    M_NFSDSTATE, M_WAITOK);
3763	stp->ls_ownerlen = len;
3764	stp->ls_op = NULL;
3765	stp->ls_flags = NFSLCK_RELEASE;
3766	stp->ls_uid = nd->nd_cred->cr_uid;
3767	clientid.lval[0] = *tl++;
3768	clientid.lval[1] = *tl;
3769	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
3770		if ((nd->nd_flag & ND_NFSV41) != 0)
3771			clientid.qval = nd->nd_clientid.qval;
3772		else if (nd->nd_clientid.qval != clientid.qval)
3773			printf("EEK14 multiple clids\n");
3774	} else {
3775		if ((nd->nd_flag & ND_NFSV41) != 0)
3776			printf("EEK! no clientid from session\n");
3777		nd->nd_flag |= ND_IMPLIEDCLID;
3778		nd->nd_clientid.qval = clientid.qval;
3779	}
3780	error = nfsrv_mtostr(nd, stp->ls_owner, len);
3781	if (error)
3782		goto nfsmout;
3783	nd->nd_repstat = nfsrv_releaselckown(stp, clientid, p);
3784	FREE((caddr_t)stp, M_NFSDSTATE);
3785
3786	NFSEXITCODE2(0, nd);
3787	return (0);
3788nfsmout:
3789	if (stp)
3790		free((caddr_t)stp, M_NFSDSTATE);
3791	NFSEXITCODE2(error, nd);
3792	return (error);
3793}
3794
3795/*
3796 * nfsv4 exchange_id service
3797 */
3798APPLESTATIC int
3799nfsrvd_exchangeid(struct nfsrv_descript *nd, __unused int isdgram,
3800    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3801{
3802	uint32_t *tl;
3803	int error = 0, i, idlen;
3804	struct nfsclient *clp = NULL;
3805	nfsquad_t clientid, confirm;
3806	uint8_t *verf;
3807	uint32_t sp4type, v41flags;
3808	uint64_t owner_minor;
3809	struct timespec verstime;
3810#ifdef INET
3811	struct sockaddr_in *sin, *rin;
3812#endif
3813#ifdef INET6
3814	struct sockaddr_in6 *sin6, *rin6;
3815#endif
3816
3817	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3818		nd->nd_repstat = NFSERR_WRONGSEC;
3819		goto nfsmout;
3820	}
3821	NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
3822	verf = (uint8_t *)tl;
3823	tl += (NFSX_VERF / NFSX_UNSIGNED);
3824	i = fxdr_unsigned(int, *tl);
3825	if (i > NFSV4_OPAQUELIMIT || i <= 0) {
3826		nd->nd_repstat = NFSERR_BADXDR;
3827		goto nfsmout;
3828	}
3829	idlen = i;
3830	if (nd->nd_flag & ND_GSS)
3831		i += nd->nd_princlen;
3832	clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
3833	    M_ZERO);
3834	clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
3835	    nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
3836	NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
3837	/* Allocated large enough for an AF_INET or AF_INET6 socket. */
3838	clp->lc_req.nr_nam = malloc(sizeof(struct sockaddr_in6), M_SONAME,
3839	    M_WAITOK | M_ZERO);
3840	switch (nd->nd_nam->sa_family) {
3841#ifdef INET
3842	case AF_INET:
3843		rin = (struct sockaddr_in *)clp->lc_req.nr_nam;
3844		sin = (struct sockaddr_in *)nd->nd_nam;
3845		rin->sin_family = AF_INET;
3846		rin->sin_len = sizeof(struct sockaddr_in);
3847		rin->sin_port = 0;
3848		rin->sin_addr.s_addr = sin->sin_addr.s_addr;
3849		break;
3850#endif
3851#ifdef INET6
3852	case AF_INET6:
3853		rin6 = (struct sockaddr_in6 *)clp->lc_req.nr_nam;
3854		sin6 = (struct sockaddr_in6 *)nd->nd_nam;
3855		rin6->sin6_family = AF_INET6;
3856		rin6->sin6_len = sizeof(struct sockaddr_in6);
3857		rin6->sin6_port = 0;
3858		rin6->sin6_addr = sin6->sin6_addr;
3859		break;
3860#endif
3861	}
3862	clp->lc_req.nr_cred = NULL;
3863	NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
3864	clp->lc_idlen = idlen;
3865	error = nfsrv_mtostr(nd, clp->lc_id, idlen);
3866	if (error != 0)
3867		goto nfsmout;
3868	if ((nd->nd_flag & ND_GSS) != 0) {
3869		clp->lc_flags = LCL_GSS | LCL_NFSV41;
3870		if ((nd->nd_flag & ND_GSSINTEGRITY) != 0)
3871			clp->lc_flags |= LCL_GSSINTEGRITY;
3872		else if ((nd->nd_flag & ND_GSSPRIVACY) != 0)
3873			clp->lc_flags |= LCL_GSSPRIVACY;
3874	} else
3875		clp->lc_flags = LCL_NFSV41;
3876	if ((nd->nd_flag & ND_GSS) != 0 && nd->nd_princlen > 0) {
3877		clp->lc_flags |= LCL_NAME;
3878		clp->lc_namelen = nd->nd_princlen;
3879		clp->lc_name = &clp->lc_id[idlen];
3880		NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
3881	} else {
3882		clp->lc_uid = nd->nd_cred->cr_uid;
3883		clp->lc_gid = nd->nd_cred->cr_gid;
3884	}
3885	NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3886	v41flags = fxdr_unsigned(uint32_t, *tl++);
3887	if ((v41flags & ~(NFSV4EXCH_SUPPMOVEDREFER | NFSV4EXCH_SUPPMOVEDMIGR |
3888	    NFSV4EXCH_BINDPRINCSTATEID | NFSV4EXCH_MASKPNFS |
3889	    NFSV4EXCH_UPDCONFIRMEDRECA)) != 0) {
3890		nd->nd_repstat = NFSERR_INVAL;
3891		goto nfsmout;
3892	}
3893	if ((v41flags & NFSV4EXCH_UPDCONFIRMEDRECA) != 0)
3894		confirm.lval[1] = 1;
3895	else
3896		confirm.lval[1] = 0;
3897	v41flags = NFSV4EXCH_USENONPNFS;
3898	sp4type = fxdr_unsigned(uint32_t, *tl);
3899	if (sp4type != NFSV4EXCH_SP4NONE) {
3900		nd->nd_repstat = NFSERR_NOTSUPP;
3901		goto nfsmout;
3902	}
3903
3904	/*
3905	 * nfsrv_setclient() does the actual work of adding it to the
3906	 * client list. If there is no error, the structure has been
3907	 * linked into the client list and clp should no longer be used
3908	 * here. When an error is returned, it has not been linked in,
3909	 * so it should be free'd.
3910	 */
3911	nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
3912	if (clp != NULL) {
3913		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3914		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3915		free(clp->lc_stateid, M_NFSDCLIENT);
3916		free(clp, M_NFSDCLIENT);
3917	}
3918	if (nd->nd_repstat == 0) {
3919		if (confirm.lval[1] != 0)
3920			v41flags |= NFSV4EXCH_CONFIRMEDR;
3921		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + 3 * NFSX_UNSIGNED);
3922		*tl++ = clientid.lval[0];			/* ClientID */
3923		*tl++ = clientid.lval[1];
3924		*tl++ = txdr_unsigned(confirm.lval[0]);		/* SequenceID */
3925		*tl++ = txdr_unsigned(v41flags);		/* Exch flags */
3926		*tl++ = txdr_unsigned(NFSV4EXCH_SP4NONE);	/* No SSV */
3927		owner_minor = 0;				/* Owner */
3928		txdr_hyper(owner_minor, tl);			/* Minor */
3929		(void)nfsm_strtom(nd, nd->nd_cred->cr_prison->pr_hostuuid,
3930		    strlen(nd->nd_cred->cr_prison->pr_hostuuid)); /* Major */
3931		(void)nfsm_strtom(nd, nd->nd_cred->cr_prison->pr_hostuuid,
3932		    strlen(nd->nd_cred->cr_prison->pr_hostuuid)); /* Scope */
3933		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
3934		*tl = txdr_unsigned(1);
3935		(void)nfsm_strtom(nd, "freebsd.org", strlen("freebsd.org"));
3936		(void)nfsm_strtom(nd, version, strlen(version));
3937		NFSM_BUILD(tl, uint32_t *, NFSX_V4TIME);
3938		verstime.tv_sec = 1293840000;		/* Jan 1, 2011 */
3939		verstime.tv_nsec = 0;
3940		txdr_nfsv4time(&verstime, tl);
3941	}
3942	NFSEXITCODE2(0, nd);
3943	return (0);
3944nfsmout:
3945	if (clp != NULL) {
3946		NFSSOCKADDRFREE(clp->lc_req.nr_nam);
3947		NFSFREEMUTEX(&clp->lc_req.nr_mtx);
3948		free(clp->lc_stateid, M_NFSDCLIENT);
3949		free(clp, M_NFSDCLIENT);
3950	}
3951	NFSEXITCODE2(error, nd);
3952	return (error);
3953}
3954
3955/*
3956 * nfsv4 create session service
3957 */
3958APPLESTATIC int
3959nfsrvd_createsession(struct nfsrv_descript *nd, __unused int isdgram,
3960    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
3961{
3962	uint32_t *tl;
3963	int error = 0;
3964	nfsquad_t clientid, confirm;
3965	struct nfsdsession *sep = NULL;
3966	uint32_t rdmacnt;
3967
3968	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
3969		nd->nd_repstat = NFSERR_WRONGSEC;
3970		goto nfsmout;
3971	}
3972	sep = (struct nfsdsession *)malloc(sizeof(struct nfsdsession),
3973	    M_NFSDSESSION, M_WAITOK | M_ZERO);
3974	sep->sess_refcnt = 1;
3975	mtx_init(&sep->sess_cbsess.nfsess_mtx, "nfscbsession", NULL, MTX_DEF);
3976	NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + 2 * NFSX_UNSIGNED);
3977	clientid.lval[0] = *tl++;
3978	clientid.lval[1] = *tl++;
3979	confirm.lval[0] = fxdr_unsigned(uint32_t, *tl++);
3980	sep->sess_crflags = fxdr_unsigned(uint32_t, *tl);
3981	/* Persistent sessions and RDMA are not supported. */
3982	sep->sess_crflags &= NFSV4CRSESS_CONNBACKCHAN;
3983
3984	/* Fore channel attributes. */
3985	NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
3986	tl++;					/* Header pad always 0. */
3987	sep->sess_maxreq = fxdr_unsigned(uint32_t, *tl++);
3988	sep->sess_maxresp = fxdr_unsigned(uint32_t, *tl++);
3989	sep->sess_maxrespcached = fxdr_unsigned(uint32_t, *tl++);
3990	sep->sess_maxops = fxdr_unsigned(uint32_t, *tl++);
3991	sep->sess_maxslots = fxdr_unsigned(uint32_t, *tl++);
3992	if (sep->sess_maxslots > NFSV4_SLOTS)
3993		sep->sess_maxslots = NFSV4_SLOTS;
3994	rdmacnt = fxdr_unsigned(uint32_t, *tl);
3995	if (rdmacnt > 1) {
3996		nd->nd_repstat = NFSERR_BADXDR;
3997		goto nfsmout;
3998	} else if (rdmacnt == 1)
3999		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4000
4001	/* Back channel attributes. */
4002	NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
4003	tl++;					/* Header pad always 0. */
4004	sep->sess_cbmaxreq = fxdr_unsigned(uint32_t, *tl++);
4005	sep->sess_cbmaxresp = fxdr_unsigned(uint32_t, *tl++);
4006	sep->sess_cbmaxrespcached = fxdr_unsigned(uint32_t, *tl++);
4007	sep->sess_cbmaxops = fxdr_unsigned(uint32_t, *tl++);
4008	sep->sess_cbsess.nfsess_foreslots = fxdr_unsigned(uint32_t, *tl++);
4009	rdmacnt = fxdr_unsigned(uint32_t, *tl);
4010	if (rdmacnt > 1) {
4011		nd->nd_repstat = NFSERR_BADXDR;
4012		goto nfsmout;
4013	} else if (rdmacnt == 1)
4014		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4015
4016	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4017	sep->sess_cbprogram = fxdr_unsigned(uint32_t, *tl);
4018
4019	/*
4020	 * nfsrv_getclient() searches the client list for a match and
4021	 * returns the appropriate NFSERR status.
4022	 */
4023	nd->nd_repstat = nfsrv_getclient(clientid, CLOPS_CONFIRM | CLOPS_RENEW,
4024	    NULL, sep, confirm, sep->sess_cbprogram, nd, p);
4025	if (nd->nd_repstat == 0) {
4026		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
4027		NFSBCOPY(sep->sess_sessionid, tl, NFSX_V4SESSIONID);
4028		NFSM_BUILD(tl, uint32_t *, 18 * NFSX_UNSIGNED);
4029		*tl++ = txdr_unsigned(confirm.lval[0]);	/* sequenceid */
4030		*tl++ = txdr_unsigned(sep->sess_crflags);
4031
4032		/* Fore channel attributes. */
4033		*tl++ = 0;
4034		*tl++ = txdr_unsigned(sep->sess_maxreq);
4035		*tl++ = txdr_unsigned(sep->sess_maxresp);
4036		*tl++ = txdr_unsigned(sep->sess_maxrespcached);
4037		*tl++ = txdr_unsigned(sep->sess_maxops);
4038		*tl++ = txdr_unsigned(sep->sess_maxslots);
4039		*tl++ = txdr_unsigned(1);
4040		*tl++ = txdr_unsigned(0);			/* No RDMA. */
4041
4042		/* Back channel attributes. */
4043		*tl++ = 0;
4044		*tl++ = txdr_unsigned(sep->sess_cbmaxreq);
4045		*tl++ = txdr_unsigned(sep->sess_cbmaxresp);
4046		*tl++ = txdr_unsigned(sep->sess_cbmaxrespcached);
4047		*tl++ = txdr_unsigned(sep->sess_cbmaxops);
4048		*tl++ = txdr_unsigned(sep->sess_cbsess.nfsess_foreslots);
4049		*tl++ = txdr_unsigned(1);
4050		*tl = txdr_unsigned(0);			/* No RDMA. */
4051	}
4052nfsmout:
4053	if (nd->nd_repstat != 0 && sep != NULL)
4054		free(sep, M_NFSDSESSION);
4055	NFSEXITCODE2(error, nd);
4056	return (error);
4057}
4058
4059/*
4060 * nfsv4 sequence service
4061 */
4062APPLESTATIC int
4063nfsrvd_sequence(struct nfsrv_descript *nd, __unused int isdgram,
4064    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
4065{
4066	uint32_t *tl;
4067	uint32_t highest_slotid, sequenceid, sflags, target_highest_slotid;
4068	int cache_this, error = 0;
4069
4070	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4071		nd->nd_repstat = NFSERR_WRONGSEC;
4072		goto nfsmout;
4073	}
4074	NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID);
4075	NFSBCOPY(tl, nd->nd_sessionid, NFSX_V4SESSIONID);
4076	NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
4077	sequenceid = fxdr_unsigned(uint32_t, *tl++);
4078	nd->nd_slotid = fxdr_unsigned(uint32_t, *tl++);
4079	highest_slotid = fxdr_unsigned(uint32_t, *tl++);
4080	if (*tl == newnfs_true)
4081		cache_this = 1;
4082	else
4083		cache_this = 0;
4084	nd->nd_flag |= ND_HASSEQUENCE;
4085	nd->nd_repstat = nfsrv_checksequence(nd, sequenceid, &highest_slotid,
4086	    &target_highest_slotid, cache_this, &sflags, p);
4087	if (nd->nd_repstat == 0) {
4088		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
4089		NFSBCOPY(nd->nd_sessionid, tl, NFSX_V4SESSIONID);
4090		NFSM_BUILD(tl, uint32_t *, 5 * NFSX_UNSIGNED);
4091		*tl++ = txdr_unsigned(sequenceid);
4092		*tl++ = txdr_unsigned(nd->nd_slotid);
4093		*tl++ = txdr_unsigned(highest_slotid);
4094		*tl++ = txdr_unsigned(target_highest_slotid);
4095		*tl = txdr_unsigned(sflags);
4096	}
4097nfsmout:
4098	NFSEXITCODE2(error, nd);
4099	return (error);
4100}
4101
4102/*
4103 * nfsv4 reclaim complete service
4104 */
4105APPLESTATIC int
4106nfsrvd_reclaimcomplete(struct nfsrv_descript *nd, __unused int isdgram,
4107    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
4108{
4109	uint32_t *tl;
4110	int error = 0, onefs;
4111
4112	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4113		nd->nd_repstat = NFSERR_WRONGSEC;
4114		goto nfsmout;
4115	}
4116	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4117	/*
4118	 * I believe that a ReclaimComplete with rca_one_fs == TRUE is only
4119	 * to be used after a file system has been transferred to a different
4120	 * file server.  However, RFC5661 is somewhat vague w.r.t. this and
4121	 * the ESXi 6.7 client does both a ReclaimComplete with rca_one_fs
4122	 * == TRUE and one with ReclaimComplete with rca_one_fs == FALSE.
4123	 * Therefore, just ignore the rca_one_fs == TRUE operation and return
4124	 * NFS_OK without doing anything.
4125	 */
4126	onefs = 0;
4127	if (*tl == newnfs_true)
4128		onefs = 1;
4129	nd->nd_repstat = nfsrv_checkreclaimcomplete(nd, onefs);
4130nfsmout:
4131	NFSEXITCODE2(error, nd);
4132	return (error);
4133}
4134
4135/*
4136 * nfsv4 destroy clientid service
4137 */
4138APPLESTATIC int
4139nfsrvd_destroyclientid(struct nfsrv_descript *nd, __unused int isdgram,
4140    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
4141{
4142	uint32_t *tl;
4143	nfsquad_t clientid;
4144	int error = 0;
4145
4146	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4147		nd->nd_repstat = NFSERR_WRONGSEC;
4148		goto nfsmout;
4149	}
4150	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
4151	clientid.lval[0] = *tl++;
4152	clientid.lval[1] = *tl;
4153	nd->nd_repstat = nfsrv_destroyclient(clientid, p);
4154nfsmout:
4155	NFSEXITCODE2(error, nd);
4156	return (error);
4157}
4158
4159/*
4160 * nfsv4 bind connection to session service
4161 */
4162APPLESTATIC int
4163nfsrvd_bindconnsess(struct nfsrv_descript *nd, __unused int isdgram,
4164    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
4165{
4166	uint32_t *tl;
4167	uint8_t sessid[NFSX_V4SESSIONID];
4168	int error = 0, foreaft;
4169
4170	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4171		nd->nd_repstat = NFSERR_WRONGSEC;
4172		goto nfsmout;
4173	}
4174	NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID + 2 * NFSX_UNSIGNED);
4175	NFSBCOPY(tl, sessid, NFSX_V4SESSIONID);
4176	tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
4177	foreaft = fxdr_unsigned(int, *tl++);
4178	if (*tl == newnfs_true) {
4179		/* RDMA is not supported. */
4180		nd->nd_repstat = NFSERR_NOTSUPP;
4181		goto nfsmout;
4182	}
4183
4184	nd->nd_repstat = nfsrv_bindconnsess(nd, sessid, &foreaft);
4185	if (nd->nd_repstat == 0) {
4186		NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 2 *
4187		    NFSX_UNSIGNED);
4188		NFSBCOPY(sessid, tl, NFSX_V4SESSIONID);
4189		tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
4190		*tl++ = txdr_unsigned(foreaft);
4191		*tl = newnfs_false;
4192	}
4193nfsmout:
4194	NFSEXITCODE2(error, nd);
4195	return (error);
4196}
4197
4198/*
4199 * nfsv4 destroy session service
4200 */
4201APPLESTATIC int
4202nfsrvd_destroysession(struct nfsrv_descript *nd, __unused int isdgram,
4203    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
4204{
4205	uint8_t *cp, sessid[NFSX_V4SESSIONID];
4206	int error = 0;
4207
4208	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4209		nd->nd_repstat = NFSERR_WRONGSEC;
4210		goto nfsmout;
4211	}
4212	NFSM_DISSECT(cp, uint8_t *, NFSX_V4SESSIONID);
4213	NFSBCOPY(cp, sessid, NFSX_V4SESSIONID);
4214	nd->nd_repstat = nfsrv_destroysession(nd, sessid);
4215nfsmout:
4216	NFSEXITCODE2(error, nd);
4217	return (error);
4218}
4219
4220/*
4221 * nfsv4 free stateid service
4222 */
4223APPLESTATIC int
4224nfsrvd_freestateid(struct nfsrv_descript *nd, __unused int isdgram,
4225    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
4226{
4227	uint32_t *tl;
4228	nfsv4stateid_t stateid;
4229	int error = 0;
4230
4231	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4232		nd->nd_repstat = NFSERR_WRONGSEC;
4233		goto nfsmout;
4234	}
4235	NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
4236	stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
4237	NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
4238	nd->nd_repstat = nfsrv_freestateid(nd, &stateid, p);
4239nfsmout:
4240	NFSEXITCODE2(error, nd);
4241	return (error);
4242}
4243
4244/*
4245 * nfsv4 test stateid service
4246 */
4247APPLESTATIC int
4248nfsrvd_teststateid(struct nfsrv_descript *nd, __unused int isdgram,
4249    __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
4250{
4251	uint32_t *tl;
4252	nfsv4stateid_t *stateidp = NULL, *tstateidp;
4253	int cnt, error = 0, i, ret;
4254
4255	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
4256		nd->nd_repstat = NFSERR_WRONGSEC;
4257		goto nfsmout;
4258	}
4259	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4260	cnt = fxdr_unsigned(int, *tl);
4261	if (cnt <= 0 || cnt > 1024) {
4262		nd->nd_repstat = NFSERR_BADXDR;
4263		goto nfsmout;
4264	}
4265	stateidp = mallocarray(cnt, sizeof(nfsv4stateid_t), M_TEMP, M_WAITOK);
4266	tstateidp = stateidp;
4267	for (i = 0; i < cnt; i++) {
4268		NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
4269		tstateidp->seqid = fxdr_unsigned(uint32_t, *tl++);
4270		NFSBCOPY(tl, tstateidp->other, NFSX_STATEIDOTHER);
4271		tstateidp++;
4272	}
4273	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
4274	*tl = txdr_unsigned(cnt);
4275	tstateidp = stateidp;
4276	for (i = 0; i < cnt; i++) {
4277		ret = nfsrv_teststateid(nd, tstateidp, p);
4278		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
4279		*tl = txdr_unsigned(ret);
4280		tstateidp++;
4281	}
4282nfsmout:
4283	free(stateidp, M_TEMP);
4284	NFSEXITCODE2(error, nd);
4285	return (error);
4286}
4287
4288/*
4289 * nfsv4 service not supported
4290 */
4291APPLESTATIC int
4292nfsrvd_notsupp(struct nfsrv_descript *nd, __unused int isdgram,
4293    __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
4294{
4295
4296	nd->nd_repstat = NFSERR_NOTSUPP;
4297	NFSEXITCODE2(0, nd);
4298	return (0);
4299}
4300
4301