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