xref: /freebsd/sys/fs/nfs/nfs_commonsubs.c (revision 683b025adebfc2bb0d488c1d8d89e14379045ccc)
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  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 
37 /*
38  * These functions support the macros and help fiddle mbuf chains for
39  * the nfs op functions. They do things like create the rpc header and
40  * copy data between mbuf chains and uio lists.
41  */
42 #ifndef APPLEKEXT
43 #include "opt_inet6.h"
44 
45 #include <fs/nfs/nfsport.h>
46 
47 #include <security/mac/mac_framework.h>
48 
49 /*
50  * Data items converted to xdr at startup, since they are constant
51  * This is kinda hokey, but may save a little time doing byte swaps
52  */
53 u_int32_t newnfs_true, newnfs_false, newnfs_xdrneg1;
54 
55 /* And other global data */
56 nfstype nfsv34_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK,
57 		      NFFIFO, NFNON };
58 enum vtype newnv2tov_type[8] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON, VNON };
59 enum vtype nv34tov_type[8]={ VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO };
60 struct timeval nfsboottime;	/* Copy boottime once, so it never changes */
61 int nfscl_ticks;
62 int nfsrv_useacl = 1;
63 struct nfssockreq nfsrv_nfsuserdsock;
64 int nfsrv_nfsuserd = 0;
65 struct nfsreqhead nfsd_reqq;
66 uid_t nfsrv_defaultuid = UID_NOBODY;
67 gid_t nfsrv_defaultgid = GID_NOGROUP;
68 int nfsrv_lease = NFSRV_LEASE;
69 int ncl_mbuf_mlen = MLEN;
70 int nfsd_enable_stringtouid = 0;
71 int nfsd_enable_uidtostring = 0;
72 NFSNAMEIDMUTEX;
73 NFSSOCKMUTEX;
74 extern int nfsrv_lughashsize;
75 
76 /*
77  * This array of structures indicates, for V4:
78  * retfh - which of 3 types of calling args are used
79  *	0 - doesn't change cfh or use a sfh
80  *	1 - replaces cfh with a new one (unless it returns an error status)
81  *	2 - uses cfh and sfh
82  * needscfh - if the op wants a cfh and premtime
83  *	0 - doesn't use a cfh
84  *	1 - uses a cfh, but doesn't want pre-op attributes
85  *	2 - uses a cfh and wants pre-op attributes
86  * savereply - indicates a non-idempotent Op
87  *	0 - not non-idempotent
88  *	1 - non-idempotent
89  * Ops that are ordered via seqid# are handled separately from these
90  * non-idempotent Ops.
91  * Define it here, since it is used by both the client and server.
92  */
93 struct nfsv4_opflag nfsv4_opflag[NFSV41_NOPS] = {
94 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* undef */
95 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* undef */
96 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* undef */
97 	{ 0, 1, 0, 0, LK_SHARED, 1, 1 },		/* Access */
98 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Close */
99 	{ 0, 2, 0, 1, LK_EXCLUSIVE, 1, 1 },		/* Commit */
100 	{ 1, 2, 1, 1, LK_EXCLUSIVE, 1, 1 },		/* Create */
101 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Delegpurge */
102 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Delegreturn */
103 	{ 0, 1, 0, 0, LK_SHARED, 1, 1 },		/* Getattr */
104 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* GetFH */
105 	{ 2, 1, 1, 1, LK_EXCLUSIVE, 1, 1 },		/* Link */
106 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Lock */
107 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* LockT */
108 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* LockU */
109 	{ 1, 2, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Lookup */
110 	{ 1, 2, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Lookupp */
111 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* NVerify */
112 	{ 1, 1, 0, 1, LK_EXCLUSIVE, 1, 0 },		/* Open */
113 	{ 1, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* OpenAttr */
114 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* OpenConfirm */
115 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* OpenDowngrade */
116 	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* PutFH */
117 	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* PutPubFH */
118 	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* PutRootFH */
119 	{ 0, 1, 0, 0, LK_SHARED, 1, 0 },		/* Read */
120 	{ 0, 1, 0, 0, LK_SHARED, 1, 1 },		/* Readdir */
121 	{ 0, 1, 0, 0, LK_SHARED, 1, 1 },		/* ReadLink */
122 	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1, 1 },		/* Remove */
123 	{ 2, 1, 1, 1, LK_EXCLUSIVE, 1, 1 },		/* Rename */
124 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Renew */
125 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* RestoreFH */
126 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* SaveFH */
127 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* SecInfo */
128 	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1, 0 },		/* Setattr */
129 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* SetClientID */
130 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* SetClientIDConfirm */
131 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Verify */
132 	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1, 0 },		/* Write */
133 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* ReleaseLockOwner */
134 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Backchannel Ctrl */
135 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Bind Conn to Sess */
136 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 },		/* Exchange ID */
137 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 },		/* Create Session */
138 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 },		/* Destroy Session */
139 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Free StateID */
140 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Get Dir Deleg */
141 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Get Device Info */
142 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Get Device List */
143 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Layout Commit */
144 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Layout Get */
145 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Layout Return */
146 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Secinfo No name */
147 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Sequence */
148 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Set SSV */
149 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Test StateID */
150 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Want Delegation */
151 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 },		/* Destroy ClientID */
152 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Reclaim Complete */
153 };
154 #endif	/* !APPLEKEXT */
155 
156 static int ncl_mbuf_mhlen = MHLEN;
157 static int nfsrv_usercnt = 0;
158 static int nfsrv_dnsnamelen;
159 static u_char *nfsrv_dnsname = NULL;
160 static int nfsrv_usermax = 999999999;
161 struct nfsrv_lughash {
162 	struct mtx		mtx;
163 	struct nfsuserhashhead	lughead;
164 };
165 static struct nfsrv_lughash	*nfsuserhash;
166 static struct nfsrv_lughash	*nfsusernamehash;
167 static struct nfsrv_lughash	*nfsgrouphash;
168 static struct nfsrv_lughash	*nfsgroupnamehash;
169 
170 /*
171  * This static array indicates whether or not the RPC generates a large
172  * reply. This is used by nfs_reply() to decide whether or not an mbuf
173  * cluster should be allocated. (If a cluster is required by an RPC
174  * marked 0 in this array, the code will still work, just not quite as
175  * efficiently.)
176  */
177 int nfs_bigreply[NFSV41_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
178     0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
179     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 };
180 
181 /* local functions */
182 static int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep);
183 static void nfsv4_wanted(struct nfsv4lock *lp);
184 static int nfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len);
185 static int nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name,
186     NFSPROC_T *p);
187 static void nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser);
188 static int nfsrv_getrefstr(struct nfsrv_descript *, u_char **, u_char **,
189     int *, int *);
190 static void nfsrv_refstrbigenough(int, u_char **, u_char **, int *);
191 
192 
193 #ifndef APPLE
194 /*
195  * copies mbuf chain to the uio scatter/gather list
196  */
197 int
198 nfsm_mbufuio(struct nfsrv_descript *nd, struct uio *uiop, int siz)
199 {
200 	char *mbufcp, *uiocp;
201 	int xfer, left, len;
202 	mbuf_t mp;
203 	long uiosiz, rem;
204 	int error = 0;
205 
206 	mp = nd->nd_md;
207 	mbufcp = nd->nd_dpos;
208 	len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - mbufcp;
209 	rem = NFSM_RNDUP(siz) - siz;
210 	while (siz > 0) {
211 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL) {
212 			error = EBADRPC;
213 			goto out;
214 		}
215 		left = uiop->uio_iov->iov_len;
216 		uiocp = uiop->uio_iov->iov_base;
217 		if (left > siz)
218 			left = siz;
219 		uiosiz = left;
220 		while (left > 0) {
221 			while (len == 0) {
222 				mp = mbuf_next(mp);
223 				if (mp == NULL) {
224 					error = EBADRPC;
225 					goto out;
226 				}
227 				mbufcp = NFSMTOD(mp, caddr_t);
228 				len = mbuf_len(mp);
229 				KASSERT(len >= 0,
230 				    ("len %d, corrupted mbuf?", len));
231 			}
232 			xfer = (left > len) ? len : left;
233 #ifdef notdef
234 			/* Not Yet.. */
235 			if (uiop->uio_iov->iov_op != NULL)
236 				(*(uiop->uio_iov->iov_op))
237 				(mbufcp, uiocp, xfer);
238 			else
239 #endif
240 			if (uiop->uio_segflg == UIO_SYSSPACE)
241 				NFSBCOPY(mbufcp, uiocp, xfer);
242 			else
243 				copyout(mbufcp, CAST_USER_ADDR_T(uiocp), xfer);
244 			left -= xfer;
245 			len -= xfer;
246 			mbufcp += xfer;
247 			uiocp += xfer;
248 			uiop->uio_offset += xfer;
249 			uiop->uio_resid -= xfer;
250 		}
251 		if (uiop->uio_iov->iov_len <= siz) {
252 			uiop->uio_iovcnt--;
253 			uiop->uio_iov++;
254 		} else {
255 			uiop->uio_iov->iov_base = (void *)
256 				((char *)uiop->uio_iov->iov_base + uiosiz);
257 			uiop->uio_iov->iov_len -= uiosiz;
258 		}
259 		siz -= uiosiz;
260 	}
261 	nd->nd_dpos = mbufcp;
262 	nd->nd_md = mp;
263 	if (rem > 0) {
264 		if (len < rem)
265 			error = nfsm_advance(nd, rem, len);
266 		else
267 			nd->nd_dpos += rem;
268 	}
269 
270 out:
271 	NFSEXITCODE2(error, nd);
272 	return (error);
273 }
274 #endif	/* !APPLE */
275 
276 /*
277  * Help break down an mbuf chain by setting the first siz bytes contiguous
278  * pointed to by returned val.
279  * This is used by the macro NFSM_DISSECT for tough
280  * cases.
281  */
282 APPLESTATIC void *
283 nfsm_dissct(struct nfsrv_descript *nd, int siz, int how)
284 {
285 	mbuf_t mp2;
286 	int siz2, xfer;
287 	caddr_t p;
288 	int left;
289 	caddr_t retp;
290 
291 	retp = NULL;
292 	left = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) - nd->nd_dpos;
293 	while (left == 0) {
294 		nd->nd_md = mbuf_next(nd->nd_md);
295 		if (nd->nd_md == NULL)
296 			return (retp);
297 		left = mbuf_len(nd->nd_md);
298 		nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
299 	}
300 	if (left >= siz) {
301 		retp = nd->nd_dpos;
302 		nd->nd_dpos += siz;
303 	} else if (mbuf_next(nd->nd_md) == NULL) {
304 		return (retp);
305 	} else if (siz > ncl_mbuf_mhlen) {
306 		panic("nfs S too big");
307 	} else {
308 		MGET(mp2, MT_DATA, how);
309 		if (mp2 == NULL)
310 			return (NULL);
311 		mbuf_setnext(mp2, mbuf_next(nd->nd_md));
312 		mbuf_setnext(nd->nd_md, mp2);
313 		mbuf_setlen(nd->nd_md, mbuf_len(nd->nd_md) - left);
314 		nd->nd_md = mp2;
315 		retp = p = NFSMTOD(mp2, caddr_t);
316 		NFSBCOPY(nd->nd_dpos, p, left);	/* Copy what was left */
317 		siz2 = siz - left;
318 		p += left;
319 		mp2 = mbuf_next(mp2);
320 		/* Loop around copying up the siz2 bytes */
321 		while (siz2 > 0) {
322 			if (mp2 == NULL)
323 				return (NULL);
324 			xfer = (siz2 > mbuf_len(mp2)) ? mbuf_len(mp2) : siz2;
325 			if (xfer > 0) {
326 				NFSBCOPY(NFSMTOD(mp2, caddr_t), p, xfer);
327 				NFSM_DATAP(mp2, xfer);
328 				mbuf_setlen(mp2, mbuf_len(mp2) - xfer);
329 				p += xfer;
330 				siz2 -= xfer;
331 			}
332 			if (siz2 > 0)
333 				mp2 = mbuf_next(mp2);
334 		}
335 		mbuf_setlen(nd->nd_md, siz);
336 		nd->nd_md = mp2;
337 		nd->nd_dpos = NFSMTOD(mp2, caddr_t);
338 	}
339 	return (retp);
340 }
341 
342 /*
343  * Advance the position in the mbuf chain.
344  * If offs == 0, this is a no-op, but it is simpler to just return from
345  * here than check for offs > 0 for all calls to nfsm_advance.
346  * If left == -1, it should be calculated here.
347  */
348 APPLESTATIC int
349 nfsm_advance(struct nfsrv_descript *nd, int offs, int left)
350 {
351 	int error = 0;
352 
353 	if (offs == 0)
354 		goto out;
355 	/*
356 	 * A negative offs should be considered a serious problem.
357 	 */
358 	if (offs < 0)
359 		panic("nfsrv_advance");
360 
361 	/*
362 	 * If left == -1, calculate it here.
363 	 */
364 	if (left == -1)
365 		left = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) -
366 		    nd->nd_dpos;
367 
368 	/*
369 	 * Loop around, advancing over the mbuf data.
370 	 */
371 	while (offs > left) {
372 		offs -= left;
373 		nd->nd_md = mbuf_next(nd->nd_md);
374 		if (nd->nd_md == NULL) {
375 			error = EBADRPC;
376 			goto out;
377 		}
378 		left = mbuf_len(nd->nd_md);
379 		nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
380 	}
381 	nd->nd_dpos += offs;
382 
383 out:
384 	NFSEXITCODE(error);
385 	return (error);
386 }
387 
388 /*
389  * Copy a string into mbuf(s).
390  * Return the number of bytes output, including XDR overheads.
391  */
392 APPLESTATIC int
393 nfsm_strtom(struct nfsrv_descript *nd, const char *cp, int siz)
394 {
395 	mbuf_t m2;
396 	int xfer, left;
397 	mbuf_t m1;
398 	int rem, bytesize;
399 	u_int32_t *tl;
400 	char *cp2;
401 
402 	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
403 	*tl = txdr_unsigned(siz);
404 	rem = NFSM_RNDUP(siz) - siz;
405 	bytesize = NFSX_UNSIGNED + siz + rem;
406 	m2 = nd->nd_mb;
407 	cp2 = nd->nd_bpos;
408 	left = M_TRAILINGSPACE(m2);
409 
410 	/*
411 	 * Loop around copying the string to mbuf(s).
412 	 */
413 	while (siz > 0) {
414 		if (left == 0) {
415 			if (siz > ncl_mbuf_mlen)
416 				NFSMCLGET(m1, M_WAITOK);
417 			else
418 				NFSMGET(m1);
419 			mbuf_setlen(m1, 0);
420 			mbuf_setnext(m2, m1);
421 			m2 = m1;
422 			cp2 = NFSMTOD(m2, caddr_t);
423 			left = M_TRAILINGSPACE(m2);
424 		}
425 		if (left >= siz)
426 			xfer = siz;
427 		else
428 			xfer = left;
429 		NFSBCOPY(cp, cp2, xfer);
430 		cp += xfer;
431 		mbuf_setlen(m2, mbuf_len(m2) + xfer);
432 		siz -= xfer;
433 		left -= xfer;
434 		if (siz == 0 && rem) {
435 			if (left < rem)
436 				panic("nfsm_strtom");
437 			NFSBZERO(cp2 + xfer, rem);
438 			mbuf_setlen(m2, mbuf_len(m2) + rem);
439 		}
440 	}
441 	nd->nd_mb = m2;
442 	nd->nd_bpos = NFSMTOD(m2, caddr_t) + mbuf_len(m2);
443 	return (bytesize);
444 }
445 
446 /*
447  * Called once to initialize data structures...
448  */
449 APPLESTATIC void
450 newnfs_init(void)
451 {
452 	static int nfs_inited = 0;
453 
454 	if (nfs_inited)
455 		return;
456 	nfs_inited = 1;
457 
458 	newnfs_true = txdr_unsigned(TRUE);
459 	newnfs_false = txdr_unsigned(FALSE);
460 	newnfs_xdrneg1 = txdr_unsigned(-1);
461 	nfscl_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
462 	if (nfscl_ticks < 1)
463 		nfscl_ticks = 1;
464 	NFSSETBOOTTIME(nfsboottime);
465 
466 	/*
467 	 * Initialize reply list and start timer
468 	 */
469 	TAILQ_INIT(&nfsd_reqq);
470 	NFS_TIMERINIT;
471 }
472 
473 /*
474  * Put a file handle in an mbuf list.
475  * If the size argument == 0, just use the default size.
476  * set_true == 1 if there should be an newnfs_true prepended on the file handle.
477  * Return the number of bytes output, including XDR overhead.
478  */
479 APPLESTATIC int
480 nfsm_fhtom(struct nfsrv_descript *nd, u_int8_t *fhp, int size, int set_true)
481 {
482 	u_int32_t *tl;
483 	u_int8_t *cp;
484 	int fullsiz, rem, bytesize = 0;
485 
486 	if (size == 0)
487 		size = NFSX_MYFH;
488 	switch (nd->nd_flag & (ND_NFSV2 | ND_NFSV3 | ND_NFSV4)) {
489 	case ND_NFSV2:
490 		if (size > NFSX_V2FH)
491 			panic("fh size > NFSX_V2FH for NFSv2");
492 		NFSM_BUILD(cp, u_int8_t *, NFSX_V2FH);
493 		NFSBCOPY(fhp, cp, size);
494 		if (size < NFSX_V2FH)
495 			NFSBZERO(cp + size, NFSX_V2FH - size);
496 		bytesize = NFSX_V2FH;
497 		break;
498 	case ND_NFSV3:
499 	case ND_NFSV4:
500 		fullsiz = NFSM_RNDUP(size);
501 		rem = fullsiz - size;
502 		if (set_true) {
503 		    bytesize = 2 * NFSX_UNSIGNED + fullsiz;
504 		    NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
505 		    *tl = newnfs_true;
506 		} else {
507 		    bytesize = NFSX_UNSIGNED + fullsiz;
508 		}
509 		(void) nfsm_strtom(nd, fhp, size);
510 		break;
511 	}
512 	return (bytesize);
513 }
514 
515 /*
516  * This function compares two net addresses by family and returns TRUE
517  * if they are the same host.
518  * If there is any doubt, return FALSE.
519  * The AF_INET family is handled as a special case so that address mbufs
520  * don't need to be saved to store "struct in_addr", which is only 4 bytes.
521  */
522 APPLESTATIC int
523 nfsaddr_match(int family, union nethostaddr *haddr, NFSSOCKADDR_T nam)
524 {
525 	struct sockaddr_in *inetaddr;
526 
527 	switch (family) {
528 	case AF_INET:
529 		inetaddr = NFSSOCKADDR(nam, struct sockaddr_in *);
530 		if (inetaddr->sin_family == AF_INET &&
531 		    inetaddr->sin_addr.s_addr == haddr->had_inet.s_addr)
532 			return (1);
533 		break;
534 #ifdef INET6
535 	case AF_INET6:
536 		{
537 		struct sockaddr_in6 *inetaddr6;
538 
539 		inetaddr6 = NFSSOCKADDR(nam, struct sockaddr_in6 *);
540 		/* XXX - should test sin6_scope_id ? */
541 		if (inetaddr6->sin6_family == AF_INET6 &&
542 		    IN6_ARE_ADDR_EQUAL(&inetaddr6->sin6_addr,
543 			  &haddr->had_inet6))
544 			return (1);
545 		}
546 		break;
547 #endif
548 	}
549 	return (0);
550 }
551 
552 /*
553  * Similar to the above, but takes to NFSSOCKADDR_T args.
554  */
555 APPLESTATIC int
556 nfsaddr2_match(NFSSOCKADDR_T nam1, NFSSOCKADDR_T nam2)
557 {
558 	struct sockaddr_in *addr1, *addr2;
559 	struct sockaddr *inaddr;
560 
561 	inaddr = NFSSOCKADDR(nam1, struct sockaddr *);
562 	switch (inaddr->sa_family) {
563 	case AF_INET:
564 		addr1 = NFSSOCKADDR(nam1, struct sockaddr_in *);
565 		addr2 = NFSSOCKADDR(nam2, struct sockaddr_in *);
566 		if (addr2->sin_family == AF_INET &&
567 		    addr1->sin_addr.s_addr == addr2->sin_addr.s_addr)
568 			return (1);
569 		break;
570 #ifdef INET6
571 	case AF_INET6:
572 		{
573 		struct sockaddr_in6 *inet6addr1, *inet6addr2;
574 
575 		inet6addr1 = NFSSOCKADDR(nam1, struct sockaddr_in6 *);
576 		inet6addr2 = NFSSOCKADDR(nam2, struct sockaddr_in6 *);
577 		/* XXX - should test sin6_scope_id ? */
578 		if (inet6addr2->sin6_family == AF_INET6 &&
579 		    IN6_ARE_ADDR_EQUAL(&inet6addr1->sin6_addr,
580 			  &inet6addr2->sin6_addr))
581 			return (1);
582 		}
583 		break;
584 #endif
585 	}
586 	return (0);
587 }
588 
589 
590 /*
591  * Trim the stuff already dissected off the mbuf list.
592  */
593 APPLESTATIC void
594 newnfs_trimleading(nd)
595 	struct nfsrv_descript *nd;
596 {
597 	mbuf_t m, n;
598 	int offs;
599 
600 	/*
601 	 * First, free up leading mbufs.
602 	 */
603 	if (nd->nd_mrep != nd->nd_md) {
604 		m = nd->nd_mrep;
605 		while (mbuf_next(m) != nd->nd_md) {
606 			if (mbuf_next(m) == NULL)
607 				panic("nfsm trim leading");
608 			m = mbuf_next(m);
609 		}
610 		mbuf_setnext(m, NULL);
611 		mbuf_freem(nd->nd_mrep);
612 	}
613 	m = nd->nd_md;
614 
615 	/*
616 	 * Now, adjust this mbuf, based on nd_dpos.
617 	 */
618 	offs = nd->nd_dpos - NFSMTOD(m, caddr_t);
619 	if (offs == mbuf_len(m)) {
620 		n = m;
621 		m = mbuf_next(m);
622 		if (m == NULL)
623 			panic("nfsm trim leading2");
624 		mbuf_setnext(n, NULL);
625 		mbuf_freem(n);
626 	} else if (offs > 0) {
627 		mbuf_setlen(m, mbuf_len(m) - offs);
628 		NFSM_DATAP(m, offs);
629 	} else if (offs < 0)
630 		panic("nfsm trimleading offs");
631 	nd->nd_mrep = m;
632 	nd->nd_md = m;
633 	nd->nd_dpos = NFSMTOD(m, caddr_t);
634 }
635 
636 /*
637  * Trim trailing data off the mbuf list being built.
638  */
639 APPLESTATIC void
640 newnfs_trimtrailing(nd, mb, bpos)
641 	struct nfsrv_descript *nd;
642 	mbuf_t mb;
643 	caddr_t bpos;
644 {
645 
646 	if (mbuf_next(mb)) {
647 		mbuf_freem(mbuf_next(mb));
648 		mbuf_setnext(mb, NULL);
649 	}
650 	mbuf_setlen(mb, bpos - NFSMTOD(mb, caddr_t));
651 	nd->nd_mb = mb;
652 	nd->nd_bpos = bpos;
653 }
654 
655 /*
656  * Dissect a file handle on the client.
657  */
658 APPLESTATIC int
659 nfsm_getfh(struct nfsrv_descript *nd, struct nfsfh **nfhpp)
660 {
661 	u_int32_t *tl;
662 	struct nfsfh *nfhp;
663 	int error, len;
664 
665 	*nfhpp = NULL;
666 	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
667 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
668 		if ((len = fxdr_unsigned(int, *tl)) <= 0 ||
669 			len > NFSX_FHMAX) {
670 			error = EBADRPC;
671 			goto nfsmout;
672 		}
673 	} else
674 		len = NFSX_V2FH;
675 	MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) + len,
676 	    M_NFSFH, M_WAITOK);
677 	error = nfsrv_mtostr(nd, nfhp->nfh_fh, len);
678 	if (error) {
679 		FREE((caddr_t)nfhp, M_NFSFH);
680 		goto nfsmout;
681 	}
682 	nfhp->nfh_len = len;
683 	*nfhpp = nfhp;
684 nfsmout:
685 	NFSEXITCODE2(error, nd);
686 	return (error);
687 }
688 
689 /*
690  * Break down the nfsv4 acl.
691  * If the aclp == NULL or won't fit in an acl, just discard the acl info.
692  */
693 APPLESTATIC int
694 nfsrv_dissectacl(struct nfsrv_descript *nd, NFSACL_T *aclp, int *aclerrp,
695     int *aclsizep, __unused NFSPROC_T *p)
696 {
697 	u_int32_t *tl;
698 	int i, aclsize;
699 	int acecnt, error = 0, aceerr = 0, acesize;
700 
701 	*aclerrp = 0;
702 	if (aclp)
703 		aclp->acl_cnt = 0;
704 	/*
705 	 * Parse out the ace entries and expect them to conform to
706 	 * what can be supported by R/W/X bits.
707 	 */
708 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
709 	aclsize = NFSX_UNSIGNED;
710 	acecnt = fxdr_unsigned(int, *tl);
711 	if (acecnt > ACL_MAX_ENTRIES)
712 		aceerr = NFSERR_ATTRNOTSUPP;
713 	if (nfsrv_useacl == 0)
714 		aceerr = NFSERR_ATTRNOTSUPP;
715 	for (i = 0; i < acecnt; i++) {
716 		if (aclp && !aceerr)
717 			error = nfsrv_dissectace(nd, &aclp->acl_entry[i],
718 			    &aceerr, &acesize, p);
719 		else
720 			error = nfsrv_skipace(nd, &acesize);
721 		if (error)
722 			goto nfsmout;
723 		aclsize += acesize;
724 	}
725 	if (aclp && !aceerr)
726 		aclp->acl_cnt = acecnt;
727 	if (aceerr)
728 		*aclerrp = aceerr;
729 	if (aclsizep)
730 		*aclsizep = aclsize;
731 nfsmout:
732 	NFSEXITCODE2(error, nd);
733 	return (error);
734 }
735 
736 /*
737  * Skip over an NFSv4 ace entry. Just dissect the xdr and discard it.
738  */
739 static int
740 nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep)
741 {
742 	u_int32_t *tl;
743 	int error, len = 0;
744 
745 	NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
746 	len = fxdr_unsigned(int, *(tl + 3));
747 	error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
748 nfsmout:
749 	*acesizep = NFSM_RNDUP(len) + (4 * NFSX_UNSIGNED);
750 	NFSEXITCODE2(error, nd);
751 	return (error);
752 }
753 
754 /*
755  * Get attribute bits from an mbuf list.
756  * Returns EBADRPC for a parsing error, 0 otherwise.
757  * If the clearinvalid flag is set, clear the bits not supported.
758  */
759 APPLESTATIC int
760 nfsrv_getattrbits(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp, int *cntp,
761     int *retnotsupp)
762 {
763 	u_int32_t *tl;
764 	int cnt, i, outcnt;
765 	int error = 0;
766 
767 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
768 	cnt = fxdr_unsigned(int, *tl);
769 	if (cnt < 0) {
770 		error = NFSERR_BADXDR;
771 		goto nfsmout;
772 	}
773 	if (cnt > NFSATTRBIT_MAXWORDS)
774 		outcnt = NFSATTRBIT_MAXWORDS;
775 	else
776 		outcnt = cnt;
777 	NFSZERO_ATTRBIT(attrbitp);
778 	if (outcnt > 0) {
779 		NFSM_DISSECT(tl, u_int32_t *, outcnt * NFSX_UNSIGNED);
780 		for (i = 0; i < outcnt; i++)
781 			attrbitp->bits[i] = fxdr_unsigned(u_int32_t, *tl++);
782 	}
783 	for (i = 0; i < (cnt - outcnt); i++) {
784 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
785 		if (retnotsupp != NULL && *tl != 0)
786 			*retnotsupp = NFSERR_ATTRNOTSUPP;
787 	}
788 	if (cntp)
789 		*cntp = NFSX_UNSIGNED + (cnt * NFSX_UNSIGNED);
790 nfsmout:
791 	NFSEXITCODE2(error, nd);
792 	return (error);
793 }
794 
795 /*
796  * Get the attributes for V4.
797  * If the compare flag is true, test for any attribute changes,
798  * otherwise return the attribute values.
799  * These attributes cover fields in "struct vattr", "struct statfs",
800  * "struct nfsfsinfo", the file handle and the lease duration.
801  * The value of retcmpp is set to 1 if all attributes are the same,
802  * and 0 otherwise.
803  * Returns EBADRPC if it can't be parsed, 0 otherwise.
804  */
805 APPLESTATIC int
806 nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
807     struct nfsvattr *nap, struct nfsfh **nfhpp, fhandle_t *fhp, int fhsize,
808     struct nfsv3_pathconf *pc, struct statfs *sbp, struct nfsstatfs *sfp,
809     struct nfsfsinfo *fsp, NFSACL_T *aclp, int compare, int *retcmpp,
810     u_int32_t *leasep, u_int32_t *rderrp, NFSPROC_T *p, struct ucred *cred)
811 {
812 	u_int32_t *tl;
813 	int i = 0, j, k, l = 0, m, bitpos, attrsum = 0;
814 	int error, tfhsize, aceerr, attrsize, cnt, retnotsup;
815 	u_char *cp, *cp2, namestr[NFSV4_SMALLSTR + 1];
816 	nfsattrbit_t attrbits, retattrbits, checkattrbits;
817 	struct nfsfh *tnfhp;
818 	struct nfsreferral *refp;
819 	u_quad_t tquad;
820 	nfsquad_t tnfsquad;
821 	struct timespec temptime;
822 	uid_t uid;
823 	gid_t gid;
824 	u_int32_t freenum = 0, tuint;
825 	u_int64_t uquad = 0, thyp, thyp2;
826 #ifdef QUOTA
827 	struct dqblk dqb;
828 	uid_t savuid;
829 #endif
830 
831 	CTASSERT(sizeof(ino_t) == sizeof(uint64_t));
832 	if (compare) {
833 		retnotsup = 0;
834 		error = nfsrv_getattrbits(nd, &attrbits, NULL, &retnotsup);
835 	} else {
836 		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
837 	}
838 	if (error)
839 		goto nfsmout;
840 
841 	if (compare) {
842 		*retcmpp = retnotsup;
843 	} else {
844 		/*
845 		 * Just set default values to some of the important ones.
846 		 */
847 		if (nap != NULL) {
848 			nap->na_type = VREG;
849 			nap->na_mode = 0;
850 			nap->na_rdev = (NFSDEV_T)0;
851 			nap->na_mtime.tv_sec = 0;
852 			nap->na_mtime.tv_nsec = 0;
853 			nap->na_gen = 0;
854 			nap->na_flags = 0;
855 			nap->na_blocksize = NFS_FABLKSIZE;
856 		}
857 		if (sbp != NULL) {
858 			sbp->f_bsize = NFS_FABLKSIZE;
859 			sbp->f_blocks = 0;
860 			sbp->f_bfree = 0;
861 			sbp->f_bavail = 0;
862 			sbp->f_files = 0;
863 			sbp->f_ffree = 0;
864 		}
865 		if (fsp != NULL) {
866 			fsp->fs_rtmax = 8192;
867 			fsp->fs_rtpref = 8192;
868 			fsp->fs_maxname = NFS_MAXNAMLEN;
869 			fsp->fs_wtmax = 8192;
870 			fsp->fs_wtpref = 8192;
871 			fsp->fs_wtmult = NFS_FABLKSIZE;
872 			fsp->fs_dtpref = 8192;
873 			fsp->fs_maxfilesize = 0xffffffffffffffffull;
874 			fsp->fs_timedelta.tv_sec = 0;
875 			fsp->fs_timedelta.tv_nsec = 1;
876 			fsp->fs_properties = (NFSV3_FSFLINK | NFSV3_FSFSYMLINK |
877 				NFSV3_FSFHOMOGENEOUS | NFSV3_FSFCANSETTIME);
878 		}
879 		if (pc != NULL) {
880 			pc->pc_linkmax = LINK_MAX;
881 			pc->pc_namemax = NAME_MAX;
882 			pc->pc_notrunc = 0;
883 			pc->pc_chownrestricted = 0;
884 			pc->pc_caseinsensitive = 0;
885 			pc->pc_casepreserving = 1;
886 		}
887 		if (sfp != NULL) {
888 			sfp->sf_ffiles = UINT64_MAX;
889 			sfp->sf_tfiles = UINT64_MAX;
890 			sfp->sf_afiles = UINT64_MAX;
891 			sfp->sf_fbytes = UINT64_MAX;
892 			sfp->sf_tbytes = UINT64_MAX;
893 			sfp->sf_abytes = UINT64_MAX;
894 		}
895 	}
896 
897 	/*
898 	 * Loop around getting the attributes.
899 	 */
900 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
901 	attrsize = fxdr_unsigned(int, *tl);
902 	for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
903 	    if (attrsum > attrsize) {
904 		error = NFSERR_BADXDR;
905 		goto nfsmout;
906 	    }
907 	    if (NFSISSET_ATTRBIT(&attrbits, bitpos))
908 		switch (bitpos) {
909 		case NFSATTRBIT_SUPPORTEDATTRS:
910 			retnotsup = 0;
911 			if (compare || nap == NULL)
912 			    error = nfsrv_getattrbits(nd, &retattrbits,
913 				&cnt, &retnotsup);
914 			else
915 			    error = nfsrv_getattrbits(nd, &nap->na_suppattr,
916 				&cnt, &retnotsup);
917 			if (error)
918 			    goto nfsmout;
919 			if (compare && !(*retcmpp)) {
920 			   NFSSETSUPP_ATTRBIT(&checkattrbits);
921 
922 			   /* Some filesystem do not support NFSv4ACL   */
923 			   if (nfsrv_useacl == 0 || nfs_supportsnfsv4acls(vp) == 0) {
924 				NFSCLRBIT_ATTRBIT(&checkattrbits, NFSATTRBIT_ACL);
925 				NFSCLRBIT_ATTRBIT(&checkattrbits, NFSATTRBIT_ACLSUPPORT);
926 		   	   }
927 			   if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
928 			       || retnotsup)
929 				*retcmpp = NFSERR_NOTSAME;
930 			}
931 			attrsum += cnt;
932 			break;
933 		case NFSATTRBIT_TYPE:
934 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
935 			if (compare) {
936 				if (!(*retcmpp)) {
937 				    if (nap->na_type != nfsv34tov_type(*tl))
938 					*retcmpp = NFSERR_NOTSAME;
939 				}
940 			} else if (nap != NULL) {
941 				nap->na_type = nfsv34tov_type(*tl);
942 			}
943 			attrsum += NFSX_UNSIGNED;
944 			break;
945 		case NFSATTRBIT_FHEXPIRETYPE:
946 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
947 			if (compare && !(*retcmpp)) {
948 				if (fxdr_unsigned(int, *tl) !=
949 					NFSV4FHTYPE_PERSISTENT)
950 					*retcmpp = NFSERR_NOTSAME;
951 			}
952 			attrsum += NFSX_UNSIGNED;
953 			break;
954 		case NFSATTRBIT_CHANGE:
955 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
956 			if (compare) {
957 				if (!(*retcmpp)) {
958 				    if (nap->na_filerev != fxdr_hyper(tl))
959 					*retcmpp = NFSERR_NOTSAME;
960 				}
961 			} else if (nap != NULL) {
962 				nap->na_filerev = fxdr_hyper(tl);
963 			}
964 			attrsum += NFSX_HYPER;
965 			break;
966 		case NFSATTRBIT_SIZE:
967 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
968 			if (compare) {
969 				if (!(*retcmpp)) {
970 				    if (nap->na_size != fxdr_hyper(tl))
971 					*retcmpp = NFSERR_NOTSAME;
972 				}
973 			} else if (nap != NULL) {
974 				nap->na_size = fxdr_hyper(tl);
975 			}
976 			attrsum += NFSX_HYPER;
977 			break;
978 		case NFSATTRBIT_LINKSUPPORT:
979 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
980 			if (compare) {
981 				if (!(*retcmpp)) {
982 				    if (fsp->fs_properties & NFSV3_FSFLINK) {
983 					if (*tl == newnfs_false)
984 						*retcmpp = NFSERR_NOTSAME;
985 				    } else {
986 					if (*tl == newnfs_true)
987 						*retcmpp = NFSERR_NOTSAME;
988 				    }
989 				}
990 			} else if (fsp != NULL) {
991 				if (*tl == newnfs_true)
992 					fsp->fs_properties |= NFSV3_FSFLINK;
993 				else
994 					fsp->fs_properties &= ~NFSV3_FSFLINK;
995 			}
996 			attrsum += NFSX_UNSIGNED;
997 			break;
998 		case NFSATTRBIT_SYMLINKSUPPORT:
999 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1000 			if (compare) {
1001 				if (!(*retcmpp)) {
1002 				    if (fsp->fs_properties & NFSV3_FSFSYMLINK) {
1003 					if (*tl == newnfs_false)
1004 						*retcmpp = NFSERR_NOTSAME;
1005 				    } else {
1006 					if (*tl == newnfs_true)
1007 						*retcmpp = NFSERR_NOTSAME;
1008 				    }
1009 				}
1010 			} else if (fsp != NULL) {
1011 				if (*tl == newnfs_true)
1012 					fsp->fs_properties |= NFSV3_FSFSYMLINK;
1013 				else
1014 					fsp->fs_properties &= ~NFSV3_FSFSYMLINK;
1015 			}
1016 			attrsum += NFSX_UNSIGNED;
1017 			break;
1018 		case NFSATTRBIT_NAMEDATTR:
1019 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1020 			if (compare && !(*retcmpp)) {
1021 				if (*tl != newnfs_false)
1022 					*retcmpp = NFSERR_NOTSAME;
1023 			}
1024 			attrsum += NFSX_UNSIGNED;
1025 			break;
1026 		case NFSATTRBIT_FSID:
1027 			NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
1028 			thyp = fxdr_hyper(tl);
1029 			tl += 2;
1030 			thyp2 = fxdr_hyper(tl);
1031 			if (compare) {
1032 			    if (*retcmpp == 0) {
1033 				if (thyp != (u_int64_t)
1034 				    vfs_statfs(vnode_mount(vp))->f_fsid.val[0] ||
1035 				    thyp2 != (u_int64_t)
1036 				    vfs_statfs(vnode_mount(vp))->f_fsid.val[1])
1037 					*retcmpp = NFSERR_NOTSAME;
1038 			    }
1039 			} else if (nap != NULL) {
1040 				nap->na_filesid[0] = thyp;
1041 				nap->na_filesid[1] = thyp2;
1042 			}
1043 			attrsum += (4 * NFSX_UNSIGNED);
1044 			break;
1045 		case NFSATTRBIT_UNIQUEHANDLES:
1046 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1047 			if (compare && !(*retcmpp)) {
1048 				if (*tl != newnfs_true)
1049 					*retcmpp = NFSERR_NOTSAME;
1050 			}
1051 			attrsum += NFSX_UNSIGNED;
1052 			break;
1053 		case NFSATTRBIT_LEASETIME:
1054 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1055 			if (compare) {
1056 				if (fxdr_unsigned(int, *tl) != nfsrv_lease &&
1057 				    !(*retcmpp))
1058 					*retcmpp = NFSERR_NOTSAME;
1059 			} else if (leasep != NULL) {
1060 				*leasep = fxdr_unsigned(u_int32_t, *tl);
1061 			}
1062 			attrsum += NFSX_UNSIGNED;
1063 			break;
1064 		case NFSATTRBIT_RDATTRERROR:
1065 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1066 			if (compare) {
1067 				 if (!(*retcmpp))
1068 					*retcmpp = NFSERR_INVAL;
1069 			} else if (rderrp != NULL) {
1070 				*rderrp = fxdr_unsigned(u_int32_t, *tl);
1071 			}
1072 			attrsum += NFSX_UNSIGNED;
1073 			break;
1074 		case NFSATTRBIT_ACL:
1075 			if (compare) {
1076 			  if (!(*retcmpp)) {
1077 			    if (nfsrv_useacl && nfs_supportsnfsv4acls(vp)) {
1078 				NFSACL_T *naclp;
1079 
1080 				naclp = acl_alloc(M_WAITOK);
1081 				error = nfsrv_dissectacl(nd, naclp, &aceerr,
1082 				    &cnt, p);
1083 				if (error) {
1084 				    acl_free(naclp);
1085 				    goto nfsmout;
1086 				}
1087 				if (aceerr || aclp == NULL ||
1088 				    nfsrv_compareacl(aclp, naclp))
1089 				    *retcmpp = NFSERR_NOTSAME;
1090 				acl_free(naclp);
1091 			    } else {
1092 				error = nfsrv_dissectacl(nd, NULL, &aceerr,
1093 				    &cnt, p);
1094 				*retcmpp = NFSERR_ATTRNOTSUPP;
1095 			    }
1096 			  }
1097 			} else {
1098 				if (vp != NULL && aclp != NULL)
1099 				    error = nfsrv_dissectacl(nd, aclp, &aceerr,
1100 					&cnt, p);
1101 				else
1102 				    error = nfsrv_dissectacl(nd, NULL, &aceerr,
1103 					&cnt, p);
1104 				if (error)
1105 				    goto nfsmout;
1106 			}
1107 
1108 			attrsum += cnt;
1109 			break;
1110 		case NFSATTRBIT_ACLSUPPORT:
1111 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1112 			if (compare && !(*retcmpp)) {
1113 				if (nfsrv_useacl && nfs_supportsnfsv4acls(vp)) {
1114 					if (fxdr_unsigned(u_int32_t, *tl) !=
1115 					    NFSV4ACE_SUPTYPES)
1116 						*retcmpp = NFSERR_NOTSAME;
1117 				} else {
1118 					*retcmpp = NFSERR_ATTRNOTSUPP;
1119 				}
1120 			}
1121 			attrsum += NFSX_UNSIGNED;
1122 			break;
1123 		case NFSATTRBIT_ARCHIVE:
1124 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1125 			if (compare && !(*retcmpp))
1126 				*retcmpp = NFSERR_ATTRNOTSUPP;
1127 			attrsum += NFSX_UNSIGNED;
1128 			break;
1129 		case NFSATTRBIT_CANSETTIME:
1130 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1131 			if (compare) {
1132 				if (!(*retcmpp)) {
1133 				    if (fsp->fs_properties & NFSV3_FSFCANSETTIME) {
1134 					if (*tl == newnfs_false)
1135 						*retcmpp = NFSERR_NOTSAME;
1136 				    } else {
1137 					if (*tl == newnfs_true)
1138 						*retcmpp = NFSERR_NOTSAME;
1139 				    }
1140 				}
1141 			} else if (fsp != NULL) {
1142 				if (*tl == newnfs_true)
1143 					fsp->fs_properties |= NFSV3_FSFCANSETTIME;
1144 				else
1145 					fsp->fs_properties &= ~NFSV3_FSFCANSETTIME;
1146 			}
1147 			attrsum += NFSX_UNSIGNED;
1148 			break;
1149 		case NFSATTRBIT_CASEINSENSITIVE:
1150 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1151 			if (compare) {
1152 				if (!(*retcmpp)) {
1153 				    if (*tl != newnfs_false)
1154 					*retcmpp = NFSERR_NOTSAME;
1155 				}
1156 			} else if (pc != NULL) {
1157 				pc->pc_caseinsensitive =
1158 				    fxdr_unsigned(u_int32_t, *tl);
1159 			}
1160 			attrsum += NFSX_UNSIGNED;
1161 			break;
1162 		case NFSATTRBIT_CASEPRESERVING:
1163 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1164 			if (compare) {
1165 				if (!(*retcmpp)) {
1166 				    if (*tl != newnfs_true)
1167 					*retcmpp = NFSERR_NOTSAME;
1168 				}
1169 			} else if (pc != NULL) {
1170 				pc->pc_casepreserving =
1171 				    fxdr_unsigned(u_int32_t, *tl);
1172 			}
1173 			attrsum += NFSX_UNSIGNED;
1174 			break;
1175 		case NFSATTRBIT_CHOWNRESTRICTED:
1176 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1177 			if (compare) {
1178 				if (!(*retcmpp)) {
1179 				    if (*tl != newnfs_true)
1180 					*retcmpp = NFSERR_NOTSAME;
1181 				}
1182 			} else if (pc != NULL) {
1183 				pc->pc_chownrestricted =
1184 				    fxdr_unsigned(u_int32_t, *tl);
1185 			}
1186 			attrsum += NFSX_UNSIGNED;
1187 			break;
1188 		case NFSATTRBIT_FILEHANDLE:
1189 			error = nfsm_getfh(nd, &tnfhp);
1190 			if (error)
1191 				goto nfsmout;
1192 			tfhsize = tnfhp->nfh_len;
1193 			if (compare) {
1194 				if (!(*retcmpp) &&
1195 				    !NFSRV_CMPFH(tnfhp->nfh_fh, tfhsize,
1196 				     fhp, fhsize))
1197 					*retcmpp = NFSERR_NOTSAME;
1198 				FREE((caddr_t)tnfhp, M_NFSFH);
1199 			} else if (nfhpp != NULL) {
1200 				*nfhpp = tnfhp;
1201 			} else {
1202 				FREE((caddr_t)tnfhp, M_NFSFH);
1203 			}
1204 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(tfhsize));
1205 			break;
1206 		case NFSATTRBIT_FILEID:
1207 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1208 			thyp = fxdr_hyper(tl);
1209 			if (compare) {
1210 				if (!(*retcmpp)) {
1211 					if (nap->na_fileid != thyp)
1212 						*retcmpp = NFSERR_NOTSAME;
1213 				}
1214 			} else if (nap != NULL)
1215 				nap->na_fileid = thyp;
1216 			attrsum += NFSX_HYPER;
1217 			break;
1218 		case NFSATTRBIT_FILESAVAIL:
1219 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1220 			if (compare) {
1221 				if (!(*retcmpp) &&
1222 				    sfp->sf_afiles != fxdr_hyper(tl))
1223 					*retcmpp = NFSERR_NOTSAME;
1224 			} else if (sfp != NULL) {
1225 				sfp->sf_afiles = fxdr_hyper(tl);
1226 			}
1227 			attrsum += NFSX_HYPER;
1228 			break;
1229 		case NFSATTRBIT_FILESFREE:
1230 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1231 			if (compare) {
1232 				if (!(*retcmpp) &&
1233 				    sfp->sf_ffiles != fxdr_hyper(tl))
1234 					*retcmpp = NFSERR_NOTSAME;
1235 			} else if (sfp != NULL) {
1236 				sfp->sf_ffiles = fxdr_hyper(tl);
1237 			}
1238 			attrsum += NFSX_HYPER;
1239 			break;
1240 		case NFSATTRBIT_FILESTOTAL:
1241 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1242 			if (compare) {
1243 				if (!(*retcmpp) &&
1244 				    sfp->sf_tfiles != fxdr_hyper(tl))
1245 					*retcmpp = NFSERR_NOTSAME;
1246 			} else if (sfp != NULL) {
1247 				sfp->sf_tfiles = fxdr_hyper(tl);
1248 			}
1249 			attrsum += NFSX_HYPER;
1250 			break;
1251 		case NFSATTRBIT_FSLOCATIONS:
1252 			error = nfsrv_getrefstr(nd, &cp, &cp2, &l, &m);
1253 			if (error)
1254 				goto nfsmout;
1255 			attrsum += l;
1256 			if (compare && !(*retcmpp)) {
1257 				refp = nfsv4root_getreferral(vp, NULL, 0);
1258 				if (refp != NULL) {
1259 					if (cp == NULL || cp2 == NULL ||
1260 					    strcmp(cp, "/") ||
1261 					    strcmp(cp2, refp->nfr_srvlist))
1262 						*retcmpp = NFSERR_NOTSAME;
1263 				} else if (m == 0) {
1264 					*retcmpp = NFSERR_NOTSAME;
1265 				}
1266 			}
1267 			if (cp != NULL)
1268 				free(cp, M_NFSSTRING);
1269 			if (cp2 != NULL)
1270 				free(cp2, M_NFSSTRING);
1271 			break;
1272 		case NFSATTRBIT_HIDDEN:
1273 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1274 			if (compare && !(*retcmpp))
1275 				*retcmpp = NFSERR_ATTRNOTSUPP;
1276 			attrsum += NFSX_UNSIGNED;
1277 			break;
1278 		case NFSATTRBIT_HOMOGENEOUS:
1279 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1280 			if (compare) {
1281 				if (!(*retcmpp)) {
1282 				    if (fsp->fs_properties &
1283 					NFSV3_FSFHOMOGENEOUS) {
1284 					if (*tl == newnfs_false)
1285 						*retcmpp = NFSERR_NOTSAME;
1286 				    } else {
1287 					if (*tl == newnfs_true)
1288 						*retcmpp = NFSERR_NOTSAME;
1289 				    }
1290 				}
1291 			} else if (fsp != NULL) {
1292 				if (*tl == newnfs_true)
1293 				    fsp->fs_properties |= NFSV3_FSFHOMOGENEOUS;
1294 				else
1295 				    fsp->fs_properties &= ~NFSV3_FSFHOMOGENEOUS;
1296 			}
1297 			attrsum += NFSX_UNSIGNED;
1298 			break;
1299 		case NFSATTRBIT_MAXFILESIZE:
1300 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1301 			tnfsquad.qval = fxdr_hyper(tl);
1302 			if (compare) {
1303 				if (!(*retcmpp)) {
1304 					tquad = NFSRV_MAXFILESIZE;
1305 					if (tquad != tnfsquad.qval)
1306 						*retcmpp = NFSERR_NOTSAME;
1307 				}
1308 			} else if (fsp != NULL) {
1309 				fsp->fs_maxfilesize = tnfsquad.qval;
1310 			}
1311 			attrsum += NFSX_HYPER;
1312 			break;
1313 		case NFSATTRBIT_MAXLINK:
1314 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1315 			if (compare) {
1316 				if (!(*retcmpp)) {
1317 				    if (fxdr_unsigned(int, *tl) != LINK_MAX)
1318 					*retcmpp = NFSERR_NOTSAME;
1319 				}
1320 			} else if (pc != NULL) {
1321 				pc->pc_linkmax = fxdr_unsigned(u_int32_t, *tl);
1322 			}
1323 			attrsum += NFSX_UNSIGNED;
1324 			break;
1325 		case NFSATTRBIT_MAXNAME:
1326 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1327 			if (compare) {
1328 				if (!(*retcmpp)) {
1329 				    if (fsp->fs_maxname !=
1330 					fxdr_unsigned(u_int32_t, *tl))
1331 						*retcmpp = NFSERR_NOTSAME;
1332 				}
1333 			} else {
1334 				tuint = fxdr_unsigned(u_int32_t, *tl);
1335 				/*
1336 				 * Some Linux NFSv4 servers report this
1337 				 * as 0 or 4billion, so I'll set it to
1338 				 * NFS_MAXNAMLEN. If a server actually creates
1339 				 * a name longer than NFS_MAXNAMLEN, it will
1340 				 * get an error back.
1341 				 */
1342 				if (tuint == 0 || tuint > NFS_MAXNAMLEN)
1343 					tuint = NFS_MAXNAMLEN;
1344 				if (fsp != NULL)
1345 					fsp->fs_maxname = tuint;
1346 				if (pc != NULL)
1347 					pc->pc_namemax = tuint;
1348 			}
1349 			attrsum += NFSX_UNSIGNED;
1350 			break;
1351 		case NFSATTRBIT_MAXREAD:
1352 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1353 			if (compare) {
1354 				if (!(*retcmpp)) {
1355 				    if (fsp->fs_rtmax != fxdr_unsigned(u_int32_t,
1356 					*(tl + 1)) || *tl != 0)
1357 					*retcmpp = NFSERR_NOTSAME;
1358 				}
1359 			} else if (fsp != NULL) {
1360 				fsp->fs_rtmax = fxdr_unsigned(u_int32_t, *++tl);
1361 				fsp->fs_rtpref = fsp->fs_rtmax;
1362 				fsp->fs_dtpref = fsp->fs_rtpref;
1363 			}
1364 			attrsum += NFSX_HYPER;
1365 			break;
1366 		case NFSATTRBIT_MAXWRITE:
1367 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1368 			if (compare) {
1369 				if (!(*retcmpp)) {
1370 				    if (fsp->fs_wtmax != fxdr_unsigned(u_int32_t,
1371 					*(tl + 1)) || *tl != 0)
1372 					*retcmpp = NFSERR_NOTSAME;
1373 				}
1374 			} else if (fsp != NULL) {
1375 				fsp->fs_wtmax = fxdr_unsigned(int, *++tl);
1376 				fsp->fs_wtpref = fsp->fs_wtmax;
1377 			}
1378 			attrsum += NFSX_HYPER;
1379 			break;
1380 		case NFSATTRBIT_MIMETYPE:
1381 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1382 			i = fxdr_unsigned(int, *tl);
1383 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(i));
1384 			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
1385 			if (error)
1386 				goto nfsmout;
1387 			if (compare && !(*retcmpp))
1388 				*retcmpp = NFSERR_ATTRNOTSUPP;
1389 			break;
1390 		case NFSATTRBIT_MODE:
1391 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1392 			if (compare) {
1393 				if (!(*retcmpp)) {
1394 				    if (nap->na_mode != nfstov_mode(*tl))
1395 					*retcmpp = NFSERR_NOTSAME;
1396 				}
1397 			} else if (nap != NULL) {
1398 				nap->na_mode = nfstov_mode(*tl);
1399 			}
1400 			attrsum += NFSX_UNSIGNED;
1401 			break;
1402 		case NFSATTRBIT_NOTRUNC:
1403 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1404 			if (compare) {
1405 				if (!(*retcmpp)) {
1406 				    if (*tl != newnfs_true)
1407 					*retcmpp = NFSERR_NOTSAME;
1408 				}
1409 			} else if (pc != NULL) {
1410 				pc->pc_notrunc = fxdr_unsigned(u_int32_t, *tl);
1411 			}
1412 			attrsum += NFSX_UNSIGNED;
1413 			break;
1414 		case NFSATTRBIT_NUMLINKS:
1415 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1416 			tuint = fxdr_unsigned(u_int32_t, *tl);
1417 			if (compare) {
1418 			    if (!(*retcmpp)) {
1419 				if ((u_int32_t)nap->na_nlink != tuint)
1420 					*retcmpp = NFSERR_NOTSAME;
1421 			    }
1422 			} else if (nap != NULL) {
1423 				nap->na_nlink = tuint;
1424 			}
1425 			attrsum += NFSX_UNSIGNED;
1426 			break;
1427 		case NFSATTRBIT_OWNER:
1428 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1429 			j = fxdr_unsigned(int, *tl);
1430 			if (j < 0) {
1431 				error = NFSERR_BADXDR;
1432 				goto nfsmout;
1433 			}
1434 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
1435 			if (j > NFSV4_SMALLSTR)
1436 				cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
1437 			else
1438 				cp = namestr;
1439 			error = nfsrv_mtostr(nd, cp, j);
1440 			if (error) {
1441 				if (j > NFSV4_SMALLSTR)
1442 					free(cp, M_NFSSTRING);
1443 				goto nfsmout;
1444 			}
1445 			if (compare) {
1446 			    if (!(*retcmpp)) {
1447 				if (nfsv4_strtouid(nd, cp, j, &uid, p) ||
1448 				    nap->na_uid != uid)
1449 				    *retcmpp = NFSERR_NOTSAME;
1450 			    }
1451 			} else if (nap != NULL) {
1452 				if (nfsv4_strtouid(nd, cp, j, &uid, p))
1453 					nap->na_uid = nfsrv_defaultuid;
1454 				else
1455 					nap->na_uid = uid;
1456 			}
1457 			if (j > NFSV4_SMALLSTR)
1458 				free(cp, M_NFSSTRING);
1459 			break;
1460 		case NFSATTRBIT_OWNERGROUP:
1461 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1462 			j = fxdr_unsigned(int, *tl);
1463 			if (j < 0) {
1464 				error =  NFSERR_BADXDR;
1465 				goto nfsmout;
1466 			}
1467 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
1468 			if (j > NFSV4_SMALLSTR)
1469 				cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
1470 			else
1471 				cp = namestr;
1472 			error = nfsrv_mtostr(nd, cp, j);
1473 			if (error) {
1474 				if (j > NFSV4_SMALLSTR)
1475 					free(cp, M_NFSSTRING);
1476 				goto nfsmout;
1477 			}
1478 			if (compare) {
1479 			    if (!(*retcmpp)) {
1480 				if (nfsv4_strtogid(nd, cp, j, &gid, p) ||
1481 				    nap->na_gid != gid)
1482 				    *retcmpp = NFSERR_NOTSAME;
1483 			    }
1484 			} else if (nap != NULL) {
1485 				if (nfsv4_strtogid(nd, cp, j, &gid, p))
1486 					nap->na_gid = nfsrv_defaultgid;
1487 				else
1488 					nap->na_gid = gid;
1489 			}
1490 			if (j > NFSV4_SMALLSTR)
1491 				free(cp, M_NFSSTRING);
1492 			break;
1493 		case NFSATTRBIT_QUOTAHARD:
1494 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1495 			if (sbp != NULL) {
1496 			    if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
1497 				freenum = sbp->f_bfree;
1498 			    else
1499 				freenum = sbp->f_bavail;
1500 #ifdef QUOTA
1501 			    /*
1502 			     * ufs_quotactl() insists that the uid argument
1503 			     * equal p_ruid for non-root quota access, so
1504 			     * we'll just make sure that's the case.
1505 			     */
1506 			    savuid = p->p_cred->p_ruid;
1507 			    p->p_cred->p_ruid = cred->cr_uid;
1508 			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1509 				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1510 				freenum = min(dqb.dqb_bhardlimit, freenum);
1511 			    p->p_cred->p_ruid = savuid;
1512 #endif	/* QUOTA */
1513 			    uquad = (u_int64_t)freenum;
1514 			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1515 			}
1516 			if (compare && !(*retcmpp)) {
1517 				if (uquad != fxdr_hyper(tl))
1518 					*retcmpp = NFSERR_NOTSAME;
1519 			}
1520 			attrsum += NFSX_HYPER;
1521 			break;
1522 		case NFSATTRBIT_QUOTASOFT:
1523 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1524 			if (sbp != NULL) {
1525 			    if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
1526 				freenum = sbp->f_bfree;
1527 			    else
1528 				freenum = sbp->f_bavail;
1529 #ifdef QUOTA
1530 			    /*
1531 			     * ufs_quotactl() insists that the uid argument
1532 			     * equal p_ruid for non-root quota access, so
1533 			     * we'll just make sure that's the case.
1534 			     */
1535 			    savuid = p->p_cred->p_ruid;
1536 			    p->p_cred->p_ruid = cred->cr_uid;
1537 			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1538 				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1539 				freenum = min(dqb.dqb_bsoftlimit, freenum);
1540 			    p->p_cred->p_ruid = savuid;
1541 #endif	/* QUOTA */
1542 			    uquad = (u_int64_t)freenum;
1543 			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1544 			}
1545 			if (compare && !(*retcmpp)) {
1546 				if (uquad != fxdr_hyper(tl))
1547 					*retcmpp = NFSERR_NOTSAME;
1548 			}
1549 			attrsum += NFSX_HYPER;
1550 			break;
1551 		case NFSATTRBIT_QUOTAUSED:
1552 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1553 			if (sbp != NULL) {
1554 			    freenum = 0;
1555 #ifdef QUOTA
1556 			    /*
1557 			     * ufs_quotactl() insists that the uid argument
1558 			     * equal p_ruid for non-root quota access, so
1559 			     * we'll just make sure that's the case.
1560 			     */
1561 			    savuid = p->p_cred->p_ruid;
1562 			    p->p_cred->p_ruid = cred->cr_uid;
1563 			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1564 				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1565 				freenum = dqb.dqb_curblocks;
1566 			    p->p_cred->p_ruid = savuid;
1567 #endif	/* QUOTA */
1568 			    uquad = (u_int64_t)freenum;
1569 			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1570 			}
1571 			if (compare && !(*retcmpp)) {
1572 				if (uquad != fxdr_hyper(tl))
1573 					*retcmpp = NFSERR_NOTSAME;
1574 			}
1575 			attrsum += NFSX_HYPER;
1576 			break;
1577 		case NFSATTRBIT_RAWDEV:
1578 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4SPECDATA);
1579 			j = fxdr_unsigned(int, *tl++);
1580 			k = fxdr_unsigned(int, *tl);
1581 			if (compare) {
1582 			    if (!(*retcmpp)) {
1583 				if (nap->na_rdev != NFSMAKEDEV(j, k))
1584 					*retcmpp = NFSERR_NOTSAME;
1585 			    }
1586 			} else if (nap != NULL) {
1587 				nap->na_rdev = NFSMAKEDEV(j, k);
1588 			}
1589 			attrsum += NFSX_V4SPECDATA;
1590 			break;
1591 		case NFSATTRBIT_SPACEAVAIL:
1592 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1593 			if (compare) {
1594 				if (!(*retcmpp) &&
1595 				    sfp->sf_abytes != fxdr_hyper(tl))
1596 					*retcmpp = NFSERR_NOTSAME;
1597 			} else if (sfp != NULL) {
1598 				sfp->sf_abytes = fxdr_hyper(tl);
1599 			}
1600 			attrsum += NFSX_HYPER;
1601 			break;
1602 		case NFSATTRBIT_SPACEFREE:
1603 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1604 			if (compare) {
1605 				if (!(*retcmpp) &&
1606 				    sfp->sf_fbytes != fxdr_hyper(tl))
1607 					*retcmpp = NFSERR_NOTSAME;
1608 			} else if (sfp != NULL) {
1609 				sfp->sf_fbytes = fxdr_hyper(tl);
1610 			}
1611 			attrsum += NFSX_HYPER;
1612 			break;
1613 		case NFSATTRBIT_SPACETOTAL:
1614 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1615 			if (compare) {
1616 				if (!(*retcmpp) &&
1617 				    sfp->sf_tbytes != fxdr_hyper(tl))
1618 					*retcmpp = NFSERR_NOTSAME;
1619 			} else if (sfp != NULL) {
1620 				sfp->sf_tbytes = fxdr_hyper(tl);
1621 			}
1622 			attrsum += NFSX_HYPER;
1623 			break;
1624 		case NFSATTRBIT_SPACEUSED:
1625 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1626 			thyp = fxdr_hyper(tl);
1627 			if (compare) {
1628 			    if (!(*retcmpp)) {
1629 				if ((u_int64_t)nap->na_bytes != thyp)
1630 					*retcmpp = NFSERR_NOTSAME;
1631 			    }
1632 			} else if (nap != NULL) {
1633 				nap->na_bytes = thyp;
1634 			}
1635 			attrsum += NFSX_HYPER;
1636 			break;
1637 		case NFSATTRBIT_SYSTEM:
1638 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1639 			if (compare && !(*retcmpp))
1640 				*retcmpp = NFSERR_ATTRNOTSUPP;
1641 			attrsum += NFSX_UNSIGNED;
1642 			break;
1643 		case NFSATTRBIT_TIMEACCESS:
1644 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1645 			fxdr_nfsv4time(tl, &temptime);
1646 			if (compare) {
1647 			    if (!(*retcmpp)) {
1648 				if (!NFS_CMPTIME(temptime, nap->na_atime))
1649 					*retcmpp = NFSERR_NOTSAME;
1650 			    }
1651 			} else if (nap != NULL) {
1652 				nap->na_atime = temptime;
1653 			}
1654 			attrsum += NFSX_V4TIME;
1655 			break;
1656 		case NFSATTRBIT_TIMEACCESSSET:
1657 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1658 			attrsum += NFSX_UNSIGNED;
1659 			i = fxdr_unsigned(int, *tl);
1660 			if (i == NFSV4SATTRTIME_TOCLIENT) {
1661 				NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1662 				attrsum += NFSX_V4TIME;
1663 			}
1664 			if (compare && !(*retcmpp))
1665 				*retcmpp = NFSERR_INVAL;
1666 			break;
1667 		case NFSATTRBIT_TIMEBACKUP:
1668 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1669 			if (compare && !(*retcmpp))
1670 				*retcmpp = NFSERR_ATTRNOTSUPP;
1671 			attrsum += NFSX_V4TIME;
1672 			break;
1673 		case NFSATTRBIT_TIMECREATE:
1674 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1675 			if (compare && !(*retcmpp))
1676 				*retcmpp = NFSERR_ATTRNOTSUPP;
1677 			attrsum += NFSX_V4TIME;
1678 			break;
1679 		case NFSATTRBIT_TIMEDELTA:
1680 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1681 			if (fsp != NULL) {
1682 			    if (compare) {
1683 				if (!(*retcmpp)) {
1684 				    if ((u_int32_t)fsp->fs_timedelta.tv_sec !=
1685 					fxdr_unsigned(u_int32_t, *(tl + 1)) ||
1686 				        (u_int32_t)fsp->fs_timedelta.tv_nsec !=
1687 					(fxdr_unsigned(u_int32_t, *(tl + 2)) %
1688 					 1000000000) ||
1689 					*tl != 0)
1690 					    *retcmpp = NFSERR_NOTSAME;
1691 				}
1692 			    } else {
1693 				fxdr_nfsv4time(tl, &fsp->fs_timedelta);
1694 			    }
1695 			}
1696 			attrsum += NFSX_V4TIME;
1697 			break;
1698 		case NFSATTRBIT_TIMEMETADATA:
1699 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1700 			fxdr_nfsv4time(tl, &temptime);
1701 			if (compare) {
1702 			    if (!(*retcmpp)) {
1703 				if (!NFS_CMPTIME(temptime, nap->na_ctime))
1704 					*retcmpp = NFSERR_NOTSAME;
1705 			    }
1706 			} else if (nap != NULL) {
1707 				nap->na_ctime = temptime;
1708 			}
1709 			attrsum += NFSX_V4TIME;
1710 			break;
1711 		case NFSATTRBIT_TIMEMODIFY:
1712 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1713 			fxdr_nfsv4time(tl, &temptime);
1714 			if (compare) {
1715 			    if (!(*retcmpp)) {
1716 				if (!NFS_CMPTIME(temptime, nap->na_mtime))
1717 					*retcmpp = NFSERR_NOTSAME;
1718 			    }
1719 			} else if (nap != NULL) {
1720 				nap->na_mtime = temptime;
1721 			}
1722 			attrsum += NFSX_V4TIME;
1723 			break;
1724 		case NFSATTRBIT_TIMEMODIFYSET:
1725 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1726 			attrsum += NFSX_UNSIGNED;
1727 			i = fxdr_unsigned(int, *tl);
1728 			if (i == NFSV4SATTRTIME_TOCLIENT) {
1729 				NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1730 				attrsum += NFSX_V4TIME;
1731 			}
1732 			if (compare && !(*retcmpp))
1733 				*retcmpp = NFSERR_INVAL;
1734 			break;
1735 		case NFSATTRBIT_MOUNTEDONFILEID:
1736 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1737 			thyp = fxdr_hyper(tl);
1738 			if (compare) {
1739 				if (!(*retcmpp)) {
1740 					if (!vp || !nfsrv_atroot(vp, &thyp2))
1741 						thyp2 = nap->na_fileid;
1742 					if (thyp2 != thyp)
1743 						*retcmpp = NFSERR_NOTSAME;
1744 				}
1745 			} else if (nap != NULL)
1746 				nap->na_mntonfileno = thyp;
1747 			attrsum += NFSX_HYPER;
1748 			break;
1749 		case NFSATTRBIT_SUPPATTREXCLCREAT:
1750 			retnotsup = 0;
1751 			error = nfsrv_getattrbits(nd, &retattrbits,
1752 			    &cnt, &retnotsup);
1753 			if (error)
1754 			    goto nfsmout;
1755 			if (compare && !(*retcmpp)) {
1756 			   NFSSETSUPP_ATTRBIT(&checkattrbits);
1757 			   NFSCLRNOTSETABLE_ATTRBIT(&checkattrbits);
1758 			   NFSCLRBIT_ATTRBIT(&checkattrbits,
1759 				NFSATTRBIT_TIMEACCESSSET);
1760 			   if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
1761 			       || retnotsup)
1762 				*retcmpp = NFSERR_NOTSAME;
1763 			}
1764 			attrsum += cnt;
1765 			break;
1766 		default:
1767 			printf("EEK! nfsv4_loadattr unknown attr=%d\n",
1768 				bitpos);
1769 			if (compare && !(*retcmpp))
1770 				*retcmpp = NFSERR_ATTRNOTSUPP;
1771 			/*
1772 			 * and get out of the loop, since we can't parse
1773 			 * the unknown attrbute data.
1774 			 */
1775 			bitpos = NFSATTRBIT_MAX;
1776 			break;
1777 		}
1778 	}
1779 
1780 	/*
1781 	 * some clients pad the attrlist, so we need to skip over the
1782 	 * padding.
1783 	 */
1784 	if (attrsum > attrsize) {
1785 		error = NFSERR_BADXDR;
1786 	} else {
1787 		attrsize = NFSM_RNDUP(attrsize);
1788 		if (attrsum < attrsize)
1789 			error = nfsm_advance(nd, attrsize - attrsum, -1);
1790 	}
1791 nfsmout:
1792 	NFSEXITCODE2(error, nd);
1793 	return (error);
1794 }
1795 
1796 /*
1797  * Implement sleep locks for newnfs. The nfslock_usecnt allows for a
1798  * shared lock and the NFSXXX_LOCK flag permits an exclusive lock.
1799  * The first argument is a pointer to an nfsv4lock structure.
1800  * The second argument is 1 iff a blocking lock is wanted.
1801  * If this argument is 0, the call waits until no thread either wants nor
1802  * holds an exclusive lock.
1803  * It returns 1 if the lock was acquired, 0 otherwise.
1804  * If several processes call this function concurrently wanting the exclusive
1805  * lock, one will get the lock and the rest will return without getting the
1806  * lock. (If the caller must have the lock, it simply calls this function in a
1807  *  loop until the function returns 1 to indicate the lock was acquired.)
1808  * Any usecnt must be decremented by calling nfsv4_relref() before
1809  * calling nfsv4_lock(). It was done this way, so nfsv4_lock() could
1810  * be called in a loop.
1811  * The isleptp argument is set to indicate if the call slept, iff not NULL
1812  * and the mp argument indicates to check for a forced dismount, iff not
1813  * NULL.
1814  */
1815 APPLESTATIC int
1816 nfsv4_lock(struct nfsv4lock *lp, int iwantlock, int *isleptp,
1817     void *mutex, struct mount *mp)
1818 {
1819 
1820 	if (isleptp)
1821 		*isleptp = 0;
1822 	/*
1823 	 * If a lock is wanted, loop around until the lock is acquired by
1824 	 * someone and then released. If I want the lock, try to acquire it.
1825 	 * For a lock to be issued, no lock must be in force and the usecnt
1826 	 * must be zero.
1827 	 */
1828 	if (iwantlock) {
1829 	    if (!(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
1830 		lp->nfslock_usecnt == 0) {
1831 		lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1832 		lp->nfslock_lock |= NFSV4LOCK_LOCK;
1833 		return (1);
1834 	    }
1835 	    lp->nfslock_lock |= NFSV4LOCK_LOCKWANTED;
1836 	}
1837 	while (lp->nfslock_lock & (NFSV4LOCK_LOCK | NFSV4LOCK_LOCKWANTED)) {
1838 		if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1839 			lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1840 			return (0);
1841 		}
1842 		lp->nfslock_lock |= NFSV4LOCK_WANTED;
1843 		if (isleptp)
1844 			*isleptp = 1;
1845 		(void) nfsmsleep(&lp->nfslock_lock, mutex,
1846 		    PZERO - 1, "nfsv4lck", NULL);
1847 		if (iwantlock && !(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
1848 		    lp->nfslock_usecnt == 0) {
1849 			lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1850 			lp->nfslock_lock |= NFSV4LOCK_LOCK;
1851 			return (1);
1852 		}
1853 	}
1854 	return (0);
1855 }
1856 
1857 /*
1858  * Release the lock acquired by nfsv4_lock().
1859  * The second argument is set to 1 to indicate the nfslock_usecnt should be
1860  * incremented, as well.
1861  */
1862 APPLESTATIC void
1863 nfsv4_unlock(struct nfsv4lock *lp, int incref)
1864 {
1865 
1866 	lp->nfslock_lock &= ~NFSV4LOCK_LOCK;
1867 	if (incref)
1868 		lp->nfslock_usecnt++;
1869 	nfsv4_wanted(lp);
1870 }
1871 
1872 /*
1873  * Release a reference cnt.
1874  */
1875 APPLESTATIC void
1876 nfsv4_relref(struct nfsv4lock *lp)
1877 {
1878 
1879 	if (lp->nfslock_usecnt <= 0)
1880 		panic("nfsv4root ref cnt");
1881 	lp->nfslock_usecnt--;
1882 	if (lp->nfslock_usecnt == 0)
1883 		nfsv4_wanted(lp);
1884 }
1885 
1886 /*
1887  * Get a reference cnt.
1888  * This function will wait for any exclusive lock to be released, but will
1889  * not wait for threads that want the exclusive lock. If priority needs
1890  * to be given to threads that need the exclusive lock, a call to nfsv4_lock()
1891  * with the 2nd argument == 0 should be done before calling nfsv4_getref().
1892  * If the mp argument is not NULL, check for MNTK_UNMOUNTF being set and
1893  * return without getting a refcnt for that case.
1894  */
1895 APPLESTATIC void
1896 nfsv4_getref(struct nfsv4lock *lp, int *isleptp, void *mutex,
1897     struct mount *mp)
1898 {
1899 
1900 	if (isleptp)
1901 		*isleptp = 0;
1902 
1903 	/*
1904 	 * Wait for a lock held.
1905 	 */
1906 	while (lp->nfslock_lock & NFSV4LOCK_LOCK) {
1907 		if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0)
1908 			return;
1909 		lp->nfslock_lock |= NFSV4LOCK_WANTED;
1910 		if (isleptp)
1911 			*isleptp = 1;
1912 		(void) nfsmsleep(&lp->nfslock_lock, mutex,
1913 		    PZERO - 1, "nfsv4gr", NULL);
1914 	}
1915 	if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0)
1916 		return;
1917 
1918 	lp->nfslock_usecnt++;
1919 }
1920 
1921 /*
1922  * Get a reference as above, but return failure instead of sleeping if
1923  * an exclusive lock is held.
1924  */
1925 APPLESTATIC int
1926 nfsv4_getref_nonblock(struct nfsv4lock *lp)
1927 {
1928 
1929 	if ((lp->nfslock_lock & NFSV4LOCK_LOCK) != 0)
1930 		return (0);
1931 
1932 	lp->nfslock_usecnt++;
1933 	return (1);
1934 }
1935 
1936 /*
1937  * Test for a lock. Return 1 if locked, 0 otherwise.
1938  */
1939 APPLESTATIC int
1940 nfsv4_testlock(struct nfsv4lock *lp)
1941 {
1942 
1943 	if ((lp->nfslock_lock & NFSV4LOCK_LOCK) == 0 &&
1944 	    lp->nfslock_usecnt == 0)
1945 		return (0);
1946 	return (1);
1947 }
1948 
1949 /*
1950  * Wake up anyone sleeping, waiting for this lock.
1951  */
1952 static void
1953 nfsv4_wanted(struct nfsv4lock *lp)
1954 {
1955 
1956 	if (lp->nfslock_lock & NFSV4LOCK_WANTED) {
1957 		lp->nfslock_lock &= ~NFSV4LOCK_WANTED;
1958 		wakeup((caddr_t)&lp->nfslock_lock);
1959 	}
1960 }
1961 
1962 /*
1963  * Copy a string from an mbuf list into a character array.
1964  * Return EBADRPC if there is an mbuf error,
1965  * 0 otherwise.
1966  */
1967 APPLESTATIC int
1968 nfsrv_mtostr(struct nfsrv_descript *nd, char *str, int siz)
1969 {
1970 	char *cp;
1971 	int xfer, len;
1972 	mbuf_t mp;
1973 	int rem, error = 0;
1974 
1975 	mp = nd->nd_md;
1976 	cp = nd->nd_dpos;
1977 	len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - cp;
1978 	rem = NFSM_RNDUP(siz) - siz;
1979 	while (siz > 0) {
1980 		if (len > siz)
1981 			xfer = siz;
1982 		else
1983 			xfer = len;
1984 		NFSBCOPY(cp, str, xfer);
1985 		str += xfer;
1986 		siz -= xfer;
1987 		if (siz > 0) {
1988 			mp = mbuf_next(mp);
1989 			if (mp == NULL) {
1990 				error = EBADRPC;
1991 				goto out;
1992 			}
1993 			cp = NFSMTOD(mp, caddr_t);
1994 			len = mbuf_len(mp);
1995 		} else {
1996 			cp += xfer;
1997 			len -= xfer;
1998 		}
1999 	}
2000 	*str = '\0';
2001 	nd->nd_dpos = cp;
2002 	nd->nd_md = mp;
2003 	if (rem > 0) {
2004 		if (len < rem)
2005 			error = nfsm_advance(nd, rem, len);
2006 		else
2007 			nd->nd_dpos += rem;
2008 	}
2009 
2010 out:
2011 	NFSEXITCODE2(error, nd);
2012 	return (error);
2013 }
2014 
2015 /*
2016  * Fill in the attributes as marked by the bitmap (V4).
2017  */
2018 APPLESTATIC int
2019 nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
2020     NFSACL_T *saclp, struct vattr *vap, fhandle_t *fhp, int rderror,
2021     nfsattrbit_t *attrbitp, struct ucred *cred, NFSPROC_T *p, int isdgram,
2022     int reterr, int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno)
2023 {
2024 	int bitpos, retnum = 0;
2025 	u_int32_t *tl;
2026 	int siz, prefixnum, error;
2027 	u_char *cp, namestr[NFSV4_SMALLSTR];
2028 	nfsattrbit_t attrbits, retbits;
2029 	nfsattrbit_t *retbitp = &retbits;
2030 	u_int32_t freenum, *retnump;
2031 	u_int64_t uquad;
2032 	struct statfs *fs;
2033 	struct nfsfsinfo fsinf;
2034 	struct timespec temptime;
2035 	NFSACL_T *aclp, *naclp = NULL;
2036 #ifdef QUOTA
2037 	struct dqblk dqb;
2038 	uid_t savuid;
2039 #endif
2040 
2041 	/*
2042 	 * First, set the bits that can be filled and get fsinfo.
2043 	 */
2044 	NFSSET_ATTRBIT(retbitp, attrbitp);
2045 	/*
2046 	 * If both p and cred are NULL, it is a client side setattr call.
2047 	 * If both p and cred are not NULL, it is a server side reply call.
2048 	 * If p is not NULL and cred is NULL, it is a client side callback
2049 	 * reply call.
2050 	 */
2051 	if (p == NULL && cred == NULL) {
2052 		NFSCLRNOTSETABLE_ATTRBIT(retbitp);
2053 		aclp = saclp;
2054 	} else {
2055 		NFSCLRNOTFILLABLE_ATTRBIT(retbitp);
2056 		naclp = acl_alloc(M_WAITOK);
2057 		aclp = naclp;
2058 	}
2059 	nfsvno_getfs(&fsinf, isdgram);
2060 #ifndef APPLE
2061 	/*
2062 	 * Get the VFS_STATFS(), since some attributes need them.
2063 	 */
2064 	fs = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
2065 	if (NFSISSETSTATFS_ATTRBIT(retbitp)) {
2066 		error = VFS_STATFS(mp, fs);
2067 		if (error != 0) {
2068 			if (reterr) {
2069 				nd->nd_repstat = NFSERR_ACCES;
2070 				free(fs, M_STATFS);
2071 				return (0);
2072 			}
2073 			NFSCLRSTATFS_ATTRBIT(retbitp);
2074 		}
2075 	}
2076 #endif
2077 
2078 	/*
2079 	 * And the NFSv4 ACL...
2080 	 */
2081 	if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT) &&
2082 	    (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
2083 		supports_nfsv4acls == 0))) {
2084 		NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT);
2085 	}
2086 	if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACL)) {
2087 		if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
2088 		    supports_nfsv4acls == 0)) {
2089 			NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
2090 		} else if (naclp != NULL) {
2091 			if (NFSVOPLOCK(vp, LK_SHARED) == 0) {
2092 				error = VOP_ACCESSX(vp, VREAD_ACL, cred, p);
2093 				if (error == 0)
2094 					error = VOP_GETACL(vp, ACL_TYPE_NFS4,
2095 					    naclp, cred, p);
2096 				NFSVOPUNLOCK(vp, 0);
2097 			} else
2098 				error = NFSERR_PERM;
2099 			if (error != 0) {
2100 				if (reterr) {
2101 					nd->nd_repstat = NFSERR_ACCES;
2102 					free(fs, M_STATFS);
2103 					return (0);
2104 				}
2105 				NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
2106 			}
2107 		}
2108 	}
2109 
2110 	/*
2111 	 * Put out the attribute bitmap for the ones being filled in
2112 	 * and get the field for the number of attributes returned.
2113 	 */
2114 	prefixnum = nfsrv_putattrbit(nd, retbitp);
2115 	NFSM_BUILD(retnump, u_int32_t *, NFSX_UNSIGNED);
2116 	prefixnum += NFSX_UNSIGNED;
2117 
2118 	/*
2119 	 * Now, loop around filling in the attributes for each bit set.
2120 	 */
2121 	for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
2122 	    if (NFSISSET_ATTRBIT(retbitp, bitpos)) {
2123 		switch (bitpos) {
2124 		case NFSATTRBIT_SUPPORTEDATTRS:
2125 			NFSSETSUPP_ATTRBIT(&attrbits);
2126 			if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL)
2127 			    && supports_nfsv4acls == 0)) {
2128 			    NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACLSUPPORT);
2129 			    NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACL);
2130 			}
2131 			retnum += nfsrv_putattrbit(nd, &attrbits);
2132 			break;
2133 		case NFSATTRBIT_TYPE:
2134 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2135 			*tl = vtonfsv34_type(vap->va_type);
2136 			retnum += NFSX_UNSIGNED;
2137 			break;
2138 		case NFSATTRBIT_FHEXPIRETYPE:
2139 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2140 			*tl = txdr_unsigned(NFSV4FHTYPE_PERSISTENT);
2141 			retnum += NFSX_UNSIGNED;
2142 			break;
2143 		case NFSATTRBIT_CHANGE:
2144 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2145 			txdr_hyper(vap->va_filerev, tl);
2146 			retnum += NFSX_HYPER;
2147 			break;
2148 		case NFSATTRBIT_SIZE:
2149 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2150 			txdr_hyper(vap->va_size, tl);
2151 			retnum += NFSX_HYPER;
2152 			break;
2153 		case NFSATTRBIT_LINKSUPPORT:
2154 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2155 			if (fsinf.fs_properties & NFSV3FSINFO_LINK)
2156 				*tl = newnfs_true;
2157 			else
2158 				*tl = newnfs_false;
2159 			retnum += NFSX_UNSIGNED;
2160 			break;
2161 		case NFSATTRBIT_SYMLINKSUPPORT:
2162 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2163 			if (fsinf.fs_properties & NFSV3FSINFO_SYMLINK)
2164 				*tl = newnfs_true;
2165 			else
2166 				*tl = newnfs_false;
2167 			retnum += NFSX_UNSIGNED;
2168 			break;
2169 		case NFSATTRBIT_NAMEDATTR:
2170 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2171 			*tl = newnfs_false;
2172 			retnum += NFSX_UNSIGNED;
2173 			break;
2174 		case NFSATTRBIT_FSID:
2175 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4FSID);
2176 			*tl++ = 0;
2177 			*tl++ = txdr_unsigned(mp->mnt_stat.f_fsid.val[0]);
2178 			*tl++ = 0;
2179 			*tl = txdr_unsigned(mp->mnt_stat.f_fsid.val[1]);
2180 			retnum += NFSX_V4FSID;
2181 			break;
2182 		case NFSATTRBIT_UNIQUEHANDLES:
2183 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2184 			*tl = newnfs_true;
2185 			retnum += NFSX_UNSIGNED;
2186 			break;
2187 		case NFSATTRBIT_LEASETIME:
2188 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2189 			*tl = txdr_unsigned(nfsrv_lease);
2190 			retnum += NFSX_UNSIGNED;
2191 			break;
2192 		case NFSATTRBIT_RDATTRERROR:
2193 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2194 			*tl = txdr_unsigned(rderror);
2195 			retnum += NFSX_UNSIGNED;
2196 			break;
2197 		/*
2198 		 * Recommended Attributes. (Only the supported ones.)
2199 		 */
2200 		case NFSATTRBIT_ACL:
2201 			retnum += nfsrv_buildacl(nd, aclp, vnode_vtype(vp), p);
2202 			break;
2203 		case NFSATTRBIT_ACLSUPPORT:
2204 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2205 			*tl = txdr_unsigned(NFSV4ACE_SUPTYPES);
2206 			retnum += NFSX_UNSIGNED;
2207 			break;
2208 		case NFSATTRBIT_CANSETTIME:
2209 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2210 			if (fsinf.fs_properties & NFSV3FSINFO_CANSETTIME)
2211 				*tl = newnfs_true;
2212 			else
2213 				*tl = newnfs_false;
2214 			retnum += NFSX_UNSIGNED;
2215 			break;
2216 		case NFSATTRBIT_CASEINSENSITIVE:
2217 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2218 			*tl = newnfs_false;
2219 			retnum += NFSX_UNSIGNED;
2220 			break;
2221 		case NFSATTRBIT_CASEPRESERVING:
2222 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2223 			*tl = newnfs_true;
2224 			retnum += NFSX_UNSIGNED;
2225 			break;
2226 		case NFSATTRBIT_CHOWNRESTRICTED:
2227 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2228 			*tl = newnfs_true;
2229 			retnum += NFSX_UNSIGNED;
2230 			break;
2231 		case NFSATTRBIT_FILEHANDLE:
2232 			retnum += nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
2233 			break;
2234 		case NFSATTRBIT_FILEID:
2235 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2236 			uquad = vap->va_fileid;
2237 			txdr_hyper(uquad, tl);
2238 			retnum += NFSX_HYPER;
2239 			break;
2240 		case NFSATTRBIT_FILESAVAIL:
2241 			/*
2242 			 * Check quota and use min(quota, f_ffree).
2243 			 */
2244 			freenum = fs->f_ffree;
2245 #ifdef QUOTA
2246 			/*
2247 			 * ufs_quotactl() insists that the uid argument
2248 			 * equal p_ruid for non-root quota access, so
2249 			 * we'll just make sure that's the case.
2250 			 */
2251 			savuid = p->p_cred->p_ruid;
2252 			p->p_cred->p_ruid = cred->cr_uid;
2253 			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2254 			    cred->cr_uid, (caddr_t)&dqb))
2255 			    freenum = min(dqb.dqb_isoftlimit-dqb.dqb_curinodes,
2256 				freenum);
2257 			p->p_cred->p_ruid = savuid;
2258 #endif	/* QUOTA */
2259 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2260 			*tl++ = 0;
2261 			*tl = txdr_unsigned(freenum);
2262 			retnum += NFSX_HYPER;
2263 			break;
2264 		case NFSATTRBIT_FILESFREE:
2265 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2266 			*tl++ = 0;
2267 			*tl = txdr_unsigned(fs->f_ffree);
2268 			retnum += NFSX_HYPER;
2269 			break;
2270 		case NFSATTRBIT_FILESTOTAL:
2271 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2272 			*tl++ = 0;
2273 			*tl = txdr_unsigned(fs->f_files);
2274 			retnum += NFSX_HYPER;
2275 			break;
2276 		case NFSATTRBIT_FSLOCATIONS:
2277 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2278 			*tl++ = 0;
2279 			*tl = 0;
2280 			retnum += 2 * NFSX_UNSIGNED;
2281 			break;
2282 		case NFSATTRBIT_HOMOGENEOUS:
2283 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2284 			if (fsinf.fs_properties & NFSV3FSINFO_HOMOGENEOUS)
2285 				*tl = newnfs_true;
2286 			else
2287 				*tl = newnfs_false;
2288 			retnum += NFSX_UNSIGNED;
2289 			break;
2290 		case NFSATTRBIT_MAXFILESIZE:
2291 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2292 			uquad = NFSRV_MAXFILESIZE;
2293 			txdr_hyper(uquad, tl);
2294 			retnum += NFSX_HYPER;
2295 			break;
2296 		case NFSATTRBIT_MAXLINK:
2297 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2298 			*tl = txdr_unsigned(LINK_MAX);
2299 			retnum += NFSX_UNSIGNED;
2300 			break;
2301 		case NFSATTRBIT_MAXNAME:
2302 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2303 			*tl = txdr_unsigned(NFS_MAXNAMLEN);
2304 			retnum += NFSX_UNSIGNED;
2305 			break;
2306 		case NFSATTRBIT_MAXREAD:
2307 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2308 			*tl++ = 0;
2309 			*tl = txdr_unsigned(fsinf.fs_rtmax);
2310 			retnum += NFSX_HYPER;
2311 			break;
2312 		case NFSATTRBIT_MAXWRITE:
2313 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2314 			*tl++ = 0;
2315 			*tl = txdr_unsigned(fsinf.fs_wtmax);
2316 			retnum += NFSX_HYPER;
2317 			break;
2318 		case NFSATTRBIT_MODE:
2319 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2320 			*tl = vtonfsv34_mode(vap->va_mode);
2321 			retnum += NFSX_UNSIGNED;
2322 			break;
2323 		case NFSATTRBIT_NOTRUNC:
2324 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2325 			*tl = newnfs_true;
2326 			retnum += NFSX_UNSIGNED;
2327 			break;
2328 		case NFSATTRBIT_NUMLINKS:
2329 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2330 			*tl = txdr_unsigned(vap->va_nlink);
2331 			retnum += NFSX_UNSIGNED;
2332 			break;
2333 		case NFSATTRBIT_OWNER:
2334 			cp = namestr;
2335 			nfsv4_uidtostr(vap->va_uid, &cp, &siz, p);
2336 			retnum += nfsm_strtom(nd, cp, siz);
2337 			if (cp != namestr)
2338 				free(cp, M_NFSSTRING);
2339 			break;
2340 		case NFSATTRBIT_OWNERGROUP:
2341 			cp = namestr;
2342 			nfsv4_gidtostr(vap->va_gid, &cp, &siz, p);
2343 			retnum += nfsm_strtom(nd, cp, siz);
2344 			if (cp != namestr)
2345 				free(cp, M_NFSSTRING);
2346 			break;
2347 		case NFSATTRBIT_QUOTAHARD:
2348 			if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
2349 				freenum = fs->f_bfree;
2350 			else
2351 				freenum = fs->f_bavail;
2352 #ifdef QUOTA
2353 			/*
2354 			 * ufs_quotactl() insists that the uid argument
2355 			 * equal p_ruid for non-root quota access, so
2356 			 * we'll just make sure that's the case.
2357 			 */
2358 			savuid = p->p_cred->p_ruid;
2359 			p->p_cred->p_ruid = cred->cr_uid;
2360 			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2361 			    cred->cr_uid, (caddr_t)&dqb))
2362 			    freenum = min(dqb.dqb_bhardlimit, freenum);
2363 			p->p_cred->p_ruid = savuid;
2364 #endif	/* QUOTA */
2365 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2366 			uquad = (u_int64_t)freenum;
2367 			NFSQUOTABLKTOBYTE(uquad, fs->f_bsize);
2368 			txdr_hyper(uquad, tl);
2369 			retnum += NFSX_HYPER;
2370 			break;
2371 		case NFSATTRBIT_QUOTASOFT:
2372 			if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
2373 				freenum = fs->f_bfree;
2374 			else
2375 				freenum = fs->f_bavail;
2376 #ifdef QUOTA
2377 			/*
2378 			 * ufs_quotactl() insists that the uid argument
2379 			 * equal p_ruid for non-root quota access, so
2380 			 * we'll just make sure that's the case.
2381 			 */
2382 			savuid = p->p_cred->p_ruid;
2383 			p->p_cred->p_ruid = cred->cr_uid;
2384 			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2385 			    cred->cr_uid, (caddr_t)&dqb))
2386 			    freenum = min(dqb.dqb_bsoftlimit, freenum);
2387 			p->p_cred->p_ruid = savuid;
2388 #endif	/* QUOTA */
2389 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2390 			uquad = (u_int64_t)freenum;
2391 			NFSQUOTABLKTOBYTE(uquad, fs->f_bsize);
2392 			txdr_hyper(uquad, tl);
2393 			retnum += NFSX_HYPER;
2394 			break;
2395 		case NFSATTRBIT_QUOTAUSED:
2396 			freenum = 0;
2397 #ifdef QUOTA
2398 			/*
2399 			 * ufs_quotactl() insists that the uid argument
2400 			 * equal p_ruid for non-root quota access, so
2401 			 * we'll just make sure that's the case.
2402 			 */
2403 			savuid = p->p_cred->p_ruid;
2404 			p->p_cred->p_ruid = cred->cr_uid;
2405 			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2406 			    cred->cr_uid, (caddr_t)&dqb))
2407 			    freenum = dqb.dqb_curblocks;
2408 			p->p_cred->p_ruid = savuid;
2409 #endif	/* QUOTA */
2410 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2411 			uquad = (u_int64_t)freenum;
2412 			NFSQUOTABLKTOBYTE(uquad, fs->f_bsize);
2413 			txdr_hyper(uquad, tl);
2414 			retnum += NFSX_HYPER;
2415 			break;
2416 		case NFSATTRBIT_RAWDEV:
2417 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4SPECDATA);
2418 			*tl++ = txdr_unsigned(NFSMAJOR(vap->va_rdev));
2419 			*tl = txdr_unsigned(NFSMINOR(vap->va_rdev));
2420 			retnum += NFSX_V4SPECDATA;
2421 			break;
2422 		case NFSATTRBIT_SPACEAVAIL:
2423 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2424 			if (priv_check_cred(cred, PRIV_VFS_BLOCKRESERVE, 0))
2425 				uquad = (u_int64_t)fs->f_bfree;
2426 			else
2427 				uquad = (u_int64_t)fs->f_bavail;
2428 			uquad *= fs->f_bsize;
2429 			txdr_hyper(uquad, tl);
2430 			retnum += NFSX_HYPER;
2431 			break;
2432 		case NFSATTRBIT_SPACEFREE:
2433 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2434 			uquad = (u_int64_t)fs->f_bfree;
2435 			uquad *= fs->f_bsize;
2436 			txdr_hyper(uquad, tl);
2437 			retnum += NFSX_HYPER;
2438 			break;
2439 		case NFSATTRBIT_SPACETOTAL:
2440 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2441 			uquad = (u_int64_t)fs->f_blocks;
2442 			uquad *= fs->f_bsize;
2443 			txdr_hyper(uquad, tl);
2444 			retnum += NFSX_HYPER;
2445 			break;
2446 		case NFSATTRBIT_SPACEUSED:
2447 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2448 			txdr_hyper(vap->va_bytes, tl);
2449 			retnum += NFSX_HYPER;
2450 			break;
2451 		case NFSATTRBIT_TIMEACCESS:
2452 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2453 			txdr_nfsv4time(&vap->va_atime, tl);
2454 			retnum += NFSX_V4TIME;
2455 			break;
2456 		case NFSATTRBIT_TIMEACCESSSET:
2457 			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
2458 				NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
2459 				*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
2460 				txdr_nfsv4time(&vap->va_atime, tl);
2461 				retnum += NFSX_V4SETTIME;
2462 			} else {
2463 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2464 				*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
2465 				retnum += NFSX_UNSIGNED;
2466 			}
2467 			break;
2468 		case NFSATTRBIT_TIMEDELTA:
2469 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2470 			temptime.tv_sec = 0;
2471 			temptime.tv_nsec = 1000000000 / hz;
2472 			txdr_nfsv4time(&temptime, tl);
2473 			retnum += NFSX_V4TIME;
2474 			break;
2475 		case NFSATTRBIT_TIMEMETADATA:
2476 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2477 			txdr_nfsv4time(&vap->va_ctime, tl);
2478 			retnum += NFSX_V4TIME;
2479 			break;
2480 		case NFSATTRBIT_TIMEMODIFY:
2481 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2482 			txdr_nfsv4time(&vap->va_mtime, tl);
2483 			retnum += NFSX_V4TIME;
2484 			break;
2485 		case NFSATTRBIT_TIMEMODIFYSET:
2486 			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
2487 				NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
2488 				*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
2489 				txdr_nfsv4time(&vap->va_mtime, tl);
2490 				retnum += NFSX_V4SETTIME;
2491 			} else {
2492 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2493 				*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
2494 				retnum += NFSX_UNSIGNED;
2495 			}
2496 			break;
2497 		case NFSATTRBIT_MOUNTEDONFILEID:
2498 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2499 			if (at_root != 0)
2500 				uquad = mounted_on_fileno;
2501 			else
2502 				uquad = vap->va_fileid;
2503 			txdr_hyper(uquad, tl);
2504 			retnum += NFSX_HYPER;
2505 			break;
2506 		case NFSATTRBIT_SUPPATTREXCLCREAT:
2507 			NFSSETSUPP_ATTRBIT(&attrbits);
2508 			NFSCLRNOTSETABLE_ATTRBIT(&attrbits);
2509 			NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET);
2510 			retnum += nfsrv_putattrbit(nd, &attrbits);
2511 			break;
2512 		default:
2513 			printf("EEK! Bad V4 attribute bitpos=%d\n", bitpos);
2514 		}
2515 	    }
2516 	}
2517 	if (naclp != NULL)
2518 		acl_free(naclp);
2519 	free(fs, M_STATFS);
2520 	*retnump = txdr_unsigned(retnum);
2521 	return (retnum + prefixnum);
2522 }
2523 
2524 /*
2525  * Put the attribute bits onto an mbuf list.
2526  * Return the number of bytes of output generated.
2527  */
2528 APPLESTATIC int
2529 nfsrv_putattrbit(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp)
2530 {
2531 	u_int32_t *tl;
2532 	int cnt, i, bytesize;
2533 
2534 	for (cnt = NFSATTRBIT_MAXWORDS; cnt > 0; cnt--)
2535 		if (attrbitp->bits[cnt - 1])
2536 			break;
2537 	bytesize = (cnt + 1) * NFSX_UNSIGNED;
2538 	NFSM_BUILD(tl, u_int32_t *, bytesize);
2539 	*tl++ = txdr_unsigned(cnt);
2540 	for (i = 0; i < cnt; i++)
2541 		*tl++ = txdr_unsigned(attrbitp->bits[i]);
2542 	return (bytesize);
2543 }
2544 
2545 /*
2546  * Convert a uid to a string.
2547  * If the lookup fails, just output the digits.
2548  * uid - the user id
2549  * cpp - points to a buffer of size NFSV4_SMALLSTR
2550  *       (malloc a larger one, as required)
2551  * retlenp - pointer to length to be returned
2552  */
2553 APPLESTATIC void
2554 nfsv4_uidtostr(uid_t uid, u_char **cpp, int *retlenp, NFSPROC_T *p)
2555 {
2556 	int i;
2557 	struct nfsusrgrp *usrp;
2558 	u_char *cp = *cpp;
2559 	uid_t tmp;
2560 	int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
2561 	struct nfsrv_lughash *hp;
2562 
2563 	cnt = 0;
2564 tryagain:
2565 	if (nfsrv_dnsnamelen > 0 && !nfsd_enable_uidtostring) {
2566 		/*
2567 		 * Always map nfsrv_defaultuid to "nobody".
2568 		 */
2569 		if (uid == nfsrv_defaultuid) {
2570 			i = nfsrv_dnsnamelen + 7;
2571 			if (i > len) {
2572 				if (len > NFSV4_SMALLSTR)
2573 					free(cp, M_NFSSTRING);
2574 				cp = malloc(i, M_NFSSTRING, M_WAITOK);
2575 				*cpp = cp;
2576 				len = i;
2577 				goto tryagain;
2578 			}
2579 			*retlenp = i;
2580 			NFSBCOPY("nobody@", cp, 7);
2581 			cp += 7;
2582 			NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2583 			return;
2584 		}
2585 		hasampersand = 0;
2586 		hp = NFSUSERHASH(uid);
2587 		mtx_lock(&hp->mtx);
2588 		TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
2589 			if (usrp->lug_uid == uid) {
2590 				if (usrp->lug_expiry < NFSD_MONOSEC)
2591 					break;
2592 				/*
2593 				 * If the name doesn't already have an '@'
2594 				 * in it, append @domainname to it.
2595 				 */
2596 				for (i = 0; i < usrp->lug_namelen; i++) {
2597 					if (usrp->lug_name[i] == '@') {
2598 						hasampersand = 1;
2599 						break;
2600 					}
2601 				}
2602 				if (hasampersand)
2603 					i = usrp->lug_namelen;
2604 				else
2605 					i = usrp->lug_namelen +
2606 					    nfsrv_dnsnamelen + 1;
2607 				if (i > len) {
2608 					mtx_unlock(&hp->mtx);
2609 					if (len > NFSV4_SMALLSTR)
2610 						free(cp, M_NFSSTRING);
2611 					cp = malloc(i, M_NFSSTRING, M_WAITOK);
2612 					*cpp = cp;
2613 					len = i;
2614 					goto tryagain;
2615 				}
2616 				*retlenp = i;
2617 				NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
2618 				if (!hasampersand) {
2619 					cp += usrp->lug_namelen;
2620 					*cp++ = '@';
2621 					NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2622 				}
2623 				TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
2624 				TAILQ_INSERT_TAIL(&hp->lughead, usrp,
2625 				    lug_numhash);
2626 				mtx_unlock(&hp->mtx);
2627 				return;
2628 			}
2629 		}
2630 		mtx_unlock(&hp->mtx);
2631 		cnt++;
2632 		ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0,
2633 		    NULL, p);
2634 		if (ret == 0 && cnt < 2)
2635 			goto tryagain;
2636 	}
2637 
2638 	/*
2639 	 * No match, just return a string of digits.
2640 	 */
2641 	tmp = uid;
2642 	i = 0;
2643 	while (tmp || i == 0) {
2644 		tmp /= 10;
2645 		i++;
2646 	}
2647 	len = (i > len) ? len : i;
2648 	*retlenp = len;
2649 	cp += (len - 1);
2650 	tmp = uid;
2651 	for (i = 0; i < len; i++) {
2652 		*cp-- = '0' + (tmp % 10);
2653 		tmp /= 10;
2654 	}
2655 	return;
2656 }
2657 
2658 /*
2659  * Get a credential for the uid with the server's group list.
2660  * If none is found, just return the credential passed in after
2661  * logging a warning message.
2662  */
2663 struct ucred *
2664 nfsrv_getgrpscred(struct ucred *oldcred)
2665 {
2666 	struct nfsusrgrp *usrp;
2667 	struct ucred *newcred;
2668 	int cnt, ret;
2669 	uid_t uid;
2670 	struct nfsrv_lughash *hp;
2671 
2672 	cnt = 0;
2673 	uid = oldcred->cr_uid;
2674 tryagain:
2675 	if (nfsrv_dnsnamelen > 0) {
2676 		hp = NFSUSERHASH(uid);
2677 		mtx_lock(&hp->mtx);
2678 		TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
2679 			if (usrp->lug_uid == uid) {
2680 				if (usrp->lug_expiry < NFSD_MONOSEC)
2681 					break;
2682 				if (usrp->lug_cred != NULL) {
2683 					newcred = crhold(usrp->lug_cred);
2684 					crfree(oldcred);
2685 				} else
2686 					newcred = oldcred;
2687 				TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
2688 				TAILQ_INSERT_TAIL(&hp->lughead, usrp,
2689 				    lug_numhash);
2690 				mtx_unlock(&hp->mtx);
2691 				return (newcred);
2692 			}
2693 		}
2694 		mtx_unlock(&hp->mtx);
2695 		cnt++;
2696 		ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0,
2697 		    NULL, curthread);
2698 		if (ret == 0 && cnt < 2)
2699 			goto tryagain;
2700 	}
2701 	return (oldcred);
2702 }
2703 
2704 /*
2705  * Convert a string to a uid.
2706  * If no conversion is possible return NFSERR_BADOWNER, otherwise
2707  * return 0.
2708  * If this is called from a client side mount using AUTH_SYS and the
2709  * string is made up entirely of digits, just convert the string to
2710  * a number.
2711  */
2712 APPLESTATIC int
2713 nfsv4_strtouid(struct nfsrv_descript *nd, u_char *str, int len, uid_t *uidp,
2714     NFSPROC_T *p)
2715 {
2716 	int i;
2717 	char *cp, *endstr, *str0;
2718 	struct nfsusrgrp *usrp;
2719 	int cnt, ret;
2720 	int error = 0;
2721 	uid_t tuid;
2722 	struct nfsrv_lughash *hp, *hp2;
2723 
2724 	if (len == 0) {
2725 		error = NFSERR_BADOWNER;
2726 		goto out;
2727 	}
2728 	/* If a string of digits and an AUTH_SYS mount, just convert it. */
2729 	str0 = str;
2730 	tuid = (uid_t)strtoul(str0, &endstr, 10);
2731 	if ((endstr - str0) == len) {
2732 		/* A numeric string. */
2733 		if ((nd->nd_flag & ND_KERBV) == 0 &&
2734 		    ((nd->nd_flag & ND_NFSCL) != 0 ||
2735 		      nfsd_enable_stringtouid != 0))
2736 			*uidp = tuid;
2737 		else
2738 			error = NFSERR_BADOWNER;
2739 		goto out;
2740 	}
2741 	/*
2742 	 * Look for an '@'.
2743 	 */
2744 	cp = strchr(str0, '@');
2745 	if (cp != NULL)
2746 		i = (int)(cp++ - str0);
2747 	else
2748 		i = len;
2749 
2750 	cnt = 0;
2751 tryagain:
2752 	if (nfsrv_dnsnamelen > 0) {
2753 		/*
2754 		 * If an '@' is found and the domain name matches, search for
2755 		 * the name with dns stripped off.
2756 		 * Mixed case alpahbetics will match for the domain name, but
2757 		 * all upper case will not.
2758 		 */
2759 		if (cnt == 0 && i < len && i > 0 &&
2760 		    (len - 1 - i) == nfsrv_dnsnamelen &&
2761 		    !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
2762 			len -= (nfsrv_dnsnamelen + 1);
2763 			*(cp - 1) = '\0';
2764 		}
2765 
2766 		/*
2767 		 * Check for the special case of "nobody".
2768 		 */
2769 		if (len == 6 && !NFSBCMP(str, "nobody", 6)) {
2770 			*uidp = nfsrv_defaultuid;
2771 			error = 0;
2772 			goto out;
2773 		}
2774 
2775 		hp = NFSUSERNAMEHASH(str, len);
2776 		mtx_lock(&hp->mtx);
2777 		TAILQ_FOREACH(usrp, &hp->lughead, lug_namehash) {
2778 			if (usrp->lug_namelen == len &&
2779 			    !NFSBCMP(usrp->lug_name, str, len)) {
2780 				if (usrp->lug_expiry < NFSD_MONOSEC)
2781 					break;
2782 				hp2 = NFSUSERHASH(usrp->lug_uid);
2783 				mtx_lock(&hp2->mtx);
2784 				TAILQ_REMOVE(&hp2->lughead, usrp, lug_numhash);
2785 				TAILQ_INSERT_TAIL(&hp2->lughead, usrp,
2786 				    lug_numhash);
2787 				*uidp = usrp->lug_uid;
2788 				mtx_unlock(&hp2->mtx);
2789 				mtx_unlock(&hp->mtx);
2790 				error = 0;
2791 				goto out;
2792 			}
2793 		}
2794 		mtx_unlock(&hp->mtx);
2795 		cnt++;
2796 		ret = nfsrv_getuser(RPCNFSUSERD_GETUSER, (uid_t)0, (gid_t)0,
2797 		    str, p);
2798 		if (ret == 0 && cnt < 2)
2799 			goto tryagain;
2800 	}
2801 	error = NFSERR_BADOWNER;
2802 
2803 out:
2804 	NFSEXITCODE(error);
2805 	return (error);
2806 }
2807 
2808 /*
2809  * Convert a gid to a string.
2810  * gid - the group id
2811  * cpp - points to a buffer of size NFSV4_SMALLSTR
2812  *       (malloc a larger one, as required)
2813  * retlenp - pointer to length to be returned
2814  */
2815 APPLESTATIC void
2816 nfsv4_gidtostr(gid_t gid, u_char **cpp, int *retlenp, NFSPROC_T *p)
2817 {
2818 	int i;
2819 	struct nfsusrgrp *usrp;
2820 	u_char *cp = *cpp;
2821 	gid_t tmp;
2822 	int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
2823 	struct nfsrv_lughash *hp;
2824 
2825 	cnt = 0;
2826 tryagain:
2827 	if (nfsrv_dnsnamelen > 0 && !nfsd_enable_uidtostring) {
2828 		/*
2829 		 * Always map nfsrv_defaultgid to "nogroup".
2830 		 */
2831 		if (gid == nfsrv_defaultgid) {
2832 			i = nfsrv_dnsnamelen + 8;
2833 			if (i > len) {
2834 				if (len > NFSV4_SMALLSTR)
2835 					free(cp, M_NFSSTRING);
2836 				cp = malloc(i, M_NFSSTRING, M_WAITOK);
2837 				*cpp = cp;
2838 				len = i;
2839 				goto tryagain;
2840 			}
2841 			*retlenp = i;
2842 			NFSBCOPY("nogroup@", cp, 8);
2843 			cp += 8;
2844 			NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2845 			return;
2846 		}
2847 		hasampersand = 0;
2848 		hp = NFSGROUPHASH(gid);
2849 		mtx_lock(&hp->mtx);
2850 		TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
2851 			if (usrp->lug_gid == gid) {
2852 				if (usrp->lug_expiry < NFSD_MONOSEC)
2853 					break;
2854 				/*
2855 				 * If the name doesn't already have an '@'
2856 				 * in it, append @domainname to it.
2857 				 */
2858 				for (i = 0; i < usrp->lug_namelen; i++) {
2859 					if (usrp->lug_name[i] == '@') {
2860 						hasampersand = 1;
2861 						break;
2862 					}
2863 				}
2864 				if (hasampersand)
2865 					i = usrp->lug_namelen;
2866 				else
2867 					i = usrp->lug_namelen +
2868 					    nfsrv_dnsnamelen + 1;
2869 				if (i > len) {
2870 					mtx_unlock(&hp->mtx);
2871 					if (len > NFSV4_SMALLSTR)
2872 						free(cp, M_NFSSTRING);
2873 					cp = malloc(i, M_NFSSTRING, M_WAITOK);
2874 					*cpp = cp;
2875 					len = i;
2876 					goto tryagain;
2877 				}
2878 				*retlenp = i;
2879 				NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
2880 				if (!hasampersand) {
2881 					cp += usrp->lug_namelen;
2882 					*cp++ = '@';
2883 					NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2884 				}
2885 				TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
2886 				TAILQ_INSERT_TAIL(&hp->lughead, usrp,
2887 				    lug_numhash);
2888 				mtx_unlock(&hp->mtx);
2889 				return;
2890 			}
2891 		}
2892 		mtx_unlock(&hp->mtx);
2893 		cnt++;
2894 		ret = nfsrv_getuser(RPCNFSUSERD_GETGID, (uid_t)0, gid,
2895 		    NULL, p);
2896 		if (ret == 0 && cnt < 2)
2897 			goto tryagain;
2898 	}
2899 
2900 	/*
2901 	 * No match, just return a string of digits.
2902 	 */
2903 	tmp = gid;
2904 	i = 0;
2905 	while (tmp || i == 0) {
2906 		tmp /= 10;
2907 		i++;
2908 	}
2909 	len = (i > len) ? len : i;
2910 	*retlenp = len;
2911 	cp += (len - 1);
2912 	tmp = gid;
2913 	for (i = 0; i < len; i++) {
2914 		*cp-- = '0' + (tmp % 10);
2915 		tmp /= 10;
2916 	}
2917 	return;
2918 }
2919 
2920 /*
2921  * Convert a string to a gid.
2922  * If no conversion is possible return NFSERR_BADOWNER, otherwise
2923  * return 0.
2924  * If this is called from a client side mount using AUTH_SYS and the
2925  * string is made up entirely of digits, just convert the string to
2926  * a number.
2927  */
2928 APPLESTATIC int
2929 nfsv4_strtogid(struct nfsrv_descript *nd, u_char *str, int len, gid_t *gidp,
2930     NFSPROC_T *p)
2931 {
2932 	int i;
2933 	char *cp, *endstr, *str0;
2934 	struct nfsusrgrp *usrp;
2935 	int cnt, ret;
2936 	int error = 0;
2937 	gid_t tgid;
2938 	struct nfsrv_lughash *hp, *hp2;
2939 
2940 	if (len == 0) {
2941 		error =  NFSERR_BADOWNER;
2942 		goto out;
2943 	}
2944 	/* If a string of digits and an AUTH_SYS mount, just convert it. */
2945 	str0 = str;
2946 	tgid = (gid_t)strtoul(str0, &endstr, 10);
2947 	if ((endstr - str0) == len) {
2948 		/* A numeric string. */
2949 		if ((nd->nd_flag & ND_KERBV) == 0 &&
2950 		    ((nd->nd_flag & ND_NFSCL) != 0 ||
2951 		      nfsd_enable_stringtouid != 0))
2952 			*gidp = tgid;
2953 		else
2954 			error = NFSERR_BADOWNER;
2955 		goto out;
2956 	}
2957 	/*
2958 	 * Look for an '@'.
2959 	 */
2960 	cp = strchr(str0, '@');
2961 	if (cp != NULL)
2962 		i = (int)(cp++ - str0);
2963 	else
2964 		i = len;
2965 
2966 	cnt = 0;
2967 tryagain:
2968 	if (nfsrv_dnsnamelen > 0) {
2969 		/*
2970 		 * If an '@' is found and the dns name matches, search for the
2971 		 * name with the dns stripped off.
2972 		 */
2973 		if (cnt == 0 && i < len && i > 0 &&
2974 		    (len - 1 - i) == nfsrv_dnsnamelen &&
2975 		    !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
2976 			len -= (nfsrv_dnsnamelen + 1);
2977 			*(cp - 1) = '\0';
2978 		}
2979 
2980 		/*
2981 		 * Check for the special case of "nogroup".
2982 		 */
2983 		if (len == 7 && !NFSBCMP(str, "nogroup", 7)) {
2984 			*gidp = nfsrv_defaultgid;
2985 			error = 0;
2986 			goto out;
2987 		}
2988 
2989 		hp = NFSGROUPNAMEHASH(str, len);
2990 		mtx_lock(&hp->mtx);
2991 		TAILQ_FOREACH(usrp, &hp->lughead, lug_namehash) {
2992 			if (usrp->lug_namelen == len &&
2993 			    !NFSBCMP(usrp->lug_name, str, len)) {
2994 				if (usrp->lug_expiry < NFSD_MONOSEC)
2995 					break;
2996 				hp2 = NFSGROUPHASH(usrp->lug_gid);
2997 				mtx_lock(&hp2->mtx);
2998 				TAILQ_REMOVE(&hp2->lughead, usrp, lug_numhash);
2999 				TAILQ_INSERT_TAIL(&hp2->lughead, usrp,
3000 				    lug_numhash);
3001 				*gidp = usrp->lug_gid;
3002 				mtx_unlock(&hp2->mtx);
3003 				mtx_unlock(&hp->mtx);
3004 				error = 0;
3005 				goto out;
3006 			}
3007 		}
3008 		mtx_unlock(&hp->mtx);
3009 		cnt++;
3010 		ret = nfsrv_getuser(RPCNFSUSERD_GETGROUP, (uid_t)0, (gid_t)0,
3011 		    str, p);
3012 		if (ret == 0 && cnt < 2)
3013 			goto tryagain;
3014 	}
3015 	error = NFSERR_BADOWNER;
3016 
3017 out:
3018 	NFSEXITCODE(error);
3019 	return (error);
3020 }
3021 
3022 /*
3023  * Cmp len chars, allowing mixed case in the first argument to match lower
3024  * case in the second, but not if the first argument is all upper case.
3025  * Return 0 for a match, 1 otherwise.
3026  */
3027 static int
3028 nfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len)
3029 {
3030 	int i;
3031 	u_char tmp;
3032 	int fndlower = 0;
3033 
3034 	for (i = 0; i < len; i++) {
3035 		if (*cp >= 'A' && *cp <= 'Z') {
3036 			tmp = *cp++ + ('a' - 'A');
3037 		} else {
3038 			tmp = *cp++;
3039 			if (tmp >= 'a' && tmp <= 'z')
3040 				fndlower = 1;
3041 		}
3042 		if (tmp != *cp2++)
3043 			return (1);
3044 	}
3045 	if (fndlower)
3046 		return (0);
3047 	else
3048 		return (1);
3049 }
3050 
3051 /*
3052  * Set the port for the nfsuserd.
3053  */
3054 APPLESTATIC int
3055 nfsrv_nfsuserdport(struct sockaddr *sad, u_short port, NFSPROC_T *p)
3056 {
3057 	struct nfssockreq *rp;
3058 	struct sockaddr_in *ad;
3059 	int error;
3060 
3061 	NFSLOCKNAMEID();
3062 	if (nfsrv_nfsuserd) {
3063 		NFSUNLOCKNAMEID();
3064 		error = EPERM;
3065 		NFSSOCKADDRFREE(sad);
3066 		goto out;
3067 	}
3068 	nfsrv_nfsuserd = 1;
3069 	NFSUNLOCKNAMEID();
3070 	/*
3071 	 * Set up the socket record and connect.
3072 	 */
3073 	rp = &nfsrv_nfsuserdsock;
3074 	rp->nr_client = NULL;
3075 	rp->nr_cred = NULL;
3076 	rp->nr_lock = (NFSR_RESERVEDPORT | NFSR_LOCALHOST);
3077 	if (sad != NULL) {
3078 		/* Use the AF_LOCAL socket address passed in. */
3079 		rp->nr_sotype = SOCK_STREAM;
3080 		rp->nr_soproto = 0;
3081 		rp->nr_nam = sad;
3082 	} else {
3083 		/* Use the port# for a UDP socket (old nfsuserd). */
3084 		rp->nr_sotype = SOCK_DGRAM;
3085 		rp->nr_soproto = IPPROTO_UDP;
3086 		NFSSOCKADDRALLOC(rp->nr_nam);
3087 		NFSSOCKADDRSIZE(rp->nr_nam, sizeof (struct sockaddr_in));
3088 		ad = NFSSOCKADDR(rp->nr_nam, struct sockaddr_in *);
3089 		ad->sin_family = AF_INET;
3090 		ad->sin_addr.s_addr = htonl((u_int32_t)0x7f000001);
3091 		ad->sin_port = port;
3092 	}
3093 	rp->nr_prog = RPCPROG_NFSUSERD;
3094 	rp->nr_vers = RPCNFSUSERD_VERS;
3095 	error = newnfs_connect(NULL, rp, NFSPROCCRED(p), p, 0);
3096 	if (error) {
3097 		NFSSOCKADDRFREE(rp->nr_nam);
3098 		nfsrv_nfsuserd = 0;
3099 	}
3100 out:
3101 	NFSEXITCODE(error);
3102 	return (error);
3103 }
3104 
3105 /*
3106  * Delete the nfsuserd port.
3107  */
3108 APPLESTATIC void
3109 nfsrv_nfsuserddelport(void)
3110 {
3111 
3112 	NFSLOCKNAMEID();
3113 	if (nfsrv_nfsuserd == 0) {
3114 		NFSUNLOCKNAMEID();
3115 		return;
3116 	}
3117 	nfsrv_nfsuserd = 0;
3118 	NFSUNLOCKNAMEID();
3119 	newnfs_disconnect(&nfsrv_nfsuserdsock);
3120 	NFSSOCKADDRFREE(nfsrv_nfsuserdsock.nr_nam);
3121 }
3122 
3123 /*
3124  * Do upcalls to the nfsuserd, for cache misses of the owner/ownergroup
3125  * name<-->id cache.
3126  * Returns 0 upon success, non-zero otherwise.
3127  */
3128 static int
3129 nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name, NFSPROC_T *p)
3130 {
3131 	u_int32_t *tl;
3132 	struct nfsrv_descript *nd;
3133 	int len;
3134 	struct nfsrv_descript nfsd;
3135 	struct ucred *cred;
3136 	int error;
3137 
3138 	NFSLOCKNAMEID();
3139 	if (nfsrv_nfsuserd == 0) {
3140 		NFSUNLOCKNAMEID();
3141 		error = EPERM;
3142 		goto out;
3143 	}
3144 	NFSUNLOCKNAMEID();
3145 	nd = &nfsd;
3146 	cred = newnfs_getcred();
3147 	nd->nd_flag = ND_GSSINITREPLY;
3148 	nfsrvd_rephead(nd);
3149 
3150 	nd->nd_procnum = procnum;
3151 	if (procnum == RPCNFSUSERD_GETUID || procnum == RPCNFSUSERD_GETGID) {
3152 		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3153 		if (procnum == RPCNFSUSERD_GETUID)
3154 			*tl = txdr_unsigned(uid);
3155 		else
3156 			*tl = txdr_unsigned(gid);
3157 	} else {
3158 		len = strlen(name);
3159 		(void) nfsm_strtom(nd, name, len);
3160 	}
3161 	error = newnfs_request(nd, NULL, NULL, &nfsrv_nfsuserdsock, NULL, NULL,
3162 		cred, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS, NULL, 0, NULL, NULL);
3163 	NFSFREECRED(cred);
3164 	if (!error) {
3165 		mbuf_freem(nd->nd_mrep);
3166 		error = nd->nd_repstat;
3167 	}
3168 out:
3169 	NFSEXITCODE(error);
3170 	return (error);
3171 }
3172 
3173 /*
3174  * This function is called from the nfssvc(2) system call, to update the
3175  * kernel user/group name list(s) for the V4 owner and ownergroup attributes.
3176  */
3177 APPLESTATIC int
3178 nfssvc_idname(struct nfsd_idargs *nidp)
3179 {
3180 	struct nfsusrgrp *nusrp, *usrp, *newusrp;
3181 	struct nfsrv_lughash *hp_name, *hp_idnum, *thp;
3182 	int i, group_locked, groupname_locked, user_locked, username_locked;
3183 	int error = 0;
3184 	u_char *cp;
3185 	gid_t *grps;
3186 	struct ucred *cr;
3187 	static int onethread = 0;
3188 	static time_t lasttime = 0;
3189 
3190 	if (nidp->nid_namelen <= 0 || nidp->nid_namelen > MAXHOSTNAMELEN) {
3191 		error = EINVAL;
3192 		goto out;
3193 	}
3194 	if (nidp->nid_flag & NFSID_INITIALIZE) {
3195 		cp = malloc(nidp->nid_namelen + 1, M_NFSSTRING, M_WAITOK);
3196 		error = copyin(CAST_USER_ADDR_T(nidp->nid_name), cp,
3197 		    nidp->nid_namelen);
3198 		if (error != 0) {
3199 			free(cp, M_NFSSTRING);
3200 			goto out;
3201 		}
3202 		if (atomic_cmpset_acq_int(&nfsrv_dnsnamelen, 0, 0) == 0) {
3203 			/*
3204 			 * Free up all the old stuff and reinitialize hash
3205 			 * lists.  All mutexes for both lists must be locked,
3206 			 * with the user/group name ones before the uid/gid
3207 			 * ones, to avoid a LOR.
3208 			 */
3209 			for (i = 0; i < nfsrv_lughashsize; i++)
3210 				mtx_lock(&nfsusernamehash[i].mtx);
3211 			for (i = 0; i < nfsrv_lughashsize; i++)
3212 				mtx_lock(&nfsuserhash[i].mtx);
3213 			for (i = 0; i < nfsrv_lughashsize; i++)
3214 				TAILQ_FOREACH_SAFE(usrp,
3215 				    &nfsuserhash[i].lughead, lug_numhash, nusrp)
3216 					nfsrv_removeuser(usrp, 1);
3217 			for (i = 0; i < nfsrv_lughashsize; i++)
3218 				mtx_unlock(&nfsuserhash[i].mtx);
3219 			for (i = 0; i < nfsrv_lughashsize; i++)
3220 				mtx_unlock(&nfsusernamehash[i].mtx);
3221 			for (i = 0; i < nfsrv_lughashsize; i++)
3222 				mtx_lock(&nfsgroupnamehash[i].mtx);
3223 			for (i = 0; i < nfsrv_lughashsize; i++)
3224 				mtx_lock(&nfsgrouphash[i].mtx);
3225 			for (i = 0; i < nfsrv_lughashsize; i++)
3226 				TAILQ_FOREACH_SAFE(usrp,
3227 				    &nfsgrouphash[i].lughead, lug_numhash,
3228 				    nusrp)
3229 					nfsrv_removeuser(usrp, 0);
3230 			for (i = 0; i < nfsrv_lughashsize; i++)
3231 				mtx_unlock(&nfsgrouphash[i].mtx);
3232 			for (i = 0; i < nfsrv_lughashsize; i++)
3233 				mtx_unlock(&nfsgroupnamehash[i].mtx);
3234 			free(nfsrv_dnsname, M_NFSSTRING);
3235 			nfsrv_dnsname = NULL;
3236 		}
3237 		if (nfsuserhash == NULL) {
3238 			/* Allocate the hash tables. */
3239 			nfsuserhash = malloc(sizeof(struct nfsrv_lughash) *
3240 			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3241 			    M_ZERO);
3242 			for (i = 0; i < nfsrv_lughashsize; i++)
3243 				mtx_init(&nfsuserhash[i].mtx, "nfsuidhash",
3244 				    NULL, MTX_DEF | MTX_DUPOK);
3245 			nfsusernamehash = malloc(sizeof(struct nfsrv_lughash) *
3246 			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3247 			    M_ZERO);
3248 			for (i = 0; i < nfsrv_lughashsize; i++)
3249 				mtx_init(&nfsusernamehash[i].mtx,
3250 				    "nfsusrhash", NULL, MTX_DEF |
3251 				    MTX_DUPOK);
3252 			nfsgrouphash = malloc(sizeof(struct nfsrv_lughash) *
3253 			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3254 			    M_ZERO);
3255 			for (i = 0; i < nfsrv_lughashsize; i++)
3256 				mtx_init(&nfsgrouphash[i].mtx, "nfsgidhash",
3257 				    NULL, MTX_DEF | MTX_DUPOK);
3258 			nfsgroupnamehash = malloc(sizeof(struct nfsrv_lughash) *
3259 			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3260 			    M_ZERO);
3261 			for (i = 0; i < nfsrv_lughashsize; i++)
3262 			    mtx_init(&nfsgroupnamehash[i].mtx,
3263 			    "nfsgrphash", NULL, MTX_DEF | MTX_DUPOK);
3264 		}
3265 		/* (Re)initialize the list heads. */
3266 		for (i = 0; i < nfsrv_lughashsize; i++)
3267 			TAILQ_INIT(&nfsuserhash[i].lughead);
3268 		for (i = 0; i < nfsrv_lughashsize; i++)
3269 			TAILQ_INIT(&nfsusernamehash[i].lughead);
3270 		for (i = 0; i < nfsrv_lughashsize; i++)
3271 			TAILQ_INIT(&nfsgrouphash[i].lughead);
3272 		for (i = 0; i < nfsrv_lughashsize; i++)
3273 			TAILQ_INIT(&nfsgroupnamehash[i].lughead);
3274 
3275 		/*
3276 		 * Put name in "DNS" string.
3277 		 */
3278 		nfsrv_dnsname = cp;
3279 		nfsrv_defaultuid = nidp->nid_uid;
3280 		nfsrv_defaultgid = nidp->nid_gid;
3281 		nfsrv_usercnt = 0;
3282 		nfsrv_usermax = nidp->nid_usermax;
3283 		atomic_store_rel_int(&nfsrv_dnsnamelen, nidp->nid_namelen);
3284 		goto out;
3285 	}
3286 
3287 	/*
3288 	 * malloc the new one now, so any potential sleep occurs before
3289 	 * manipulation of the lists.
3290 	 */
3291 	newusrp = malloc(sizeof(struct nfsusrgrp) + nidp->nid_namelen,
3292 	    M_NFSUSERGROUP, M_WAITOK | M_ZERO);
3293 	error = copyin(CAST_USER_ADDR_T(nidp->nid_name), newusrp->lug_name,
3294 	    nidp->nid_namelen);
3295 	if (error == 0 && nidp->nid_ngroup > 0 &&
3296 	    (nidp->nid_flag & NFSID_ADDUID) != 0) {
3297 		grps = malloc(sizeof(gid_t) * nidp->nid_ngroup, M_TEMP,
3298 		    M_WAITOK);
3299 		error = copyin(CAST_USER_ADDR_T(nidp->nid_grps), grps,
3300 		    sizeof(gid_t) * nidp->nid_ngroup);
3301 		if (error == 0) {
3302 			/*
3303 			 * Create a credential just like svc_getcred(),
3304 			 * but using the group list provided.
3305 			 */
3306 			cr = crget();
3307 			cr->cr_uid = cr->cr_ruid = cr->cr_svuid = nidp->nid_uid;
3308 			crsetgroups(cr, nidp->nid_ngroup, grps);
3309 			cr->cr_rgid = cr->cr_svgid = cr->cr_groups[0];
3310 			cr->cr_prison = &prison0;
3311 			prison_hold(cr->cr_prison);
3312 #ifdef MAC
3313 			mac_cred_associate_nfsd(cr);
3314 #endif
3315 			newusrp->lug_cred = cr;
3316 		}
3317 		free(grps, M_TEMP);
3318 	}
3319 	if (error) {
3320 		free(newusrp, M_NFSUSERGROUP);
3321 		goto out;
3322 	}
3323 	newusrp->lug_namelen = nidp->nid_namelen;
3324 
3325 	/*
3326 	 * The lock order is username[0]->[nfsrv_lughashsize - 1] followed
3327 	 * by uid[0]->[nfsrv_lughashsize - 1], with the same for group.
3328 	 * The flags user_locked, username_locked, group_locked and
3329 	 * groupname_locked are set to indicate all of those hash lists are
3330 	 * locked. hp_name != NULL  and hp_idnum != NULL indicates that
3331 	 * the respective one mutex is locked.
3332 	 */
3333 	user_locked = username_locked = group_locked = groupname_locked = 0;
3334 	hp_name = hp_idnum = NULL;
3335 
3336 	/*
3337 	 * Delete old entries, as required.
3338 	 */
3339 	if (nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID)) {
3340 		/* Must lock all username hash lists first, to avoid a LOR. */
3341 		for (i = 0; i < nfsrv_lughashsize; i++)
3342 			mtx_lock(&nfsusernamehash[i].mtx);
3343 		username_locked = 1;
3344 		hp_idnum = NFSUSERHASH(nidp->nid_uid);
3345 		mtx_lock(&hp_idnum->mtx);
3346 		TAILQ_FOREACH_SAFE(usrp, &hp_idnum->lughead, lug_numhash,
3347 		    nusrp) {
3348 			if (usrp->lug_uid == nidp->nid_uid)
3349 				nfsrv_removeuser(usrp, 1);
3350 		}
3351 	} else if (nidp->nid_flag & (NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) {
3352 		hp_name = NFSUSERNAMEHASH(newusrp->lug_name,
3353 		    newusrp->lug_namelen);
3354 		mtx_lock(&hp_name->mtx);
3355 		TAILQ_FOREACH_SAFE(usrp, &hp_name->lughead, lug_namehash,
3356 		    nusrp) {
3357 			if (usrp->lug_namelen == newusrp->lug_namelen &&
3358 			    !NFSBCMP(usrp->lug_name, newusrp->lug_name,
3359 			    usrp->lug_namelen)) {
3360 				thp = NFSUSERHASH(usrp->lug_uid);
3361 				mtx_lock(&thp->mtx);
3362 				nfsrv_removeuser(usrp, 1);
3363 				mtx_unlock(&thp->mtx);
3364 			}
3365 		}
3366 		hp_idnum = NFSUSERHASH(nidp->nid_uid);
3367 		mtx_lock(&hp_idnum->mtx);
3368 	} else if (nidp->nid_flag & (NFSID_DELGID | NFSID_ADDGID)) {
3369 		/* Must lock all groupname hash lists first, to avoid a LOR. */
3370 		for (i = 0; i < nfsrv_lughashsize; i++)
3371 			mtx_lock(&nfsgroupnamehash[i].mtx);
3372 		groupname_locked = 1;
3373 		hp_idnum = NFSGROUPHASH(nidp->nid_gid);
3374 		mtx_lock(&hp_idnum->mtx);
3375 		TAILQ_FOREACH_SAFE(usrp, &hp_idnum->lughead, lug_numhash,
3376 		    nusrp) {
3377 			if (usrp->lug_gid == nidp->nid_gid)
3378 				nfsrv_removeuser(usrp, 0);
3379 		}
3380 	} else if (nidp->nid_flag & (NFSID_DELGROUPNAME | NFSID_ADDGROUPNAME)) {
3381 		hp_name = NFSGROUPNAMEHASH(newusrp->lug_name,
3382 		    newusrp->lug_namelen);
3383 		mtx_lock(&hp_name->mtx);
3384 		TAILQ_FOREACH_SAFE(usrp, &hp_name->lughead, lug_namehash,
3385 		    nusrp) {
3386 			if (usrp->lug_namelen == newusrp->lug_namelen &&
3387 			    !NFSBCMP(usrp->lug_name, newusrp->lug_name,
3388 			    usrp->lug_namelen)) {
3389 				thp = NFSGROUPHASH(usrp->lug_gid);
3390 				mtx_lock(&thp->mtx);
3391 				nfsrv_removeuser(usrp, 0);
3392 				mtx_unlock(&thp->mtx);
3393 			}
3394 		}
3395 		hp_idnum = NFSGROUPHASH(nidp->nid_gid);
3396 		mtx_lock(&hp_idnum->mtx);
3397 	}
3398 
3399 	/*
3400 	 * Now, we can add the new one.
3401 	 */
3402 	if (nidp->nid_usertimeout)
3403 		newusrp->lug_expiry = NFSD_MONOSEC + nidp->nid_usertimeout;
3404 	else
3405 		newusrp->lug_expiry = NFSD_MONOSEC + 5;
3406 	if (nidp->nid_flag & (NFSID_ADDUID | NFSID_ADDUSERNAME)) {
3407 		newusrp->lug_uid = nidp->nid_uid;
3408 		thp = NFSUSERHASH(newusrp->lug_uid);
3409 		mtx_assert(&thp->mtx, MA_OWNED);
3410 		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_numhash);
3411 		thp = NFSUSERNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
3412 		mtx_assert(&thp->mtx, MA_OWNED);
3413 		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_namehash);
3414 		atomic_add_int(&nfsrv_usercnt, 1);
3415 	} else if (nidp->nid_flag & (NFSID_ADDGID | NFSID_ADDGROUPNAME)) {
3416 		newusrp->lug_gid = nidp->nid_gid;
3417 		thp = NFSGROUPHASH(newusrp->lug_gid);
3418 		mtx_assert(&thp->mtx, MA_OWNED);
3419 		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_numhash);
3420 		thp = NFSGROUPNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
3421 		mtx_assert(&thp->mtx, MA_OWNED);
3422 		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_namehash);
3423 		atomic_add_int(&nfsrv_usercnt, 1);
3424 	} else {
3425 		if (newusrp->lug_cred != NULL)
3426 			crfree(newusrp->lug_cred);
3427 		free(newusrp, M_NFSUSERGROUP);
3428 	}
3429 
3430 	/*
3431 	 * Once per second, allow one thread to trim the cache.
3432 	 */
3433 	if (lasttime < NFSD_MONOSEC &&
3434 	    atomic_cmpset_acq_int(&onethread, 0, 1) != 0) {
3435 		/*
3436 		 * First, unlock the single mutexes, so that all entries
3437 		 * can be locked and any LOR is avoided.
3438 		 */
3439 		if (hp_name != NULL) {
3440 			mtx_unlock(&hp_name->mtx);
3441 			hp_name = NULL;
3442 		}
3443 		if (hp_idnum != NULL) {
3444 			mtx_unlock(&hp_idnum->mtx);
3445 			hp_idnum = NULL;
3446 		}
3447 
3448 		if ((nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID |
3449 		    NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) != 0) {
3450 			if (username_locked == 0) {
3451 				for (i = 0; i < nfsrv_lughashsize; i++)
3452 					mtx_lock(&nfsusernamehash[i].mtx);
3453 				username_locked = 1;
3454 			}
3455 			KASSERT(user_locked == 0,
3456 			    ("nfssvc_idname: user_locked"));
3457 			for (i = 0; i < nfsrv_lughashsize; i++)
3458 				mtx_lock(&nfsuserhash[i].mtx);
3459 			user_locked = 1;
3460 			for (i = 0; i < nfsrv_lughashsize; i++) {
3461 				TAILQ_FOREACH_SAFE(usrp,
3462 				    &nfsuserhash[i].lughead, lug_numhash,
3463 				    nusrp)
3464 					if (usrp->lug_expiry < NFSD_MONOSEC)
3465 						nfsrv_removeuser(usrp, 1);
3466 			}
3467 			for (i = 0; i < nfsrv_lughashsize; i++) {
3468 				/*
3469 				 * Trim the cache using an approximate LRU
3470 				 * algorithm.  This code deletes the least
3471 				 * recently used entry on each hash list.
3472 				 */
3473 				if (nfsrv_usercnt <= nfsrv_usermax)
3474 					break;
3475 				usrp = TAILQ_FIRST(&nfsuserhash[i].lughead);
3476 				if (usrp != NULL)
3477 					nfsrv_removeuser(usrp, 1);
3478 			}
3479 		} else {
3480 			if (groupname_locked == 0) {
3481 				for (i = 0; i < nfsrv_lughashsize; i++)
3482 					mtx_lock(&nfsgroupnamehash[i].mtx);
3483 				groupname_locked = 1;
3484 			}
3485 			KASSERT(group_locked == 0,
3486 			    ("nfssvc_idname: group_locked"));
3487 			for (i = 0; i < nfsrv_lughashsize; i++)
3488 				mtx_lock(&nfsgrouphash[i].mtx);
3489 			group_locked = 1;
3490 			for (i = 0; i < nfsrv_lughashsize; i++) {
3491 				TAILQ_FOREACH_SAFE(usrp,
3492 				    &nfsgrouphash[i].lughead, lug_numhash,
3493 				    nusrp)
3494 					if (usrp->lug_expiry < NFSD_MONOSEC)
3495 						nfsrv_removeuser(usrp, 0);
3496 			}
3497 			for (i = 0; i < nfsrv_lughashsize; i++) {
3498 				/*
3499 				 * Trim the cache using an approximate LRU
3500 				 * algorithm.  This code deletes the least
3501 				 * recently user entry on each hash list.
3502 				 */
3503 				if (nfsrv_usercnt <= nfsrv_usermax)
3504 					break;
3505 				usrp = TAILQ_FIRST(&nfsgrouphash[i].lughead);
3506 				if (usrp != NULL)
3507 					nfsrv_removeuser(usrp, 0);
3508 			}
3509 		}
3510 		lasttime = NFSD_MONOSEC;
3511 		atomic_store_rel_int(&onethread, 0);
3512 	}
3513 
3514 	/* Now, unlock all locked mutexes. */
3515 	if (hp_idnum != NULL)
3516 		mtx_unlock(&hp_idnum->mtx);
3517 	if (hp_name != NULL)
3518 		mtx_unlock(&hp_name->mtx);
3519 	if (user_locked != 0)
3520 		for (i = 0; i < nfsrv_lughashsize; i++)
3521 			mtx_unlock(&nfsuserhash[i].mtx);
3522 	if (username_locked != 0)
3523 		for (i = 0; i < nfsrv_lughashsize; i++)
3524 			mtx_unlock(&nfsusernamehash[i].mtx);
3525 	if (group_locked != 0)
3526 		for (i = 0; i < nfsrv_lughashsize; i++)
3527 			mtx_unlock(&nfsgrouphash[i].mtx);
3528 	if (groupname_locked != 0)
3529 		for (i = 0; i < nfsrv_lughashsize; i++)
3530 			mtx_unlock(&nfsgroupnamehash[i].mtx);
3531 out:
3532 	NFSEXITCODE(error);
3533 	return (error);
3534 }
3535 
3536 /*
3537  * Remove a user/group name element.
3538  */
3539 static void
3540 nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser)
3541 {
3542 	struct nfsrv_lughash *hp;
3543 
3544 	if (isuser != 0) {
3545 		hp = NFSUSERHASH(usrp->lug_uid);
3546 		mtx_assert(&hp->mtx, MA_OWNED);
3547 		TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
3548 		hp = NFSUSERNAMEHASH(usrp->lug_name, usrp->lug_namelen);
3549 		mtx_assert(&hp->mtx, MA_OWNED);
3550 		TAILQ_REMOVE(&hp->lughead, usrp, lug_namehash);
3551 	} else {
3552 		hp = NFSGROUPHASH(usrp->lug_gid);
3553 		mtx_assert(&hp->mtx, MA_OWNED);
3554 		TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
3555 		hp = NFSGROUPNAMEHASH(usrp->lug_name, usrp->lug_namelen);
3556 		mtx_assert(&hp->mtx, MA_OWNED);
3557 		TAILQ_REMOVE(&hp->lughead, usrp, lug_namehash);
3558 	}
3559 	atomic_add_int(&nfsrv_usercnt, -1);
3560 	if (usrp->lug_cred != NULL)
3561 		crfree(usrp->lug_cred);
3562 	free(usrp, M_NFSUSERGROUP);
3563 }
3564 
3565 /*
3566  * Free up all the allocations related to the name<-->id cache.
3567  * This function should only be called when the nfsuserd daemon isn't
3568  * running, since it doesn't do any locking.
3569  * This function is meant to be used when the nfscommon module is unloaded.
3570  */
3571 APPLESTATIC void
3572 nfsrv_cleanusergroup(void)
3573 {
3574 	struct nfsrv_lughash *hp, *hp2;
3575 	struct nfsusrgrp *nusrp, *usrp;
3576 	int i;
3577 
3578 	if (nfsuserhash == NULL)
3579 		return;
3580 
3581 	for (i = 0; i < nfsrv_lughashsize; i++) {
3582 		hp = &nfsuserhash[i];
3583 		TAILQ_FOREACH_SAFE(usrp, &hp->lughead, lug_numhash, nusrp) {
3584 			TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
3585 			hp2 = NFSUSERNAMEHASH(usrp->lug_name,
3586 			    usrp->lug_namelen);
3587 			TAILQ_REMOVE(&hp2->lughead, usrp, lug_namehash);
3588 			if (usrp->lug_cred != NULL)
3589 				crfree(usrp->lug_cred);
3590 			free(usrp, M_NFSUSERGROUP);
3591 		}
3592 		hp = &nfsgrouphash[i];
3593 		TAILQ_FOREACH_SAFE(usrp, &hp->lughead, lug_numhash, nusrp) {
3594 			TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
3595 			hp2 = NFSGROUPNAMEHASH(usrp->lug_name,
3596 			    usrp->lug_namelen);
3597 			TAILQ_REMOVE(&hp2->lughead, usrp, lug_namehash);
3598 			if (usrp->lug_cred != NULL)
3599 				crfree(usrp->lug_cred);
3600 			free(usrp, M_NFSUSERGROUP);
3601 		}
3602 		mtx_destroy(&nfsuserhash[i].mtx);
3603 		mtx_destroy(&nfsusernamehash[i].mtx);
3604 		mtx_destroy(&nfsgroupnamehash[i].mtx);
3605 		mtx_destroy(&nfsgrouphash[i].mtx);
3606 	}
3607 	free(nfsuserhash, M_NFSUSERGROUP);
3608 	free(nfsusernamehash, M_NFSUSERGROUP);
3609 	free(nfsgrouphash, M_NFSUSERGROUP);
3610 	free(nfsgroupnamehash, M_NFSUSERGROUP);
3611 	free(nfsrv_dnsname, M_NFSSTRING);
3612 }
3613 
3614 /*
3615  * This function scans a byte string and checks for UTF-8 compliance.
3616  * It returns 0 if it conforms and NFSERR_INVAL if not.
3617  */
3618 APPLESTATIC int
3619 nfsrv_checkutf8(u_int8_t *cp, int len)
3620 {
3621 	u_int32_t val = 0x0;
3622 	int cnt = 0, gotd = 0, shift = 0;
3623 	u_int8_t byte;
3624 	static int utf8_shift[5] = { 7, 11, 16, 21, 26 };
3625 	int error = 0;
3626 
3627 	/*
3628 	 * Here are what the variables are used for:
3629 	 * val - the calculated value of a multibyte char, used to check
3630 	 *       that it was coded with the correct range
3631 	 * cnt - the number of 10xxxxxx bytes to follow
3632 	 * gotd - set for a char of Dxxx, so D800<->DFFF can be checked for
3633 	 * shift - lower order bits of range (ie. "val >> shift" should
3634 	 *       not be 0, in other words, dividing by the lower bound
3635 	 *       of the range should get a non-zero value)
3636 	 * byte - used to calculate cnt
3637 	 */
3638 	while (len > 0) {
3639 		if (cnt > 0) {
3640 			/* This handles the 10xxxxxx bytes */
3641 			if ((*cp & 0xc0) != 0x80 ||
3642 			    (gotd && (*cp & 0x20))) {
3643 				error = NFSERR_INVAL;
3644 				goto out;
3645 			}
3646 			gotd = 0;
3647 			val <<= 6;
3648 			val |= (*cp & 0x3f);
3649 			cnt--;
3650 			if (cnt == 0 && (val >> shift) == 0x0) {
3651 				error = NFSERR_INVAL;
3652 				goto out;
3653 			}
3654 		} else if (*cp & 0x80) {
3655 			/* first byte of multi byte char */
3656 			byte = *cp;
3657 			while ((byte & 0x40) && cnt < 6) {
3658 				cnt++;
3659 				byte <<= 1;
3660 			}
3661 			if (cnt == 0 || cnt == 6) {
3662 				error = NFSERR_INVAL;
3663 				goto out;
3664 			}
3665 			val = (*cp & (0x3f >> cnt));
3666 			shift = utf8_shift[cnt - 1];
3667 			if (cnt == 2 && val == 0xd)
3668 				/* Check for the 0xd800-0xdfff case */
3669 				gotd = 1;
3670 		}
3671 		cp++;
3672 		len--;
3673 	}
3674 	if (cnt > 0)
3675 		error = NFSERR_INVAL;
3676 
3677 out:
3678 	NFSEXITCODE(error);
3679 	return (error);
3680 }
3681 
3682 /*
3683  * Parse the xdr for an NFSv4 FsLocations attribute. Return two malloc'd
3684  * strings, one with the root path in it and the other with the list of
3685  * locations. The list is in the same format as is found in nfr_refs.
3686  * It is a "," separated list of entries, where each of them is of the
3687  * form <server>:<rootpath>. For example
3688  * "nfsv4-test:/sub2,nfsv4-test2:/user/mnt,nfsv4-test2:/user/mnt2"
3689  * The nilp argument is set to 1 for the special case of a null fs_root
3690  * and an empty server list.
3691  * It returns NFSERR_BADXDR, if the xdr can't be parsed and returns the
3692  * number of xdr bytes parsed in sump.
3693  */
3694 static int
3695 nfsrv_getrefstr(struct nfsrv_descript *nd, u_char **fsrootp, u_char **srvp,
3696     int *sump, int *nilp)
3697 {
3698 	u_int32_t *tl;
3699 	u_char *cp = NULL, *cp2 = NULL, *cp3, *str;
3700 	int i, j, len, stringlen, cnt, slen, siz, xdrsum, error = 0, nsrv;
3701 	struct list {
3702 		SLIST_ENTRY(list) next;
3703 		int len;
3704 		u_char host[1];
3705 	} *lsp, *nlsp;
3706 	SLIST_HEAD(, list) head;
3707 
3708 	*fsrootp = NULL;
3709 	*srvp = NULL;
3710 	*nilp = 0;
3711 
3712 	/*
3713 	 * Get the fs_root path and check for the special case of null path
3714 	 * and 0 length server list.
3715 	 */
3716 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3717 	len = fxdr_unsigned(int, *tl);
3718 	if (len < 0 || len > 10240) {
3719 		error = NFSERR_BADXDR;
3720 		goto nfsmout;
3721 	}
3722 	if (len == 0) {
3723 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3724 		if (*tl != 0) {
3725 			error = NFSERR_BADXDR;
3726 			goto nfsmout;
3727 		}
3728 		*nilp = 1;
3729 		*sump = 2 * NFSX_UNSIGNED;
3730 		error = 0;
3731 		goto nfsmout;
3732 	}
3733 	cp = malloc(len + 1, M_NFSSTRING, M_WAITOK);
3734 	error = nfsrv_mtostr(nd, cp, len);
3735 	if (!error) {
3736 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3737 		cnt = fxdr_unsigned(int, *tl);
3738 		if (cnt <= 0)
3739 			error = NFSERR_BADXDR;
3740 	}
3741 	if (error)
3742 		goto nfsmout;
3743 
3744 	/*
3745 	 * Now, loop through the location list and make up the srvlist.
3746 	 */
3747 	xdrsum = (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
3748 	cp2 = cp3 = malloc(1024, M_NFSSTRING, M_WAITOK);
3749 	slen = 1024;
3750 	siz = 0;
3751 	for (i = 0; i < cnt; i++) {
3752 		SLIST_INIT(&head);
3753 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3754 		nsrv = fxdr_unsigned(int, *tl);
3755 		if (nsrv <= 0) {
3756 			error = NFSERR_BADXDR;
3757 			goto nfsmout;
3758 		}
3759 
3760 		/*
3761 		 * Handle the first server by putting it in the srvstr.
3762 		 */
3763 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3764 		len = fxdr_unsigned(int, *tl);
3765 		if (len <= 0 || len > 1024) {
3766 			error = NFSERR_BADXDR;
3767 			goto nfsmout;
3768 		}
3769 		nfsrv_refstrbigenough(siz + len + 3, &cp2, &cp3, &slen);
3770 		if (cp3 != cp2) {
3771 			*cp3++ = ',';
3772 			siz++;
3773 		}
3774 		error = nfsrv_mtostr(nd, cp3, len);
3775 		if (error)
3776 			goto nfsmout;
3777 		cp3 += len;
3778 		*cp3++ = ':';
3779 		siz += (len + 1);
3780 		xdrsum += (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
3781 		for (j = 1; j < nsrv; j++) {
3782 			/*
3783 			 * Yuck, put them in an slist and process them later.
3784 			 */
3785 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3786 			len = fxdr_unsigned(int, *tl);
3787 			if (len <= 0 || len > 1024) {
3788 				error = NFSERR_BADXDR;
3789 				goto nfsmout;
3790 			}
3791 			lsp = (struct list *)malloc(sizeof (struct list)
3792 			    + len, M_TEMP, M_WAITOK);
3793 			error = nfsrv_mtostr(nd, lsp->host, len);
3794 			if (error)
3795 				goto nfsmout;
3796 			xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
3797 			lsp->len = len;
3798 			SLIST_INSERT_HEAD(&head, lsp, next);
3799 		}
3800 
3801 		/*
3802 		 * Finally, we can get the path.
3803 		 */
3804 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3805 		len = fxdr_unsigned(int, *tl);
3806 		if (len <= 0 || len > 1024) {
3807 			error = NFSERR_BADXDR;
3808 			goto nfsmout;
3809 		}
3810 		nfsrv_refstrbigenough(siz + len + 1, &cp2, &cp3, &slen);
3811 		error = nfsrv_mtostr(nd, cp3, len);
3812 		if (error)
3813 			goto nfsmout;
3814 		xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
3815 		str = cp3;
3816 		stringlen = len;
3817 		cp3 += len;
3818 		siz += len;
3819 		SLIST_FOREACH_SAFE(lsp, &head, next, nlsp) {
3820 			nfsrv_refstrbigenough(siz + lsp->len + stringlen + 3,
3821 			    &cp2, &cp3, &slen);
3822 			*cp3++ = ',';
3823 			NFSBCOPY(lsp->host, cp3, lsp->len);
3824 			cp3 += lsp->len;
3825 			*cp3++ = ':';
3826 			NFSBCOPY(str, cp3, stringlen);
3827 			cp3 += stringlen;
3828 			*cp3 = '\0';
3829 			siz += (lsp->len + stringlen + 2);
3830 			free((caddr_t)lsp, M_TEMP);
3831 		}
3832 	}
3833 	*fsrootp = cp;
3834 	*srvp = cp2;
3835 	*sump = xdrsum;
3836 	NFSEXITCODE2(0, nd);
3837 	return (0);
3838 nfsmout:
3839 	if (cp != NULL)
3840 		free(cp, M_NFSSTRING);
3841 	if (cp2 != NULL)
3842 		free(cp2, M_NFSSTRING);
3843 	NFSEXITCODE2(error, nd);
3844 	return (error);
3845 }
3846 
3847 /*
3848  * Make the malloc'd space large enough. This is a pain, but the xdr
3849  * doesn't set an upper bound on the side, so...
3850  */
3851 static void
3852 nfsrv_refstrbigenough(int siz, u_char **cpp, u_char **cpp2, int *slenp)
3853 {
3854 	u_char *cp;
3855 	int i;
3856 
3857 	if (siz <= *slenp)
3858 		return;
3859 	cp = malloc(siz + 1024, M_NFSSTRING, M_WAITOK);
3860 	NFSBCOPY(*cpp, cp, *slenp);
3861 	free(*cpp, M_NFSSTRING);
3862 	i = *cpp2 - *cpp;
3863 	*cpp = cp;
3864 	*cpp2 = cp + i;
3865 	*slenp = siz + 1024;
3866 }
3867 
3868 /*
3869  * Initialize the reply header data structures.
3870  */
3871 APPLESTATIC void
3872 nfsrvd_rephead(struct nfsrv_descript *nd)
3873 {
3874 	mbuf_t mreq;
3875 
3876 	/*
3877 	 * If this is a big reply, use a cluster.
3878 	 */
3879 	if ((nd->nd_flag & ND_GSSINITREPLY) == 0 &&
3880 	    nfs_bigreply[nd->nd_procnum]) {
3881 		NFSMCLGET(mreq, M_WAITOK);
3882 		nd->nd_mreq = mreq;
3883 		nd->nd_mb = mreq;
3884 	} else {
3885 		NFSMGET(mreq);
3886 		nd->nd_mreq = mreq;
3887 		nd->nd_mb = mreq;
3888 	}
3889 	nd->nd_bpos = NFSMTOD(mreq, caddr_t);
3890 	mbuf_setlen(mreq, 0);
3891 
3892 	if ((nd->nd_flag & ND_GSSINITREPLY) == 0)
3893 		NFSM_BUILD(nd->nd_errp, int *, NFSX_UNSIGNED);
3894 }
3895 
3896 /*
3897  * Lock a socket against others.
3898  * Currently used to serialize connect/disconnect attempts.
3899  */
3900 int
3901 newnfs_sndlock(int *flagp)
3902 {
3903 	struct timespec ts;
3904 
3905 	NFSLOCKSOCK();
3906 	while (*flagp & NFSR_SNDLOCK) {
3907 		*flagp |= NFSR_WANTSND;
3908 		ts.tv_sec = 0;
3909 		ts.tv_nsec = 0;
3910 		(void) nfsmsleep((caddr_t)flagp, NFSSOCKMUTEXPTR,
3911 		    PZERO - 1, "nfsndlck", &ts);
3912 	}
3913 	*flagp |= NFSR_SNDLOCK;
3914 	NFSUNLOCKSOCK();
3915 	return (0);
3916 }
3917 
3918 /*
3919  * Unlock the stream socket for others.
3920  */
3921 void
3922 newnfs_sndunlock(int *flagp)
3923 {
3924 
3925 	NFSLOCKSOCK();
3926 	if ((*flagp & NFSR_SNDLOCK) == 0)
3927 		panic("nfs sndunlock");
3928 	*flagp &= ~NFSR_SNDLOCK;
3929 	if (*flagp & NFSR_WANTSND) {
3930 		*flagp &= ~NFSR_WANTSND;
3931 		wakeup((caddr_t)flagp);
3932 	}
3933 	NFSUNLOCKSOCK();
3934 }
3935 
3936 APPLESTATIC int
3937 nfsv4_getipaddr(struct nfsrv_descript *nd, struct sockaddr_storage *sa,
3938     int *isudp)
3939 {
3940 	struct sockaddr_in *sad;
3941 	struct sockaddr_in6 *sad6;
3942 	struct in_addr saddr;
3943 	uint32_t portnum, *tl;
3944 	int af = 0, i, j, k;
3945 	char addr[64], protocol[5], *cp;
3946 	int cantparse = 0, error = 0;
3947 	uint16_t portv;
3948 
3949 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3950 	i = fxdr_unsigned(int, *tl);
3951 	if (i >= 3 && i <= 4) {
3952 		error = nfsrv_mtostr(nd, protocol, i);
3953 		if (error)
3954 			goto nfsmout;
3955 		if (strcmp(protocol, "tcp") == 0) {
3956 			af = AF_INET;
3957 			*isudp = 0;
3958 		} else if (strcmp(protocol, "udp") == 0) {
3959 			af = AF_INET;
3960 			*isudp = 1;
3961 		} else if (strcmp(protocol, "tcp6") == 0) {
3962 			af = AF_INET6;
3963 			*isudp = 0;
3964 		} else if (strcmp(protocol, "udp6") == 0) {
3965 			af = AF_INET6;
3966 			*isudp = 1;
3967 		} else
3968 			cantparse = 1;
3969 	} else {
3970 		cantparse = 1;
3971 		if (i > 0) {
3972 			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
3973 			if (error)
3974 				goto nfsmout;
3975 		}
3976 	}
3977 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3978 	i = fxdr_unsigned(int, *tl);
3979 	if (i < 0) {
3980 		error = NFSERR_BADXDR;
3981 		goto nfsmout;
3982 	} else if (cantparse == 0 && i >= 11 && i < 64) {
3983 		/*
3984 		 * The shortest address is 11chars and the longest is < 64.
3985 		 */
3986 		error = nfsrv_mtostr(nd, addr, i);
3987 		if (error)
3988 			goto nfsmout;
3989 
3990 		/* Find the port# at the end and extract that. */
3991 		i = strlen(addr);
3992 		k = 0;
3993 		cp = &addr[i - 1];
3994 		/* Count back two '.'s from end to get port# field. */
3995 		for (j = 0; j < i; j++) {
3996 			if (*cp == '.') {
3997 				k++;
3998 				if (k == 2)
3999 					break;
4000 			}
4001 			cp--;
4002 		}
4003 		if (k == 2) {
4004 			/*
4005 			 * The NFSv4 port# is appended as .N.N, where N is
4006 			 * a decimal # in the range 0-255, just like an inet4
4007 			 * address. Cheat and use inet_aton(), which will
4008 			 * return a Class A address and then shift the high
4009 			 * order 8bits over to convert it to the port#.
4010 			 */
4011 			*cp++ = '\0';
4012 			if (inet_aton(cp, &saddr) == 1) {
4013 				portnum = ntohl(saddr.s_addr);
4014 				portv = (uint16_t)((portnum >> 16) |
4015 				    (portnum & 0xff));
4016 			} else
4017 				cantparse = 1;
4018 		} else
4019 			cantparse = 1;
4020 		if (cantparse == 0) {
4021 			if (af == AF_INET) {
4022 				sad = (struct sockaddr_in *)sa;
4023 				if (inet_pton(af, addr, &sad->sin_addr) == 1) {
4024 					sad->sin_len = sizeof(*sad);
4025 					sad->sin_family = AF_INET;
4026 					sad->sin_port = htons(portv);
4027 					return (0);
4028 				}
4029 			} else {
4030 				sad6 = (struct sockaddr_in6 *)sa;
4031 				if (inet_pton(af, addr, &sad6->sin6_addr)
4032 				    == 1) {
4033 					sad6->sin6_len = sizeof(*sad6);
4034 					sad6->sin6_family = AF_INET6;
4035 					sad6->sin6_port = htons(portv);
4036 					return (0);
4037 				}
4038 			}
4039 		}
4040 	} else {
4041 		if (i > 0) {
4042 			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
4043 			if (error)
4044 				goto nfsmout;
4045 		}
4046 	}
4047 	error = EPERM;
4048 nfsmout:
4049 	return (error);
4050 }
4051 
4052 /*
4053  * Handle an NFSv4.1 Sequence request for the session.
4054  * If reply != NULL, use it to return the cached reply, as required.
4055  * The client gets a cached reply via this call for callbacks, however the
4056  * server gets a cached reply via the nfsv4_seqsess_cachereply() call.
4057  */
4058 int
4059 nfsv4_seqsession(uint32_t seqid, uint32_t slotid, uint32_t highslot,
4060     struct nfsslot *slots, struct mbuf **reply, uint16_t maxslot)
4061 {
4062 	int error;
4063 
4064 	error = 0;
4065 	if (reply != NULL)
4066 		*reply = NULL;
4067 	if (slotid > maxslot)
4068 		return (NFSERR_BADSLOT);
4069 	if (seqid == slots[slotid].nfssl_seq) {
4070 		/* A retry. */
4071 		if (slots[slotid].nfssl_inprog != 0)
4072 			error = NFSERR_DELAY;
4073 		else if (slots[slotid].nfssl_reply != NULL) {
4074 			if (reply != NULL) {
4075 				*reply = slots[slotid].nfssl_reply;
4076 				slots[slotid].nfssl_reply = NULL;
4077 			}
4078 			slots[slotid].nfssl_inprog = 1;
4079 			error = NFSERR_REPLYFROMCACHE;
4080 		} else
4081 			/* No reply cached, so just do it. */
4082 			slots[slotid].nfssl_inprog = 1;
4083 	} else if ((slots[slotid].nfssl_seq + 1) == seqid) {
4084 		if (slots[slotid].nfssl_reply != NULL)
4085 			m_freem(slots[slotid].nfssl_reply);
4086 		slots[slotid].nfssl_reply = NULL;
4087 		slots[slotid].nfssl_inprog = 1;
4088 		slots[slotid].nfssl_seq++;
4089 	} else
4090 		error = NFSERR_SEQMISORDERED;
4091 	return (error);
4092 }
4093 
4094 /*
4095  * Cache this reply for the slot.
4096  * Use the "rep" argument to return the cached reply if repstat is set to
4097  * NFSERR_REPLYFROMCACHE. The client never sets repstat to this value.
4098  */
4099 void
4100 nfsv4_seqsess_cacherep(uint32_t slotid, struct nfsslot *slots, int repstat,
4101    struct mbuf **rep)
4102 {
4103 
4104 	if (repstat == NFSERR_REPLYFROMCACHE) {
4105 		*rep = slots[slotid].nfssl_reply;
4106 		slots[slotid].nfssl_reply = NULL;
4107 	} else {
4108 		if (slots[slotid].nfssl_reply != NULL)
4109 			m_freem(slots[slotid].nfssl_reply);
4110 		slots[slotid].nfssl_reply = *rep;
4111 	}
4112 	slots[slotid].nfssl_inprog = 0;
4113 }
4114 
4115 /*
4116  * Generate the xdr for an NFSv4.1 Sequence Operation.
4117  */
4118 APPLESTATIC void
4119 nfsv4_setsequence(struct nfsmount *nmp, struct nfsrv_descript *nd,
4120     struct nfsclsession *sep, int dont_replycache)
4121 {
4122 	uint32_t *tl, slotseq = 0;
4123 	int error, maxslot, slotpos;
4124 	uint8_t sessionid[NFSX_V4SESSIONID];
4125 
4126 	error = nfsv4_sequencelookup(nmp, sep, &slotpos, &maxslot, &slotseq,
4127 	    sessionid);
4128 
4129 	/* Build the Sequence arguments. */
4130 	NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED);
4131 	nd->nd_sequence = tl;
4132 	bcopy(sessionid, tl, NFSX_V4SESSIONID);
4133 	tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
4134 	nd->nd_slotseq = tl;
4135 	if (error == 0) {
4136 		*tl++ = txdr_unsigned(slotseq);
4137 		*tl++ = txdr_unsigned(slotpos);
4138 		*tl++ = txdr_unsigned(maxslot);
4139 		if (dont_replycache == 0)
4140 			*tl = newnfs_true;
4141 		else
4142 			*tl = newnfs_false;
4143 	} else {
4144 		/*
4145 		 * There are two errors and the rest of the session can
4146 		 * just be zeros.
4147 		 * NFSERR_BADSESSION: This bad session should just generate
4148 		 *    the same error again when the RPC is retried.
4149 		 * ESTALE: A forced dismount is in progress and will cause the
4150 		 *    RPC to fail later.
4151 		 */
4152 		*tl++ = 0;
4153 		*tl++ = 0;
4154 		*tl++ = 0;
4155 		*tl = 0;
4156 	}
4157 	nd->nd_flag |= ND_HASSEQUENCE;
4158 }
4159 
4160 int
4161 nfsv4_sequencelookup(struct nfsmount *nmp, struct nfsclsession *sep,
4162     int *slotposp, int *maxslotp, uint32_t *slotseqp, uint8_t *sessionid)
4163 {
4164 	int i, maxslot, slotpos;
4165 	uint64_t bitval;
4166 
4167 	/* Find an unused slot. */
4168 	slotpos = -1;
4169 	maxslot = -1;
4170 	mtx_lock(&sep->nfsess_mtx);
4171 	do {
4172 		if (nmp != NULL && sep->nfsess_defunct != 0) {
4173 			/* Just return the bad session. */
4174 			bcopy(sep->nfsess_sessionid, sessionid,
4175 			    NFSX_V4SESSIONID);
4176 			mtx_unlock(&sep->nfsess_mtx);
4177 			return (NFSERR_BADSESSION);
4178 		}
4179 		bitval = 1;
4180 		for (i = 0; i < sep->nfsess_foreslots; i++) {
4181 			if ((bitval & sep->nfsess_slots) == 0) {
4182 				slotpos = i;
4183 				sep->nfsess_slots |= bitval;
4184 				sep->nfsess_slotseq[i]++;
4185 				*slotseqp = sep->nfsess_slotseq[i];
4186 				break;
4187 			}
4188 			bitval <<= 1;
4189 		}
4190 		if (slotpos == -1) {
4191 			/*
4192 			 * If a forced dismount is in progress, just return.
4193 			 * This RPC attempt will fail when it calls
4194 			 * newnfs_request().
4195 			 */
4196 			if (nmp != NULL &&
4197 			    (nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF)
4198 			    != 0) {
4199 				mtx_unlock(&sep->nfsess_mtx);
4200 				return (ESTALE);
4201 			}
4202 			/* Wake up once/sec, to check for a forced dismount. */
4203 			(void)mtx_sleep(&sep->nfsess_slots, &sep->nfsess_mtx,
4204 			    PZERO, "nfsclseq", hz);
4205 		}
4206 	} while (slotpos == -1);
4207 	/* Now, find the highest slot in use. (nfsc_slots is 64bits) */
4208 	bitval = 1;
4209 	for (i = 0; i < 64; i++) {
4210 		if ((bitval & sep->nfsess_slots) != 0)
4211 			maxslot = i;
4212 		bitval <<= 1;
4213 	}
4214 	bcopy(sep->nfsess_sessionid, sessionid, NFSX_V4SESSIONID);
4215 	mtx_unlock(&sep->nfsess_mtx);
4216 	*slotposp = slotpos;
4217 	*maxslotp = maxslot;
4218 	return (0);
4219 }
4220 
4221 /*
4222  * Free a session slot.
4223  */
4224 APPLESTATIC void
4225 nfsv4_freeslot(struct nfsclsession *sep, int slot)
4226 {
4227 	uint64_t bitval;
4228 
4229 	bitval = 1;
4230 	if (slot > 0)
4231 		bitval <<= slot;
4232 	mtx_lock(&sep->nfsess_mtx);
4233 	if ((bitval & sep->nfsess_slots) == 0)
4234 		printf("freeing free slot!!\n");
4235 	sep->nfsess_slots &= ~bitval;
4236 	wakeup(&sep->nfsess_slots);
4237 	mtx_unlock(&sep->nfsess_mtx);
4238 }
4239 
4240