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