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