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