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