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