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