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