1191783Srmacklem/*-
2191783Srmacklem * Copyright (c) 1989, 1993
3191783Srmacklem *	The Regents of the University of California.  All rights reserved.
4191783Srmacklem *
5191783Srmacklem * This code is derived from software contributed to Berkeley by
6191783Srmacklem * Rick Macklem at The University of Guelph.
7191783Srmacklem *
8191783Srmacklem * Redistribution and use in source and binary forms, with or without
9191783Srmacklem * modification, are permitted provided that the following conditions
10191783Srmacklem * are met:
11191783Srmacklem * 1. Redistributions of source code must retain the above copyright
12191783Srmacklem *    notice, this list of conditions and the following disclaimer.
13191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright
14191783Srmacklem *    notice, this list of conditions and the following disclaimer in the
15191783Srmacklem *    documentation and/or other materials provided with the distribution.
16191783Srmacklem * 4. Neither the name of the University nor the names of its contributors
17191783Srmacklem *    may be used to endorse or promote products derived from this software
18191783Srmacklem *    without specific prior written permission.
19191783Srmacklem *
20191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23191783Srmacklem * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30191783Srmacklem * SUCH DAMAGE.
31191783Srmacklem *
32191783Srmacklem */
33191783Srmacklem
34191783Srmacklem#include <sys/cdefs.h>
35191783Srmacklem__FBSDID("$FreeBSD$");
36191783Srmacklem
37191783Srmacklem/*
38191783Srmacklem * These functions support the macros and help fiddle mbuf chains for
39191783Srmacklem * the nfs op functions. They do things like create the rpc header and
40191783Srmacklem * copy data between mbuf chains and uio lists.
41191783Srmacklem */
42191783Srmacklem#ifndef APPLEKEXT
43229802Srmacklem#include "opt_inet6.h"
44229802Srmacklem
45191783Srmacklem#include <fs/nfs/nfsport.h>
46191783Srmacklem
47191783Srmacklem/*
48191783Srmacklem * Data items converted to xdr at startup, since they are constant
49191783Srmacklem * This is kinda hokey, but may save a little time doing byte swaps
50191783Srmacklem */
51191783Srmacklemu_int32_t newnfs_true, newnfs_false, newnfs_xdrneg1;
52191783Srmacklem
53191783Srmacklem/* And other global data */
54191783Srmacklemnfstype nfsv34_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK,
55191783Srmacklem		      NFFIFO, NFNON };
56191783Srmacklemenum vtype newnv2tov_type[8] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON, VNON };
57191783Srmacklemenum vtype nv34tov_type[8]={ VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO };
58191783Srmacklemstruct timeval nfsboottime;	/* Copy boottime once, so it never changes */
59191783Srmacklemint nfscl_ticks;
60191783Srmacklemint nfsrv_useacl = 1;
61191783Srmacklemstruct nfssockreq nfsrv_nfsuserdsock;
62191783Srmacklemint nfsrv_nfsuserd = 0;
63191783Srmacklemstruct nfsreqhead nfsd_reqq;
64191783Srmacklemuid_t nfsrv_defaultuid;
65191783Srmacklemgid_t nfsrv_defaultgid;
66191783Srmacklemint nfsrv_lease = NFSRV_LEASE;
67191783Srmacklemint ncl_mbuf_mlen = MLEN;
68191783SrmacklemNFSNAMEIDMUTEX;
69191783SrmacklemNFSSOCKMUTEX;
70191783Srmacklem
71191783Srmacklem/*
72191783Srmacklem * This array of structures indicates, for V4:
73191783Srmacklem * retfh - which of 3 types of calling args are used
74191783Srmacklem *	0 - doesn't change cfh or use a sfh
75191783Srmacklem *	1 - replaces cfh with a new one (unless it returns an error status)
76191783Srmacklem *	2 - uses cfh and sfh
77191783Srmacklem * needscfh - if the op wants a cfh and premtime
78191783Srmacklem *	0 - doesn't use a cfh
79191783Srmacklem *	1 - uses a cfh, but doesn't want pre-op attributes
80191783Srmacklem *	2 - uses a cfh and wants pre-op attributes
81191783Srmacklem * savereply - indicates a non-idempotent Op
82191783Srmacklem *	0 - not non-idempotent
83191783Srmacklem *	1 - non-idempotent
84191783Srmacklem * Ops that are ordered via seqid# are handled separately from these
85191783Srmacklem * non-idempotent Ops.
86191783Srmacklem * Define it here, since it is used by both the client and server.
87191783Srmacklem */
88244042Srmacklemstruct nfsv4_opflag nfsv4_opflag[NFSV41_NOPS] = {
89244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* undef */
90244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* undef */
91244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* undef */
92244042Srmacklem	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Access */
93244042Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Close */
94244042Srmacklem	{ 0, 2, 0, 1, LK_EXCLUSIVE, 1 },		/* Commit */
95244042Srmacklem	{ 1, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Create */
96244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Delegpurge */
97244042Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Delegreturn */
98244042Srmacklem	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Getattr */
99244042Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* GetFH */
100244042Srmacklem	{ 2, 1, 1, 1, LK_EXCLUSIVE, 1 },		/* Link */
101244042Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Lock */
102244042Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* LockT */
103244042Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* LockU */
104244042Srmacklem	{ 1, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Lookup */
105244042Srmacklem	{ 1, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Lookupp */
106244042Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* NVerify */
107244042Srmacklem	{ 1, 1, 0, 1, LK_EXCLUSIVE, 1 },		/* Open */
108244042Srmacklem	{ 1, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* OpenAttr */
109244042Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* OpenConfirm */
110244042Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* OpenDowngrade */
111244042Srmacklem	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* PutFH */
112244042Srmacklem	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* PutPubFH */
113244042Srmacklem	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* PutRootFH */
114244042Srmacklem	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Read */
115244042Srmacklem	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Readdir */
116244042Srmacklem	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* ReadLink */
117244042Srmacklem	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Remove */
118244042Srmacklem	{ 2, 1, 1, 1, LK_EXCLUSIVE, 1 },		/* Rename */
119244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Renew */
120244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* RestoreFH */
121244042Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* SaveFH */
122244042Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* SecInfo */
123244042Srmacklem	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Setattr */
124244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* SetClientID */
125244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* SetClientIDConfirm */
126244042Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Verify */
127244042Srmacklem	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Write */
128244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* ReleaseLockOwner */
129244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Backchannel Ctrl */
130244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Bind Conn to Sess */
131244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Exchange ID */
132244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Create Session */
133244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Destroy Session */
134244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Free StateID */
135244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Get Dir Deleg */
136244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Get Device Info */
137244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Get Device List */
138244042Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Layout Commit */
139244042Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Layout Get */
140244042Srmacklem	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Layout Return */
141244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Secinfo No name */
142244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Sequence */
143244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Set SSV */
144244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Test StateID */
145244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Want Delegation */
146244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Destroy ClientID */
147244042Srmacklem	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Reclaim Complete */
148191783Srmacklem};
149191783Srmacklem#endif	/* !APPLEKEXT */
150191783Srmacklem
151191783Srmacklemstatic int ncl_mbuf_mhlen = MHLEN;
152191783Srmacklemstatic int nfsrv_usercnt = 0;
153191783Srmacklemstatic int nfsrv_dnsnamelen;
154191783Srmacklemstatic u_char *nfsrv_dnsname = NULL;
155191783Srmacklemstatic int nfsrv_usermax = 999999999;
156191783Srmacklemstatic struct nfsuserhashhead nfsuserhash[NFSUSERHASHSIZE];
157191783Srmacklemstatic struct nfsuserhashhead nfsusernamehash[NFSUSERHASHSIZE];
158191783Srmacklemstatic struct nfsuserhashhead nfsgrouphash[NFSGROUPHASHSIZE];
159191783Srmacklemstatic struct nfsuserhashhead nfsgroupnamehash[NFSGROUPHASHSIZE];
160191783Srmacklemstatic struct nfsuserlruhead nfsuserlruhead;
161191783Srmacklem
162191783Srmacklem/*
163191783Srmacklem * This static array indicates whether or not the RPC generates a large
164191783Srmacklem * reply. This is used by nfs_reply() to decide whether or not an mbuf
165191783Srmacklem * cluster should be allocated. (If a cluster is required by an RPC
166191783Srmacklem * marked 0 in this array, the code will still work, just not quite as
167191783Srmacklem * efficiently.)
168191783Srmacklem */
169244042Srmacklemint nfs_bigreply[NFSV41_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
170191783Srmacklem    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,
171244042Srmacklem    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 };
172191783Srmacklem
173191783Srmacklem/* local functions */
174191783Srmacklemstatic int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep);
175191783Srmacklemstatic void nfsv4_wanted(struct nfsv4lock *lp);
176191783Srmacklemstatic int nfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len);
177191783Srmacklemstatic int nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name,
178191783Srmacklem    NFSPROC_T *p);
179191783Srmacklemstatic void nfsrv_removeuser(struct nfsusrgrp *usrp);
180191783Srmacklemstatic int nfsrv_getrefstr(struct nfsrv_descript *, u_char **, u_char **,
181191783Srmacklem    int *, int *);
182191783Srmacklemstatic void nfsrv_refstrbigenough(int, u_char **, u_char **, int *);
183191783Srmacklem
184191783Srmacklem
185191783Srmacklem#ifndef APPLE
186191783Srmacklem/*
187191783Srmacklem * copies mbuf chain to the uio scatter/gather list
188191783Srmacklem */
189191783Srmacklemint
190191783Srmacklemnfsm_mbufuio(struct nfsrv_descript *nd, struct uio *uiop, int siz)
191191783Srmacklem{
192191783Srmacklem	char *mbufcp, *uiocp;
193191783Srmacklem	int xfer, left, len;
194191783Srmacklem	mbuf_t mp;
195191783Srmacklem	long uiosiz, rem;
196191783Srmacklem	int error = 0;
197191783Srmacklem
198191783Srmacklem	mp = nd->nd_md;
199191783Srmacklem	mbufcp = nd->nd_dpos;
200191783Srmacklem	len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - mbufcp;
201191783Srmacklem	rem = NFSM_RNDUP(siz) - siz;
202191783Srmacklem	while (siz > 0) {
203224086Szack		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL) {
204224086Szack			error = EBADRPC;
205224086Szack			goto out;
206224086Szack		}
207191783Srmacklem		left = uiop->uio_iov->iov_len;
208191783Srmacklem		uiocp = uiop->uio_iov->iov_base;
209191783Srmacklem		if (left > siz)
210191783Srmacklem			left = siz;
211191783Srmacklem		uiosiz = left;
212191783Srmacklem		while (left > 0) {
213191783Srmacklem			while (len == 0) {
214191783Srmacklem				mp = mbuf_next(mp);
215224086Szack				if (mp == NULL) {
216224086Szack					error = EBADRPC;
217224086Szack					goto out;
218224086Szack				}
219191783Srmacklem				mbufcp = NFSMTOD(mp, caddr_t);
220191783Srmacklem				len = mbuf_len(mp);
221246213Skib				KASSERT(len > 0, ("len %d", len));
222191783Srmacklem			}
223191783Srmacklem			xfer = (left > len) ? len : left;
224191783Srmacklem#ifdef notdef
225191783Srmacklem			/* Not Yet.. */
226191783Srmacklem			if (uiop->uio_iov->iov_op != NULL)
227191783Srmacklem				(*(uiop->uio_iov->iov_op))
228191783Srmacklem				(mbufcp, uiocp, xfer);
229191783Srmacklem			else
230191783Srmacklem#endif
231191783Srmacklem			if (uiop->uio_segflg == UIO_SYSSPACE)
232191783Srmacklem				NFSBCOPY(mbufcp, uiocp, xfer);
233191783Srmacklem			else
234191783Srmacklem				copyout(mbufcp, CAST_USER_ADDR_T(uiocp), xfer);
235191783Srmacklem			left -= xfer;
236191783Srmacklem			len -= xfer;
237191783Srmacklem			mbufcp += xfer;
238191783Srmacklem			uiocp += xfer;
239191783Srmacklem			uiop->uio_offset += xfer;
240191783Srmacklem			uiop->uio_resid -= xfer;
241191783Srmacklem		}
242191783Srmacklem		if (uiop->uio_iov->iov_len <= siz) {
243191783Srmacklem			uiop->uio_iovcnt--;
244191783Srmacklem			uiop->uio_iov++;
245191783Srmacklem		} else {
246191783Srmacklem			uiop->uio_iov->iov_base = (void *)
247191783Srmacklem				((char *)uiop->uio_iov->iov_base + uiosiz);
248191783Srmacklem			uiop->uio_iov->iov_len -= uiosiz;
249191783Srmacklem		}
250191783Srmacklem		siz -= uiosiz;
251191783Srmacklem	}
252191783Srmacklem	nd->nd_dpos = mbufcp;
253191783Srmacklem	nd->nd_md = mp;
254191783Srmacklem	if (rem > 0) {
255191783Srmacklem		if (len < rem)
256191783Srmacklem			error = nfsm_advance(nd, rem, len);
257191783Srmacklem		else
258191783Srmacklem			nd->nd_dpos += rem;
259191783Srmacklem	}
260224086Szack
261224086Szackout:
262224086Szack	NFSEXITCODE2(error, nd);
263191783Srmacklem	return (error);
264191783Srmacklem}
265191783Srmacklem#endif	/* !APPLE */
266191783Srmacklem
267191783Srmacklem/*
268191783Srmacklem * Help break down an mbuf chain by setting the first siz bytes contiguous
269191783Srmacklem * pointed to by returned val.
270191783Srmacklem * This is used by the macro NFSM_DISSECT for tough
271191783Srmacklem * cases.
272191783Srmacklem */
273191783SrmacklemAPPLESTATIC void *
274249592Skennfsm_dissct(struct nfsrv_descript *nd, int siz, int how)
275191783Srmacklem{
276191783Srmacklem	mbuf_t mp2;
277191783Srmacklem	int siz2, xfer;
278191783Srmacklem	caddr_t p;
279191783Srmacklem	int left;
280191783Srmacklem	caddr_t retp;
281191783Srmacklem
282191783Srmacklem	retp = NULL;
283191783Srmacklem	left = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) - nd->nd_dpos;
284191783Srmacklem	while (left == 0) {
285191783Srmacklem		nd->nd_md = mbuf_next(nd->nd_md);
286191783Srmacklem		if (nd->nd_md == NULL)
287191783Srmacklem			return (retp);
288191783Srmacklem		left = mbuf_len(nd->nd_md);
289191783Srmacklem		nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
290191783Srmacklem	}
291191783Srmacklem	if (left >= siz) {
292191783Srmacklem		retp = nd->nd_dpos;
293191783Srmacklem		nd->nd_dpos += siz;
294191783Srmacklem	} else if (mbuf_next(nd->nd_md) == NULL) {
295191783Srmacklem		return (retp);
296191783Srmacklem	} else if (siz > ncl_mbuf_mhlen) {
297191783Srmacklem		panic("nfs S too big");
298191783Srmacklem	} else {
299249592Sken		MGET(mp2, MT_DATA, how);
300249592Sken		if (mp2 == NULL)
301249592Sken			return (NULL);
302191783Srmacklem		mbuf_setnext(mp2, mbuf_next(nd->nd_md));
303191783Srmacklem		mbuf_setnext(nd->nd_md, mp2);
304191783Srmacklem		mbuf_setlen(nd->nd_md, mbuf_len(nd->nd_md) - left);
305191783Srmacklem		nd->nd_md = mp2;
306191783Srmacklem		retp = p = NFSMTOD(mp2, caddr_t);
307191783Srmacklem		NFSBCOPY(nd->nd_dpos, p, left);	/* Copy what was left */
308191783Srmacklem		siz2 = siz - left;
309191783Srmacklem		p += left;
310191783Srmacklem		mp2 = mbuf_next(mp2);
311191783Srmacklem		/* Loop around copying up the siz2 bytes */
312191783Srmacklem		while (siz2 > 0) {
313191783Srmacklem			if (mp2 == NULL)
314191783Srmacklem				return (NULL);
315191783Srmacklem			xfer = (siz2 > mbuf_len(mp2)) ? mbuf_len(mp2) : siz2;
316191783Srmacklem			if (xfer > 0) {
317191783Srmacklem				NFSBCOPY(NFSMTOD(mp2, caddr_t), p, xfer);
318191783Srmacklem				NFSM_DATAP(mp2, xfer);
319191783Srmacklem				mbuf_setlen(mp2, mbuf_len(mp2) - xfer);
320191783Srmacklem				p += xfer;
321191783Srmacklem				siz2 -= xfer;
322191783Srmacklem			}
323191783Srmacklem			if (siz2 > 0)
324191783Srmacklem				mp2 = mbuf_next(mp2);
325191783Srmacklem		}
326191783Srmacklem		mbuf_setlen(nd->nd_md, siz);
327191783Srmacklem		nd->nd_md = mp2;
328191783Srmacklem		nd->nd_dpos = NFSMTOD(mp2, caddr_t);
329191783Srmacklem	}
330191783Srmacklem	return (retp);
331191783Srmacklem}
332191783Srmacklem
333191783Srmacklem/*
334191783Srmacklem * Advance the position in the mbuf chain.
335191783Srmacklem * If offs == 0, this is a no-op, but it is simpler to just return from
336191783Srmacklem * here than check for offs > 0 for all calls to nfsm_advance.
337191783Srmacklem * If left == -1, it should be calculated here.
338191783Srmacklem */
339191783SrmacklemAPPLESTATIC int
340191783Srmacklemnfsm_advance(struct nfsrv_descript *nd, int offs, int left)
341191783Srmacklem{
342224086Szack	int error = 0;
343191783Srmacklem
344191783Srmacklem	if (offs == 0)
345224086Szack		goto out;
346191783Srmacklem	/*
347191783Srmacklem	 * A negative offs should be considered a serious problem.
348191783Srmacklem	 */
349191783Srmacklem	if (offs < 0)
350191783Srmacklem		panic("nfsrv_advance");
351191783Srmacklem
352191783Srmacklem	/*
353191783Srmacklem	 * If left == -1, calculate it here.
354191783Srmacklem	 */
355191783Srmacklem	if (left == -1)
356191783Srmacklem		left = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) -
357191783Srmacklem		    nd->nd_dpos;
358191783Srmacklem
359191783Srmacklem	/*
360191783Srmacklem	 * Loop around, advancing over the mbuf data.
361191783Srmacklem	 */
362191783Srmacklem	while (offs > left) {
363191783Srmacklem		offs -= left;
364191783Srmacklem		nd->nd_md = mbuf_next(nd->nd_md);
365224086Szack		if (nd->nd_md == NULL) {
366224086Szack			error = EBADRPC;
367224086Szack			goto out;
368224086Szack		}
369191783Srmacklem		left = mbuf_len(nd->nd_md);
370191783Srmacklem		nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
371191783Srmacklem	}
372191783Srmacklem	nd->nd_dpos += offs;
373224086Szack
374224086Szackout:
375224086Szack	NFSEXITCODE(error);
376224086Szack	return (error);
377191783Srmacklem}
378191783Srmacklem
379191783Srmacklem/*
380191783Srmacklem * Copy a string into mbuf(s).
381191783Srmacklem * Return the number of bytes output, including XDR overheads.
382191783Srmacklem */
383191783SrmacklemAPPLESTATIC int
384191783Srmacklemnfsm_strtom(struct nfsrv_descript *nd, const char *cp, int siz)
385191783Srmacklem{
386191783Srmacklem	mbuf_t m2;
387191783Srmacklem	int xfer, left;
388191783Srmacklem	mbuf_t m1;
389191783Srmacklem	int rem, bytesize;
390191783Srmacklem	u_int32_t *tl;
391191783Srmacklem	char *cp2;
392191783Srmacklem
393191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
394191783Srmacklem	*tl = txdr_unsigned(siz);
395191783Srmacklem	rem = NFSM_RNDUP(siz) - siz;
396191783Srmacklem	bytesize = NFSX_UNSIGNED + siz + rem;
397191783Srmacklem	m2 = nd->nd_mb;
398191783Srmacklem	cp2 = nd->nd_bpos;
399191783Srmacklem	left = M_TRAILINGSPACE(m2);
400191783Srmacklem
401191783Srmacklem	/*
402191783Srmacklem	 * Loop around copying the string to mbuf(s).
403191783Srmacklem	 */
404191783Srmacklem	while (siz > 0) {
405191783Srmacklem		if (left == 0) {
406191783Srmacklem			if (siz > ncl_mbuf_mlen)
407243882Sglebius				NFSMCLGET(m1, M_WAITOK);
408191783Srmacklem			else
409191783Srmacklem				NFSMGET(m1);
410191783Srmacklem			mbuf_setlen(m1, 0);
411191783Srmacklem			mbuf_setnext(m2, m1);
412191783Srmacklem			m2 = m1;
413191783Srmacklem			cp2 = NFSMTOD(m2, caddr_t);
414191783Srmacklem			left = M_TRAILINGSPACE(m2);
415191783Srmacklem		}
416191783Srmacklem		if (left >= siz)
417191783Srmacklem			xfer = siz;
418191783Srmacklem		else
419191783Srmacklem			xfer = left;
420191783Srmacklem		NFSBCOPY(cp, cp2, xfer);
421191783Srmacklem		cp += xfer;
422191783Srmacklem		mbuf_setlen(m2, mbuf_len(m2) + xfer);
423191783Srmacklem		siz -= xfer;
424191783Srmacklem		left -= xfer;
425191783Srmacklem		if (siz == 0 && rem) {
426191783Srmacklem			if (left < rem)
427191783Srmacklem				panic("nfsm_strtom");
428191783Srmacklem			NFSBZERO(cp2 + xfer, rem);
429191783Srmacklem			mbuf_setlen(m2, mbuf_len(m2) + rem);
430191783Srmacklem		}
431191783Srmacklem	}
432191783Srmacklem	nd->nd_mb = m2;
433191783Srmacklem	nd->nd_bpos = NFSMTOD(m2, caddr_t) + mbuf_len(m2);
434191783Srmacklem	return (bytesize);
435191783Srmacklem}
436191783Srmacklem
437191783Srmacklem/*
438191783Srmacklem * Called once to initialize data structures...
439191783Srmacklem */
440191783SrmacklemAPPLESTATIC void
441191783Srmacklemnewnfs_init(void)
442191783Srmacklem{
443191783Srmacklem	static int nfs_inited = 0;
444191783Srmacklem
445191783Srmacklem	if (nfs_inited)
446191783Srmacklem		return;
447191783Srmacklem	nfs_inited = 1;
448191783Srmacklem
449191783Srmacklem	newnfs_true = txdr_unsigned(TRUE);
450191783Srmacklem	newnfs_false = txdr_unsigned(FALSE);
451191783Srmacklem	newnfs_xdrneg1 = txdr_unsigned(-1);
452191783Srmacklem	nfscl_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
453191783Srmacklem	if (nfscl_ticks < 1)
454191783Srmacklem		nfscl_ticks = 1;
455191783Srmacklem	NFSSETBOOTTIME(nfsboottime);
456191783Srmacklem
457191783Srmacklem	/*
458191783Srmacklem	 * Initialize reply list and start timer
459191783Srmacklem	 */
460191783Srmacklem	TAILQ_INIT(&nfsd_reqq);
461191783Srmacklem	NFS_TIMERINIT;
462191783Srmacklem}
463191783Srmacklem
464191783Srmacklem/*
465191783Srmacklem * Put a file handle in an mbuf list.
466191783Srmacklem * If the size argument == 0, just use the default size.
467191783Srmacklem * set_true == 1 if there should be an newnfs_true prepended on the file handle.
468191783Srmacklem * Return the number of bytes output, including XDR overhead.
469191783Srmacklem */
470191783SrmacklemAPPLESTATIC int
471191783Srmacklemnfsm_fhtom(struct nfsrv_descript *nd, u_int8_t *fhp, int size, int set_true)
472191783Srmacklem{
473191783Srmacklem	u_int32_t *tl;
474191783Srmacklem	u_int8_t *cp;
475191783Srmacklem	int fullsiz, rem, bytesize = 0;
476191783Srmacklem
477191783Srmacklem	if (size == 0)
478191783Srmacklem		size = NFSX_MYFH;
479191783Srmacklem	switch (nd->nd_flag & (ND_NFSV2 | ND_NFSV3 | ND_NFSV4)) {
480191783Srmacklem	case ND_NFSV2:
481191783Srmacklem		if (size > NFSX_V2FH)
482191783Srmacklem			panic("fh size > NFSX_V2FH for NFSv2");
483191783Srmacklem		NFSM_BUILD(cp, u_int8_t *, NFSX_V2FH);
484191783Srmacklem		NFSBCOPY(fhp, cp, size);
485191783Srmacklem		if (size < NFSX_V2FH)
486191783Srmacklem			NFSBZERO(cp + size, NFSX_V2FH - size);
487191783Srmacklem		bytesize = NFSX_V2FH;
488191783Srmacklem		break;
489191783Srmacklem	case ND_NFSV3:
490191783Srmacklem	case ND_NFSV4:
491191783Srmacklem		fullsiz = NFSM_RNDUP(size);
492191783Srmacklem		rem = fullsiz - size;
493191783Srmacklem		if (set_true) {
494191783Srmacklem		    bytesize = 2 * NFSX_UNSIGNED + fullsiz;
495191783Srmacklem		    NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
496191783Srmacklem		    *tl = newnfs_true;
497191783Srmacklem		} else {
498191783Srmacklem		    bytesize = NFSX_UNSIGNED + fullsiz;
499191783Srmacklem		}
500191783Srmacklem		(void) nfsm_strtom(nd, fhp, size);
501191783Srmacklem		break;
502191783Srmacklem	};
503191783Srmacklem	return (bytesize);
504191783Srmacklem}
505191783Srmacklem
506191783Srmacklem/*
507191783Srmacklem * This function compares two net addresses by family and returns TRUE
508191783Srmacklem * if they are the same host.
509191783Srmacklem * If there is any doubt, return FALSE.
510191783Srmacklem * The AF_INET family is handled as a special case so that address mbufs
511191783Srmacklem * don't need to be saved to store "struct in_addr", which is only 4 bytes.
512191783Srmacklem */
513191783SrmacklemAPPLESTATIC int
514191783Srmacklemnfsaddr_match(int family, union nethostaddr *haddr, NFSSOCKADDR_T nam)
515191783Srmacklem{
516191783Srmacklem	struct sockaddr_in *inetaddr;
517191783Srmacklem
518191783Srmacklem	switch (family) {
519191783Srmacklem	case AF_INET:
520191783Srmacklem		inetaddr = NFSSOCKADDR(nam, struct sockaddr_in *);
521191783Srmacklem		if (inetaddr->sin_family == AF_INET &&
522191783Srmacklem		    inetaddr->sin_addr.s_addr == haddr->had_inet.s_addr)
523191783Srmacklem			return (1);
524191783Srmacklem		break;
525191783Srmacklem#ifdef INET6
526191783Srmacklem	case AF_INET6:
527191783Srmacklem		{
528191783Srmacklem		struct sockaddr_in6 *inetaddr6;
529191783Srmacklem
530191783Srmacklem		inetaddr6 = NFSSOCKADDR(nam, struct sockaddr_in6 *);
531191783Srmacklem		/* XXX - should test sin6_scope_id ? */
532191783Srmacklem		if (inetaddr6->sin6_family == AF_INET6 &&
533191783Srmacklem		    IN6_ARE_ADDR_EQUAL(&inetaddr6->sin6_addr,
534191783Srmacklem			  &haddr->had_inet6))
535191783Srmacklem			return (1);
536191783Srmacklem		}
537191783Srmacklem		break;
538191783Srmacklem#endif
539191783Srmacklem	};
540191783Srmacklem	return (0);
541191783Srmacklem}
542191783Srmacklem
543191783Srmacklem/*
544191783Srmacklem * Similar to the above, but takes to NFSSOCKADDR_T args.
545191783Srmacklem */
546191783SrmacklemAPPLESTATIC int
547191783Srmacklemnfsaddr2_match(NFSSOCKADDR_T nam1, NFSSOCKADDR_T nam2)
548191783Srmacklem{
549191783Srmacklem	struct sockaddr_in *addr1, *addr2;
550191783Srmacklem	struct sockaddr *inaddr;
551191783Srmacklem
552191783Srmacklem	inaddr = NFSSOCKADDR(nam1, struct sockaddr *);
553191783Srmacklem	switch (inaddr->sa_family) {
554191783Srmacklem	case AF_INET:
555191783Srmacklem		addr1 = NFSSOCKADDR(nam1, struct sockaddr_in *);
556191783Srmacklem		addr2 = NFSSOCKADDR(nam2, struct sockaddr_in *);
557191783Srmacklem		if (addr2->sin_family == AF_INET &&
558191783Srmacklem		    addr1->sin_addr.s_addr == addr2->sin_addr.s_addr)
559191783Srmacklem			return (1);
560191783Srmacklem		break;
561191783Srmacklem#ifdef INET6
562191783Srmacklem	case AF_INET6:
563191783Srmacklem		{
564191783Srmacklem		struct sockaddr_in6 *inet6addr1, *inet6addr2;
565191783Srmacklem
566191783Srmacklem		inet6addr1 = NFSSOCKADDR(nam1, struct sockaddr_in6 *);
567191783Srmacklem		inet6addr2 = NFSSOCKADDR(nam2, struct sockaddr_in6 *);
568191783Srmacklem		/* XXX - should test sin6_scope_id ? */
569191783Srmacklem		if (inet6addr2->sin6_family == AF_INET6 &&
570191783Srmacklem		    IN6_ARE_ADDR_EQUAL(&inet6addr1->sin6_addr,
571191783Srmacklem			  &inet6addr2->sin6_addr))
572191783Srmacklem			return (1);
573191783Srmacklem		}
574191783Srmacklem		break;
575191783Srmacklem#endif
576191783Srmacklem	};
577191783Srmacklem	return (0);
578191783Srmacklem}
579191783Srmacklem
580191783Srmacklem
581191783Srmacklem/*
582191783Srmacklem * Trim the stuff already dissected off the mbuf list.
583191783Srmacklem */
584191783SrmacklemAPPLESTATIC void
585191783Srmacklemnewnfs_trimleading(nd)
586191783Srmacklem	struct nfsrv_descript *nd;
587191783Srmacklem{
588191783Srmacklem	mbuf_t m, n;
589191783Srmacklem	int offs;
590191783Srmacklem
591191783Srmacklem	/*
592191783Srmacklem	 * First, free up leading mbufs.
593191783Srmacklem	 */
594191783Srmacklem	if (nd->nd_mrep != nd->nd_md) {
595191783Srmacklem		m = nd->nd_mrep;
596191783Srmacklem		while (mbuf_next(m) != nd->nd_md) {
597191783Srmacklem			if (mbuf_next(m) == NULL)
598191783Srmacklem				panic("nfsm trim leading");
599191783Srmacklem			m = mbuf_next(m);
600191783Srmacklem		}
601191783Srmacklem		mbuf_setnext(m, NULL);
602191783Srmacklem		mbuf_freem(nd->nd_mrep);
603191783Srmacklem	}
604191783Srmacklem	m = nd->nd_md;
605191783Srmacklem
606191783Srmacklem	/*
607191783Srmacklem	 * Now, adjust this mbuf, based on nd_dpos.
608191783Srmacklem	 */
609191783Srmacklem	offs = nd->nd_dpos - NFSMTOD(m, caddr_t);
610191783Srmacklem	if (offs == mbuf_len(m)) {
611191783Srmacklem		n = m;
612191783Srmacklem		m = mbuf_next(m);
613191783Srmacklem		if (m == NULL)
614191783Srmacklem			panic("nfsm trim leading2");
615191783Srmacklem		mbuf_setnext(n, NULL);
616191783Srmacklem		mbuf_freem(n);
617191783Srmacklem	} else if (offs > 0) {
618191783Srmacklem		mbuf_setlen(m, mbuf_len(m) - offs);
619191783Srmacklem		NFSM_DATAP(m, offs);
620191783Srmacklem	} else if (offs < 0)
621191783Srmacklem		panic("nfsm trimleading offs");
622191783Srmacklem	nd->nd_mrep = m;
623191783Srmacklem	nd->nd_md = m;
624191783Srmacklem	nd->nd_dpos = NFSMTOD(m, caddr_t);
625191783Srmacklem}
626191783Srmacklem
627191783Srmacklem/*
628191783Srmacklem * Trim trailing data off the mbuf list being built.
629191783Srmacklem */
630191783SrmacklemAPPLESTATIC void
631191783Srmacklemnewnfs_trimtrailing(nd, mb, bpos)
632191783Srmacklem	struct nfsrv_descript *nd;
633191783Srmacklem	mbuf_t mb;
634191783Srmacklem	caddr_t bpos;
635191783Srmacklem{
636191783Srmacklem
637191783Srmacklem	if (mbuf_next(mb)) {
638191783Srmacklem		mbuf_freem(mbuf_next(mb));
639191783Srmacklem		mbuf_setnext(mb, NULL);
640191783Srmacklem	}
641191783Srmacklem	mbuf_setlen(mb, bpos - NFSMTOD(mb, caddr_t));
642191783Srmacklem	nd->nd_mb = mb;
643191783Srmacklem	nd->nd_bpos = bpos;
644191783Srmacklem}
645191783Srmacklem
646191783Srmacklem/*
647191783Srmacklem * Dissect a file handle on the client.
648191783Srmacklem */
649191783SrmacklemAPPLESTATIC int
650191783Srmacklemnfsm_getfh(struct nfsrv_descript *nd, struct nfsfh **nfhpp)
651191783Srmacklem{
652191783Srmacklem	u_int32_t *tl;
653191783Srmacklem	struct nfsfh *nfhp;
654191783Srmacklem	int error, len;
655191783Srmacklem
656191783Srmacklem	*nfhpp = NULL;
657191783Srmacklem	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
658191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
659191783Srmacklem		if ((len = fxdr_unsigned(int, *tl)) <= 0 ||
660224086Szack			len > NFSX_FHMAX) {
661224086Szack			error = EBADRPC;
662224086Szack			goto nfsmout;
663224086Szack		}
664191783Srmacklem	} else
665191783Srmacklem		len = NFSX_V2FH;
666191783Srmacklem	MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) + len,
667191783Srmacklem	    M_NFSFH, M_WAITOK);
668191783Srmacklem	error = nfsrv_mtostr(nd, nfhp->nfh_fh, len);
669191783Srmacklem	if (error) {
670191783Srmacklem		FREE((caddr_t)nfhp, M_NFSFH);
671224086Szack		goto nfsmout;
672191783Srmacklem	}
673191783Srmacklem	nfhp->nfh_len = len;
674191783Srmacklem	*nfhpp = nfhp;
675191783Srmacklemnfsmout:
676224086Szack	NFSEXITCODE2(error, nd);
677191783Srmacklem	return (error);
678191783Srmacklem}
679191783Srmacklem
680191783Srmacklem/*
681191783Srmacklem * Break down the nfsv4 acl.
682191783Srmacklem * If the aclp == NULL or won't fit in an acl, just discard the acl info.
683191783Srmacklem */
684191783SrmacklemAPPLESTATIC int
685191783Srmacklemnfsrv_dissectacl(struct nfsrv_descript *nd, NFSACL_T *aclp, int *aclerrp,
686191783Srmacklem    int *aclsizep, __unused NFSPROC_T *p)
687191783Srmacklem{
688191783Srmacklem	u_int32_t *tl;
689191783Srmacklem	int i, aclsize;
690191783Srmacklem	int acecnt, error = 0, aceerr = 0, acesize;
691191783Srmacklem
692191783Srmacklem	*aclerrp = 0;
693191783Srmacklem	if (aclp)
694191783Srmacklem		aclp->acl_cnt = 0;
695191783Srmacklem	/*
696191783Srmacklem	 * Parse out the ace entries and expect them to conform to
697191783Srmacklem	 * what can be supported by R/W/X bits.
698191783Srmacklem	 */
699191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
700191783Srmacklem	aclsize = NFSX_UNSIGNED;
701191783Srmacklem	acecnt = fxdr_unsigned(int, *tl);
702191783Srmacklem	if (acecnt > ACL_MAX_ENTRIES)
703224077Szack		aceerr = NFSERR_ATTRNOTSUPP;
704191783Srmacklem	if (nfsrv_useacl == 0)
705224077Szack		aceerr = NFSERR_ATTRNOTSUPP;
706191783Srmacklem	for (i = 0; i < acecnt; i++) {
707191783Srmacklem		if (aclp && !aceerr)
708191783Srmacklem			error = nfsrv_dissectace(nd, &aclp->acl_entry[i],
709191783Srmacklem			    &aceerr, &acesize, p);
710191783Srmacklem		else
711191783Srmacklem			error = nfsrv_skipace(nd, &acesize);
712191783Srmacklem		if (error)
713224086Szack			goto nfsmout;
714191783Srmacklem		aclsize += acesize;
715191783Srmacklem	}
716191783Srmacklem	if (aclp && !aceerr)
717191783Srmacklem		aclp->acl_cnt = acecnt;
718191783Srmacklem	if (aceerr)
719191783Srmacklem		*aclerrp = aceerr;
720191783Srmacklem	if (aclsizep)
721191783Srmacklem		*aclsizep = aclsize;
722191783Srmacklemnfsmout:
723224086Szack	NFSEXITCODE2(error, nd);
724191783Srmacklem	return (error);
725191783Srmacklem}
726191783Srmacklem
727191783Srmacklem/*
728191783Srmacklem * Skip over an NFSv4 ace entry. Just dissect the xdr and discard it.
729191783Srmacklem */
730191783Srmacklemstatic int
731191783Srmacklemnfsrv_skipace(struct nfsrv_descript *nd, int *acesizep)
732191783Srmacklem{
733191783Srmacklem	u_int32_t *tl;
734191783Srmacklem	int error, len = 0;
735191783Srmacklem
736191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
737191783Srmacklem	len = fxdr_unsigned(int, *(tl + 3));
738191783Srmacklem	error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
739191783Srmacklemnfsmout:
740191783Srmacklem	*acesizep = NFSM_RNDUP(len) + (4 * NFSX_UNSIGNED);
741224086Szack	NFSEXITCODE2(error, nd);
742191783Srmacklem	return (error);
743191783Srmacklem}
744191783Srmacklem
745191783Srmacklem/*
746191783Srmacklem * Get attribute bits from an mbuf list.
747191783Srmacklem * Returns EBADRPC for a parsing error, 0 otherwise.
748191783Srmacklem * If the clearinvalid flag is set, clear the bits not supported.
749191783Srmacklem */
750191783SrmacklemAPPLESTATIC int
751191783Srmacklemnfsrv_getattrbits(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp, int *cntp,
752191783Srmacklem    int *retnotsupp)
753191783Srmacklem{
754191783Srmacklem	u_int32_t *tl;
755191783Srmacklem	int cnt, i, outcnt;
756191783Srmacklem	int error = 0;
757191783Srmacklem
758191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
759191783Srmacklem	cnt = fxdr_unsigned(int, *tl);
760224086Szack	if (cnt < 0) {
761224086Szack		error = NFSERR_BADXDR;
762224086Szack		goto nfsmout;
763224086Szack	}
764253506Srmacklem	if (cnt > NFSATTRBIT_MAXWORDS)
765191783Srmacklem		outcnt = NFSATTRBIT_MAXWORDS;
766253506Srmacklem	else
767191783Srmacklem		outcnt = cnt;
768191783Srmacklem	NFSZERO_ATTRBIT(attrbitp);
769191783Srmacklem	if (outcnt > 0) {
770191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, outcnt * NFSX_UNSIGNED);
771191783Srmacklem		for (i = 0; i < outcnt; i++)
772191783Srmacklem			attrbitp->bits[i] = fxdr_unsigned(u_int32_t, *tl++);
773191783Srmacklem	}
774253506Srmacklem	for (i = 0; i < (cnt - outcnt); i++) {
775253506Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
776253506Srmacklem		if (retnotsupp != NULL && *tl != 0)
777253506Srmacklem			*retnotsupp = NFSERR_ATTRNOTSUPP;
778253506Srmacklem	}
779191783Srmacklem	if (cntp)
780191783Srmacklem		*cntp = NFSX_UNSIGNED + (cnt * NFSX_UNSIGNED);
781191783Srmacklemnfsmout:
782224086Szack	NFSEXITCODE2(error, nd);
783191783Srmacklem	return (error);
784191783Srmacklem}
785191783Srmacklem
786191783Srmacklem/*
787191783Srmacklem * Get the attributes for V4.
788191783Srmacklem * If the compare flag is true, test for any attribute changes,
789191783Srmacklem * otherwise return the attribute values.
790191783Srmacklem * These attributes cover fields in "struct vattr", "struct statfs",
791191783Srmacklem * "struct nfsfsinfo", the file handle and the lease duration.
792191783Srmacklem * The value of retcmpp is set to 1 if all attributes are the same,
793191783Srmacklem * and 0 otherwise.
794191783Srmacklem * Returns EBADRPC if it can't be parsed, 0 otherwise.
795191783Srmacklem */
796191783SrmacklemAPPLESTATIC int
797191783Srmacklemnfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
798191783Srmacklem    struct nfsvattr *nap, struct nfsfh **nfhpp, fhandle_t *fhp, int fhsize,
799191783Srmacklem    struct nfsv3_pathconf *pc, struct statfs *sbp, struct nfsstatfs *sfp,
800191783Srmacklem    struct nfsfsinfo *fsp, NFSACL_T *aclp, int compare, int *retcmpp,
801191783Srmacklem    u_int32_t *leasep, u_int32_t *rderrp, NFSPROC_T *p, struct ucred *cred)
802191783Srmacklem{
803191783Srmacklem	u_int32_t *tl;
804224086Szack	int i = 0, j, k, l = 0, m, bitpos, attrsum = 0;
805191783Srmacklem	int error, tfhsize, aceerr, attrsize, cnt, retnotsup;
806191783Srmacklem	u_char *cp, *cp2, namestr[NFSV4_SMALLSTR + 1];
807191783Srmacklem	nfsattrbit_t attrbits, retattrbits, checkattrbits;
808191783Srmacklem	struct nfsfh *tnfhp;
809191783Srmacklem	struct nfsreferral *refp;
810191783Srmacklem	u_quad_t tquad;
811191783Srmacklem	nfsquad_t tnfsquad;
812191783Srmacklem	struct timespec temptime;
813191783Srmacklem	uid_t uid;
814191783Srmacklem	gid_t gid;
815191783Srmacklem	long fid;
816191783Srmacklem	u_int32_t freenum = 0, tuint;
817191783Srmacklem	u_int64_t uquad = 0, thyp, thyp2;
818191783Srmacklem#ifdef QUOTA
819191783Srmacklem	struct dqblk dqb;
820191783Srmacklem	uid_t savuid;
821191783Srmacklem#endif
822191783Srmacklem
823191783Srmacklem	if (compare) {
824191783Srmacklem		retnotsup = 0;
825191783Srmacklem		error = nfsrv_getattrbits(nd, &attrbits, NULL, &retnotsup);
826191783Srmacklem	} else {
827191783Srmacklem		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
828191783Srmacklem	}
829191783Srmacklem	if (error)
830224086Szack		goto nfsmout;
831191783Srmacklem
832191783Srmacklem	if (compare) {
833191783Srmacklem		*retcmpp = retnotsup;
834191783Srmacklem	} else {
835191783Srmacklem		/*
836191783Srmacklem		 * Just set default values to some of the important ones.
837191783Srmacklem		 */
838191783Srmacklem		if (nap != NULL) {
839191783Srmacklem			nap->na_type = VREG;
840191783Srmacklem			nap->na_mode = 0;
841191783Srmacklem			nap->na_rdev = (NFSDEV_T)0;
842191783Srmacklem			nap->na_mtime.tv_sec = 0;
843191783Srmacklem			nap->na_mtime.tv_nsec = 0;
844191783Srmacklem			nap->na_gen = 0;
845191783Srmacklem			nap->na_flags = 0;
846191783Srmacklem			nap->na_blocksize = NFS_FABLKSIZE;
847191783Srmacklem		}
848191783Srmacklem		if (sbp != NULL) {
849191783Srmacklem			sbp->f_bsize = NFS_FABLKSIZE;
850191783Srmacklem			sbp->f_blocks = 0;
851191783Srmacklem			sbp->f_bfree = 0;
852191783Srmacklem			sbp->f_bavail = 0;
853191783Srmacklem			sbp->f_files = 0;
854191783Srmacklem			sbp->f_ffree = 0;
855191783Srmacklem		}
856191783Srmacklem		if (fsp != NULL) {
857191783Srmacklem			fsp->fs_rtmax = 8192;
858191783Srmacklem			fsp->fs_rtpref = 8192;
859191783Srmacklem			fsp->fs_maxname = NFS_MAXNAMLEN;
860191783Srmacklem			fsp->fs_wtmax = 8192;
861191783Srmacklem			fsp->fs_wtpref = 8192;
862191783Srmacklem			fsp->fs_wtmult = NFS_FABLKSIZE;
863191783Srmacklem			fsp->fs_dtpref = 8192;
864191783Srmacklem			fsp->fs_maxfilesize = 0xffffffffffffffffull;
865191783Srmacklem			fsp->fs_timedelta.tv_sec = 0;
866191783Srmacklem			fsp->fs_timedelta.tv_nsec = 1;
867191783Srmacklem			fsp->fs_properties = (NFSV3_FSFLINK | NFSV3_FSFSYMLINK |
868191783Srmacklem				NFSV3_FSFHOMOGENEOUS | NFSV3_FSFCANSETTIME);
869191783Srmacklem		}
870191783Srmacklem		if (pc != NULL) {
871191783Srmacklem			pc->pc_linkmax = LINK_MAX;
872191783Srmacklem			pc->pc_namemax = NAME_MAX;
873191783Srmacklem			pc->pc_notrunc = 0;
874191783Srmacklem			pc->pc_chownrestricted = 0;
875191783Srmacklem			pc->pc_caseinsensitive = 0;
876191783Srmacklem			pc->pc_casepreserving = 1;
877191783Srmacklem		}
878191783Srmacklem	}
879191783Srmacklem
880191783Srmacklem	/*
881191783Srmacklem	 * Loop around getting the attributes.
882191783Srmacklem	 */
883191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
884191783Srmacklem	attrsize = fxdr_unsigned(int, *tl);
885191783Srmacklem	for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
886191783Srmacklem	    if (attrsum > attrsize) {
887191783Srmacklem		error = NFSERR_BADXDR;
888191783Srmacklem		goto nfsmout;
889191783Srmacklem	    }
890191783Srmacklem	    if (NFSISSET_ATTRBIT(&attrbits, bitpos))
891191783Srmacklem		switch (bitpos) {
892191783Srmacklem		case NFSATTRBIT_SUPPORTEDATTRS:
893191783Srmacklem			retnotsup = 0;
894191783Srmacklem			if (compare || nap == NULL)
895191783Srmacklem			    error = nfsrv_getattrbits(nd, &retattrbits,
896191783Srmacklem				&cnt, &retnotsup);
897191783Srmacklem			else
898191783Srmacklem			    error = nfsrv_getattrbits(nd, &nap->na_suppattr,
899191783Srmacklem				&cnt, &retnotsup);
900191783Srmacklem			if (error)
901224086Szack			    goto nfsmout;
902191783Srmacklem			if (compare && !(*retcmpp)) {
903191783Srmacklem			   NFSSETSUPP_ATTRBIT(&checkattrbits);
904191783Srmacklem			   if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
905191783Srmacklem			       || retnotsup)
906191783Srmacklem				*retcmpp = NFSERR_NOTSAME;
907191783Srmacklem			}
908191783Srmacklem			attrsum += cnt;
909191783Srmacklem			break;
910191783Srmacklem		case NFSATTRBIT_TYPE:
911191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
912191783Srmacklem			if (compare) {
913191783Srmacklem				if (!(*retcmpp)) {
914191783Srmacklem				    if (nap->na_type != nfsv34tov_type(*tl))
915191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
916191783Srmacklem				}
917191783Srmacklem			} else if (nap != NULL) {
918191783Srmacklem				nap->na_type = nfsv34tov_type(*tl);
919191783Srmacklem			}
920191783Srmacklem			attrsum += NFSX_UNSIGNED;
921191783Srmacklem			break;
922191783Srmacklem		case NFSATTRBIT_FHEXPIRETYPE:
923191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
924191783Srmacklem			if (compare && !(*retcmpp)) {
925191783Srmacklem				if (fxdr_unsigned(int, *tl) !=
926191783Srmacklem					NFSV4FHTYPE_PERSISTENT)
927191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
928191783Srmacklem			}
929191783Srmacklem			attrsum += NFSX_UNSIGNED;
930191783Srmacklem			break;
931191783Srmacklem		case NFSATTRBIT_CHANGE:
932191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
933191783Srmacklem			if (compare) {
934191783Srmacklem				if (!(*retcmpp)) {
935191783Srmacklem				    if (nap->na_filerev != fxdr_hyper(tl))
936191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
937191783Srmacklem				}
938191783Srmacklem			} else if (nap != NULL) {
939191783Srmacklem				nap->na_filerev = fxdr_hyper(tl);
940191783Srmacklem			}
941191783Srmacklem			attrsum += NFSX_HYPER;
942191783Srmacklem			break;
943191783Srmacklem		case NFSATTRBIT_SIZE:
944191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
945191783Srmacklem			if (compare) {
946191783Srmacklem				if (!(*retcmpp)) {
947191783Srmacklem				    if (nap->na_size != fxdr_hyper(tl))
948191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
949191783Srmacklem				}
950191783Srmacklem			} else if (nap != NULL) {
951191783Srmacklem				nap->na_size = fxdr_hyper(tl);
952191783Srmacklem			}
953191783Srmacklem			attrsum += NFSX_HYPER;
954191783Srmacklem			break;
955191783Srmacklem		case NFSATTRBIT_LINKSUPPORT:
956191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
957191783Srmacklem			if (compare) {
958191783Srmacklem				if (!(*retcmpp)) {
959191783Srmacklem				    if (fsp->fs_properties & NFSV3_FSFLINK) {
960191783Srmacklem					if (*tl == newnfs_false)
961191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
962191783Srmacklem				    } else {
963191783Srmacklem					if (*tl == newnfs_true)
964191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
965191783Srmacklem				    }
966191783Srmacklem				}
967191783Srmacklem			} else if (fsp != NULL) {
968191783Srmacklem				if (*tl == newnfs_true)
969191783Srmacklem					fsp->fs_properties |= NFSV3_FSFLINK;
970191783Srmacklem				else
971191783Srmacklem					fsp->fs_properties &= ~NFSV3_FSFLINK;
972191783Srmacklem			}
973191783Srmacklem			attrsum += NFSX_UNSIGNED;
974191783Srmacklem			break;
975191783Srmacklem		case NFSATTRBIT_SYMLINKSUPPORT:
976191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
977191783Srmacklem			if (compare) {
978191783Srmacklem				if (!(*retcmpp)) {
979191783Srmacklem				    if (fsp->fs_properties & NFSV3_FSFSYMLINK) {
980191783Srmacklem					if (*tl == newnfs_false)
981191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
982191783Srmacklem				    } else {
983191783Srmacklem					if (*tl == newnfs_true)
984191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
985191783Srmacklem				    }
986191783Srmacklem				}
987191783Srmacklem			} else if (fsp != NULL) {
988191783Srmacklem				if (*tl == newnfs_true)
989191783Srmacklem					fsp->fs_properties |= NFSV3_FSFSYMLINK;
990191783Srmacklem				else
991191783Srmacklem					fsp->fs_properties &= ~NFSV3_FSFSYMLINK;
992191783Srmacklem			}
993191783Srmacklem			attrsum += NFSX_UNSIGNED;
994191783Srmacklem			break;
995191783Srmacklem		case NFSATTRBIT_NAMEDATTR:
996191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
997191783Srmacklem			if (compare && !(*retcmpp)) {
998191783Srmacklem				if (*tl != newnfs_false)
999191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1000191783Srmacklem			}
1001191783Srmacklem			attrsum += NFSX_UNSIGNED;
1002191783Srmacklem			break;
1003191783Srmacklem		case NFSATTRBIT_FSID:
1004191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
1005191783Srmacklem			thyp = fxdr_hyper(tl);
1006191783Srmacklem			tl += 2;
1007191783Srmacklem			thyp2 = fxdr_hyper(tl);
1008191783Srmacklem			if (compare) {
1009191783Srmacklem			    if (*retcmpp == 0) {
1010191783Srmacklem				if (thyp != (u_int64_t)
1011191783Srmacklem				    vfs_statfs(vnode_mount(vp))->f_fsid.val[0] ||
1012191783Srmacklem				    thyp2 != (u_int64_t)
1013191783Srmacklem				    vfs_statfs(vnode_mount(vp))->f_fsid.val[1])
1014191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1015191783Srmacklem			    }
1016191783Srmacklem			} else if (nap != NULL) {
1017191783Srmacklem				nap->na_filesid[0] = thyp;
1018191783Srmacklem				nap->na_filesid[1] = thyp2;
1019191783Srmacklem			}
1020191783Srmacklem			attrsum += (4 * NFSX_UNSIGNED);
1021191783Srmacklem			break;
1022191783Srmacklem		case NFSATTRBIT_UNIQUEHANDLES:
1023191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1024191783Srmacklem			if (compare && !(*retcmpp)) {
1025191783Srmacklem				if (*tl != newnfs_true)
1026191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1027191783Srmacklem			}
1028191783Srmacklem			attrsum += NFSX_UNSIGNED;
1029191783Srmacklem			break;
1030191783Srmacklem		case NFSATTRBIT_LEASETIME:
1031191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1032191783Srmacklem			if (compare) {
1033191783Srmacklem				if (fxdr_unsigned(int, *tl) != nfsrv_lease &&
1034191783Srmacklem				    !(*retcmpp))
1035191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1036191783Srmacklem			} else if (leasep != NULL) {
1037191783Srmacklem				*leasep = fxdr_unsigned(u_int32_t, *tl);
1038191783Srmacklem			}
1039191783Srmacklem			attrsum += NFSX_UNSIGNED;
1040191783Srmacklem			break;
1041191783Srmacklem		case NFSATTRBIT_RDATTRERROR:
1042191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1043191783Srmacklem			if (compare) {
1044191783Srmacklem				 if (!(*retcmpp))
1045191783Srmacklem					*retcmpp = NFSERR_INVAL;
1046191783Srmacklem			} else if (rderrp != NULL) {
1047191783Srmacklem				*rderrp = fxdr_unsigned(u_int32_t, *tl);
1048191783Srmacklem			}
1049191783Srmacklem			attrsum += NFSX_UNSIGNED;
1050191783Srmacklem			break;
1051191783Srmacklem		case NFSATTRBIT_ACL:
1052191783Srmacklem			if (compare) {
1053191783Srmacklem			  if (!(*retcmpp)) {
1054191783Srmacklem			    if (nfsrv_useacl) {
1055191783Srmacklem				NFSACL_T *naclp;
1056191783Srmacklem
1057192861Srmacklem				naclp = acl_alloc(M_WAITOK);
1058191783Srmacklem				error = nfsrv_dissectacl(nd, naclp, &aceerr,
1059191783Srmacklem				    &cnt, p);
1060191783Srmacklem				if (error) {
1061191783Srmacklem				    acl_free(naclp);
1062224086Szack				    goto nfsmout;
1063191783Srmacklem				}
1064224121Szack				if (aceerr || aclp == NULL ||
1065224121Szack				    nfsrv_compareacl(aclp, naclp))
1066191783Srmacklem				    *retcmpp = NFSERR_NOTSAME;
1067191783Srmacklem				acl_free(naclp);
1068200069Strasz			    } else {
1069191783Srmacklem				error = nfsrv_dissectacl(nd, NULL, &aceerr,
1070191783Srmacklem				    &cnt, p);
1071191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1072191783Srmacklem			    }
1073191783Srmacklem			  }
1074191783Srmacklem			} else {
1075191783Srmacklem			    if (vp != NULL && aclp != NULL)
1076191783Srmacklem				error = nfsrv_dissectacl(nd, aclp, &aceerr,
1077191783Srmacklem				    &cnt, p);
1078191783Srmacklem			    else
1079191783Srmacklem				error = nfsrv_dissectacl(nd, NULL, &aceerr,
1080191783Srmacklem				    &cnt, p);
1081191783Srmacklem			    if (error)
1082224086Szack				goto nfsmout;
1083191783Srmacklem			}
1084191783Srmacklem			attrsum += cnt;
1085191783Srmacklem			break;
1086191783Srmacklem		case NFSATTRBIT_ACLSUPPORT:
1087191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1088191783Srmacklem			if (compare && !(*retcmpp)) {
1089191783Srmacklem				if (nfsrv_useacl) {
1090191783Srmacklem					if (fxdr_unsigned(u_int32_t, *tl) !=
1091191783Srmacklem					    NFSV4ACE_SUPTYPES)
1092191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1093191783Srmacklem				} else {
1094191783Srmacklem					*retcmpp = NFSERR_ATTRNOTSUPP;
1095191783Srmacklem				}
1096191783Srmacklem			}
1097191783Srmacklem			attrsum += NFSX_UNSIGNED;
1098191783Srmacklem			break;
1099191783Srmacklem		case NFSATTRBIT_ARCHIVE:
1100191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1101191783Srmacklem			if (compare && !(*retcmpp))
1102191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1103191783Srmacklem			attrsum += NFSX_UNSIGNED;
1104191783Srmacklem			break;
1105191783Srmacklem		case NFSATTRBIT_CANSETTIME:
1106191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1107191783Srmacklem			if (compare) {
1108191783Srmacklem				if (!(*retcmpp)) {
1109191783Srmacklem				    if (fsp->fs_properties & NFSV3_FSFCANSETTIME) {
1110191783Srmacklem					if (*tl == newnfs_false)
1111191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1112191783Srmacklem				    } else {
1113191783Srmacklem					if (*tl == newnfs_true)
1114191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1115191783Srmacklem				    }
1116191783Srmacklem				}
1117191783Srmacklem			} else if (fsp != NULL) {
1118191783Srmacklem				if (*tl == newnfs_true)
1119191783Srmacklem					fsp->fs_properties |= NFSV3_FSFCANSETTIME;
1120191783Srmacklem				else
1121191783Srmacklem					fsp->fs_properties &= ~NFSV3_FSFCANSETTIME;
1122191783Srmacklem			}
1123191783Srmacklem			attrsum += NFSX_UNSIGNED;
1124191783Srmacklem			break;
1125191783Srmacklem		case NFSATTRBIT_CASEINSENSITIVE:
1126191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1127191783Srmacklem			if (compare) {
1128191783Srmacklem				if (!(*retcmpp)) {
1129191783Srmacklem				    if (*tl != newnfs_false)
1130191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1131191783Srmacklem				}
1132191783Srmacklem			} else if (pc != NULL) {
1133191783Srmacklem				pc->pc_caseinsensitive =
1134191783Srmacklem				    fxdr_unsigned(u_int32_t, *tl);
1135191783Srmacklem			}
1136191783Srmacklem			attrsum += NFSX_UNSIGNED;
1137191783Srmacklem			break;
1138191783Srmacklem		case NFSATTRBIT_CASEPRESERVING:
1139191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1140191783Srmacklem			if (compare) {
1141191783Srmacklem				if (!(*retcmpp)) {
1142191783Srmacklem				    if (*tl != newnfs_true)
1143191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1144191783Srmacklem				}
1145191783Srmacklem			} else if (pc != NULL) {
1146191783Srmacklem				pc->pc_casepreserving =
1147191783Srmacklem				    fxdr_unsigned(u_int32_t, *tl);
1148191783Srmacklem			}
1149191783Srmacklem			attrsum += NFSX_UNSIGNED;
1150191783Srmacklem			break;
1151191783Srmacklem		case NFSATTRBIT_CHOWNRESTRICTED:
1152191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1153191783Srmacklem			if (compare) {
1154191783Srmacklem				if (!(*retcmpp)) {
1155224121Szack				    if (*tl != newnfs_true)
1156224121Szack					*retcmpp = NFSERR_NOTSAME;
1157191783Srmacklem				}
1158191783Srmacklem			} else if (pc != NULL) {
1159191783Srmacklem				pc->pc_chownrestricted =
1160191783Srmacklem				    fxdr_unsigned(u_int32_t, *tl);
1161191783Srmacklem			}
1162191783Srmacklem			attrsum += NFSX_UNSIGNED;
1163191783Srmacklem			break;
1164191783Srmacklem		case NFSATTRBIT_FILEHANDLE:
1165191783Srmacklem			error = nfsm_getfh(nd, &tnfhp);
1166191783Srmacklem			if (error)
1167224086Szack				goto nfsmout;
1168191783Srmacklem			tfhsize = tnfhp->nfh_len;
1169191783Srmacklem			if (compare) {
1170191783Srmacklem				if (!(*retcmpp) &&
1171191783Srmacklem				    !NFSRV_CMPFH(tnfhp->nfh_fh, tfhsize,
1172191783Srmacklem				     fhp, fhsize))
1173191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1174191783Srmacklem				FREE((caddr_t)tnfhp, M_NFSFH);
1175191783Srmacklem			} else if (nfhpp != NULL) {
1176191783Srmacklem				*nfhpp = tnfhp;
1177191783Srmacklem			} else {
1178191783Srmacklem				FREE((caddr_t)tnfhp, M_NFSFH);
1179191783Srmacklem			}
1180191783Srmacklem			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(tfhsize));
1181191783Srmacklem			break;
1182191783Srmacklem		case NFSATTRBIT_FILEID:
1183191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1184191783Srmacklem			thyp = fxdr_hyper(tl);
1185191783Srmacklem			if (compare) {
1186191783Srmacklem				if (!(*retcmpp)) {
1187191783Srmacklem				    if ((u_int64_t)nap->na_fileid != thyp)
1188191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1189191783Srmacklem				}
1190191783Srmacklem			} else if (nap != NULL) {
1191191783Srmacklem				if (*tl++)
1192191783Srmacklem					printf("NFSv4 fileid > 32bits\n");
1193191783Srmacklem				nap->na_fileid = thyp;
1194191783Srmacklem			}
1195191783Srmacklem			attrsum += NFSX_HYPER;
1196191783Srmacklem			break;
1197191783Srmacklem		case NFSATTRBIT_FILESAVAIL:
1198191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1199191783Srmacklem			if (compare) {
1200191783Srmacklem				if (!(*retcmpp) &&
1201191783Srmacklem				    sfp->sf_afiles != fxdr_hyper(tl))
1202191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1203191783Srmacklem			} else if (sfp != NULL) {
1204191783Srmacklem				sfp->sf_afiles = fxdr_hyper(tl);
1205191783Srmacklem			}
1206191783Srmacklem			attrsum += NFSX_HYPER;
1207191783Srmacklem			break;
1208191783Srmacklem		case NFSATTRBIT_FILESFREE:
1209191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1210191783Srmacklem			if (compare) {
1211191783Srmacklem				if (!(*retcmpp) &&
1212191783Srmacklem				    sfp->sf_ffiles != fxdr_hyper(tl))
1213191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1214191783Srmacklem			} else if (sfp != NULL) {
1215191783Srmacklem				sfp->sf_ffiles = fxdr_hyper(tl);
1216191783Srmacklem			}
1217191783Srmacklem			attrsum += NFSX_HYPER;
1218191783Srmacklem			break;
1219191783Srmacklem		case NFSATTRBIT_FILESTOTAL:
1220191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1221191783Srmacklem			if (compare) {
1222191783Srmacklem				if (!(*retcmpp) &&
1223191783Srmacklem				    sfp->sf_tfiles != fxdr_hyper(tl))
1224191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1225191783Srmacklem			} else if (sfp != NULL) {
1226191783Srmacklem				sfp->sf_tfiles = fxdr_hyper(tl);
1227191783Srmacklem			}
1228191783Srmacklem			attrsum += NFSX_HYPER;
1229191783Srmacklem			break;
1230191783Srmacklem		case NFSATTRBIT_FSLOCATIONS:
1231191783Srmacklem			error = nfsrv_getrefstr(nd, &cp, &cp2, &l, &m);
1232191783Srmacklem			if (error)
1233224086Szack				goto nfsmout;
1234191783Srmacklem			attrsum += l;
1235191783Srmacklem			if (compare && !(*retcmpp)) {
1236191783Srmacklem				refp = nfsv4root_getreferral(vp, NULL, 0);
1237191783Srmacklem				if (refp != NULL) {
1238191783Srmacklem					if (cp == NULL || cp2 == NULL ||
1239191783Srmacklem					    strcmp(cp, "/") ||
1240191783Srmacklem					    strcmp(cp2, refp->nfr_srvlist))
1241191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1242191783Srmacklem				} else if (m == 0) {
1243191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1244191783Srmacklem				}
1245191783Srmacklem			}
1246191783Srmacklem			if (cp != NULL)
1247191783Srmacklem				free(cp, M_NFSSTRING);
1248191783Srmacklem			if (cp2 != NULL)
1249191783Srmacklem				free(cp2, M_NFSSTRING);
1250191783Srmacklem			break;
1251191783Srmacklem		case NFSATTRBIT_HIDDEN:
1252191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1253191783Srmacklem			if (compare && !(*retcmpp))
1254191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1255191783Srmacklem			attrsum += NFSX_UNSIGNED;
1256191783Srmacklem			break;
1257191783Srmacklem		case NFSATTRBIT_HOMOGENEOUS:
1258191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1259191783Srmacklem			if (compare) {
1260191783Srmacklem				if (!(*retcmpp)) {
1261191783Srmacklem				    if (fsp->fs_properties &
1262191783Srmacklem					NFSV3_FSFHOMOGENEOUS) {
1263191783Srmacklem					if (*tl == newnfs_false)
1264191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1265191783Srmacklem				    } else {
1266191783Srmacklem					if (*tl == newnfs_true)
1267191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1268191783Srmacklem				    }
1269191783Srmacklem				}
1270191783Srmacklem			} else if (fsp != NULL) {
1271191783Srmacklem				if (*tl == newnfs_true)
1272191783Srmacklem				    fsp->fs_properties |= NFSV3_FSFHOMOGENEOUS;
1273191783Srmacklem				else
1274191783Srmacklem				    fsp->fs_properties &= ~NFSV3_FSFHOMOGENEOUS;
1275191783Srmacklem			}
1276191783Srmacklem			attrsum += NFSX_UNSIGNED;
1277191783Srmacklem			break;
1278191783Srmacklem		case NFSATTRBIT_MAXFILESIZE:
1279191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1280191783Srmacklem			tnfsquad.qval = fxdr_hyper(tl);
1281191783Srmacklem			if (compare) {
1282191783Srmacklem				if (!(*retcmpp)) {
1283191783Srmacklem					tquad = NFSRV_MAXFILESIZE;
1284191783Srmacklem					if (tquad != tnfsquad.qval)
1285191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1286191783Srmacklem				}
1287191783Srmacklem			} else if (fsp != NULL) {
1288191783Srmacklem				fsp->fs_maxfilesize = tnfsquad.qval;
1289191783Srmacklem			}
1290191783Srmacklem			attrsum += NFSX_HYPER;
1291191783Srmacklem			break;
1292191783Srmacklem		case NFSATTRBIT_MAXLINK:
1293191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1294191783Srmacklem			if (compare) {
1295191783Srmacklem				if (!(*retcmpp)) {
1296191783Srmacklem				    if (fxdr_unsigned(int, *tl) != LINK_MAX)
1297191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1298191783Srmacklem				}
1299191783Srmacklem			} else if (pc != NULL) {
1300191783Srmacklem				pc->pc_linkmax = fxdr_unsigned(u_int32_t, *tl);
1301191783Srmacklem			}
1302191783Srmacklem			attrsum += NFSX_UNSIGNED;
1303191783Srmacklem			break;
1304191783Srmacklem		case NFSATTRBIT_MAXNAME:
1305191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1306191783Srmacklem			if (compare) {
1307191783Srmacklem				if (!(*retcmpp)) {
1308191783Srmacklem				    if (fsp->fs_maxname !=
1309191783Srmacklem					fxdr_unsigned(u_int32_t, *tl))
1310191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1311191783Srmacklem				}
1312191783Srmacklem			} else {
1313191783Srmacklem				tuint = fxdr_unsigned(u_int32_t, *tl);
1314191783Srmacklem				/*
1315191783Srmacklem				 * Some Linux NFSv4 servers report this
1316191783Srmacklem				 * as 0 or 4billion, so I'll set it to
1317191783Srmacklem				 * NFS_MAXNAMLEN. If a server actually creates
1318191783Srmacklem				 * a name longer than NFS_MAXNAMLEN, it will
1319191783Srmacklem				 * get an error back.
1320191783Srmacklem				 */
1321191783Srmacklem				if (tuint == 0 || tuint > NFS_MAXNAMLEN)
1322191783Srmacklem					tuint = NFS_MAXNAMLEN;
1323191783Srmacklem				if (fsp != NULL)
1324191783Srmacklem					fsp->fs_maxname = tuint;
1325191783Srmacklem				if (pc != NULL)
1326191783Srmacklem					pc->pc_namemax = tuint;
1327191783Srmacklem			}
1328191783Srmacklem			attrsum += NFSX_UNSIGNED;
1329191783Srmacklem			break;
1330191783Srmacklem		case NFSATTRBIT_MAXREAD:
1331191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1332191783Srmacklem			if (compare) {
1333191783Srmacklem				if (!(*retcmpp)) {
1334191783Srmacklem				    if (fsp->fs_rtmax != fxdr_unsigned(u_int32_t,
1335191783Srmacklem					*(tl + 1)) || *tl != 0)
1336191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1337191783Srmacklem				}
1338191783Srmacklem			} else if (fsp != NULL) {
1339191783Srmacklem				fsp->fs_rtmax = fxdr_unsigned(u_int32_t, *++tl);
1340191783Srmacklem				fsp->fs_rtpref = fsp->fs_rtmax;
1341191783Srmacklem				fsp->fs_dtpref = fsp->fs_rtpref;
1342191783Srmacklem			}
1343191783Srmacklem			attrsum += NFSX_HYPER;
1344191783Srmacklem			break;
1345191783Srmacklem		case NFSATTRBIT_MAXWRITE:
1346191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1347191783Srmacklem			if (compare) {
1348191783Srmacklem				if (!(*retcmpp)) {
1349191783Srmacklem				    if (fsp->fs_wtmax != fxdr_unsigned(u_int32_t,
1350191783Srmacklem					*(tl + 1)) || *tl != 0)
1351191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1352191783Srmacklem				}
1353191783Srmacklem			} else if (fsp != NULL) {
1354191783Srmacklem				fsp->fs_wtmax = fxdr_unsigned(int, *++tl);
1355191783Srmacklem				fsp->fs_wtpref = fsp->fs_wtmax;
1356191783Srmacklem			}
1357191783Srmacklem			attrsum += NFSX_HYPER;
1358191783Srmacklem			break;
1359191783Srmacklem		case NFSATTRBIT_MIMETYPE:
1360191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1361191783Srmacklem			i = fxdr_unsigned(int, *tl);
1362191783Srmacklem			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(i));
1363191783Srmacklem			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
1364191783Srmacklem			if (error)
1365191783Srmacklem				goto nfsmout;
1366191783Srmacklem			if (compare && !(*retcmpp))
1367191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1368191783Srmacklem			break;
1369191783Srmacklem		case NFSATTRBIT_MODE:
1370191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1371191783Srmacklem			if (compare) {
1372191783Srmacklem				if (!(*retcmpp)) {
1373191783Srmacklem				    if (nap->na_mode != nfstov_mode(*tl))
1374191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1375191783Srmacklem				}
1376191783Srmacklem			} else if (nap != NULL) {
1377191783Srmacklem				nap->na_mode = nfstov_mode(*tl);
1378191783Srmacklem			}
1379191783Srmacklem			attrsum += NFSX_UNSIGNED;
1380191783Srmacklem			break;
1381191783Srmacklem		case NFSATTRBIT_NOTRUNC:
1382191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1383191783Srmacklem			if (compare) {
1384191783Srmacklem				if (!(*retcmpp)) {
1385191783Srmacklem				    if (*tl != newnfs_true)
1386191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1387191783Srmacklem				}
1388191783Srmacklem			} else if (pc != NULL) {
1389191783Srmacklem				pc->pc_notrunc = fxdr_unsigned(u_int32_t, *tl);
1390191783Srmacklem			}
1391191783Srmacklem			attrsum += NFSX_UNSIGNED;
1392191783Srmacklem			break;
1393191783Srmacklem		case NFSATTRBIT_NUMLINKS:
1394191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1395191783Srmacklem			tuint = fxdr_unsigned(u_int32_t, *tl);
1396191783Srmacklem			if (compare) {
1397191783Srmacklem			    if (!(*retcmpp)) {
1398191783Srmacklem				if ((u_int32_t)nap->na_nlink != tuint)
1399191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1400191783Srmacklem			    }
1401191783Srmacklem			} else if (nap != NULL) {
1402191783Srmacklem				nap->na_nlink = tuint;
1403191783Srmacklem			}
1404191783Srmacklem			attrsum += NFSX_UNSIGNED;
1405191783Srmacklem			break;
1406191783Srmacklem		case NFSATTRBIT_OWNER:
1407191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1408191783Srmacklem			j = fxdr_unsigned(int, *tl);
1409224086Szack			if (j < 0) {
1410224086Szack				error = NFSERR_BADXDR;
1411224086Szack				goto nfsmout;
1412224086Szack			}
1413191783Srmacklem			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
1414191783Srmacklem			if (j > NFSV4_SMALLSTR)
1415191783Srmacklem				cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
1416191783Srmacklem			else
1417191783Srmacklem				cp = namestr;
1418191783Srmacklem			error = nfsrv_mtostr(nd, cp, j);
1419191783Srmacklem			if (error) {
1420191783Srmacklem				if (j > NFSV4_SMALLSTR)
1421191783Srmacklem					free(cp, M_NFSSTRING);
1422224086Szack				goto nfsmout;
1423191783Srmacklem			}
1424191783Srmacklem			if (compare) {
1425191783Srmacklem			    if (!(*retcmpp)) {
1426240720Srmacklem				if (nfsv4_strtouid(nd, cp, j, &uid, p) ||
1427191783Srmacklem				    nap->na_uid != uid)
1428191783Srmacklem				    *retcmpp = NFSERR_NOTSAME;
1429191783Srmacklem			    }
1430191783Srmacklem			} else if (nap != NULL) {
1431240720Srmacklem				if (nfsv4_strtouid(nd, cp, j, &uid, p))
1432191783Srmacklem					nap->na_uid = nfsrv_defaultuid;
1433191783Srmacklem				else
1434191783Srmacklem					nap->na_uid = uid;
1435191783Srmacklem			}
1436191783Srmacklem			if (j > NFSV4_SMALLSTR)
1437191783Srmacklem				free(cp, M_NFSSTRING);
1438191783Srmacklem			break;
1439191783Srmacklem		case NFSATTRBIT_OWNERGROUP:
1440191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1441191783Srmacklem			j = fxdr_unsigned(int, *tl);
1442224086Szack			if (j < 0) {
1443224086Szack				error =  NFSERR_BADXDR;
1444224086Szack				goto nfsmout;
1445224086Szack			}
1446191783Srmacklem			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
1447191783Srmacklem			if (j > NFSV4_SMALLSTR)
1448191783Srmacklem				cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
1449191783Srmacklem			else
1450191783Srmacklem				cp = namestr;
1451191783Srmacklem			error = nfsrv_mtostr(nd, cp, j);
1452191783Srmacklem			if (error) {
1453191783Srmacklem				if (j > NFSV4_SMALLSTR)
1454191783Srmacklem					free(cp, M_NFSSTRING);
1455224086Szack				goto nfsmout;
1456191783Srmacklem			}
1457191783Srmacklem			if (compare) {
1458191783Srmacklem			    if (!(*retcmpp)) {
1459240720Srmacklem				if (nfsv4_strtogid(nd, cp, j, &gid, p) ||
1460191783Srmacklem				    nap->na_gid != gid)
1461191783Srmacklem				    *retcmpp = NFSERR_NOTSAME;
1462191783Srmacklem			    }
1463191783Srmacklem			} else if (nap != NULL) {
1464240720Srmacklem				if (nfsv4_strtogid(nd, cp, j, &gid, p))
1465191783Srmacklem					nap->na_gid = nfsrv_defaultgid;
1466191783Srmacklem				else
1467191783Srmacklem					nap->na_gid = gid;
1468191783Srmacklem			}
1469191783Srmacklem			if (j > NFSV4_SMALLSTR)
1470191783Srmacklem				free(cp, M_NFSSTRING);
1471191783Srmacklem			break;
1472191783Srmacklem		case NFSATTRBIT_QUOTAHARD:
1473191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1474191783Srmacklem			if (sbp != NULL) {
1475191783Srmacklem			    if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
1476191783Srmacklem				freenum = sbp->f_bfree;
1477191783Srmacklem			    else
1478191783Srmacklem				freenum = sbp->f_bavail;
1479191783Srmacklem#ifdef QUOTA
1480191783Srmacklem			    /*
1481191783Srmacklem			     * ufs_quotactl() insists that the uid argument
1482191783Srmacklem			     * equal p_ruid for non-root quota access, so
1483191783Srmacklem			     * we'll just make sure that's the case.
1484191783Srmacklem			     */
1485191783Srmacklem			    savuid = p->p_cred->p_ruid;
1486191783Srmacklem			    p->p_cred->p_ruid = cred->cr_uid;
1487191783Srmacklem			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1488191990Sattilio				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1489191783Srmacklem				freenum = min(dqb.dqb_bhardlimit, freenum);
1490191783Srmacklem			    p->p_cred->p_ruid = savuid;
1491191783Srmacklem#endif	/* QUOTA */
1492191783Srmacklem			    uquad = (u_int64_t)freenum;
1493191783Srmacklem			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1494191783Srmacklem			}
1495191783Srmacklem			if (compare && !(*retcmpp)) {
1496191783Srmacklem				if (uquad != fxdr_hyper(tl))
1497191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1498191783Srmacklem			}
1499191783Srmacklem			attrsum += NFSX_HYPER;
1500191783Srmacklem			break;
1501191783Srmacklem		case NFSATTRBIT_QUOTASOFT:
1502191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1503191783Srmacklem			if (sbp != NULL) {
1504191783Srmacklem			    if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
1505191783Srmacklem				freenum = sbp->f_bfree;
1506191783Srmacklem			    else
1507191783Srmacklem				freenum = sbp->f_bavail;
1508191783Srmacklem#ifdef QUOTA
1509191783Srmacklem			    /*
1510191783Srmacklem			     * ufs_quotactl() insists that the uid argument
1511191783Srmacklem			     * equal p_ruid for non-root quota access, so
1512191783Srmacklem			     * we'll just make sure that's the case.
1513191783Srmacklem			     */
1514191783Srmacklem			    savuid = p->p_cred->p_ruid;
1515191783Srmacklem			    p->p_cred->p_ruid = cred->cr_uid;
1516191783Srmacklem			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1517191990Sattilio				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1518191783Srmacklem				freenum = min(dqb.dqb_bsoftlimit, freenum);
1519191783Srmacklem			    p->p_cred->p_ruid = savuid;
1520191783Srmacklem#endif	/* QUOTA */
1521191783Srmacklem			    uquad = (u_int64_t)freenum;
1522191783Srmacklem			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1523191783Srmacklem			}
1524191783Srmacklem			if (compare && !(*retcmpp)) {
1525191783Srmacklem				if (uquad != fxdr_hyper(tl))
1526191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1527191783Srmacklem			}
1528191783Srmacklem			attrsum += NFSX_HYPER;
1529191783Srmacklem			break;
1530191783Srmacklem		case NFSATTRBIT_QUOTAUSED:
1531191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1532191783Srmacklem			if (sbp != NULL) {
1533191783Srmacklem			    freenum = 0;
1534191783Srmacklem#ifdef QUOTA
1535191783Srmacklem			    /*
1536191783Srmacklem			     * ufs_quotactl() insists that the uid argument
1537191783Srmacklem			     * equal p_ruid for non-root quota access, so
1538191783Srmacklem			     * we'll just make sure that's the case.
1539191783Srmacklem			     */
1540191783Srmacklem			    savuid = p->p_cred->p_ruid;
1541191783Srmacklem			    p->p_cred->p_ruid = cred->cr_uid;
1542191783Srmacklem			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1543191990Sattilio				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1544191783Srmacklem				freenum = dqb.dqb_curblocks;
1545191783Srmacklem			    p->p_cred->p_ruid = savuid;
1546191783Srmacklem#endif	/* QUOTA */
1547191783Srmacklem			    uquad = (u_int64_t)freenum;
1548191783Srmacklem			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1549191783Srmacklem			}
1550191783Srmacklem			if (compare && !(*retcmpp)) {
1551191783Srmacklem				if (uquad != fxdr_hyper(tl))
1552191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1553191783Srmacklem			}
1554191783Srmacklem			attrsum += NFSX_HYPER;
1555191783Srmacklem			break;
1556191783Srmacklem		case NFSATTRBIT_RAWDEV:
1557191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4SPECDATA);
1558191783Srmacklem			j = fxdr_unsigned(int, *tl++);
1559191783Srmacklem			k = fxdr_unsigned(int, *tl);
1560191783Srmacklem			if (compare) {
1561191783Srmacklem			    if (!(*retcmpp)) {
1562191783Srmacklem				if (nap->na_rdev != NFSMAKEDEV(j, k))
1563191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1564191783Srmacklem			    }
1565191783Srmacklem			} else if (nap != NULL) {
1566191783Srmacklem				nap->na_rdev = NFSMAKEDEV(j, k);
1567191783Srmacklem			}
1568191783Srmacklem			attrsum += NFSX_V4SPECDATA;
1569191783Srmacklem			break;
1570191783Srmacklem		case NFSATTRBIT_SPACEAVAIL:
1571191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1572191783Srmacklem			if (compare) {
1573191783Srmacklem				if (!(*retcmpp) &&
1574191783Srmacklem				    sfp->sf_abytes != fxdr_hyper(tl))
1575191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1576191783Srmacklem			} else if (sfp != NULL) {
1577191783Srmacklem				sfp->sf_abytes = fxdr_hyper(tl);
1578191783Srmacklem			}
1579191783Srmacklem			attrsum += NFSX_HYPER;
1580191783Srmacklem			break;
1581191783Srmacklem		case NFSATTRBIT_SPACEFREE:
1582191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1583191783Srmacklem			if (compare) {
1584191783Srmacklem				if (!(*retcmpp) &&
1585191783Srmacklem				    sfp->sf_fbytes != fxdr_hyper(tl))
1586191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1587191783Srmacklem			} else if (sfp != NULL) {
1588191783Srmacklem				sfp->sf_fbytes = fxdr_hyper(tl);
1589191783Srmacklem			}
1590191783Srmacklem			attrsum += NFSX_HYPER;
1591191783Srmacklem			break;
1592191783Srmacklem		case NFSATTRBIT_SPACETOTAL:
1593191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1594191783Srmacklem			if (compare) {
1595191783Srmacklem				if (!(*retcmpp) &&
1596191783Srmacklem				    sfp->sf_tbytes != fxdr_hyper(tl))
1597191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1598191783Srmacklem			} else if (sfp != NULL) {
1599191783Srmacklem				sfp->sf_tbytes = fxdr_hyper(tl);
1600191783Srmacklem			}
1601191783Srmacklem			attrsum += NFSX_HYPER;
1602191783Srmacklem			break;
1603191783Srmacklem		case NFSATTRBIT_SPACEUSED:
1604191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1605191783Srmacklem			thyp = fxdr_hyper(tl);
1606191783Srmacklem			if (compare) {
1607191783Srmacklem			    if (!(*retcmpp)) {
1608191783Srmacklem				if ((u_int64_t)nap->na_bytes != thyp)
1609191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1610191783Srmacklem			    }
1611191783Srmacklem			} else if (nap != NULL) {
1612191783Srmacklem				nap->na_bytes = thyp;
1613191783Srmacklem			}
1614191783Srmacklem			attrsum += NFSX_HYPER;
1615191783Srmacklem			break;
1616191783Srmacklem		case NFSATTRBIT_SYSTEM:
1617191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1618191783Srmacklem			if (compare && !(*retcmpp))
1619191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1620191783Srmacklem			attrsum += NFSX_UNSIGNED;
1621191783Srmacklem			break;
1622191783Srmacklem		case NFSATTRBIT_TIMEACCESS:
1623191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1624191783Srmacklem			fxdr_nfsv4time(tl, &temptime);
1625191783Srmacklem			if (compare) {
1626191783Srmacklem			    if (!(*retcmpp)) {
1627191783Srmacklem				if (!NFS_CMPTIME(temptime, nap->na_atime))
1628191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1629191783Srmacklem			    }
1630191783Srmacklem			} else if (nap != NULL) {
1631191783Srmacklem				nap->na_atime = temptime;
1632191783Srmacklem			}
1633191783Srmacklem			attrsum += NFSX_V4TIME;
1634191783Srmacklem			break;
1635191783Srmacklem		case NFSATTRBIT_TIMEACCESSSET:
1636191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1637191783Srmacklem			attrsum += NFSX_UNSIGNED;
1638191783Srmacklem			i = fxdr_unsigned(int, *tl);
1639191783Srmacklem			if (i == NFSV4SATTRTIME_TOCLIENT) {
1640191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1641191783Srmacklem				attrsum += NFSX_V4TIME;
1642191783Srmacklem			}
1643191783Srmacklem			if (compare && !(*retcmpp))
1644191783Srmacklem				*retcmpp = NFSERR_INVAL;
1645191783Srmacklem			break;
1646191783Srmacklem		case NFSATTRBIT_TIMEBACKUP:
1647191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1648191783Srmacklem			if (compare && !(*retcmpp))
1649191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1650191783Srmacklem			attrsum += NFSX_V4TIME;
1651191783Srmacklem			break;
1652191783Srmacklem		case NFSATTRBIT_TIMECREATE:
1653191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1654191783Srmacklem			if (compare && !(*retcmpp))
1655191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1656191783Srmacklem			attrsum += NFSX_V4TIME;
1657191783Srmacklem			break;
1658191783Srmacklem		case NFSATTRBIT_TIMEDELTA:
1659191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1660191783Srmacklem			if (fsp != NULL) {
1661191783Srmacklem			    if (compare) {
1662191783Srmacklem				if (!(*retcmpp)) {
1663191783Srmacklem				    if ((u_int32_t)fsp->fs_timedelta.tv_sec !=
1664191783Srmacklem					fxdr_unsigned(u_int32_t, *(tl + 1)) ||
1665191783Srmacklem				        (u_int32_t)fsp->fs_timedelta.tv_nsec !=
1666191783Srmacklem					(fxdr_unsigned(u_int32_t, *(tl + 2)) %
1667191783Srmacklem					 1000000000) ||
1668191783Srmacklem					*tl != 0)
1669191783Srmacklem					    *retcmpp = NFSERR_NOTSAME;
1670191783Srmacklem				}
1671191783Srmacklem			    } else {
1672191783Srmacklem				fxdr_nfsv4time(tl, &fsp->fs_timedelta);
1673191783Srmacklem			    }
1674191783Srmacklem			}
1675191783Srmacklem			attrsum += NFSX_V4TIME;
1676191783Srmacklem			break;
1677191783Srmacklem		case NFSATTRBIT_TIMEMETADATA:
1678191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1679191783Srmacklem			fxdr_nfsv4time(tl, &temptime);
1680191783Srmacklem			if (compare) {
1681191783Srmacklem			    if (!(*retcmpp)) {
1682191783Srmacklem				if (!NFS_CMPTIME(temptime, nap->na_ctime))
1683191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1684191783Srmacklem			    }
1685191783Srmacklem			} else if (nap != NULL) {
1686191783Srmacklem				nap->na_ctime = temptime;
1687191783Srmacklem			}
1688191783Srmacklem			attrsum += NFSX_V4TIME;
1689191783Srmacklem			break;
1690191783Srmacklem		case NFSATTRBIT_TIMEMODIFY:
1691191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1692191783Srmacklem			fxdr_nfsv4time(tl, &temptime);
1693191783Srmacklem			if (compare) {
1694191783Srmacklem			    if (!(*retcmpp)) {
1695191783Srmacklem				if (!NFS_CMPTIME(temptime, nap->na_mtime))
1696191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1697191783Srmacklem			    }
1698191783Srmacklem			} else if (nap != NULL) {
1699191783Srmacklem				nap->na_mtime = temptime;
1700191783Srmacklem			}
1701191783Srmacklem			attrsum += NFSX_V4TIME;
1702191783Srmacklem			break;
1703191783Srmacklem		case NFSATTRBIT_TIMEMODIFYSET:
1704191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1705191783Srmacklem			attrsum += NFSX_UNSIGNED;
1706191783Srmacklem			i = fxdr_unsigned(int, *tl);
1707191783Srmacklem			if (i == NFSV4SATTRTIME_TOCLIENT) {
1708191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1709191783Srmacklem				attrsum += NFSX_V4TIME;
1710191783Srmacklem			}
1711191783Srmacklem			if (compare && !(*retcmpp))
1712191783Srmacklem				*retcmpp = NFSERR_INVAL;
1713191783Srmacklem			break;
1714191783Srmacklem		case NFSATTRBIT_MOUNTEDONFILEID:
1715191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1716191783Srmacklem			thyp = fxdr_hyper(tl);
1717191783Srmacklem			if (compare) {
1718191783Srmacklem			    if (!(*retcmpp)) {
1719191783Srmacklem				if (*tl++) {
1720191783Srmacklem					*retcmpp = NFSERR_NOTSAME;
1721191783Srmacklem				} else {
1722191783Srmacklem					if (!vp || !nfsrv_atroot(vp, &fid))
1723191783Srmacklem						fid = nap->na_fileid;
1724191783Srmacklem					if ((u_int64_t)fid != thyp)
1725191783Srmacklem						*retcmpp = NFSERR_NOTSAME;
1726191783Srmacklem				}
1727191783Srmacklem			    }
1728191783Srmacklem			} else if (nap != NULL) {
1729191783Srmacklem			    if (*tl++)
1730191783Srmacklem				printf("NFSv4 mounted on fileid > 32bits\n");
1731191783Srmacklem			    nap->na_mntonfileno = thyp;
1732191783Srmacklem			}
1733191783Srmacklem			attrsum += NFSX_HYPER;
1734191783Srmacklem			break;
1735191783Srmacklem		default:
1736191783Srmacklem			printf("EEK! nfsv4_loadattr unknown attr=%d\n",
1737191783Srmacklem				bitpos);
1738191783Srmacklem			if (compare && !(*retcmpp))
1739191783Srmacklem				*retcmpp = NFSERR_ATTRNOTSUPP;
1740191783Srmacklem			/*
1741191783Srmacklem			 * and get out of the loop, since we can't parse
1742191783Srmacklem			 * the unknown attrbute data.
1743191783Srmacklem			 */
1744191783Srmacklem			bitpos = NFSATTRBIT_MAX;
1745191783Srmacklem			break;
1746191783Srmacklem		};
1747191783Srmacklem	}
1748191783Srmacklem
1749191783Srmacklem	/*
1750191783Srmacklem	 * some clients pad the attrlist, so we need to skip over the
1751191783Srmacklem	 * padding.
1752191783Srmacklem	 */
1753191783Srmacklem	if (attrsum > attrsize) {
1754191783Srmacklem		error = NFSERR_BADXDR;
1755191783Srmacklem	} else {
1756191783Srmacklem		attrsize = NFSM_RNDUP(attrsize);
1757191783Srmacklem		if (attrsum < attrsize)
1758191783Srmacklem			error = nfsm_advance(nd, attrsize - attrsum, -1);
1759191783Srmacklem	}
1760191783Srmacklemnfsmout:
1761224086Szack	NFSEXITCODE2(error, nd);
1762191783Srmacklem	return (error);
1763191783Srmacklem}
1764191783Srmacklem
1765191783Srmacklem/*
1766191783Srmacklem * Implement sleep locks for newnfs. The nfslock_usecnt allows for a
1767191783Srmacklem * shared lock and the NFSXXX_LOCK flag permits an exclusive lock.
1768191783Srmacklem * The first argument is a pointer to an nfsv4lock structure.
1769191783Srmacklem * The second argument is 1 iff a blocking lock is wanted.
1770191783Srmacklem * If this argument is 0, the call waits until no thread either wants nor
1771191783Srmacklem * holds an exclusive lock.
1772191783Srmacklem * It returns 1 if the lock was acquired, 0 otherwise.
1773191783Srmacklem * If several processes call this function concurrently wanting the exclusive
1774191783Srmacklem * lock, one will get the lock and the rest will return without getting the
1775191783Srmacklem * lock. (If the caller must have the lock, it simply calls this function in a
1776191783Srmacklem *  loop until the function returns 1 to indicate the lock was acquired.)
1777191783Srmacklem * Any usecnt must be decremented by calling nfsv4_relref() before
1778191783Srmacklem * calling nfsv4_lock(). It was done this way, so nfsv4_lock() could
1779191783Srmacklem * be called in a loop.
1780222389Srmacklem * The isleptp argument is set to indicate if the call slept, iff not NULL
1781222389Srmacklem * and the mp argument indicates to check for a forced dismount, iff not
1782222389Srmacklem * NULL.
1783191783Srmacklem */
1784191783SrmacklemAPPLESTATIC int
1785191783Srmacklemnfsv4_lock(struct nfsv4lock *lp, int iwantlock, int *isleptp,
1786222389Srmacklem    void *mutex, struct mount *mp)
1787191783Srmacklem{
1788191783Srmacklem
1789191783Srmacklem	if (isleptp)
1790191783Srmacklem		*isleptp = 0;
1791191783Srmacklem	/*
1792191783Srmacklem	 * If a lock is wanted, loop around until the lock is acquired by
1793191783Srmacklem	 * someone and then released. If I want the lock, try to acquire it.
1794191783Srmacklem	 * For a lock to be issued, no lock must be in force and the usecnt
1795191783Srmacklem	 * must be zero.
1796191783Srmacklem	 */
1797191783Srmacklem	if (iwantlock) {
1798191783Srmacklem	    if (!(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
1799191783Srmacklem		lp->nfslock_usecnt == 0) {
1800191783Srmacklem		lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1801191783Srmacklem		lp->nfslock_lock |= NFSV4LOCK_LOCK;
1802191783Srmacklem		return (1);
1803191783Srmacklem	    }
1804191783Srmacklem	    lp->nfslock_lock |= NFSV4LOCK_LOCKWANTED;
1805191783Srmacklem	}
1806191783Srmacklem	while (lp->nfslock_lock & (NFSV4LOCK_LOCK | NFSV4LOCK_LOCKWANTED)) {
1807222389Srmacklem		if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1808222389Srmacklem			lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1809222389Srmacklem			return (0);
1810222389Srmacklem		}
1811191783Srmacklem		lp->nfslock_lock |= NFSV4LOCK_WANTED;
1812191783Srmacklem		if (isleptp)
1813191783Srmacklem			*isleptp = 1;
1814191783Srmacklem		(void) nfsmsleep(&lp->nfslock_lock, mutex,
1815191783Srmacklem		    PZERO - 1, "nfsv4lck", NULL);
1816191783Srmacklem		if (iwantlock && !(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
1817191783Srmacklem		    lp->nfslock_usecnt == 0) {
1818191783Srmacklem			lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1819191783Srmacklem			lp->nfslock_lock |= NFSV4LOCK_LOCK;
1820191783Srmacklem			return (1);
1821191783Srmacklem		}
1822191783Srmacklem	}
1823191783Srmacklem	return (0);
1824191783Srmacklem}
1825191783Srmacklem
1826191783Srmacklem/*
1827191783Srmacklem * Release the lock acquired by nfsv4_lock().
1828191783Srmacklem * The second argument is set to 1 to indicate the nfslock_usecnt should be
1829191783Srmacklem * incremented, as well.
1830191783Srmacklem */
1831191783SrmacklemAPPLESTATIC void
1832191783Srmacklemnfsv4_unlock(struct nfsv4lock *lp, int incref)
1833191783Srmacklem{
1834191783Srmacklem
1835191783Srmacklem	lp->nfslock_lock &= ~NFSV4LOCK_LOCK;
1836191783Srmacklem	if (incref)
1837191783Srmacklem		lp->nfslock_usecnt++;
1838191783Srmacklem	nfsv4_wanted(lp);
1839191783Srmacklem}
1840191783Srmacklem
1841191783Srmacklem/*
1842191783Srmacklem * Release a reference cnt.
1843191783Srmacklem */
1844191783SrmacklemAPPLESTATIC void
1845191783Srmacklemnfsv4_relref(struct nfsv4lock *lp)
1846191783Srmacklem{
1847191783Srmacklem
1848191783Srmacklem	if (lp->nfslock_usecnt <= 0)
1849191783Srmacklem		panic("nfsv4root ref cnt");
1850191783Srmacklem	lp->nfslock_usecnt--;
1851191783Srmacklem	if (lp->nfslock_usecnt == 0)
1852191783Srmacklem		nfsv4_wanted(lp);
1853191783Srmacklem}
1854191783Srmacklem
1855191783Srmacklem/*
1856191783Srmacklem * Get a reference cnt.
1857191783Srmacklem * This function will wait for any exclusive lock to be released, but will
1858191783Srmacklem * not wait for threads that want the exclusive lock. If priority needs
1859191783Srmacklem * to be given to threads that need the exclusive lock, a call to nfsv4_lock()
1860191783Srmacklem * with the 2nd argument == 0 should be done before calling nfsv4_getref().
1861222389Srmacklem * If the mp argument is not NULL, check for MNTK_UNMOUNTF being set and
1862222389Srmacklem * return without getting a refcnt for that case.
1863191783Srmacklem */
1864191783SrmacklemAPPLESTATIC void
1865222389Srmacklemnfsv4_getref(struct nfsv4lock *lp, int *isleptp, void *mutex,
1866222389Srmacklem    struct mount *mp)
1867191783Srmacklem{
1868191783Srmacklem
1869191783Srmacklem	if (isleptp)
1870191783Srmacklem		*isleptp = 0;
1871191783Srmacklem
1872191783Srmacklem	/*
1873191783Srmacklem	 * Wait for a lock held.
1874191783Srmacklem	 */
1875191783Srmacklem	while (lp->nfslock_lock & NFSV4LOCK_LOCK) {
1876222389Srmacklem		if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0)
1877222389Srmacklem			return;
1878191783Srmacklem		lp->nfslock_lock |= NFSV4LOCK_WANTED;
1879191783Srmacklem		if (isleptp)
1880191783Srmacklem			*isleptp = 1;
1881191783Srmacklem		(void) nfsmsleep(&lp->nfslock_lock, mutex,
1882244042Srmacklem		    PZERO - 1, "nfsv4gr", NULL);
1883191783Srmacklem	}
1884222389Srmacklem	if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0)
1885222389Srmacklem		return;
1886191783Srmacklem
1887191783Srmacklem	lp->nfslock_usecnt++;
1888191783Srmacklem}
1889191783Srmacklem
1890191783Srmacklem/*
1891211951Srmacklem * Get a reference as above, but return failure instead of sleeping if
1892211951Srmacklem * an exclusive lock is held.
1893211951Srmacklem */
1894211951SrmacklemAPPLESTATIC int
1895211951Srmacklemnfsv4_getref_nonblock(struct nfsv4lock *lp)
1896211951Srmacklem{
1897211951Srmacklem
1898211951Srmacklem	if ((lp->nfslock_lock & NFSV4LOCK_LOCK) != 0)
1899211951Srmacklem		return (0);
1900211951Srmacklem
1901211951Srmacklem	lp->nfslock_usecnt++;
1902211951Srmacklem	return (1);
1903211951Srmacklem}
1904211951Srmacklem
1905211951Srmacklem/*
1906205941Srmacklem * Test for a lock. Return 1 if locked, 0 otherwise.
1907205941Srmacklem */
1908205941SrmacklemAPPLESTATIC int
1909205941Srmacklemnfsv4_testlock(struct nfsv4lock *lp)
1910205941Srmacklem{
1911205941Srmacklem
1912205941Srmacklem	if ((lp->nfslock_lock & NFSV4LOCK_LOCK) == 0 &&
1913205941Srmacklem	    lp->nfslock_usecnt == 0)
1914205941Srmacklem		return (0);
1915205941Srmacklem	return (1);
1916205941Srmacklem}
1917205941Srmacklem
1918205941Srmacklem/*
1919191783Srmacklem * Wake up anyone sleeping, waiting for this lock.
1920191783Srmacklem */
1921191783Srmacklemstatic void
1922191783Srmacklemnfsv4_wanted(struct nfsv4lock *lp)
1923191783Srmacklem{
1924191783Srmacklem
1925191783Srmacklem	if (lp->nfslock_lock & NFSV4LOCK_WANTED) {
1926191783Srmacklem		lp->nfslock_lock &= ~NFSV4LOCK_WANTED;
1927191783Srmacklem		wakeup((caddr_t)&lp->nfslock_lock);
1928191783Srmacklem	}
1929191783Srmacklem}
1930191783Srmacklem
1931191783Srmacklem/*
1932191783Srmacklem * Copy a string from an mbuf list into a character array.
1933191783Srmacklem * Return EBADRPC if there is an mbuf error,
1934191783Srmacklem * 0 otherwise.
1935191783Srmacklem */
1936191783SrmacklemAPPLESTATIC int
1937191783Srmacklemnfsrv_mtostr(struct nfsrv_descript *nd, char *str, int siz)
1938191783Srmacklem{
1939191783Srmacklem	char *cp;
1940191783Srmacklem	int xfer, len;
1941191783Srmacklem	mbuf_t mp;
1942191783Srmacklem	int rem, error = 0;
1943191783Srmacklem
1944191783Srmacklem	mp = nd->nd_md;
1945191783Srmacklem	cp = nd->nd_dpos;
1946191783Srmacklem	len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - cp;
1947191783Srmacklem	rem = NFSM_RNDUP(siz) - siz;
1948191783Srmacklem	while (siz > 0) {
1949191783Srmacklem		if (len > siz)
1950191783Srmacklem			xfer = siz;
1951191783Srmacklem		else
1952191783Srmacklem			xfer = len;
1953191783Srmacklem		NFSBCOPY(cp, str, xfer);
1954191783Srmacklem		str += xfer;
1955191783Srmacklem		siz -= xfer;
1956191783Srmacklem		if (siz > 0) {
1957191783Srmacklem			mp = mbuf_next(mp);
1958224086Szack			if (mp == NULL) {
1959224086Szack				error = EBADRPC;
1960224086Szack				goto out;
1961224086Szack			}
1962191783Srmacklem			cp = NFSMTOD(mp, caddr_t);
1963191783Srmacklem			len = mbuf_len(mp);
1964191783Srmacklem		} else {
1965191783Srmacklem			cp += xfer;
1966191783Srmacklem			len -= xfer;
1967191783Srmacklem		}
1968191783Srmacklem	}
1969191783Srmacklem	*str = '\0';
1970191783Srmacklem	nd->nd_dpos = cp;
1971191783Srmacklem	nd->nd_md = mp;
1972191783Srmacklem	if (rem > 0) {
1973191783Srmacklem		if (len < rem)
1974191783Srmacklem			error = nfsm_advance(nd, rem, len);
1975191783Srmacklem		else
1976191783Srmacklem			nd->nd_dpos += rem;
1977191783Srmacklem	}
1978224086Szack
1979224086Szackout:
1980224086Szack	NFSEXITCODE2(error, nd);
1981191783Srmacklem	return (error);
1982191783Srmacklem}
1983191783Srmacklem
1984191783Srmacklem/*
1985191783Srmacklem * Fill in the attributes as marked by the bitmap (V4).
1986191783Srmacklem */
1987191783SrmacklemAPPLESTATIC int
1988220645Srmacklemnfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
1989220645Srmacklem    NFSACL_T *saclp, struct vattr *vap, fhandle_t *fhp, int rderror,
1990220645Srmacklem    nfsattrbit_t *attrbitp, struct ucred *cred, NFSPROC_T *p, int isdgram,
1991220648Srmacklem    int reterr, int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno)
1992191783Srmacklem{
1993191783Srmacklem	int bitpos, retnum = 0;
1994191783Srmacklem	u_int32_t *tl;
1995191783Srmacklem	int siz, prefixnum, error;
1996191783Srmacklem	u_char *cp, namestr[NFSV4_SMALLSTR];
1997191783Srmacklem	nfsattrbit_t attrbits, retbits;
1998191783Srmacklem	nfsattrbit_t *retbitp = &retbits;
1999191783Srmacklem	u_int32_t freenum, *retnump;
2000191783Srmacklem	u_int64_t uquad;
2001191783Srmacklem	struct statfs fs;
2002191783Srmacklem	struct nfsfsinfo fsinf;
2003191783Srmacklem	struct timespec temptime;
2004191783Srmacklem	NFSACL_T *aclp, *naclp = NULL;
2005191783Srmacklem#ifdef QUOTA
2006191783Srmacklem	struct dqblk dqb;
2007191783Srmacklem	uid_t savuid;
2008191783Srmacklem#endif
2009191783Srmacklem
2010191783Srmacklem	/*
2011191783Srmacklem	 * First, set the bits that can be filled and get fsinfo.
2012191783Srmacklem	 */
2013191783Srmacklem	NFSSET_ATTRBIT(retbitp, attrbitp);
2014191783Srmacklem	/* If p and cred are NULL, it is a client side call */
2015191783Srmacklem	if (p == NULL && cred == NULL) {
2016191783Srmacklem		NFSCLRNOTSETABLE_ATTRBIT(retbitp);
2017191783Srmacklem		aclp = saclp;
2018191783Srmacklem	} else {
2019191783Srmacklem		NFSCLRNOTFILLABLE_ATTRBIT(retbitp);
2020192861Srmacklem		naclp = acl_alloc(M_WAITOK);
2021191783Srmacklem		aclp = naclp;
2022191783Srmacklem	}
2023191783Srmacklem	nfsvno_getfs(&fsinf, isdgram);
2024191783Srmacklem#ifndef APPLE
2025191783Srmacklem	/*
2026191783Srmacklem	 * Get the VFS_STATFS(), since some attributes need them.
2027191783Srmacklem	 */
2028191783Srmacklem	if (NFSISSETSTATFS_ATTRBIT(retbitp)) {
2029220645Srmacklem		error = VFS_STATFS(mp, &fs);
2030191783Srmacklem		if (error != 0) {
2031191783Srmacklem			if (reterr) {
2032191783Srmacklem				nd->nd_repstat = NFSERR_ACCES;
2033191783Srmacklem				return (0);
2034191783Srmacklem			}
2035191783Srmacklem			NFSCLRSTATFS_ATTRBIT(retbitp);
2036191783Srmacklem		}
2037191783Srmacklem	}
2038191783Srmacklem#endif
2039191783Srmacklem
2040191783Srmacklem	/*
2041191783Srmacklem	 * And the NFSv4 ACL...
2042191783Srmacklem	 */
2043200069Strasz	if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT) &&
2044200069Strasz	    (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
2045220648Srmacklem		supports_nfsv4acls == 0))) {
2046191783Srmacklem		NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT);
2047191783Srmacklem	}
2048191783Srmacklem	if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACL)) {
2049191783Srmacklem		if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
2050220648Srmacklem		    supports_nfsv4acls == 0)) {
2051191783Srmacklem			NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
2052191783Srmacklem		} else if (naclp != NULL) {
2053224081Szack			if (NFSVOPLOCK(vp, LK_SHARED) == 0) {
2054217535Srmacklem				error = VOP_ACCESSX(vp, VREAD_ACL, cred, p);
2055216700Srmacklem				if (error == 0)
2056216700Srmacklem					error = VOP_GETACL(vp, ACL_TYPE_NFS4,
2057216700Srmacklem					    naclp, cred, p);
2058224082Szack				NFSVOPUNLOCK(vp, 0);
2059216700Srmacklem			} else
2060216700Srmacklem				error = NFSERR_PERM;
2061191783Srmacklem			if (error != 0) {
2062191783Srmacklem				if (reterr) {
2063191783Srmacklem					nd->nd_repstat = NFSERR_ACCES;
2064191783Srmacklem					return (0);
2065191783Srmacklem				}
2066191783Srmacklem				NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
2067191783Srmacklem			}
2068191783Srmacklem		}
2069191783Srmacklem	}
2070191783Srmacklem	/*
2071191783Srmacklem	 * Put out the attribute bitmap for the ones being filled in
2072191783Srmacklem	 * and get the field for the number of attributes returned.
2073191783Srmacklem	 */
2074191783Srmacklem	prefixnum = nfsrv_putattrbit(nd, retbitp);
2075191783Srmacklem	NFSM_BUILD(retnump, u_int32_t *, NFSX_UNSIGNED);
2076191783Srmacklem	prefixnum += NFSX_UNSIGNED;
2077191783Srmacklem
2078191783Srmacklem	/*
2079191783Srmacklem	 * Now, loop around filling in the attributes for each bit set.
2080191783Srmacklem	 */
2081191783Srmacklem	for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
2082191783Srmacklem	    if (NFSISSET_ATTRBIT(retbitp, bitpos)) {
2083191783Srmacklem		switch (bitpos) {
2084191783Srmacklem		case NFSATTRBIT_SUPPORTEDATTRS:
2085191783Srmacklem			NFSSETSUPP_ATTRBIT(&attrbits);
2086191783Srmacklem			if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL)
2087220648Srmacklem			    && supports_nfsv4acls == 0)) {
2088191783Srmacklem			    NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACLSUPPORT);
2089191783Srmacklem			    NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACL);
2090191783Srmacklem			}
2091191783Srmacklem			retnum += nfsrv_putattrbit(nd, &attrbits);
2092191783Srmacklem			break;
2093191783Srmacklem		case NFSATTRBIT_TYPE:
2094191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2095191783Srmacklem			*tl = vtonfsv34_type(vap->va_type);
2096191783Srmacklem			retnum += NFSX_UNSIGNED;
2097191783Srmacklem			break;
2098191783Srmacklem		case NFSATTRBIT_FHEXPIRETYPE:
2099191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2100191783Srmacklem			*tl = txdr_unsigned(NFSV4FHTYPE_PERSISTENT);
2101191783Srmacklem			retnum += NFSX_UNSIGNED;
2102191783Srmacklem			break;
2103191783Srmacklem		case NFSATTRBIT_CHANGE:
2104191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2105191783Srmacklem			txdr_hyper(vap->va_filerev, tl);
2106191783Srmacklem			retnum += NFSX_HYPER;
2107191783Srmacklem			break;
2108191783Srmacklem		case NFSATTRBIT_SIZE:
2109191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2110191783Srmacklem			txdr_hyper(vap->va_size, tl);
2111191783Srmacklem			retnum += NFSX_HYPER;
2112191783Srmacklem			break;
2113191783Srmacklem		case NFSATTRBIT_LINKSUPPORT:
2114191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2115191783Srmacklem			if (fsinf.fs_properties & NFSV3FSINFO_LINK)
2116191783Srmacklem				*tl = newnfs_true;
2117191783Srmacklem			else
2118191783Srmacklem				*tl = newnfs_false;
2119191783Srmacklem			retnum += NFSX_UNSIGNED;
2120191783Srmacklem			break;
2121191783Srmacklem		case NFSATTRBIT_SYMLINKSUPPORT:
2122191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2123191783Srmacklem			if (fsinf.fs_properties & NFSV3FSINFO_SYMLINK)
2124191783Srmacklem				*tl = newnfs_true;
2125191783Srmacklem			else
2126191783Srmacklem				*tl = newnfs_false;
2127191783Srmacklem			retnum += NFSX_UNSIGNED;
2128191783Srmacklem			break;
2129191783Srmacklem		case NFSATTRBIT_NAMEDATTR:
2130191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2131191783Srmacklem			*tl = newnfs_false;
2132191783Srmacklem			retnum += NFSX_UNSIGNED;
2133191783Srmacklem			break;
2134191783Srmacklem		case NFSATTRBIT_FSID:
2135191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_V4FSID);
2136191783Srmacklem			*tl++ = 0;
2137220645Srmacklem			*tl++ = txdr_unsigned(mp->mnt_stat.f_fsid.val[0]);
2138191783Srmacklem			*tl++ = 0;
2139220645Srmacklem			*tl = txdr_unsigned(mp->mnt_stat.f_fsid.val[1]);
2140191783Srmacklem			retnum += NFSX_V4FSID;
2141191783Srmacklem			break;
2142191783Srmacklem		case NFSATTRBIT_UNIQUEHANDLES:
2143191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2144191783Srmacklem			*tl = newnfs_true;
2145191783Srmacklem			retnum += NFSX_UNSIGNED;
2146191783Srmacklem			break;
2147191783Srmacklem		case NFSATTRBIT_LEASETIME:
2148191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2149191783Srmacklem			*tl = txdr_unsigned(nfsrv_lease);
2150191783Srmacklem			retnum += NFSX_UNSIGNED;
2151191783Srmacklem			break;
2152191783Srmacklem		case NFSATTRBIT_RDATTRERROR:
2153191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2154191783Srmacklem			*tl = txdr_unsigned(rderror);
2155191783Srmacklem			retnum += NFSX_UNSIGNED;
2156191783Srmacklem			break;
2157191783Srmacklem		/*
2158191783Srmacklem		 * Recommended Attributes. (Only the supported ones.)
2159191783Srmacklem		 */
2160191783Srmacklem		case NFSATTRBIT_ACL:
2161191783Srmacklem			retnum += nfsrv_buildacl(nd, aclp, vnode_vtype(vp), p);
2162191783Srmacklem			break;
2163191783Srmacklem		case NFSATTRBIT_ACLSUPPORT:
2164191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2165191783Srmacklem			*tl = txdr_unsigned(NFSV4ACE_SUPTYPES);
2166191783Srmacklem			retnum += NFSX_UNSIGNED;
2167191783Srmacklem			break;
2168191783Srmacklem		case NFSATTRBIT_CANSETTIME:
2169191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2170191783Srmacklem			if (fsinf.fs_properties & NFSV3FSINFO_CANSETTIME)
2171191783Srmacklem				*tl = newnfs_true;
2172191783Srmacklem			else
2173191783Srmacklem				*tl = newnfs_false;
2174191783Srmacklem			retnum += NFSX_UNSIGNED;
2175191783Srmacklem			break;
2176191783Srmacklem		case NFSATTRBIT_CASEINSENSITIVE:
2177191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2178191783Srmacklem			*tl = newnfs_false;
2179191783Srmacklem			retnum += NFSX_UNSIGNED;
2180191783Srmacklem			break;
2181191783Srmacklem		case NFSATTRBIT_CASEPRESERVING:
2182191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2183191783Srmacklem			*tl = newnfs_true;
2184191783Srmacklem			retnum += NFSX_UNSIGNED;
2185191783Srmacklem			break;
2186191783Srmacklem		case NFSATTRBIT_CHOWNRESTRICTED:
2187191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2188224121Szack			*tl = newnfs_true;
2189191783Srmacklem			retnum += NFSX_UNSIGNED;
2190191783Srmacklem			break;
2191191783Srmacklem		case NFSATTRBIT_FILEHANDLE:
2192191783Srmacklem			retnum += nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
2193191783Srmacklem			break;
2194191783Srmacklem		case NFSATTRBIT_FILEID:
2195191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2196191783Srmacklem			*tl++ = 0;
2197191783Srmacklem			*tl = txdr_unsigned(vap->va_fileid);
2198191783Srmacklem			retnum += NFSX_HYPER;
2199191783Srmacklem			break;
2200191783Srmacklem		case NFSATTRBIT_FILESAVAIL:
2201191783Srmacklem			/*
2202191783Srmacklem			 * Check quota and use min(quota, f_ffree).
2203191783Srmacklem			 */
2204191783Srmacklem			freenum = fs.f_ffree;
2205191783Srmacklem#ifdef QUOTA
2206191783Srmacklem			/*
2207191783Srmacklem			 * ufs_quotactl() insists that the uid argument
2208191783Srmacklem			 * equal p_ruid for non-root quota access, so
2209191783Srmacklem			 * we'll just make sure that's the case.
2210191783Srmacklem			 */
2211191783Srmacklem			savuid = p->p_cred->p_ruid;
2212191783Srmacklem			p->p_cred->p_ruid = cred->cr_uid;
2213220645Srmacklem			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2214191990Sattilio			    cred->cr_uid, (caddr_t)&dqb))
2215191783Srmacklem			    freenum = min(dqb.dqb_isoftlimit-dqb.dqb_curinodes,
2216191783Srmacklem				freenum);
2217191783Srmacklem			p->p_cred->p_ruid = savuid;
2218191783Srmacklem#endif	/* QUOTA */
2219191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2220191783Srmacklem			*tl++ = 0;
2221191783Srmacklem			*tl = txdr_unsigned(freenum);
2222191783Srmacklem			retnum += NFSX_HYPER;
2223191783Srmacklem			break;
2224191783Srmacklem		case NFSATTRBIT_FILESFREE:
2225191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2226191783Srmacklem			*tl++ = 0;
2227191783Srmacklem			*tl = txdr_unsigned(fs.f_ffree);
2228191783Srmacklem			retnum += NFSX_HYPER;
2229191783Srmacklem			break;
2230191783Srmacklem		case NFSATTRBIT_FILESTOTAL:
2231191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2232191783Srmacklem			*tl++ = 0;
2233191783Srmacklem			*tl = txdr_unsigned(fs.f_files);
2234191783Srmacklem			retnum += NFSX_HYPER;
2235191783Srmacklem			break;
2236191783Srmacklem		case NFSATTRBIT_FSLOCATIONS:
2237191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2238191783Srmacklem			*tl++ = 0;
2239191783Srmacklem			*tl = 0;
2240191783Srmacklem			retnum += 2 * NFSX_UNSIGNED;
2241191783Srmacklem			break;
2242191783Srmacklem		case NFSATTRBIT_HOMOGENEOUS:
2243191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2244191783Srmacklem			if (fsinf.fs_properties & NFSV3FSINFO_HOMOGENEOUS)
2245191783Srmacklem				*tl = newnfs_true;
2246191783Srmacklem			else
2247191783Srmacklem				*tl = newnfs_false;
2248191783Srmacklem			retnum += NFSX_UNSIGNED;
2249191783Srmacklem			break;
2250191783Srmacklem		case NFSATTRBIT_MAXFILESIZE:
2251191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2252191783Srmacklem			uquad = NFSRV_MAXFILESIZE;
2253191783Srmacklem			txdr_hyper(uquad, tl);
2254191783Srmacklem			retnum += NFSX_HYPER;
2255191783Srmacklem			break;
2256191783Srmacklem		case NFSATTRBIT_MAXLINK:
2257191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2258191783Srmacklem			*tl = txdr_unsigned(LINK_MAX);
2259191783Srmacklem			retnum += NFSX_UNSIGNED;
2260191783Srmacklem			break;
2261191783Srmacklem		case NFSATTRBIT_MAXNAME:
2262191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2263191783Srmacklem			*tl = txdr_unsigned(NFS_MAXNAMLEN);
2264191783Srmacklem			retnum += NFSX_UNSIGNED;
2265191783Srmacklem			break;
2266191783Srmacklem		case NFSATTRBIT_MAXREAD:
2267191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2268191783Srmacklem			*tl++ = 0;
2269191783Srmacklem			*tl = txdr_unsigned(fsinf.fs_rtmax);
2270191783Srmacklem			retnum += NFSX_HYPER;
2271191783Srmacklem			break;
2272191783Srmacklem		case NFSATTRBIT_MAXWRITE:
2273191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2274191783Srmacklem			*tl++ = 0;
2275191783Srmacklem			*tl = txdr_unsigned(fsinf.fs_wtmax);
2276191783Srmacklem			retnum += NFSX_HYPER;
2277191783Srmacklem			break;
2278191783Srmacklem		case NFSATTRBIT_MODE:
2279191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2280191783Srmacklem			*tl = vtonfsv34_mode(vap->va_mode);
2281191783Srmacklem			retnum += NFSX_UNSIGNED;
2282191783Srmacklem			break;
2283191783Srmacklem		case NFSATTRBIT_NOTRUNC:
2284191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2285191783Srmacklem			*tl = newnfs_true;
2286191783Srmacklem			retnum += NFSX_UNSIGNED;
2287191783Srmacklem			break;
2288191783Srmacklem		case NFSATTRBIT_NUMLINKS:
2289191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2290191783Srmacklem			*tl = txdr_unsigned(vap->va_nlink);
2291191783Srmacklem			retnum += NFSX_UNSIGNED;
2292191783Srmacklem			break;
2293191783Srmacklem		case NFSATTRBIT_OWNER:
2294191783Srmacklem			cp = namestr;
2295191783Srmacklem			nfsv4_uidtostr(vap->va_uid, &cp, &siz, p);
2296191783Srmacklem			retnum += nfsm_strtom(nd, cp, siz);
2297191783Srmacklem			if (cp != namestr)
2298191783Srmacklem				free(cp, M_NFSSTRING);
2299191783Srmacklem			break;
2300191783Srmacklem		case NFSATTRBIT_OWNERGROUP:
2301191783Srmacklem			cp = namestr;
2302191783Srmacklem			nfsv4_gidtostr(vap->va_gid, &cp, &siz, p);
2303191783Srmacklem			retnum += nfsm_strtom(nd, cp, siz);
2304191783Srmacklem			if (cp != namestr)
2305191783Srmacklem				free(cp, M_NFSSTRING);
2306191783Srmacklem			break;
2307191783Srmacklem		case NFSATTRBIT_QUOTAHARD:
2308191783Srmacklem			if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
2309191783Srmacklem				freenum = fs.f_bfree;
2310191783Srmacklem			else
2311191783Srmacklem				freenum = fs.f_bavail;
2312191783Srmacklem#ifdef QUOTA
2313191783Srmacklem			/*
2314191783Srmacklem			 * ufs_quotactl() insists that the uid argument
2315191783Srmacklem			 * equal p_ruid for non-root quota access, so
2316191783Srmacklem			 * we'll just make sure that's the case.
2317191783Srmacklem			 */
2318191783Srmacklem			savuid = p->p_cred->p_ruid;
2319191783Srmacklem			p->p_cred->p_ruid = cred->cr_uid;
2320220645Srmacklem			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2321191990Sattilio			    cred->cr_uid, (caddr_t)&dqb))
2322191783Srmacklem			    freenum = min(dqb.dqb_bhardlimit, freenum);
2323191783Srmacklem			p->p_cred->p_ruid = savuid;
2324191783Srmacklem#endif	/* QUOTA */
2325191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2326191783Srmacklem			uquad = (u_int64_t)freenum;
2327191783Srmacklem			NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
2328191783Srmacklem			txdr_hyper(uquad, tl);
2329191783Srmacklem			retnum += NFSX_HYPER;
2330191783Srmacklem			break;
2331191783Srmacklem		case NFSATTRBIT_QUOTASOFT:
2332191783Srmacklem			if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
2333191783Srmacklem				freenum = fs.f_bfree;
2334191783Srmacklem			else
2335191783Srmacklem				freenum = fs.f_bavail;
2336191783Srmacklem#ifdef QUOTA
2337191783Srmacklem			/*
2338191783Srmacklem			 * ufs_quotactl() insists that the uid argument
2339191783Srmacklem			 * equal p_ruid for non-root quota access, so
2340191783Srmacklem			 * we'll just make sure that's the case.
2341191783Srmacklem			 */
2342191783Srmacklem			savuid = p->p_cred->p_ruid;
2343191783Srmacklem			p->p_cred->p_ruid = cred->cr_uid;
2344220645Srmacklem			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2345191990Sattilio			    cred->cr_uid, (caddr_t)&dqb))
2346191783Srmacklem			    freenum = min(dqb.dqb_bsoftlimit, freenum);
2347191783Srmacklem			p->p_cred->p_ruid = savuid;
2348191783Srmacklem#endif	/* QUOTA */
2349191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2350191783Srmacklem			uquad = (u_int64_t)freenum;
2351191783Srmacklem			NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
2352191783Srmacklem			txdr_hyper(uquad, tl);
2353191783Srmacklem			retnum += NFSX_HYPER;
2354191783Srmacklem			break;
2355191783Srmacklem		case NFSATTRBIT_QUOTAUSED:
2356191783Srmacklem			freenum = 0;
2357191783Srmacklem#ifdef QUOTA
2358191783Srmacklem			/*
2359191783Srmacklem			 * ufs_quotactl() insists that the uid argument
2360191783Srmacklem			 * equal p_ruid for non-root quota access, so
2361191783Srmacklem			 * we'll just make sure that's the case.
2362191783Srmacklem			 */
2363191783Srmacklem			savuid = p->p_cred->p_ruid;
2364191783Srmacklem			p->p_cred->p_ruid = cred->cr_uid;
2365220645Srmacklem			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2366191990Sattilio			    cred->cr_uid, (caddr_t)&dqb))
2367191783Srmacklem			    freenum = dqb.dqb_curblocks;
2368191783Srmacklem			p->p_cred->p_ruid = savuid;
2369191783Srmacklem#endif	/* QUOTA */
2370191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2371191783Srmacklem			uquad = (u_int64_t)freenum;
2372191783Srmacklem			NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
2373191783Srmacklem			txdr_hyper(uquad, tl);
2374191783Srmacklem			retnum += NFSX_HYPER;
2375191783Srmacklem			break;
2376191783Srmacklem		case NFSATTRBIT_RAWDEV:
2377191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_V4SPECDATA);
2378191783Srmacklem			*tl++ = txdr_unsigned(NFSMAJOR(vap->va_rdev));
2379191783Srmacklem			*tl = txdr_unsigned(NFSMINOR(vap->va_rdev));
2380191783Srmacklem			retnum += NFSX_V4SPECDATA;
2381191783Srmacklem			break;
2382191783Srmacklem		case NFSATTRBIT_SPACEAVAIL:
2383191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2384191783Srmacklem			if (priv_check_cred(cred, PRIV_VFS_BLOCKRESERVE, 0))
2385191783Srmacklem				uquad = (u_int64_t)fs.f_bfree;
2386191783Srmacklem			else
2387191783Srmacklem				uquad = (u_int64_t)fs.f_bavail;
2388191783Srmacklem			uquad *= fs.f_bsize;
2389191783Srmacklem			txdr_hyper(uquad, tl);
2390191783Srmacklem			retnum += NFSX_HYPER;
2391191783Srmacklem			break;
2392191783Srmacklem		case NFSATTRBIT_SPACEFREE:
2393191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2394191783Srmacklem			uquad = (u_int64_t)fs.f_bfree;
2395191783Srmacklem			uquad *= fs.f_bsize;
2396191783Srmacklem			txdr_hyper(uquad, tl);
2397191783Srmacklem			retnum += NFSX_HYPER;
2398191783Srmacklem			break;
2399191783Srmacklem		case NFSATTRBIT_SPACETOTAL:
2400191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2401191783Srmacklem			uquad = (u_int64_t)fs.f_blocks;
2402191783Srmacklem			uquad *= fs.f_bsize;
2403191783Srmacklem			txdr_hyper(uquad, tl);
2404191783Srmacklem			retnum += NFSX_HYPER;
2405191783Srmacklem			break;
2406191783Srmacklem		case NFSATTRBIT_SPACEUSED:
2407191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2408191783Srmacklem			txdr_hyper(vap->va_bytes, tl);
2409191783Srmacklem			retnum += NFSX_HYPER;
2410191783Srmacklem			break;
2411191783Srmacklem		case NFSATTRBIT_TIMEACCESS:
2412191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2413191783Srmacklem			txdr_nfsv4time(&vap->va_atime, tl);
2414191783Srmacklem			retnum += NFSX_V4TIME;
2415191783Srmacklem			break;
2416191783Srmacklem		case NFSATTRBIT_TIMEACCESSSET:
2417245508Sjhb			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
2418191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
2419191783Srmacklem				*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
2420191783Srmacklem				txdr_nfsv4time(&vap->va_atime, tl);
2421191783Srmacklem				retnum += NFSX_V4SETTIME;
2422191783Srmacklem			} else {
2423191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2424191783Srmacklem				*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
2425191783Srmacklem				retnum += NFSX_UNSIGNED;
2426191783Srmacklem			}
2427191783Srmacklem			break;
2428191783Srmacklem		case NFSATTRBIT_TIMEDELTA:
2429191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2430191783Srmacklem			temptime.tv_sec = 0;
2431191783Srmacklem			temptime.tv_nsec = 1000000000 / hz;
2432191783Srmacklem			txdr_nfsv4time(&temptime, tl);
2433191783Srmacklem			retnum += NFSX_V4TIME;
2434191783Srmacklem			break;
2435191783Srmacklem		case NFSATTRBIT_TIMEMETADATA:
2436191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2437191783Srmacklem			txdr_nfsv4time(&vap->va_ctime, tl);
2438191783Srmacklem			retnum += NFSX_V4TIME;
2439191783Srmacklem			break;
2440191783Srmacklem		case NFSATTRBIT_TIMEMODIFY:
2441191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2442191783Srmacklem			txdr_nfsv4time(&vap->va_mtime, tl);
2443191783Srmacklem			retnum += NFSX_V4TIME;
2444191783Srmacklem			break;
2445191783Srmacklem		case NFSATTRBIT_TIMEMODIFYSET:
2446245508Sjhb			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
2447191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
2448191783Srmacklem				*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
2449191783Srmacklem				txdr_nfsv4time(&vap->va_mtime, tl);
2450191783Srmacklem				retnum += NFSX_V4SETTIME;
2451191783Srmacklem			} else {
2452191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2453191783Srmacklem				*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
2454191783Srmacklem				retnum += NFSX_UNSIGNED;
2455191783Srmacklem			}
2456191783Srmacklem			break;
2457191783Srmacklem		case NFSATTRBIT_MOUNTEDONFILEID:
2458191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2459220645Srmacklem			if (at_root != 0)
2460220645Srmacklem				uquad = mounted_on_fileno;
2461191783Srmacklem			else
2462220645Srmacklem				uquad = (u_int64_t)vap->va_fileid;
2463220645Srmacklem			txdr_hyper(uquad, tl);
2464191783Srmacklem			retnum += NFSX_HYPER;
2465191783Srmacklem			break;
2466191783Srmacklem		default:
2467191783Srmacklem			printf("EEK! Bad V4 attribute bitpos=%d\n", bitpos);
2468191783Srmacklem		};
2469191783Srmacklem	    }
2470191783Srmacklem	}
2471191783Srmacklem	if (naclp != NULL)
2472191783Srmacklem		acl_free(naclp);
2473191783Srmacklem	*retnump = txdr_unsigned(retnum);
2474191783Srmacklem	return (retnum + prefixnum);
2475191783Srmacklem}
2476191783Srmacklem
2477191783Srmacklem/*
2478191783Srmacklem * Put the attribute bits onto an mbuf list.
2479191783Srmacklem * Return the number of bytes of output generated.
2480191783Srmacklem */
2481191783SrmacklemAPPLESTATIC int
2482191783Srmacklemnfsrv_putattrbit(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp)
2483191783Srmacklem{
2484191783Srmacklem	u_int32_t *tl;
2485191783Srmacklem	int cnt, i, bytesize;
2486191783Srmacklem
2487191783Srmacklem	for (cnt = NFSATTRBIT_MAXWORDS; cnt > 0; cnt--)
2488191783Srmacklem		if (attrbitp->bits[cnt - 1])
2489191783Srmacklem			break;
2490191783Srmacklem	bytesize = (cnt + 1) * NFSX_UNSIGNED;
2491191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, bytesize);
2492191783Srmacklem	*tl++ = txdr_unsigned(cnt);
2493191783Srmacklem	for (i = 0; i < cnt; i++)
2494191783Srmacklem		*tl++ = txdr_unsigned(attrbitp->bits[i]);
2495191783Srmacklem	return (bytesize);
2496191783Srmacklem}
2497191783Srmacklem
2498191783Srmacklem/*
2499191783Srmacklem * Convert a uid to a string.
2500191783Srmacklem * If the lookup fails, just output the digits.
2501191783Srmacklem * uid - the user id
2502191783Srmacklem * cpp - points to a buffer of size NFSV4_SMALLSTR
2503191783Srmacklem *       (malloc a larger one, as required)
2504191783Srmacklem * retlenp - pointer to length to be returned
2505191783Srmacklem */
2506191783SrmacklemAPPLESTATIC void
2507191783Srmacklemnfsv4_uidtostr(uid_t uid, u_char **cpp, int *retlenp, NFSPROC_T *p)
2508191783Srmacklem{
2509191783Srmacklem	int i;
2510191783Srmacklem	struct nfsusrgrp *usrp;
2511191783Srmacklem	u_char *cp = *cpp;
2512191783Srmacklem	uid_t tmp;
2513191783Srmacklem	int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
2514191783Srmacklem
2515191783Srmacklem	cnt = 0;
2516191783Srmacklemtryagain:
2517191783Srmacklem	NFSLOCKNAMEID();
2518191783Srmacklem	if (nfsrv_dnsname) {
2519191783Srmacklem		/*
2520191783Srmacklem		 * Always map nfsrv_defaultuid to "nobody".
2521191783Srmacklem		 */
2522191783Srmacklem		if (uid == nfsrv_defaultuid) {
2523191783Srmacklem			i = nfsrv_dnsnamelen + 7;
2524191783Srmacklem			if (i > len) {
2525191783Srmacklem				NFSUNLOCKNAMEID();
2526191783Srmacklem				if (len > NFSV4_SMALLSTR)
2527191783Srmacklem					free(cp, M_NFSSTRING);
2528191783Srmacklem				cp = malloc(i, M_NFSSTRING, M_WAITOK);
2529191783Srmacklem				*cpp = cp;
2530191783Srmacklem				len = i;
2531191783Srmacklem				goto tryagain;
2532191783Srmacklem			}
2533191783Srmacklem			*retlenp = i;
2534191783Srmacklem			NFSBCOPY("nobody@", cp, 7);
2535191783Srmacklem			cp += 7;
2536191783Srmacklem			NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2537191783Srmacklem			NFSUNLOCKNAMEID();
2538191783Srmacklem			return;
2539191783Srmacklem		}
2540191783Srmacklem		hasampersand = 0;
2541191783Srmacklem		LIST_FOREACH(usrp, NFSUSERHASH(uid), lug_numhash) {
2542191783Srmacklem			if (usrp->lug_uid == uid) {
2543191783Srmacklem				if (usrp->lug_expiry < NFSD_MONOSEC)
2544191783Srmacklem					break;
2545191783Srmacklem				/*
2546191783Srmacklem				 * If the name doesn't already have an '@'
2547191783Srmacklem				 * in it, append @domainname to it.
2548191783Srmacklem				 */
2549191783Srmacklem				for (i = 0; i < usrp->lug_namelen; i++) {
2550191783Srmacklem					if (usrp->lug_name[i] == '@') {
2551191783Srmacklem						hasampersand = 1;
2552191783Srmacklem						break;
2553191783Srmacklem					}
2554191783Srmacklem				}
2555191783Srmacklem				if (hasampersand)
2556191783Srmacklem					i = usrp->lug_namelen;
2557191783Srmacklem				else
2558191783Srmacklem					i = usrp->lug_namelen +
2559191783Srmacklem					    nfsrv_dnsnamelen + 1;
2560191783Srmacklem				if (i > len) {
2561191783Srmacklem					NFSUNLOCKNAMEID();
2562191783Srmacklem					if (len > NFSV4_SMALLSTR)
2563191783Srmacklem						free(cp, M_NFSSTRING);
2564191783Srmacklem					cp = malloc(i, M_NFSSTRING, M_WAITOK);
2565191783Srmacklem					*cpp = cp;
2566191783Srmacklem					len = i;
2567191783Srmacklem					goto tryagain;
2568191783Srmacklem				}
2569191783Srmacklem				*retlenp = i;
2570191783Srmacklem				NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
2571191783Srmacklem				if (!hasampersand) {
2572191783Srmacklem					cp += usrp->lug_namelen;
2573191783Srmacklem					*cp++ = '@';
2574191783Srmacklem					NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2575191783Srmacklem				}
2576191783Srmacklem				TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
2577191783Srmacklem				TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
2578191783Srmacklem				NFSUNLOCKNAMEID();
2579191783Srmacklem				return;
2580191783Srmacklem			}
2581191783Srmacklem		}
2582191783Srmacklem		NFSUNLOCKNAMEID();
2583191783Srmacklem		cnt++;
2584191783Srmacklem		ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0,
2585191783Srmacklem		    NULL, p);
2586191783Srmacklem		if (ret == 0 && cnt < 2)
2587191783Srmacklem			goto tryagain;
2588191783Srmacklem	} else {
2589191783Srmacklem		NFSUNLOCKNAMEID();
2590191783Srmacklem	}
2591191783Srmacklem
2592191783Srmacklem	/*
2593191783Srmacklem	 * No match, just return a string of digits.
2594191783Srmacklem	 */
2595191783Srmacklem	tmp = uid;
2596191783Srmacklem	i = 0;
2597191783Srmacklem	while (tmp || i == 0) {
2598191783Srmacklem		tmp /= 10;
2599191783Srmacklem		i++;
2600191783Srmacklem	}
2601191783Srmacklem	len = (i > len) ? len : i;
2602191783Srmacklem	*retlenp = len;
2603191783Srmacklem	cp += (len - 1);
2604191783Srmacklem	tmp = uid;
2605191783Srmacklem	for (i = 0; i < len; i++) {
2606191783Srmacklem		*cp-- = '0' + (tmp % 10);
2607191783Srmacklem		tmp /= 10;
2608191783Srmacklem	}
2609191783Srmacklem	return;
2610191783Srmacklem}
2611191783Srmacklem
2612191783Srmacklem/*
2613191783Srmacklem * Convert a string to a uid.
2614191783Srmacklem * If no conversion is possible return NFSERR_BADOWNER, otherwise
2615191783Srmacklem * return 0.
2616240720Srmacklem * If this is called from a client side mount using AUTH_SYS and the
2617240720Srmacklem * string is made up entirely of digits, just convert the string to
2618240720Srmacklem * a number.
2619191783Srmacklem */
2620191783SrmacklemAPPLESTATIC int
2621240720Srmacklemnfsv4_strtouid(struct nfsrv_descript *nd, u_char *str, int len, uid_t *uidp,
2622240720Srmacklem    NFSPROC_T *p)
2623191783Srmacklem{
2624191783Srmacklem	int i;
2625240720Srmacklem	char *cp, *endstr, *str0;
2626191783Srmacklem	struct nfsusrgrp *usrp;
2627191783Srmacklem	int cnt, ret;
2628224086Szack	int error = 0;
2629240720Srmacklem	uid_t tuid;
2630191783Srmacklem
2631224086Szack	if (len == 0) {
2632224086Szack		error = NFSERR_BADOWNER;
2633224086Szack		goto out;
2634224086Szack	}
2635240720Srmacklem	/* If a string of digits and an AUTH_SYS mount, just convert it. */
2636240720Srmacklem	str0 = str;
2637240720Srmacklem	tuid = (uid_t)strtoul(str0, &endstr, 10);
2638240720Srmacklem	if ((endstr - str0) == len &&
2639240720Srmacklem	    (nd->nd_flag & (ND_KERBV | ND_NFSCL)) == ND_NFSCL) {
2640240720Srmacklem		*uidp = tuid;
2641240720Srmacklem		goto out;
2642240720Srmacklem	}
2643191783Srmacklem	/*
2644191783Srmacklem	 * Look for an '@'.
2645191783Srmacklem	 */
2646240720Srmacklem	cp = strchr(str0, '@');
2647240720Srmacklem	if (cp != NULL)
2648240720Srmacklem		i = (int)(cp++ - str0);
2649240720Srmacklem	else
2650240720Srmacklem		i = len;
2651191783Srmacklem
2652191783Srmacklem	cnt = 0;
2653191783Srmacklemtryagain:
2654191783Srmacklem	NFSLOCKNAMEID();
2655191783Srmacklem	/*
2656191783Srmacklem	 * If an '@' is found and the domain name matches, search for the name
2657191783Srmacklem	 * with dns stripped off.
2658191783Srmacklem	 * Mixed case alpahbetics will match for the domain name, but all
2659191783Srmacklem	 * upper case will not.
2660191783Srmacklem	 */
2661191783Srmacklem	if (cnt == 0 && i < len && i > 0 && nfsrv_dnsname &&
2662191783Srmacklem	    (len - 1 - i) == nfsrv_dnsnamelen &&
2663191783Srmacklem	    !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
2664191783Srmacklem		len -= (nfsrv_dnsnamelen + 1);
2665191783Srmacklem		*(cp - 1) = '\0';
2666191783Srmacklem	}
2667191783Srmacklem
2668191783Srmacklem	/*
2669191783Srmacklem	 * Check for the special case of "nobody".
2670191783Srmacklem	 */
2671191783Srmacklem	if (len == 6 && !NFSBCMP(str, "nobody", 6)) {
2672191783Srmacklem		*uidp = nfsrv_defaultuid;
2673191783Srmacklem		NFSUNLOCKNAMEID();
2674224086Szack		error = 0;
2675224086Szack		goto out;
2676191783Srmacklem	}
2677191783Srmacklem
2678191783Srmacklem	LIST_FOREACH(usrp, NFSUSERNAMEHASH(str, len), lug_namehash) {
2679191783Srmacklem		if (usrp->lug_namelen == len &&
2680191783Srmacklem		    !NFSBCMP(usrp->lug_name, str, len)) {
2681191783Srmacklem			if (usrp->lug_expiry < NFSD_MONOSEC)
2682191783Srmacklem				break;
2683191783Srmacklem			*uidp = usrp->lug_uid;
2684191783Srmacklem			TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
2685191783Srmacklem			TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
2686191783Srmacklem			NFSUNLOCKNAMEID();
2687224086Szack			error = 0;
2688224086Szack			goto out;
2689191783Srmacklem		}
2690191783Srmacklem	}
2691191783Srmacklem	NFSUNLOCKNAMEID();
2692191783Srmacklem	cnt++;
2693191783Srmacklem	ret = nfsrv_getuser(RPCNFSUSERD_GETUSER, (uid_t)0, (gid_t)0,
2694191783Srmacklem	    str, p);
2695191783Srmacklem	if (ret == 0 && cnt < 2)
2696191783Srmacklem		goto tryagain;
2697224086Szack	error = NFSERR_BADOWNER;
2698224086Szack
2699224086Szackout:
2700224086Szack	NFSEXITCODE(error);
2701224086Szack	return (error);
2702191783Srmacklem}
2703191783Srmacklem
2704191783Srmacklem/*
2705191783Srmacklem * Convert a gid to a string.
2706191783Srmacklem * gid - the group id
2707191783Srmacklem * cpp - points to a buffer of size NFSV4_SMALLSTR
2708191783Srmacklem *       (malloc a larger one, as required)
2709191783Srmacklem * retlenp - pointer to length to be returned
2710191783Srmacklem */
2711191783SrmacklemAPPLESTATIC void
2712191783Srmacklemnfsv4_gidtostr(gid_t gid, u_char **cpp, int *retlenp, NFSPROC_T *p)
2713191783Srmacklem{
2714191783Srmacklem	int i;
2715191783Srmacklem	struct nfsusrgrp *usrp;
2716191783Srmacklem	u_char *cp = *cpp;
2717191783Srmacklem	gid_t tmp;
2718191783Srmacklem	int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
2719191783Srmacklem
2720191783Srmacklem	cnt = 0;
2721191783Srmacklemtryagain:
2722191783Srmacklem	NFSLOCKNAMEID();
2723191783Srmacklem	if (nfsrv_dnsname) {
2724191783Srmacklem		/*
2725191783Srmacklem		 * Always map nfsrv_defaultgid to "nogroup".
2726191783Srmacklem		 */
2727191783Srmacklem		if (gid == nfsrv_defaultgid) {
2728191783Srmacklem			i = nfsrv_dnsnamelen + 8;
2729191783Srmacklem			if (i > len) {
2730191783Srmacklem				NFSUNLOCKNAMEID();
2731191783Srmacklem				if (len > NFSV4_SMALLSTR)
2732191783Srmacklem					free(cp, M_NFSSTRING);
2733191783Srmacklem				cp = malloc(i, M_NFSSTRING, M_WAITOK);
2734191783Srmacklem				*cpp = cp;
2735191783Srmacklem				len = i;
2736191783Srmacklem				goto tryagain;
2737191783Srmacklem			}
2738191783Srmacklem			*retlenp = i;
2739191783Srmacklem			NFSBCOPY("nogroup@", cp, 8);
2740191783Srmacklem			cp += 8;
2741191783Srmacklem			NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2742191783Srmacklem			NFSUNLOCKNAMEID();
2743191783Srmacklem			return;
2744191783Srmacklem		}
2745191783Srmacklem		hasampersand = 0;
2746191783Srmacklem		LIST_FOREACH(usrp, NFSGROUPHASH(gid), lug_numhash) {
2747191783Srmacklem			if (usrp->lug_gid == gid) {
2748191783Srmacklem				if (usrp->lug_expiry < NFSD_MONOSEC)
2749191783Srmacklem					break;
2750191783Srmacklem				/*
2751191783Srmacklem				 * If the name doesn't already have an '@'
2752191783Srmacklem				 * in it, append @domainname to it.
2753191783Srmacklem				 */
2754191783Srmacklem				for (i = 0; i < usrp->lug_namelen; i++) {
2755191783Srmacklem					if (usrp->lug_name[i] == '@') {
2756191783Srmacklem						hasampersand = 1;
2757191783Srmacklem						break;
2758191783Srmacklem					}
2759191783Srmacklem				}
2760191783Srmacklem				if (hasampersand)
2761191783Srmacklem					i = usrp->lug_namelen;
2762191783Srmacklem				else
2763191783Srmacklem					i = usrp->lug_namelen +
2764191783Srmacklem					    nfsrv_dnsnamelen + 1;
2765191783Srmacklem				if (i > len) {
2766191783Srmacklem					NFSUNLOCKNAMEID();
2767191783Srmacklem					if (len > NFSV4_SMALLSTR)
2768191783Srmacklem						free(cp, M_NFSSTRING);
2769191783Srmacklem					cp = malloc(i, M_NFSSTRING, M_WAITOK);
2770191783Srmacklem					*cpp = cp;
2771191783Srmacklem					len = i;
2772191783Srmacklem					goto tryagain;
2773191783Srmacklem				}
2774191783Srmacklem				*retlenp = i;
2775191783Srmacklem				NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
2776191783Srmacklem				if (!hasampersand) {
2777191783Srmacklem					cp += usrp->lug_namelen;
2778191783Srmacklem					*cp++ = '@';
2779191783Srmacklem					NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2780191783Srmacklem				}
2781191783Srmacklem				TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
2782191783Srmacklem				TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
2783191783Srmacklem				NFSUNLOCKNAMEID();
2784191783Srmacklem				return;
2785191783Srmacklem			}
2786191783Srmacklem		}
2787191783Srmacklem		NFSUNLOCKNAMEID();
2788191783Srmacklem		cnt++;
2789191783Srmacklem		ret = nfsrv_getuser(RPCNFSUSERD_GETGID, (uid_t)0, gid,
2790191783Srmacklem		    NULL, p);
2791191783Srmacklem		if (ret == 0 && cnt < 2)
2792191783Srmacklem			goto tryagain;
2793191783Srmacklem	} else {
2794191783Srmacklem		NFSUNLOCKNAMEID();
2795191783Srmacklem	}
2796191783Srmacklem
2797191783Srmacklem	/*
2798191783Srmacklem	 * No match, just return a string of digits.
2799191783Srmacklem	 */
2800191783Srmacklem	tmp = gid;
2801191783Srmacklem	i = 0;
2802191783Srmacklem	while (tmp || i == 0) {
2803191783Srmacklem		tmp /= 10;
2804191783Srmacklem		i++;
2805191783Srmacklem	}
2806191783Srmacklem	len = (i > len) ? len : i;
2807191783Srmacklem	*retlenp = len;
2808191783Srmacklem	cp += (len - 1);
2809191783Srmacklem	tmp = gid;
2810191783Srmacklem	for (i = 0; i < len; i++) {
2811191783Srmacklem		*cp-- = '0' + (tmp % 10);
2812191783Srmacklem		tmp /= 10;
2813191783Srmacklem	}
2814191783Srmacklem	return;
2815191783Srmacklem}
2816191783Srmacklem
2817191783Srmacklem/*
2818191783Srmacklem * Convert a string to a gid.
2819240720Srmacklem * If no conversion is possible return NFSERR_BADOWNER, otherwise
2820240720Srmacklem * return 0.
2821240720Srmacklem * If this is called from a client side mount using AUTH_SYS and the
2822240720Srmacklem * string is made up entirely of digits, just convert the string to
2823240720Srmacklem * a number.
2824191783Srmacklem */
2825191783SrmacklemAPPLESTATIC int
2826240720Srmacklemnfsv4_strtogid(struct nfsrv_descript *nd, u_char *str, int len, gid_t *gidp,
2827240720Srmacklem    NFSPROC_T *p)
2828191783Srmacklem{
2829191783Srmacklem	int i;
2830240720Srmacklem	char *cp, *endstr, *str0;
2831191783Srmacklem	struct nfsusrgrp *usrp;
2832191783Srmacklem	int cnt, ret;
2833224086Szack	int error = 0;
2834240720Srmacklem	gid_t tgid;
2835191783Srmacklem
2836224086Szack	if (len == 0) {
2837224086Szack		error =  NFSERR_BADOWNER;
2838224086Szack		goto out;
2839224086Szack	}
2840240720Srmacklem	/* If a string of digits and an AUTH_SYS mount, just convert it. */
2841240720Srmacklem	str0 = str;
2842240720Srmacklem	tgid = (gid_t)strtoul(str0, &endstr, 10);
2843240720Srmacklem	if ((endstr - str0) == len &&
2844240720Srmacklem	    (nd->nd_flag & (ND_KERBV | ND_NFSCL)) == ND_NFSCL) {
2845240720Srmacklem		*gidp = tgid;
2846240720Srmacklem		goto out;
2847240720Srmacklem	}
2848191783Srmacklem	/*
2849191783Srmacklem	 * Look for an '@'.
2850191783Srmacklem	 */
2851240720Srmacklem	cp = strchr(str0, '@');
2852240720Srmacklem	if (cp != NULL)
2853240720Srmacklem		i = (int)(cp++ - str0);
2854240720Srmacklem	else
2855240720Srmacklem		i = len;
2856191783Srmacklem
2857191783Srmacklem	cnt = 0;
2858191783Srmacklemtryagain:
2859191783Srmacklem	NFSLOCKNAMEID();
2860191783Srmacklem	/*
2861191783Srmacklem	 * If an '@' is found and the dns name matches, search for the name
2862191783Srmacklem	 * with the dns stripped off.
2863191783Srmacklem	 */
2864191783Srmacklem	if (cnt == 0 && i < len && i > 0 && nfsrv_dnsname &&
2865191783Srmacklem	    (len - 1 - i) == nfsrv_dnsnamelen &&
2866191783Srmacklem	    !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
2867191783Srmacklem		len -= (nfsrv_dnsnamelen + 1);
2868191783Srmacklem		*(cp - 1) = '\0';
2869191783Srmacklem	}
2870191783Srmacklem
2871191783Srmacklem	/*
2872191783Srmacklem	 * Check for the special case of "nogroup".
2873191783Srmacklem	 */
2874191783Srmacklem	if (len == 7 && !NFSBCMP(str, "nogroup", 7)) {
2875191783Srmacklem		*gidp = nfsrv_defaultgid;
2876191783Srmacklem		NFSUNLOCKNAMEID();
2877224086Szack		error = 0;
2878224086Szack		goto out;
2879191783Srmacklem	}
2880191783Srmacklem
2881191783Srmacklem	LIST_FOREACH(usrp, NFSGROUPNAMEHASH(str, len), lug_namehash) {
2882191783Srmacklem		if (usrp->lug_namelen == len &&
2883191783Srmacklem		    !NFSBCMP(usrp->lug_name, str, len)) {
2884191783Srmacklem			if (usrp->lug_expiry < NFSD_MONOSEC)
2885191783Srmacklem				break;
2886191783Srmacklem			*gidp = usrp->lug_gid;
2887191783Srmacklem			TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
2888191783Srmacklem			TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
2889191783Srmacklem			NFSUNLOCKNAMEID();
2890224086Szack			error = 0;
2891224086Szack			goto out;
2892191783Srmacklem		}
2893191783Srmacklem	}
2894191783Srmacklem	NFSUNLOCKNAMEID();
2895191783Srmacklem	cnt++;
2896191783Srmacklem	ret = nfsrv_getuser(RPCNFSUSERD_GETGROUP, (uid_t)0, (gid_t)0,
2897191783Srmacklem	    str, p);
2898191783Srmacklem	if (ret == 0 && cnt < 2)
2899191783Srmacklem		goto tryagain;
2900224086Szack	error = NFSERR_BADOWNER;
2901224086Szack
2902224086Szackout:
2903224086Szack	NFSEXITCODE(error);
2904224086Szack	return (error);
2905191783Srmacklem}
2906191783Srmacklem
2907191783Srmacklem/*
2908191783Srmacklem * Cmp len chars, allowing mixed case in the first argument to match lower
2909191783Srmacklem * case in the second, but not if the first argument is all upper case.
2910191783Srmacklem * Return 0 for a match, 1 otherwise.
2911191783Srmacklem */
2912191783Srmacklemstatic int
2913191783Srmacklemnfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len)
2914191783Srmacklem{
2915191783Srmacklem	int i;
2916191783Srmacklem	u_char tmp;
2917191783Srmacklem	int fndlower = 0;
2918191783Srmacklem
2919191783Srmacklem	for (i = 0; i < len; i++) {
2920191783Srmacklem		if (*cp >= 'A' && *cp <= 'Z') {
2921191783Srmacklem			tmp = *cp++ + ('a' - 'A');
2922191783Srmacklem		} else {
2923191783Srmacklem			tmp = *cp++;
2924191783Srmacklem			if (tmp >= 'a' && tmp <= 'z')
2925191783Srmacklem				fndlower = 1;
2926191783Srmacklem		}
2927191783Srmacklem		if (tmp != *cp2++)
2928191783Srmacklem			return (1);
2929191783Srmacklem	}
2930191783Srmacklem	if (fndlower)
2931191783Srmacklem		return (0);
2932191783Srmacklem	else
2933191783Srmacklem		return (1);
2934191783Srmacklem}
2935191783Srmacklem
2936191783Srmacklem/*
2937191783Srmacklem * Set the port for the nfsuserd.
2938191783Srmacklem */
2939191783SrmacklemAPPLESTATIC int
2940191783Srmacklemnfsrv_nfsuserdport(u_short port, NFSPROC_T *p)
2941191783Srmacklem{
2942191783Srmacklem	struct nfssockreq *rp;
2943191783Srmacklem	struct sockaddr_in *ad;
2944191783Srmacklem	int error;
2945191783Srmacklem
2946191783Srmacklem	NFSLOCKNAMEID();
2947191783Srmacklem	if (nfsrv_nfsuserd) {
2948191783Srmacklem		NFSUNLOCKNAMEID();
2949224086Szack		error = EPERM;
2950224086Szack		goto out;
2951191783Srmacklem	}
2952191783Srmacklem	nfsrv_nfsuserd = 1;
2953191783Srmacklem	NFSUNLOCKNAMEID();
2954191783Srmacklem	/*
2955191783Srmacklem	 * Set up the socket record and connect.
2956191783Srmacklem	 */
2957191783Srmacklem	rp = &nfsrv_nfsuserdsock;
2958191783Srmacklem	rp->nr_client = NULL;
2959191783Srmacklem	rp->nr_sotype = SOCK_DGRAM;
2960191783Srmacklem	rp->nr_soproto = IPPROTO_UDP;
2961191783Srmacklem	rp->nr_lock = (NFSR_RESERVEDPORT | NFSR_LOCALHOST);
2962191783Srmacklem	rp->nr_cred = NULL;
2963191783Srmacklem	NFSSOCKADDRALLOC(rp->nr_nam);
2964191783Srmacklem	NFSSOCKADDRSIZE(rp->nr_nam, sizeof (struct sockaddr_in));
2965191783Srmacklem	ad = NFSSOCKADDR(rp->nr_nam, struct sockaddr_in *);
2966191783Srmacklem	ad->sin_family = AF_INET;
2967191783Srmacklem	ad->sin_addr.s_addr = htonl((u_int32_t)0x7f000001);	/* 127.0.0.1 */
2968191783Srmacklem	ad->sin_port = port;
2969191783Srmacklem	rp->nr_prog = RPCPROG_NFSUSERD;
2970191783Srmacklem	rp->nr_vers = RPCNFSUSERD_VERS;
2971191783Srmacklem	error = newnfs_connect(NULL, rp, NFSPROCCRED(p), p, 0);
2972191783Srmacklem	if (error) {
2973191783Srmacklem		NFSSOCKADDRFREE(rp->nr_nam);
2974191783Srmacklem		nfsrv_nfsuserd = 0;
2975191783Srmacklem	}
2976224086Szackout:
2977224086Szack	NFSEXITCODE(error);
2978191783Srmacklem	return (error);
2979191783Srmacklem}
2980191783Srmacklem
2981191783Srmacklem/*
2982191783Srmacklem * Delete the nfsuserd port.
2983191783Srmacklem */
2984191783SrmacklemAPPLESTATIC void
2985191783Srmacklemnfsrv_nfsuserddelport(void)
2986191783Srmacklem{
2987191783Srmacklem
2988191783Srmacklem	NFSLOCKNAMEID();
2989191783Srmacklem	if (nfsrv_nfsuserd == 0) {
2990191783Srmacklem		NFSUNLOCKNAMEID();
2991191783Srmacklem		return;
2992191783Srmacklem	}
2993191783Srmacklem	nfsrv_nfsuserd = 0;
2994191783Srmacklem	NFSUNLOCKNAMEID();
2995191783Srmacklem	newnfs_disconnect(&nfsrv_nfsuserdsock);
2996191783Srmacklem	NFSSOCKADDRFREE(nfsrv_nfsuserdsock.nr_nam);
2997191783Srmacklem}
2998191783Srmacklem
2999191783Srmacklem/*
3000191783Srmacklem * Do upcalls to the nfsuserd, for cache misses of the owner/ownergroup
3001191783Srmacklem * name<-->id cache.
3002191783Srmacklem * Returns 0 upon success, non-zero otherwise.
3003191783Srmacklem */
3004191783Srmacklemstatic int
3005191783Srmacklemnfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name, NFSPROC_T *p)
3006191783Srmacklem{
3007191783Srmacklem	u_int32_t *tl;
3008191783Srmacklem	struct nfsrv_descript *nd;
3009191783Srmacklem	int len;
3010191783Srmacklem	struct nfsrv_descript nfsd;
3011191783Srmacklem	struct ucred *cred;
3012191783Srmacklem	int error;
3013191783Srmacklem
3014191783Srmacklem	NFSLOCKNAMEID();
3015191783Srmacklem	if (nfsrv_nfsuserd == 0) {
3016191783Srmacklem		NFSUNLOCKNAMEID();
3017224086Szack		error = EPERM;
3018224086Szack		goto out;
3019191783Srmacklem	}
3020191783Srmacklem	NFSUNLOCKNAMEID();
3021191783Srmacklem	nd = &nfsd;
3022191783Srmacklem	cred = newnfs_getcred();
3023191783Srmacklem	nd->nd_flag = ND_GSSINITREPLY;
3024191783Srmacklem	nfsrvd_rephead(nd);
3025191783Srmacklem
3026191783Srmacklem	nd->nd_procnum = procnum;
3027191783Srmacklem	if (procnum == RPCNFSUSERD_GETUID || procnum == RPCNFSUSERD_GETGID) {
3028191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3029191783Srmacklem		if (procnum == RPCNFSUSERD_GETUID)
3030191783Srmacklem			*tl = txdr_unsigned(uid);
3031191783Srmacklem		else
3032191783Srmacklem			*tl = txdr_unsigned(gid);
3033191783Srmacklem	} else {
3034191783Srmacklem		len = strlen(name);
3035191783Srmacklem		(void) nfsm_strtom(nd, name, len);
3036191783Srmacklem	}
3037191783Srmacklem	error = newnfs_request(nd, NULL, NULL, &nfsrv_nfsuserdsock, NULL, NULL,
3038244042Srmacklem		cred, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS, NULL, 0, NULL, NULL);
3039191783Srmacklem	NFSFREECRED(cred);
3040191783Srmacklem	if (!error) {
3041191783Srmacklem		mbuf_freem(nd->nd_mrep);
3042191783Srmacklem		error = nd->nd_repstat;
3043191783Srmacklem	}
3044224086Szackout:
3045224086Szack	NFSEXITCODE(error);
3046191783Srmacklem	return (error);
3047191783Srmacklem}
3048191783Srmacklem
3049191783Srmacklem/*
3050191783Srmacklem * This function is called from the nfssvc(2) system call, to update the
3051191783Srmacklem * kernel user/group name list(s) for the V4 owner and ownergroup attributes.
3052191783Srmacklem */
3053191783SrmacklemAPPLESTATIC int
3054191783Srmacklemnfssvc_idname(struct nfsd_idargs *nidp)
3055191783Srmacklem{
3056191783Srmacklem	struct nfsusrgrp *nusrp, *usrp, *newusrp;
3057191783Srmacklem	struct nfsuserhashhead *hp;
3058191783Srmacklem	int i;
3059191783Srmacklem	int error = 0;
3060191783Srmacklem	u_char *cp;
3061191783Srmacklem
3062191783Srmacklem	if (nidp->nid_flag & NFSID_INITIALIZE) {
3063191783Srmacklem	    cp = (u_char *)malloc(nidp->nid_namelen + 1,
3064191783Srmacklem		M_NFSSTRING, M_WAITOK);
3065191783Srmacklem	    error = copyin(CAST_USER_ADDR_T(nidp->nid_name), cp,
3066191783Srmacklem		nidp->nid_namelen);
3067191783Srmacklem	    NFSLOCKNAMEID();
3068191783Srmacklem	    if (nfsrv_dnsname) {
3069191783Srmacklem		/*
3070191783Srmacklem		 * Free up all the old stuff and reinitialize hash lists.
3071191783Srmacklem		 */
3072191783Srmacklem		TAILQ_FOREACH_SAFE(usrp, &nfsuserlruhead, lug_lru, nusrp) {
3073191783Srmacklem			nfsrv_removeuser(usrp);
3074191783Srmacklem		}
3075191783Srmacklem		free(nfsrv_dnsname, M_NFSSTRING);
3076191783Srmacklem		nfsrv_dnsname = NULL;
3077191783Srmacklem	    }
3078191783Srmacklem	    TAILQ_INIT(&nfsuserlruhead);
3079191783Srmacklem	    for (i = 0; i < NFSUSERHASHSIZE; i++)
3080191783Srmacklem		LIST_INIT(&nfsuserhash[i]);
3081191783Srmacklem	    for (i = 0; i < NFSGROUPHASHSIZE; i++)
3082191783Srmacklem		LIST_INIT(&nfsgrouphash[i]);
3083191783Srmacklem	    for (i = 0; i < NFSUSERHASHSIZE; i++)
3084191783Srmacklem		LIST_INIT(&nfsusernamehash[i]);
3085191783Srmacklem	    for (i = 0; i < NFSGROUPHASHSIZE; i++)
3086191783Srmacklem		LIST_INIT(&nfsgroupnamehash[i]);
3087191783Srmacklem
3088191783Srmacklem	    /*
3089191783Srmacklem	     * Put name in "DNS" string.
3090191783Srmacklem	     */
3091191783Srmacklem	    if (!error) {
3092191783Srmacklem		nfsrv_dnsname = cp;
3093191783Srmacklem		nfsrv_dnsnamelen = nidp->nid_namelen;
3094191783Srmacklem		nfsrv_defaultuid = nidp->nid_uid;
3095191783Srmacklem		nfsrv_defaultgid = nidp->nid_gid;
3096191783Srmacklem		nfsrv_usercnt = 0;
3097191783Srmacklem		nfsrv_usermax = nidp->nid_usermax;
3098191783Srmacklem	    }
3099191783Srmacklem	    NFSUNLOCKNAMEID();
3100191783Srmacklem	    if (error)
3101191783Srmacklem		free(cp, M_NFSSTRING);
3102224086Szack	    goto out;
3103191783Srmacklem	}
3104191783Srmacklem
3105191783Srmacklem	/*
3106191783Srmacklem	 * malloc the new one now, so any potential sleep occurs before
3107191783Srmacklem	 * manipulation of the lists.
3108191783Srmacklem	 */
3109191783Srmacklem	MALLOC(newusrp, struct nfsusrgrp *, sizeof (struct nfsusrgrp) +
3110191783Srmacklem	    nidp->nid_namelen, M_NFSUSERGROUP, M_WAITOK);
3111191783Srmacklem	error = copyin(CAST_USER_ADDR_T(nidp->nid_name), newusrp->lug_name,
3112191783Srmacklem	    nidp->nid_namelen);
3113191783Srmacklem	if (error) {
3114191783Srmacklem		free((caddr_t)newusrp, M_NFSUSERGROUP);
3115224086Szack		goto out;
3116191783Srmacklem	}
3117191783Srmacklem	newusrp->lug_namelen = nidp->nid_namelen;
3118191783Srmacklem
3119191783Srmacklem	NFSLOCKNAMEID();
3120191783Srmacklem	/*
3121191783Srmacklem	 * Delete old entries, as required.
3122191783Srmacklem	 */
3123191783Srmacklem	if (nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID)) {
3124191783Srmacklem		hp = NFSUSERHASH(nidp->nid_uid);
3125191783Srmacklem		LIST_FOREACH_SAFE(usrp, hp, lug_numhash, nusrp) {
3126191783Srmacklem			if (usrp->lug_uid == nidp->nid_uid)
3127191783Srmacklem				nfsrv_removeuser(usrp);
3128191783Srmacklem		}
3129191783Srmacklem	}
3130191783Srmacklem	if (nidp->nid_flag & (NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) {
3131191783Srmacklem		hp = NFSUSERNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
3132191783Srmacklem		LIST_FOREACH_SAFE(usrp, hp, lug_namehash, nusrp) {
3133191783Srmacklem			if (usrp->lug_namelen == newusrp->lug_namelen &&
3134191783Srmacklem			    !NFSBCMP(usrp->lug_name, newusrp->lug_name,
3135191783Srmacklem			    usrp->lug_namelen))
3136191783Srmacklem				nfsrv_removeuser(usrp);
3137191783Srmacklem		}
3138191783Srmacklem	}
3139191783Srmacklem	if (nidp->nid_flag & (NFSID_DELGID | NFSID_ADDGID)) {
3140191783Srmacklem		hp = NFSGROUPHASH(nidp->nid_gid);
3141191783Srmacklem		LIST_FOREACH_SAFE(usrp, hp, lug_numhash, nusrp) {
3142191783Srmacklem			if (usrp->lug_gid == nidp->nid_gid)
3143191783Srmacklem				nfsrv_removeuser(usrp);
3144191783Srmacklem		}
3145191783Srmacklem	}
3146191783Srmacklem	if (nidp->nid_flag & (NFSID_DELGROUPNAME | NFSID_ADDGROUPNAME)) {
3147191783Srmacklem		hp = NFSGROUPNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
3148191783Srmacklem		LIST_FOREACH_SAFE(usrp, hp, lug_namehash, nusrp) {
3149191783Srmacklem			if (usrp->lug_namelen == newusrp->lug_namelen &&
3150191783Srmacklem			    !NFSBCMP(usrp->lug_name, newusrp->lug_name,
3151191783Srmacklem			    usrp->lug_namelen))
3152191783Srmacklem				nfsrv_removeuser(usrp);
3153191783Srmacklem		}
3154191783Srmacklem	}
3155191783Srmacklem	TAILQ_FOREACH_SAFE(usrp, &nfsuserlruhead, lug_lru, nusrp) {
3156191783Srmacklem		if (usrp->lug_expiry < NFSD_MONOSEC)
3157191783Srmacklem			nfsrv_removeuser(usrp);
3158191783Srmacklem	}
3159191783Srmacklem	while (nfsrv_usercnt >= nfsrv_usermax) {
3160191783Srmacklem		usrp = TAILQ_FIRST(&nfsuserlruhead);
3161191783Srmacklem		nfsrv_removeuser(usrp);
3162191783Srmacklem	}
3163191783Srmacklem
3164191783Srmacklem	/*
3165191783Srmacklem	 * Now, we can add the new one.
3166191783Srmacklem	 */
3167191783Srmacklem	if (nidp->nid_usertimeout)
3168191783Srmacklem		newusrp->lug_expiry = NFSD_MONOSEC + nidp->nid_usertimeout;
3169191783Srmacklem	else
3170191783Srmacklem		newusrp->lug_expiry = NFSD_MONOSEC + 5;
3171191783Srmacklem	if (nidp->nid_flag & (NFSID_ADDUID | NFSID_ADDUSERNAME)) {
3172191783Srmacklem		newusrp->lug_uid = nidp->nid_uid;
3173191783Srmacklem		LIST_INSERT_HEAD(NFSUSERHASH(newusrp->lug_uid), newusrp,
3174191783Srmacklem		    lug_numhash);
3175191783Srmacklem		LIST_INSERT_HEAD(NFSUSERNAMEHASH(newusrp->lug_name,
3176191783Srmacklem		    newusrp->lug_namelen), newusrp, lug_namehash);
3177191783Srmacklem		TAILQ_INSERT_TAIL(&nfsuserlruhead, newusrp, lug_lru);
3178191783Srmacklem		nfsrv_usercnt++;
3179191783Srmacklem	} else if (nidp->nid_flag & (NFSID_ADDGID | NFSID_ADDGROUPNAME)) {
3180191783Srmacklem		newusrp->lug_gid = nidp->nid_gid;
3181191783Srmacklem		LIST_INSERT_HEAD(NFSGROUPHASH(newusrp->lug_gid), newusrp,
3182191783Srmacklem		    lug_numhash);
3183191783Srmacklem		LIST_INSERT_HEAD(NFSGROUPNAMEHASH(newusrp->lug_name,
3184191783Srmacklem		    newusrp->lug_namelen), newusrp, lug_namehash);
3185191783Srmacklem		TAILQ_INSERT_TAIL(&nfsuserlruhead, newusrp, lug_lru);
3186191783Srmacklem		nfsrv_usercnt++;
3187191783Srmacklem	} else
3188191783Srmacklem		FREE((caddr_t)newusrp, M_NFSUSERGROUP);
3189191783Srmacklem	NFSUNLOCKNAMEID();
3190224086Szackout:
3191224086Szack	NFSEXITCODE(error);
3192191783Srmacklem	return (error);
3193191783Srmacklem}
3194191783Srmacklem
3195191783Srmacklem/*
3196191783Srmacklem * Remove a user/group name element.
3197191783Srmacklem */
3198191783Srmacklemstatic void
3199191783Srmacklemnfsrv_removeuser(struct nfsusrgrp *usrp)
3200191783Srmacklem{
3201191783Srmacklem
3202191783Srmacklem	NFSNAMEIDREQUIRED();
3203191783Srmacklem	LIST_REMOVE(usrp, lug_numhash);
3204191783Srmacklem	LIST_REMOVE(usrp, lug_namehash);
3205191783Srmacklem	TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
3206191783Srmacklem	nfsrv_usercnt--;
3207191783Srmacklem	FREE((caddr_t)usrp, M_NFSUSERGROUP);
3208191783Srmacklem}
3209191783Srmacklem
3210191783Srmacklem/*
3211191783Srmacklem * This function scans a byte string and checks for UTF-8 compliance.
3212191783Srmacklem * It returns 0 if it conforms and NFSERR_INVAL if not.
3213191783Srmacklem */
3214191783SrmacklemAPPLESTATIC int
3215191783Srmacklemnfsrv_checkutf8(u_int8_t *cp, int len)
3216191783Srmacklem{
3217191783Srmacklem	u_int32_t val = 0x0;
3218191783Srmacklem	int cnt = 0, gotd = 0, shift = 0;
3219191783Srmacklem	u_int8_t byte;
3220191783Srmacklem	static int utf8_shift[5] = { 7, 11, 16, 21, 26 };
3221224086Szack	int error = 0;
3222191783Srmacklem
3223191783Srmacklem	/*
3224191783Srmacklem	 * Here are what the variables are used for:
3225191783Srmacklem	 * val - the calculated value of a multibyte char, used to check
3226191783Srmacklem	 *       that it was coded with the correct range
3227191783Srmacklem	 * cnt - the number of 10xxxxxx bytes to follow
3228191783Srmacklem	 * gotd - set for a char of Dxxx, so D800<->DFFF can be checked for
3229191783Srmacklem	 * shift - lower order bits of range (ie. "val >> shift" should
3230191783Srmacklem	 *       not be 0, in other words, dividing by the lower bound
3231191783Srmacklem	 *       of the range should get a non-zero value)
3232191783Srmacklem	 * byte - used to calculate cnt
3233191783Srmacklem	 */
3234191783Srmacklem	while (len > 0) {
3235191783Srmacklem		if (cnt > 0) {
3236191783Srmacklem			/* This handles the 10xxxxxx bytes */
3237191783Srmacklem			if ((*cp & 0xc0) != 0x80 ||
3238224086Szack			    (gotd && (*cp & 0x20))) {
3239224086Szack				error = NFSERR_INVAL;
3240224086Szack				goto out;
3241224086Szack			}
3242191783Srmacklem			gotd = 0;
3243191783Srmacklem			val <<= 6;
3244191783Srmacklem			val |= (*cp & 0x3f);
3245191783Srmacklem			cnt--;
3246224086Szack			if (cnt == 0 && (val >> shift) == 0x0) {
3247224086Szack				error = NFSERR_INVAL;
3248224086Szack				goto out;
3249224086Szack			}
3250191783Srmacklem		} else if (*cp & 0x80) {
3251191783Srmacklem			/* first byte of multi byte char */
3252191783Srmacklem			byte = *cp;
3253191783Srmacklem			while ((byte & 0x40) && cnt < 6) {
3254191783Srmacklem				cnt++;
3255191783Srmacklem				byte <<= 1;
3256191783Srmacklem			}
3257224086Szack			if (cnt == 0 || cnt == 6) {
3258224086Szack				error = NFSERR_INVAL;
3259224086Szack				goto out;
3260224086Szack			}
3261191783Srmacklem			val = (*cp & (0x3f >> cnt));
3262191783Srmacklem			shift = utf8_shift[cnt - 1];
3263191783Srmacklem			if (cnt == 2 && val == 0xd)
3264191783Srmacklem				/* Check for the 0xd800-0xdfff case */
3265191783Srmacklem				gotd = 1;
3266191783Srmacklem		}
3267191783Srmacklem		cp++;
3268191783Srmacklem		len--;
3269191783Srmacklem	}
3270191783Srmacklem	if (cnt > 0)
3271224086Szack		error = NFSERR_INVAL;
3272224086Szack
3273224086Szackout:
3274224086Szack	NFSEXITCODE(error);
3275224086Szack	return (error);
3276191783Srmacklem}
3277191783Srmacklem
3278191783Srmacklem/*
3279191783Srmacklem * Parse the xdr for an NFSv4 FsLocations attribute. Return two malloc'd
3280191783Srmacklem * strings, one with the root path in it and the other with the list of
3281191783Srmacklem * locations. The list is in the same format as is found in nfr_refs.
3282191783Srmacklem * It is a "," separated list of entries, where each of them is of the
3283191783Srmacklem * form <server>:<rootpath>. For example
3284191783Srmacklem * "nfsv4-test:/sub2,nfsv4-test2:/user/mnt,nfsv4-test2:/user/mnt2"
3285191783Srmacklem * The nilp argument is set to 1 for the special case of a null fs_root
3286191783Srmacklem * and an empty server list.
3287191783Srmacklem * It returns NFSERR_BADXDR, if the xdr can't be parsed and returns the
3288191783Srmacklem * number of xdr bytes parsed in sump.
3289191783Srmacklem */
3290191783Srmacklemstatic int
3291191783Srmacklemnfsrv_getrefstr(struct nfsrv_descript *nd, u_char **fsrootp, u_char **srvp,
3292191783Srmacklem    int *sump, int *nilp)
3293191783Srmacklem{
3294191783Srmacklem	u_int32_t *tl;
3295191783Srmacklem	u_char *cp = NULL, *cp2 = NULL, *cp3, *str;
3296224086Szack	int i, j, len, stringlen, cnt, slen, siz, xdrsum, error = 0, nsrv;
3297191783Srmacklem	struct list {
3298191783Srmacklem		SLIST_ENTRY(list) next;
3299191783Srmacklem		int len;
3300191783Srmacklem		u_char host[1];
3301191783Srmacklem	} *lsp, *nlsp;
3302191783Srmacklem	SLIST_HEAD(, list) head;
3303191783Srmacklem
3304191783Srmacklem	*fsrootp = NULL;
3305191783Srmacklem	*srvp = NULL;
3306191783Srmacklem	*nilp = 0;
3307191783Srmacklem
3308191783Srmacklem	/*
3309191783Srmacklem	 * Get the fs_root path and check for the special case of null path
3310191783Srmacklem	 * and 0 length server list.
3311191783Srmacklem	 */
3312191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3313191783Srmacklem	len = fxdr_unsigned(int, *tl);
3314224086Szack	if (len < 0 || len > 10240) {
3315224086Szack		error = NFSERR_BADXDR;
3316224086Szack		goto nfsmout;
3317224086Szack	}
3318191783Srmacklem	if (len == 0) {
3319191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3320224086Szack		if (*tl != 0) {
3321224086Szack			error = NFSERR_BADXDR;
3322224086Szack			goto nfsmout;
3323224086Szack		}
3324191783Srmacklem		*nilp = 1;
3325191783Srmacklem		*sump = 2 * NFSX_UNSIGNED;
3326224086Szack		error = 0;
3327224086Szack		goto nfsmout;
3328191783Srmacklem	}
3329191783Srmacklem	cp = malloc(len + 1, M_NFSSTRING, M_WAITOK);
3330191783Srmacklem	error = nfsrv_mtostr(nd, cp, len);
3331191783Srmacklem	if (!error) {
3332191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3333191783Srmacklem		cnt = fxdr_unsigned(int, *tl);
3334191783Srmacklem		if (cnt <= 0)
3335191783Srmacklem			error = NFSERR_BADXDR;
3336191783Srmacklem	}
3337224086Szack	if (error)
3338224086Szack		goto nfsmout;
3339191783Srmacklem
3340191783Srmacklem	/*
3341191783Srmacklem	 * Now, loop through the location list and make up the srvlist.
3342191783Srmacklem	 */
3343191783Srmacklem	xdrsum = (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
3344191783Srmacklem	cp2 = cp3 = malloc(1024, M_NFSSTRING, M_WAITOK);
3345191783Srmacklem	slen = 1024;
3346191783Srmacklem	siz = 0;
3347191783Srmacklem	for (i = 0; i < cnt; i++) {
3348191783Srmacklem		SLIST_INIT(&head);
3349191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3350191783Srmacklem		nsrv = fxdr_unsigned(int, *tl);
3351191783Srmacklem		if (nsrv <= 0) {
3352224086Szack			error = NFSERR_BADXDR;
3353224086Szack			goto nfsmout;
3354191783Srmacklem		}
3355191783Srmacklem
3356191783Srmacklem		/*
3357191783Srmacklem		 * Handle the first server by putting it in the srvstr.
3358191783Srmacklem		 */
3359191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3360191783Srmacklem		len = fxdr_unsigned(int, *tl);
3361191783Srmacklem		if (len <= 0 || len > 1024) {
3362224086Szack			error = NFSERR_BADXDR;
3363224086Szack			goto nfsmout;
3364191783Srmacklem		}
3365191783Srmacklem		nfsrv_refstrbigenough(siz + len + 3, &cp2, &cp3, &slen);
3366191783Srmacklem		if (cp3 != cp2) {
3367191783Srmacklem			*cp3++ = ',';
3368191783Srmacklem			siz++;
3369191783Srmacklem		}
3370191783Srmacklem		error = nfsrv_mtostr(nd, cp3, len);
3371224086Szack		if (error)
3372224086Szack			goto nfsmout;
3373191783Srmacklem		cp3 += len;
3374191783Srmacklem		*cp3++ = ':';
3375191783Srmacklem		siz += (len + 1);
3376191783Srmacklem		xdrsum += (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
3377191783Srmacklem		for (j = 1; j < nsrv; j++) {
3378191783Srmacklem			/*
3379191783Srmacklem			 * Yuck, put them in an slist and process them later.
3380191783Srmacklem			 */
3381191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3382191783Srmacklem			len = fxdr_unsigned(int, *tl);
3383191783Srmacklem			if (len <= 0 || len > 1024) {
3384224086Szack				error = NFSERR_BADXDR;
3385224086Szack				goto nfsmout;
3386191783Srmacklem			}
3387191783Srmacklem			lsp = (struct list *)malloc(sizeof (struct list)
3388191783Srmacklem			    + len, M_TEMP, M_WAITOK);
3389191783Srmacklem			error = nfsrv_mtostr(nd, lsp->host, len);
3390224086Szack			if (error)
3391224086Szack				goto nfsmout;
3392191783Srmacklem			xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
3393191783Srmacklem			lsp->len = len;
3394191783Srmacklem			SLIST_INSERT_HEAD(&head, lsp, next);
3395191783Srmacklem		}
3396191783Srmacklem
3397191783Srmacklem		/*
3398191783Srmacklem		 * Finally, we can get the path.
3399191783Srmacklem		 */
3400191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3401191783Srmacklem		len = fxdr_unsigned(int, *tl);
3402191783Srmacklem		if (len <= 0 || len > 1024) {
3403224086Szack			error = NFSERR_BADXDR;
3404224086Szack			goto nfsmout;
3405191783Srmacklem		}
3406191783Srmacklem		nfsrv_refstrbigenough(siz + len + 1, &cp2, &cp3, &slen);
3407191783Srmacklem		error = nfsrv_mtostr(nd, cp3, len);
3408224086Szack		if (error)
3409224086Szack			goto nfsmout;
3410191783Srmacklem		xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
3411191783Srmacklem		str = cp3;
3412191783Srmacklem		stringlen = len;
3413191783Srmacklem		cp3 += len;
3414191783Srmacklem		siz += len;
3415191783Srmacklem		SLIST_FOREACH_SAFE(lsp, &head, next, nlsp) {
3416191783Srmacklem			nfsrv_refstrbigenough(siz + lsp->len + stringlen + 3,
3417191783Srmacklem			    &cp2, &cp3, &slen);
3418191783Srmacklem			*cp3++ = ',';
3419191783Srmacklem			NFSBCOPY(lsp->host, cp3, lsp->len);
3420191783Srmacklem			cp3 += lsp->len;
3421191783Srmacklem			*cp3++ = ':';
3422191783Srmacklem			NFSBCOPY(str, cp3, stringlen);
3423191783Srmacklem			cp3 += stringlen;
3424191783Srmacklem			*cp3 = '\0';
3425191783Srmacklem			siz += (lsp->len + stringlen + 2);
3426191783Srmacklem			free((caddr_t)lsp, M_TEMP);
3427191783Srmacklem		}
3428191783Srmacklem	}
3429191783Srmacklem	*fsrootp = cp;
3430191783Srmacklem	*srvp = cp2;
3431191783Srmacklem	*sump = xdrsum;
3432224086Szack	NFSEXITCODE2(0, nd);
3433191783Srmacklem	return (0);
3434191783Srmacklemnfsmout:
3435191783Srmacklem	if (cp != NULL)
3436191783Srmacklem		free(cp, M_NFSSTRING);
3437191783Srmacklem	if (cp2 != NULL)
3438191783Srmacklem		free(cp2, M_NFSSTRING);
3439224086Szack	NFSEXITCODE2(error, nd);
3440191783Srmacklem	return (error);
3441191783Srmacklem}
3442191783Srmacklem
3443191783Srmacklem/*
3444191783Srmacklem * Make the malloc'd space large enough. This is a pain, but the xdr
3445191783Srmacklem * doesn't set an upper bound on the side, so...
3446191783Srmacklem */
3447191783Srmacklemstatic void
3448191783Srmacklemnfsrv_refstrbigenough(int siz, u_char **cpp, u_char **cpp2, int *slenp)
3449191783Srmacklem{
3450191783Srmacklem	u_char *cp;
3451191783Srmacklem	int i;
3452191783Srmacklem
3453191783Srmacklem	if (siz <= *slenp)
3454191783Srmacklem		return;
3455191783Srmacklem	cp = malloc(siz + 1024, M_NFSSTRING, M_WAITOK);
3456191783Srmacklem	NFSBCOPY(*cpp, cp, *slenp);
3457191783Srmacklem	free(*cpp, M_NFSSTRING);
3458191783Srmacklem	i = *cpp2 - *cpp;
3459191783Srmacklem	*cpp = cp;
3460191783Srmacklem	*cpp2 = cp + i;
3461191783Srmacklem	*slenp = siz + 1024;
3462191783Srmacklem}
3463191783Srmacklem
3464191783Srmacklem/*
3465191783Srmacklem * Initialize the reply header data structures.
3466191783Srmacklem */
3467191783SrmacklemAPPLESTATIC void
3468191783Srmacklemnfsrvd_rephead(struct nfsrv_descript *nd)
3469191783Srmacklem{
3470191783Srmacklem	mbuf_t mreq;
3471191783Srmacklem
3472191783Srmacklem	/*
3473191783Srmacklem	 * If this is a big reply, use a cluster.
3474191783Srmacklem	 */
3475191783Srmacklem	if ((nd->nd_flag & ND_GSSINITREPLY) == 0 &&
3476191783Srmacklem	    nfs_bigreply[nd->nd_procnum]) {
3477243882Sglebius		NFSMCLGET(mreq, M_WAITOK);
3478191783Srmacklem		nd->nd_mreq = mreq;
3479191783Srmacklem		nd->nd_mb = mreq;
3480191783Srmacklem	} else {
3481191783Srmacklem		NFSMGET(mreq);
3482191783Srmacklem		nd->nd_mreq = mreq;
3483191783Srmacklem		nd->nd_mb = mreq;
3484191783Srmacklem	}
3485191783Srmacklem	nd->nd_bpos = NFSMTOD(mreq, caddr_t);
3486191783Srmacklem	mbuf_setlen(mreq, 0);
3487191783Srmacklem
3488191783Srmacklem	if ((nd->nd_flag & ND_GSSINITREPLY) == 0)
3489191783Srmacklem		NFSM_BUILD(nd->nd_errp, int *, NFSX_UNSIGNED);
3490191783Srmacklem}
3491191783Srmacklem
3492191783Srmacklem/*
3493191783Srmacklem * Lock a socket against others.
3494191783Srmacklem * Currently used to serialize connect/disconnect attempts.
3495191783Srmacklem */
3496191783Srmacklemint
3497191783Srmacklemnewnfs_sndlock(int *flagp)
3498191783Srmacklem{
3499191783Srmacklem	struct timespec ts;
3500191783Srmacklem
3501191783Srmacklem	NFSLOCKSOCK();
3502191783Srmacklem	while (*flagp & NFSR_SNDLOCK) {
3503191783Srmacklem		*flagp |= NFSR_WANTSND;
3504191783Srmacklem		ts.tv_sec = 0;
3505191783Srmacklem		ts.tv_nsec = 0;
3506191783Srmacklem		(void) nfsmsleep((caddr_t)flagp, NFSSOCKMUTEXPTR,
3507191783Srmacklem		    PZERO - 1, "nfsndlck", &ts);
3508191783Srmacklem	}
3509191783Srmacklem	*flagp |= NFSR_SNDLOCK;
3510191783Srmacklem	NFSUNLOCKSOCK();
3511191783Srmacklem	return (0);
3512191783Srmacklem}
3513191783Srmacklem
3514191783Srmacklem/*
3515191783Srmacklem * Unlock the stream socket for others.
3516191783Srmacklem */
3517191783Srmacklemvoid
3518191783Srmacklemnewnfs_sndunlock(int *flagp)
3519191783Srmacklem{
3520191783Srmacklem
3521191783Srmacklem	NFSLOCKSOCK();
3522191783Srmacklem	if ((*flagp & NFSR_SNDLOCK) == 0)
3523191783Srmacklem		panic("nfs sndunlock");
3524191783Srmacklem	*flagp &= ~NFSR_SNDLOCK;
3525191783Srmacklem	if (*flagp & NFSR_WANTSND) {
3526191783Srmacklem		*flagp &= ~NFSR_WANTSND;
3527191783Srmacklem		wakeup((caddr_t)flagp);
3528191783Srmacklem	}
3529191783Srmacklem	NFSUNLOCKSOCK();
3530191783Srmacklem}
3531191783Srmacklem
3532244042SrmacklemAPPLESTATIC int
3533244042Srmacklemnfsv4_getipaddr(struct nfsrv_descript *nd, struct sockaddr_storage *sa,
3534244042Srmacklem    int *isudp)
3535244042Srmacklem{
3536244042Srmacklem	struct sockaddr_in *sad;
3537244042Srmacklem	struct sockaddr_in6 *sad6;
3538244042Srmacklem	struct in_addr saddr;
3539244042Srmacklem	uint32_t portnum, *tl;
3540244042Srmacklem	int af = 0, i, j, k;
3541244042Srmacklem	char addr[64], protocol[5], *cp;
3542244042Srmacklem	int cantparse = 0, error = 0;
3543244042Srmacklem	uint16_t portv;
3544244042Srmacklem
3545244042Srmacklem	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3546244042Srmacklem	i = fxdr_unsigned(int, *tl);
3547244042Srmacklem	if (i >= 3 && i <= 4) {
3548244042Srmacklem		error = nfsrv_mtostr(nd, protocol, i);
3549244042Srmacklem		if (error)
3550244042Srmacklem			goto nfsmout;
3551244042Srmacklem		if (strcmp(protocol, "tcp") == 0) {
3552244042Srmacklem			af = AF_INET;
3553244042Srmacklem			*isudp = 0;
3554244042Srmacklem		} else if (strcmp(protocol, "udp") == 0) {
3555244042Srmacklem			af = AF_INET;
3556244042Srmacklem			*isudp = 1;
3557244042Srmacklem		} else if (strcmp(protocol, "tcp6") == 0) {
3558244042Srmacklem			af = AF_INET6;
3559244042Srmacklem			*isudp = 0;
3560244042Srmacklem		} else if (strcmp(protocol, "udp6") == 0) {
3561244042Srmacklem			af = AF_INET6;
3562244042Srmacklem			*isudp = 1;
3563244042Srmacklem		} else
3564244042Srmacklem			cantparse = 1;
3565244042Srmacklem	} else {
3566244042Srmacklem		cantparse = 1;
3567244042Srmacklem		if (i > 0) {
3568244042Srmacklem			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
3569244042Srmacklem			if (error)
3570244042Srmacklem				goto nfsmout;
3571244042Srmacklem		}
3572244042Srmacklem	}
3573244042Srmacklem	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3574244042Srmacklem	i = fxdr_unsigned(int, *tl);
3575244042Srmacklem	if (i < 0) {
3576244042Srmacklem		error = NFSERR_BADXDR;
3577244042Srmacklem		goto nfsmout;
3578244042Srmacklem	} else if (cantparse == 0 && i >= 11 && i < 64) {
3579244042Srmacklem		/*
3580244042Srmacklem		 * The shortest address is 11chars and the longest is < 64.
3581244042Srmacklem		 */
3582244042Srmacklem		error = nfsrv_mtostr(nd, addr, i);
3583244042Srmacklem		if (error)
3584244042Srmacklem			goto nfsmout;
3585244042Srmacklem
3586244042Srmacklem		/* Find the port# at the end and extract that. */
3587244042Srmacklem		i = strlen(addr);
3588244042Srmacklem		k = 0;
3589244042Srmacklem		cp = &addr[i - 1];
3590244042Srmacklem		/* Count back two '.'s from end to get port# field. */
3591244042Srmacklem		for (j = 0; j < i; j++) {
3592244042Srmacklem			if (*cp == '.') {
3593244042Srmacklem				k++;
3594244042Srmacklem				if (k == 2)
3595244042Srmacklem					break;
3596244042Srmacklem			}
3597244042Srmacklem			cp--;
3598244042Srmacklem		}
3599244042Srmacklem		if (k == 2) {
3600244042Srmacklem			/*
3601244042Srmacklem			 * The NFSv4 port# is appended as .N.N, where N is
3602244042Srmacklem			 * a decimal # in the range 0-255, just like an inet4
3603244042Srmacklem			 * address. Cheat and use inet_aton(), which will
3604244042Srmacklem			 * return a Class A address and then shift the high
3605244042Srmacklem			 * order 8bits over to convert it to the port#.
3606244042Srmacklem			 */
3607244042Srmacklem			*cp++ = '\0';
3608244042Srmacklem			if (inet_aton(cp, &saddr) == 1) {
3609244042Srmacklem				portnum = ntohl(saddr.s_addr);
3610244042Srmacklem				portv = (uint16_t)((portnum >> 16) |
3611244042Srmacklem				    (portnum & 0xff));
3612244042Srmacklem			} else
3613244042Srmacklem				cantparse = 1;
3614244042Srmacklem		} else
3615244042Srmacklem			cantparse = 1;
3616244042Srmacklem		if (cantparse == 0) {
3617244042Srmacklem			if (af == AF_INET) {
3618244042Srmacklem				sad = (struct sockaddr_in *)sa;
3619244042Srmacklem				if (inet_pton(af, addr, &sad->sin_addr) == 1) {
3620244042Srmacklem					sad->sin_len = sizeof(*sad);
3621244042Srmacklem					sad->sin_family = AF_INET;
3622244042Srmacklem					sad->sin_port = htons(portv);
3623244042Srmacklem					return (0);
3624244042Srmacklem				}
3625244042Srmacklem			} else {
3626244042Srmacklem				sad6 = (struct sockaddr_in6 *)sa;
3627244042Srmacklem				if (inet_pton(af, addr, &sad6->sin6_addr)
3628244042Srmacklem				    == 1) {
3629244042Srmacklem					sad6->sin6_len = sizeof(*sad6);
3630244042Srmacklem					sad6->sin6_family = AF_INET6;
3631244042Srmacklem					sad6->sin6_port = htons(portv);
3632244042Srmacklem					return (0);
3633244042Srmacklem				}
3634244042Srmacklem			}
3635244042Srmacklem		}
3636244042Srmacklem	} else {
3637244042Srmacklem		if (i > 0) {
3638244042Srmacklem			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
3639244042Srmacklem			if (error)
3640244042Srmacklem				goto nfsmout;
3641244042Srmacklem		}
3642244042Srmacklem	}
3643244042Srmacklem	error = EPERM;
3644244042Srmacklemnfsmout:
3645244042Srmacklem	return (error);
3646244042Srmacklem}
3647244042Srmacklem
3648244042Srmacklem/*
3649244042Srmacklem * Handle an NFSv4.1 Sequence request for the session.
3650244042Srmacklem */
3651244042Srmacklemint
3652244042Srmacklemnfsv4_seqsession(uint32_t seqid, uint32_t slotid, uint32_t highslot,
3653244042Srmacklem    struct nfsslot *slots, struct mbuf **reply, uint16_t maxslot)
3654244042Srmacklem{
3655244042Srmacklem	int error;
3656244042Srmacklem
3657244042Srmacklem	error = 0;
3658244042Srmacklem	*reply = NULL;
3659244042Srmacklem	if (slotid > maxslot)
3660244042Srmacklem		return (NFSERR_BADSLOT);
3661244042Srmacklem	if (seqid == slots[slotid].nfssl_seq) {
3662244042Srmacklem		/* A retry. */
3663244042Srmacklem		if (slots[slotid].nfssl_inprog != 0)
3664244042Srmacklem			error = NFSERR_DELAY;
3665244042Srmacklem		else if (slots[slotid].nfssl_reply != NULL) {
3666244042Srmacklem			*reply = slots[slotid].nfssl_reply;
3667244042Srmacklem			slots[slotid].nfssl_reply = NULL;
3668244042Srmacklem			slots[slotid].nfssl_inprog = 1;
3669244042Srmacklem		} else
3670244042Srmacklem			error = NFSERR_SEQMISORDERED;
3671244042Srmacklem	} else if ((slots[slotid].nfssl_seq + 1) == seqid) {
3672244042Srmacklem		m_freem(slots[slotid].nfssl_reply);
3673244042Srmacklem		slots[slotid].nfssl_reply = NULL;
3674244042Srmacklem		slots[slotid].nfssl_inprog = 1;
3675244042Srmacklem		slots[slotid].nfssl_seq++;
3676244042Srmacklem	} else
3677244042Srmacklem		error = NFSERR_SEQMISORDERED;
3678244042Srmacklem	return (error);
3679244042Srmacklem}
3680244042Srmacklem
3681244042Srmacklem/*
3682244042Srmacklem * Cache this reply for the slot.
3683244042Srmacklem */
3684244042Srmacklemvoid
3685244042Srmacklemnfsv4_seqsess_cacherep(uint32_t slotid, struct nfsslot *slots, struct mbuf *rep)
3686244042Srmacklem{
3687244042Srmacklem
3688244042Srmacklem	slots[slotid].nfssl_reply = rep;
3689244042Srmacklem	slots[slotid].nfssl_inprog = 0;
3690244042Srmacklem}
3691244042Srmacklem
3692244042Srmacklem/*
3693244042Srmacklem * Generate the xdr for an NFSv4.1 Sequence Operation.
3694244042Srmacklem */
3695244042SrmacklemAPPLESTATIC void
3696244042Srmacklemnfsv4_setsequence(struct nfsrv_descript *nd, struct nfsclsession *sep,
3697244042Srmacklem    int dont_replycache)
3698244042Srmacklem{
3699244042Srmacklem	uint32_t *tl, slotseq = 0;
3700244042Srmacklem	int i, maxslot, slotpos;
3701244042Srmacklem	uint64_t bitval;
3702244042Srmacklem	uint8_t sessionid[NFSX_V4SESSIONID];
3703244042Srmacklem
3704244042Srmacklem	/* Find an unused slot. */
3705244042Srmacklem	slotpos = -1;
3706244042Srmacklem	maxslot = -1;
3707244042Srmacklem	mtx_lock(&sep->nfsess_mtx);
3708244042Srmacklem	do {
3709244042Srmacklem		bitval = 1;
3710244042Srmacklem		for (i = 0; i < sep->nfsess_foreslots; i++) {
3711244042Srmacklem			if ((bitval & sep->nfsess_slots) == 0) {
3712244042Srmacklem				slotpos = i;
3713244042Srmacklem				sep->nfsess_slots |= bitval;
3714244042Srmacklem				sep->nfsess_slotseq[i]++;
3715244042Srmacklem				slotseq = sep->nfsess_slotseq[i];
3716244042Srmacklem				break;
3717244042Srmacklem			}
3718244042Srmacklem			bitval <<= 1;
3719244042Srmacklem		}
3720244042Srmacklem		if (slotpos == -1)
3721244042Srmacklem			(void)mtx_sleep(&sep->nfsess_slots, &sep->nfsess_mtx,
3722244042Srmacklem			    PZERO, "nfsclseq", 0);
3723244042Srmacklem	} while (slotpos == -1);
3724244042Srmacklem	/* Now, find the highest slot in use. (nfsc_slots is 64bits) */
3725244042Srmacklem	bitval = 1;
3726244042Srmacklem	for (i = 0; i < 64; i++) {
3727244042Srmacklem		if ((bitval & sep->nfsess_slots) != 0)
3728244042Srmacklem			maxslot = i;
3729244042Srmacklem		bitval <<= 1;
3730244042Srmacklem	}
3731244042Srmacklem	bcopy(sep->nfsess_sessionid, sessionid, NFSX_V4SESSIONID);
3732244042Srmacklem	mtx_unlock(&sep->nfsess_mtx);
3733244042Srmacklem	KASSERT(maxslot >= 0, ("nfscl_setsequence neg maxslot"));
3734244042Srmacklem
3735244042Srmacklem	/* Build the Sequence arguments. */
3736244042Srmacklem	NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED);
3737244042Srmacklem	bcopy(sessionid, tl, NFSX_V4SESSIONID);
3738244042Srmacklem	tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3739244042Srmacklem	nd->nd_slotseq = tl;
3740244042Srmacklem	*tl++ = txdr_unsigned(slotseq);
3741244042Srmacklem	*tl++ = txdr_unsigned(slotpos);
3742244042Srmacklem	*tl++ = txdr_unsigned(maxslot);
3743244042Srmacklem	if (dont_replycache == 0)
3744244042Srmacklem		*tl = newnfs_true;
3745244042Srmacklem	else
3746244042Srmacklem		*tl = newnfs_false;
3747244042Srmacklem	nd->nd_flag |= ND_HASSEQUENCE;
3748244042Srmacklem}
3749244042Srmacklem
3750244042Srmacklem/*
3751244042Srmacklem * Free a session slot.
3752244042Srmacklem */
3753244042SrmacklemAPPLESTATIC void
3754244042Srmacklemnfsv4_freeslot(struct nfsclsession *sep, int slot)
3755244042Srmacklem{
3756244042Srmacklem	uint64_t bitval;
3757244042Srmacklem
3758244042Srmacklem	bitval = 1;
3759244042Srmacklem	if (slot > 0)
3760244042Srmacklem		bitval <<= slot;
3761244042Srmacklem	mtx_lock(&sep->nfsess_mtx);
3762244042Srmacklem	if ((bitval & sep->nfsess_slots) == 0)
3763244042Srmacklem		printf("freeing free slot!!\n");
3764244042Srmacklem	sep->nfsess_slots &= ~bitval;
3765244042Srmacklem	wakeup(&sep->nfsess_slots);
3766244042Srmacklem	mtx_unlock(&sep->nfsess_mtx);
3767244042Srmacklem}
3768244042Srmacklem
3769