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