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