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