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