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