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