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