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