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