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