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