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