1 /*
2 * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29 /*
30 * Copyright (c) 1989, 1993
31 * The Regents of the University of California. All rights reserved.
32 *
33 * This code is derived from software contributed to Berkeley by
34 * Rick Macklem at The University of Guelph.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 * must display the following acknowledgement:
46 * This product includes software developed by the University of
47 * California, Berkeley and its contributors.
48 * 4. Neither the name of the University nor the names of its contributors
49 * may be used to endorse or promote products derived from this software
50 * without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 *
64 * @(#)nfs_subs.c 8.8 (Berkeley) 5/22/95
65 * FreeBSD-Id: nfs_subs.c,v 1.47 1997/11/07 08:53:24 phk Exp $
66 */
67
68 #include <nfs/nfs_conf.h>
69 #if CONFIG_NFS_SERVER
70
71 /*
72 * These functions support the macros and help fiddle mbuf chains for
73 * the nfs op functions. They do things like create the rpc header and
74 * copy data between mbuf chains and uio lists.
75 */
76 #include <sys/kauth.h>
77 #include <sys/mount_internal.h>
78 #include <sys/vnode_internal.h>
79 #include <sys/mbuf.h>
80 #include <sys/kpi_mbuf.h>
81 #include <sys/un.h>
82 #include <sys/domain.h>
83
84 #include <nfs/rpcv2.h>
85 #include <nfs/nfsproto.h>
86 #include <nfs/nfs.h>
87 #define _NFS_XDR_SUBS_FUNCS_ /* define this to get xdrbuf function definitions */
88 #include <nfs/xdr_subs.h>
89 #include <nfs/nfsm_subs.h>
90 #include <nfs/nfs_gss.h>
91
92 /*
93 * NFS globals
94 */
95 struct nfsrvstats __attribute__((aligned(8))) nfsrvstats;
96 size_t nfs_mbuf_mhlen = 0, nfs_mbuf_minclsize = 0;
97
98 /* NFS debugging support */
99 uint32_t nfsrv_debug_ctl;
100
101 #include <libkern/libkern.h>
102 #include <stdarg.h>
103
104 static mount_t nfsrv_getvfs_by_mntonname(char *path);
105
106 void
nfs_printf(unsigned int debug_control,unsigned int facility,unsigned int level,const char * fmt,...)107 nfs_printf(unsigned int debug_control, unsigned int facility, unsigned int level, const char *fmt, ...)
108 {
109 va_list ap;
110
111 if (__NFS_IS_DBG(debug_control, facility, level)) {
112 va_start(ap, fmt);
113 vprintf(fmt, ap);
114 va_end(ap);
115 }
116 }
117
118
119 #define DISPLAYLEN 16
120
121 static bool
isprint(int ch)122 isprint(int ch)
123 {
124 return ch >= 0x20 && ch <= 0x7e;
125 }
126
127 static void
hexdump(void * data,size_t len)128 hexdump(void *data, size_t len)
129 {
130 size_t i, j;
131 unsigned char *d = data;
132 char *p, disbuf[3 * DISPLAYLEN + 1];
133
134 for (i = 0; i < len; i += DISPLAYLEN) {
135 for (p = disbuf, j = 0; (j + i) < len && j < DISPLAYLEN; j++, p += 3) {
136 snprintf(p, 4, "%2.2x ", d[i + j]);
137 }
138 for (; j < DISPLAYLEN; j++, p += 3) {
139 snprintf(p, 4, " ");
140 }
141 printf("%s ", disbuf);
142 for (p = disbuf, j = 0; (j + i) < len && j < DISPLAYLEN; j++, p++) {
143 snprintf(p, 2, "%c", isprint(d[i + j]) ? d[i + j] : '.');
144 }
145 printf("%s\n", disbuf);
146 }
147 }
148
149 void
nfs_dump_mbuf(const char * func,int lineno,const char * msg,mbuf_t mb)150 nfs_dump_mbuf(const char *func, int lineno, const char *msg, mbuf_t mb)
151 {
152 mbuf_t m;
153
154 printf("%s:%d %s\n", func, lineno, msg);
155 for (m = mb; m; m = mbuf_next(m)) {
156 hexdump(mtod(m, void *), mbuf_len(m));
157 }
158 }
159
160 /*
161 * functions to convert between NFS and VFS types
162 */
163 nfstype
vtonfs_type(enum vtype vtype,int nfsvers)164 vtonfs_type(enum vtype vtype, int nfsvers)
165 {
166 switch (vtype) {
167 case VNON:
168 return NFNON;
169 case VREG:
170 return NFREG;
171 case VDIR:
172 return NFDIR;
173 case VBLK:
174 return NFBLK;
175 case VCHR:
176 return NFCHR;
177 case VLNK:
178 return NFLNK;
179 case VSOCK:
180 if (nfsvers > NFS_VER2) {
181 return NFSOCK;
182 }
183 return NFNON;
184 case VFIFO:
185 if (nfsvers > NFS_VER2) {
186 return NFFIFO;
187 }
188 return NFNON;
189 case VBAD:
190 case VSTR:
191 case VCPLX:
192 default:
193 return NFNON;
194 }
195 }
196
197 enum vtype
nfstov_type(nfstype nvtype,int nfsvers)198 nfstov_type(nfstype nvtype, int nfsvers)
199 {
200 switch (nvtype) {
201 case NFNON:
202 return VNON;
203 case NFREG:
204 return VREG;
205 case NFDIR:
206 return VDIR;
207 case NFBLK:
208 return VBLK;
209 case NFCHR:
210 return VCHR;
211 case NFLNK:
212 return VLNK;
213 case NFSOCK:
214 if (nfsvers > NFS_VER2) {
215 return VSOCK;
216 }
217 OS_FALLTHROUGH;
218 case NFFIFO:
219 if (nfsvers > NFS_VER2) {
220 return VFIFO;
221 }
222 OS_FALLTHROUGH;
223 case NFATTRDIR:
224 if (nfsvers > NFS_VER3) {
225 return VDIR;
226 }
227 OS_FALLTHROUGH;
228 case NFNAMEDATTR:
229 if (nfsvers > NFS_VER3) {
230 return VREG;
231 }
232 OS_FALLTHROUGH;
233 default:
234 return VNON;
235 }
236 }
237
238 int
vtonfsv2_mode(enum vtype vtype,mode_t m)239 vtonfsv2_mode(enum vtype vtype, mode_t m)
240 {
241 switch (vtype) {
242 case VNON:
243 case VREG:
244 case VDIR:
245 case VBLK:
246 case VCHR:
247 case VLNK:
248 case VSOCK:
249 return MAKEIMODE(vtype, m);
250 case VFIFO:
251 return MAKEIMODE(VCHR, m);
252 case VBAD:
253 case VSTR:
254 case VCPLX:
255 default:
256 return MAKEIMODE(VNON, m);
257 }
258 }
259
260 /*
261 * Mapping of old NFS Version 2 RPC numbers to generic numbers.
262 */
263 int nfsv3_procid[NFS_NPROCS] = {
264 NFSPROC_NULL,
265 NFSPROC_GETATTR,
266 NFSPROC_SETATTR,
267 NFSPROC_NOOP,
268 NFSPROC_LOOKUP,
269 NFSPROC_READLINK,
270 NFSPROC_READ,
271 NFSPROC_NOOP,
272 NFSPROC_WRITE,
273 NFSPROC_CREATE,
274 NFSPROC_REMOVE,
275 NFSPROC_RENAME,
276 NFSPROC_LINK,
277 NFSPROC_SYMLINK,
278 NFSPROC_MKDIR,
279 NFSPROC_RMDIR,
280 NFSPROC_READDIR,
281 NFSPROC_FSSTAT,
282 NFSPROC_NOOP,
283 NFSPROC_NOOP,
284 NFSPROC_NOOP,
285 NFSPROC_NOOP,
286 NFSPROC_NOOP
287 };
288
289 /*
290 * and the reverse mapping from generic to Version 2 procedure numbers
291 */
292 int nfsv2_procid[NFS_NPROCS] = {
293 NFSV2PROC_NULL,
294 NFSV2PROC_GETATTR,
295 NFSV2PROC_SETATTR,
296 NFSV2PROC_LOOKUP,
297 NFSV2PROC_NOOP,
298 NFSV2PROC_READLINK,
299 NFSV2PROC_READ,
300 NFSV2PROC_WRITE,
301 NFSV2PROC_CREATE,
302 NFSV2PROC_MKDIR,
303 NFSV2PROC_SYMLINK,
304 NFSV2PROC_CREATE,
305 NFSV2PROC_REMOVE,
306 NFSV2PROC_RMDIR,
307 NFSV2PROC_RENAME,
308 NFSV2PROC_LINK,
309 NFSV2PROC_READDIR,
310 NFSV2PROC_NOOP,
311 NFSV2PROC_STATFS,
312 NFSV2PROC_NOOP,
313 NFSV2PROC_NOOP,
314 NFSV2PROC_NOOP,
315 NFSV2PROC_NOOP
316 };
317
318
319 /*
320 * initialize NFS's cache of mbuf constants
321 */
322 void
nfs_mbuf_init(void)323 nfs_mbuf_init(void)
324 {
325 struct mbuf_stat ms;
326
327 mbuf_stats(&ms);
328 nfs_mbuf_mhlen = ms.mhlen;
329 nfs_mbuf_minclsize = ms.minclsize;
330 }
331
332 static void
nfs_netopt_free(struct nfs_netopt * no)333 nfs_netopt_free(struct nfs_netopt *no)
334 {
335 if (no->no_addr) {
336 kfree_data(no->no_addr, no->no_addr->sa_len);
337 }
338 if (no->no_mask) {
339 kfree_data(no->no_mask, no->no_mask->sa_len);
340 }
341
342 kfree_type(struct nfs_netopt, no);
343 }
344
345 /*
346 * allocate a list of mbufs to hold the given amount of data
347 */
348 int
nfsm_mbuf_get_list(size_t size,mbuf_t * mp,int * mbcnt)349 nfsm_mbuf_get_list(size_t size, mbuf_t *mp, int *mbcnt)
350 {
351 int error, cnt;
352 mbuf_t mhead, mlast, m;
353 size_t len, mlen;
354
355 error = cnt = 0;
356 mhead = mlast = NULL;
357 len = 0;
358
359 while (len < size) {
360 nfsm_mbuf_getcluster(error, &m, (size - len));
361 if (error) {
362 break;
363 }
364 if (!mhead) {
365 mhead = m;
366 }
367 if (mlast && ((error = mbuf_setnext(mlast, m)))) {
368 mbuf_free(m);
369 break;
370 }
371 mlen = mbuf_maxlen(m);
372 if ((len + mlen) > size) {
373 mlen = size - len;
374 }
375 mbuf_setlen(m, mlen);
376 len += mlen;
377 cnt++;
378 mlast = m;
379 }
380
381 if (!error) {
382 *mp = mhead;
383 *mbcnt = cnt;
384 }
385 return error;
386 }
387
388 /*
389 * nfsm_chain_new_mbuf()
390 *
391 * Add a new mbuf to the given chain.
392 */
393 int
nfsm_chain_new_mbuf(struct nfsm_chain * nmc,size_t sizehint)394 nfsm_chain_new_mbuf(struct nfsm_chain *nmc, size_t sizehint)
395 {
396 mbuf_t mb;
397 int error = 0;
398
399 if (nmc->nmc_flags & NFSM_CHAIN_FLAG_ADD_CLUSTERS) {
400 sizehint = nfs_mbuf_minclsize;
401 }
402
403 /* allocate a new mbuf */
404 nfsm_mbuf_getcluster(error, &mb, sizehint);
405 if (error) {
406 return error;
407 }
408 if (mb == NULL) {
409 panic("got NULL mbuf?");
410 }
411
412 /* do we have a current mbuf? */
413 if (nmc->nmc_mcur) {
414 /* first cap off current mbuf */
415 mbuf_setlen(nmc->nmc_mcur, nmc->nmc_ptr - mtod(nmc->nmc_mcur, caddr_t));
416 /* then append the new mbuf */
417 error = mbuf_setnext(nmc->nmc_mcur, mb);
418 if (error) {
419 mbuf_free(mb);
420 return error;
421 }
422 }
423
424 /* set up for using the new mbuf */
425 nmc->nmc_mcur = mb;
426 nmc->nmc_ptr = mtod(mb, caddr_t);
427 nmc->nmc_left = mbuf_trailingspace(mb);
428
429 return 0;
430 }
431
432 /*
433 * nfsm_chain_add_opaque_f()
434 *
435 * Add "len" bytes of opaque data pointed to by "buf" to the given chain.
436 */
437 int
nfsm_chain_add_opaque_f(struct nfsm_chain * nmc,const u_char * buf,size_t len)438 nfsm_chain_add_opaque_f(struct nfsm_chain *nmc, const u_char *buf, size_t len)
439 {
440 size_t paddedlen, tlen;
441 int error;
442
443 paddedlen = nfsm_rndup(len);
444
445 while (paddedlen) {
446 if (!nmc->nmc_left) {
447 error = nfsm_chain_new_mbuf(nmc, paddedlen);
448 if (error) {
449 return error;
450 }
451 }
452 tlen = MIN(nmc->nmc_left, paddedlen);
453 if (tlen) {
454 if (len) {
455 if (tlen > len) {
456 tlen = len;
457 }
458 bcopy(buf, nmc->nmc_ptr, tlen);
459 } else {
460 bzero(nmc->nmc_ptr, tlen);
461 }
462 nmc->nmc_ptr += tlen;
463 nmc->nmc_left -= tlen;
464 paddedlen -= tlen;
465 if (len) {
466 buf += tlen;
467 len -= tlen;
468 }
469 }
470 }
471 return 0;
472 }
473
474 /*
475 * nfsm_chain_add_opaque_nopad_f()
476 *
477 * Add "len" bytes of opaque data pointed to by "buf" to the given chain.
478 * Do not XDR pad.
479 */
480 int
nfsm_chain_add_opaque_nopad_f(struct nfsm_chain * nmc,const u_char * buf,size_t len)481 nfsm_chain_add_opaque_nopad_f(struct nfsm_chain *nmc, const u_char *buf, size_t len)
482 {
483 size_t tlen;
484 int error;
485
486 while (len > 0) {
487 if (nmc->nmc_left <= 0) {
488 error = nfsm_chain_new_mbuf(nmc, len);
489 if (error) {
490 return error;
491 }
492 }
493 tlen = MIN(nmc->nmc_left, len);
494 bcopy(buf, nmc->nmc_ptr, tlen);
495 nmc->nmc_ptr += tlen;
496 nmc->nmc_left -= tlen;
497 len -= tlen;
498 buf += tlen;
499 }
500 return 0;
501 }
502
503 /*
504 * Find the length of the NFS mbuf chain
505 * up to the current encoding/decoding offset.
506 */
507 size_t
nfsm_chain_offset(struct nfsm_chain * nmc)508 nfsm_chain_offset(struct nfsm_chain *nmc)
509 {
510 mbuf_t mb;
511 size_t len = 0;
512
513 for (mb = nmc->nmc_mhead; mb; mb = mbuf_next(mb)) {
514 if (mb == nmc->nmc_mcur) {
515 return len + (nmc->nmc_ptr - mtod(mb, caddr_t));
516 }
517 len += mbuf_len(mb);
518 }
519
520 return len;
521 }
522
523 /*
524 * nfsm_chain_advance()
525 *
526 * Advance an nfsm_chain by "len" bytes.
527 */
528 int
nfsm_chain_advance(struct nfsm_chain * nmc,size_t len)529 nfsm_chain_advance(struct nfsm_chain *nmc, size_t len)
530 {
531 mbuf_t mb;
532
533 while (len) {
534 if (nmc->nmc_left >= len) {
535 nmc->nmc_left -= len;
536 nmc->nmc_ptr += len;
537 return 0;
538 }
539 len -= nmc->nmc_left;
540 nmc->nmc_mcur = mb = mbuf_next(nmc->nmc_mcur);
541 if (!mb) {
542 return EBADRPC;
543 }
544 nmc->nmc_ptr = mtod(mb, caddr_t);
545 nmc->nmc_left = mbuf_len(mb);
546 }
547
548 return 0;
549 }
550
551 #if 0
552
553 /*
554 * nfsm_chain_reverse()
555 *
556 * Reverse decode offset in an nfsm_chain by "len" bytes.
557 */
558 int
559 nfsm_chain_reverse(struct nfsm_chain *nmc, size_t len)
560 {
561 size_t mlen, new_offset;
562 int error = 0;
563
564 mlen = nmc->nmc_ptr - mtod(nmc->nmc_mcur, caddr_t);
565 if (len <= mlen) {
566 nmc->nmc_ptr -= len;
567 nmc->nmc_left += len;
568 return 0;
569 }
570
571 new_offset = nfsm_chain_offset(nmc) - len;
572 nfsm_chain_dissect_init(error, nmc, nmc->nmc_mhead);
573 if (error) {
574 return error;
575 }
576
577 return nfsm_chain_advance(nmc, new_offset);
578 }
579
580 #endif
581
582 /*
583 * nfsm_chain_get_opaque_pointer_f()
584 *
585 * Return a pointer to the next "len" bytes of contiguous data in
586 * the mbuf chain. If the next "len" bytes are not contiguous, we
587 * try to manipulate the mbuf chain so that it is.
588 *
589 * The nfsm_chain is advanced by nfsm_rndup("len") bytes.
590 */
591 int
nfsm_chain_get_opaque_pointer_f(struct nfsm_chain * nmc,uint32_t len,u_char ** pptr)592 nfsm_chain_get_opaque_pointer_f(struct nfsm_chain *nmc, uint32_t len, u_char **pptr)
593 {
594 mbuf_t mbcur, mb;
595 uint32_t padlen;
596 size_t mblen, cplen, need, left;
597 u_char *ptr;
598 int error = 0;
599
600 /* move to next mbuf with data */
601 while (nmc->nmc_mcur && (nmc->nmc_left == 0)) {
602 mb = mbuf_next(nmc->nmc_mcur);
603 nmc->nmc_mcur = mb;
604 if (!mb) {
605 break;
606 }
607 nmc->nmc_ptr = mtod(mb, caddr_t);
608 nmc->nmc_left = mbuf_len(mb);
609 }
610 /* check if we've run out of data */
611 if (!nmc->nmc_mcur) {
612 return EBADRPC;
613 }
614
615 /* do we already have a contiguous buffer? */
616 if (nmc->nmc_left >= len) {
617 /* the returned pointer will be the current pointer */
618 *pptr = (u_char*)nmc->nmc_ptr;
619 error = nfsm_chain_advance(nmc, nfsm_rndup(len));
620 return error;
621 }
622
623 padlen = nfsm_rndup(len) - len;
624
625 /* we need (len - left) more bytes */
626 mbcur = nmc->nmc_mcur;
627 left = nmc->nmc_left;
628 need = len - left;
629
630 if (need > mbuf_trailingspace(mbcur)) {
631 /*
632 * The needed bytes won't fit in the current mbuf so we'll
633 * allocate a new mbuf to hold the contiguous range of data.
634 */
635 nfsm_mbuf_getcluster(error, &mb, len);
636 if (error) {
637 return error;
638 }
639 /* double check that this mbuf can hold all the data */
640 if (mbuf_maxlen(mb) < len) {
641 mbuf_free(mb);
642 return EOVERFLOW;
643 }
644
645 /* the returned pointer will be the new mbuf's data pointer */
646 *pptr = ptr = mtod(mb, u_char *);
647
648 /* copy "left" bytes to the new mbuf */
649 bcopy(nmc->nmc_ptr, ptr, left);
650 ptr += left;
651 mbuf_setlen(mb, left);
652
653 /* insert the new mbuf between the current and next mbufs */
654 error = mbuf_setnext(mb, mbuf_next(mbcur));
655 if (!error) {
656 error = mbuf_setnext(mbcur, mb);
657 }
658 if (error) {
659 mbuf_free(mb);
660 return error;
661 }
662
663 /* reduce current mbuf's length by "left" */
664 mbuf_setlen(mbcur, mbuf_len(mbcur) - left);
665
666 /*
667 * update nmc's state to point at the end of the mbuf
668 * where the needed data will be copied to.
669 */
670 nmc->nmc_mcur = mbcur = mb;
671 nmc->nmc_left = 0;
672 nmc->nmc_ptr = (caddr_t)ptr;
673 } else {
674 /* The rest of the data will fit in this mbuf. */
675
676 /* the returned pointer will be the current pointer */
677 *pptr = (u_char*)nmc->nmc_ptr;
678
679 /*
680 * update nmc's state to point at the end of the mbuf
681 * where the needed data will be copied to.
682 */
683 nmc->nmc_ptr += left;
684 nmc->nmc_left = 0;
685 }
686
687 /*
688 * move the next "need" bytes into the current
689 * mbuf from the mbufs that follow
690 */
691
692 /* extend current mbuf length */
693 mbuf_setlen(mbcur, mbuf_len(mbcur) + need);
694
695 /* mb follows mbufs we're copying/compacting data from */
696 mb = mbuf_next(mbcur);
697
698 while (need && mb) {
699 /* copy as much as we need/can */
700 ptr = mtod(mb, u_char *);
701 mblen = mbuf_len(mb);
702 cplen = MIN(mblen, need);
703 if (cplen) {
704 bcopy(ptr, nmc->nmc_ptr, cplen);
705 /*
706 * update the mbuf's pointer and length to reflect that
707 * the data was shifted to an earlier mbuf in the chain
708 */
709 error = mbuf_setdata(mb, ptr + cplen, mblen - cplen);
710 if (error) {
711 mbuf_setlen(mbcur, mbuf_len(mbcur) - need);
712 return error;
713 }
714 /* update pointer/need */
715 nmc->nmc_ptr += cplen;
716 need -= cplen;
717 }
718 /* if more needed, go to next mbuf */
719 if (need) {
720 mb = mbuf_next(mb);
721 }
722 }
723
724 /* did we run out of data in the mbuf chain? */
725 if (need) {
726 mbuf_setlen(mbcur, mbuf_len(mbcur) - need);
727 return EBADRPC;
728 }
729
730 /*
731 * update nmc's state to point after this contiguous data
732 *
733 * "mb" points to the last mbuf we copied data from so we
734 * just set nmc to point at whatever remains in that mbuf.
735 */
736 nmc->nmc_mcur = mb;
737 nmc->nmc_ptr = mtod(mb, caddr_t);
738 nmc->nmc_left = mbuf_len(mb);
739
740 /* move past any padding */
741 if (padlen) {
742 error = nfsm_chain_advance(nmc, padlen);
743 }
744
745 return error;
746 }
747
748 /*
749 * nfsm_chain_get_opaque_f()
750 *
751 * Read the next "len" bytes in the chain into "buf".
752 * The nfsm_chain is advanced by nfsm_rndup("len") bytes.
753 */
754 int
nfsm_chain_get_opaque_f(struct nfsm_chain * nmc,size_t len,u_char * buf)755 nfsm_chain_get_opaque_f(struct nfsm_chain *nmc, size_t len, u_char *buf)
756 {
757 size_t cplen, padlen;
758 int error = 0;
759
760 padlen = nfsm_rndup(len) - len;
761
762 /* loop through mbufs copying all the data we need */
763 while (len && nmc->nmc_mcur) {
764 /* copy as much as we need/can */
765 cplen = MIN(nmc->nmc_left, len);
766 if (cplen) {
767 bcopy(nmc->nmc_ptr, buf, cplen);
768 nmc->nmc_ptr += cplen;
769 nmc->nmc_left -= cplen;
770 buf += cplen;
771 len -= cplen;
772 }
773 /* if more needed, go to next mbuf */
774 if (len) {
775 mbuf_t mb = mbuf_next(nmc->nmc_mcur);
776 nmc->nmc_mcur = mb;
777 nmc->nmc_ptr = mb ? mtod(mb, caddr_t) : NULL;
778 nmc->nmc_left = mb ? mbuf_len(mb) : 0;
779 }
780 }
781
782 /* did we run out of data in the mbuf chain? */
783 if (len) {
784 return EBADRPC;
785 }
786
787 if (padlen) {
788 nfsm_chain_adv(error, nmc, padlen);
789 }
790
791 return error;
792 }
793
794 /*
795 * nfsm_chain_get_uio()
796 *
797 * Read the next "len" bytes in the chain into the given uio.
798 * The nfsm_chain is advanced by nfsm_rndup("len") bytes.
799 */
800 int
nfsm_chain_get_uio(struct nfsm_chain * nmc,size_t len,uio_t uio)801 nfsm_chain_get_uio(struct nfsm_chain *nmc, size_t len, uio_t uio)
802 {
803 size_t cplen, padlen;
804 int error = 0;
805
806 padlen = nfsm_rndup(len) - len;
807
808 /* loop through mbufs copying all the data we need */
809 while (len && nmc->nmc_mcur) {
810 /* copy as much as we need/can */
811 cplen = MIN(nmc->nmc_left, len);
812 if (cplen) {
813 cplen = MIN(cplen, INT32_MAX);
814 error = uiomove(nmc->nmc_ptr, (int)cplen, uio);
815 if (error) {
816 return error;
817 }
818 nmc->nmc_ptr += cplen;
819 nmc->nmc_left -= cplen;
820 len -= cplen;
821 }
822 /* if more needed, go to next mbuf */
823 if (len) {
824 mbuf_t mb = mbuf_next(nmc->nmc_mcur);
825 nmc->nmc_mcur = mb;
826 nmc->nmc_ptr = mb ? mtod(mb, caddr_t) : NULL;
827 nmc->nmc_left = mb ? mbuf_len(mb) : 0;
828 }
829 }
830
831 /* did we run out of data in the mbuf chain? */
832 if (len) {
833 return EBADRPC;
834 }
835
836 if (padlen) {
837 nfsm_chain_adv(error, nmc, padlen);
838 }
839
840 return error;
841 }
842
843 /*
844 * Schedule a callout thread to run an NFS timer function
845 * interval milliseconds in the future.
846 */
847 void
nfs_interval_timer_start(thread_call_t call,time_t interval)848 nfs_interval_timer_start(thread_call_t call, time_t interval)
849 {
850 uint64_t deadline;
851
852 clock_interval_to_deadline((int)interval, 1000 * 1000, &deadline);
853 thread_call_enter_delayed(call, deadline);
854 }
855
856 int nfsrv_cmp_secflavs(struct nfs_sec *, struct nfs_sec *);
857 int nfsrv_hang_addrlist(struct nfs_export *, struct user_nfs_export_args *);
858 int nfsrv_free_netopt(struct radix_node *, void *);
859 int nfsrv_free_addrlist(struct nfs_export *, struct user_nfs_export_args *);
860 struct nfs_export_options *nfsrv_export_lookup(struct nfs_export *, mbuf_t);
861 struct nfs_export *nfsrv_fhtoexport(struct nfs_filehandle *);
862 struct nfs_user_stat_node *nfsrv_get_user_stat_node(struct nfs_active_user_list *, struct sockaddr *, uid_t);
863 void nfsrv_init_user_list(struct nfs_active_user_list *);
864 void nfsrv_free_user_list(struct nfs_active_user_list *);
865
866 /*
867 * add NFSv3 WCC data to an mbuf chain
868 */
869 int
nfsm_chain_add_wcc_data_f(struct nfsrv_descript * nd,struct nfsm_chain * nmc,int preattrerr,struct vnode_attr * prevap,int postattrerr,struct vnode_attr * postvap)870 nfsm_chain_add_wcc_data_f(
871 struct nfsrv_descript *nd,
872 struct nfsm_chain *nmc,
873 int preattrerr,
874 struct vnode_attr *prevap,
875 int postattrerr,
876 struct vnode_attr *postvap)
877 {
878 int error = 0;
879
880 if (preattrerr) {
881 nfsm_chain_add_32(error, nmc, FALSE);
882 } else {
883 nfsm_chain_add_32(error, nmc, TRUE);
884 nfsm_chain_add_64(error, nmc, prevap->va_data_size);
885 nfsm_chain_add_time(error, nmc, NFS_VER3, &prevap->va_modify_time);
886 nfsm_chain_add_time(error, nmc, NFS_VER3, &prevap->va_change_time);
887 }
888 nfsm_chain_add_postop_attr(error, nd, nmc, postattrerr, postvap);
889
890 return error;
891 }
892
893 /*
894 * Extract a lookup path from the given mbufs and store it in
895 * a newly allocated buffer saved in the given nameidata structure.
896 */
897 int
nfsm_chain_get_path_namei(struct nfsm_chain * nmc,uint32_t len,struct nameidata * nip)898 nfsm_chain_get_path_namei(
899 struct nfsm_chain *nmc,
900 uint32_t len,
901 struct nameidata *nip)
902 {
903 struct componentname *cnp = &nip->ni_cnd;
904 int error = 0;
905 char *cp;
906
907 if (len > (MAXPATHLEN - 1)) {
908 return ENAMETOOLONG;
909 }
910
911 /*
912 * Get a buffer for the name to be translated, and copy the
913 * name into the buffer.
914 */
915 cnp->cn_pnbuf = zalloc(ZV_NAMEI);
916 cnp->cn_pnlen = MAXPATHLEN;
917 cnp->cn_flags |= HASBUF;
918
919 /* Copy the name from the mbuf list to the string */
920 cp = cnp->cn_pnbuf;
921 nfsm_chain_get_opaque(error, nmc, len, cp);
922 if (error) {
923 goto out;
924 }
925 cnp->cn_pnbuf[len] = '\0';
926
927 /* sanity check the string */
928 if ((strlen(cp) != len) || strchr(cp, '/')) {
929 error = EACCES;
930 }
931 out:
932 if (error) {
933 if (cnp->cn_pnbuf) {
934 NFS_ZFREE(ZV_NAMEI, cnp->cn_pnbuf);
935 }
936 cnp->cn_flags &= ~HASBUF;
937 } else {
938 nip->ni_pathlen = len;
939 }
940 return error;
941 }
942
943 /*
944 * Set up nameidata for a lookup() call and do it.
945 */
946 int
nfsrv_namei(struct nfsrv_descript * nd,vfs_context_t ctx,struct nameidata * nip,struct nfs_filehandle * nfhp,vnode_t * retdirp,struct nfs_export ** nxp,struct nfs_export_options ** nxop)947 nfsrv_namei(
948 struct nfsrv_descript *nd,
949 vfs_context_t ctx,
950 struct nameidata *nip,
951 struct nfs_filehandle *nfhp,
952 vnode_t *retdirp,
953 struct nfs_export **nxp,
954 struct nfs_export_options **nxop)
955 {
956 vnode_t dp;
957 int error;
958 struct componentname *cnp = &nip->ni_cnd;
959 uint32_t cnflags;
960 char *tmppn;
961
962 *retdirp = NULL;
963
964 /*
965 * Extract and set starting directory.
966 */
967 error = nfsrv_fhtovp(nfhp, nd, &dp, nxp, nxop);
968 if (error) {
969 goto out;
970 }
971 error = nfsrv_credcheck(nd, ctx, *nxp, *nxop);
972 if (error || (vnode_vtype(dp) != VDIR)) {
973 vnode_put(dp);
974 error = ENOTDIR;
975 goto out;
976 }
977 *retdirp = dp;
978
979 nip->ni_cnd.cn_context = ctx;
980
981 if (*nxop && ((*nxop)->nxo_flags & NX_READONLY)) {
982 cnp->cn_flags |= RDONLY;
983 }
984
985 cnp->cn_flags |= NOCROSSMOUNT;
986 cnp->cn_nameptr = cnp->cn_pnbuf;
987 nip->ni_usedvp = nip->ni_startdir = dp;
988 nip->ni_rootdir = rootvnode;
989
990 /*
991 * And call lookup() to do the real work
992 */
993 cnflags = nip->ni_cnd.cn_flags; /* store in case we have to restore */
994 while ((error = lookup(nip)) == ERECYCLE) {
995 nip->ni_cnd.cn_flags = cnflags;
996 cnp->cn_nameptr = cnp->cn_pnbuf;
997 nip->ni_usedvp = nip->ni_dvp = nip->ni_startdir = dp;
998 }
999 if (error) {
1000 goto out;
1001 }
1002
1003 /* Check for encountering a symbolic link */
1004 if (cnp->cn_flags & ISSYMLINK) {
1005 if (cnp->cn_flags & (LOCKPARENT | WANTPARENT)) {
1006 vnode_put(nip->ni_dvp);
1007 }
1008 if (nip->ni_vp) {
1009 vnode_put(nip->ni_vp);
1010 nip->ni_vp = NULL;
1011 }
1012 error = EINVAL;
1013 }
1014 out:
1015 if (error) {
1016 tmppn = cnp->cn_pnbuf;
1017 cnp->cn_pnbuf = NULL;
1018 cnp->cn_flags &= ~HASBUF;
1019 NFS_ZFREE(ZV_NAMEI, tmppn);
1020 }
1021 return error;
1022 }
1023
1024 /*
1025 * A fiddled version of m_adj() that ensures null fill to a 4-byte
1026 * boundary and only trims off the back end
1027 */
1028 void
nfsm_adj(mbuf_t mp,int len,int nul)1029 nfsm_adj(mbuf_t mp, int len, int nul)
1030 {
1031 mbuf_t m, mnext;
1032 int count, i;
1033 long mlen;
1034 char *cp;
1035
1036 /*
1037 * Trim from tail. Scan the mbuf chain,
1038 * calculating its length and finding the last mbuf.
1039 * If the adjustment only affects this mbuf, then just
1040 * adjust and return. Otherwise, rescan and truncate
1041 * after the remaining size.
1042 */
1043 count = 0;
1044 m = mp;
1045 for (;;) {
1046 mlen = mbuf_len(m);
1047 count += mlen;
1048 mnext = mbuf_next(m);
1049 if (mnext == NULL) {
1050 break;
1051 }
1052 m = mnext;
1053 }
1054 if (mlen > len) {
1055 mlen -= len;
1056 mbuf_setlen(m, mlen);
1057 if (nul > 0) {
1058 cp = mtod(m, caddr_t) + mlen - nul;
1059 for (i = 0; i < nul; i++) {
1060 *cp++ = '\0';
1061 }
1062 }
1063 return;
1064 }
1065 count -= len;
1066 if (count < 0) {
1067 count = 0;
1068 }
1069 /*
1070 * Correct length for chain is "count".
1071 * Find the mbuf with last data, adjust its length,
1072 * and toss data from remaining mbufs on chain.
1073 */
1074 for (m = mp; m; m = mbuf_next(m)) {
1075 mlen = mbuf_len(m);
1076 if (mlen >= count) {
1077 mlen = count;
1078 mbuf_setlen(m, count);
1079 if (nul > 0) {
1080 cp = mtod(m, caddr_t) + mlen - nul;
1081 for (i = 0; i < nul; i++) {
1082 *cp++ = '\0';
1083 }
1084 }
1085 break;
1086 }
1087 count -= mlen;
1088 }
1089 for (m = mbuf_next(m); m; m = mbuf_next(m)) {
1090 mbuf_setlen(m, 0);
1091 }
1092 }
1093
1094 /*
1095 * Trim the header out of the mbuf list and trim off any trailing
1096 * junk so that the mbuf list has only the write data.
1097 */
1098 int
nfsm_chain_trim_data(struct nfsm_chain * nmc,int len,int * mlen)1099 nfsm_chain_trim_data(struct nfsm_chain *nmc, int len, int *mlen)
1100 {
1101 int cnt = 0;
1102 long dlen, adjust;
1103 caddr_t data;
1104 mbuf_t m;
1105
1106 if (mlen) {
1107 *mlen = 0;
1108 }
1109
1110 /* trim header */
1111 for (m = nmc->nmc_mhead; m && (m != nmc->nmc_mcur); m = mbuf_next(m)) {
1112 mbuf_setlen(m, 0);
1113 }
1114 if (!m) {
1115 return EIO;
1116 }
1117
1118 /* trim current mbuf */
1119 data = mtod(m, caddr_t);
1120 dlen = mbuf_len(m);
1121 adjust = nmc->nmc_ptr - data;
1122 dlen -= adjust;
1123 if ((dlen > 0) && (adjust > 0)) {
1124 if (mbuf_setdata(m, nmc->nmc_ptr, dlen)) {
1125 return EIO;
1126 }
1127 } else {
1128 mbuf_setlen(m, dlen);
1129 }
1130
1131 /* skip next len bytes */
1132 for (; m && (cnt < len); m = mbuf_next(m)) {
1133 dlen = mbuf_len(m);
1134 cnt += dlen;
1135 if (cnt > len) {
1136 /* truncate to end of data */
1137 mbuf_setlen(m, dlen - (cnt - len));
1138 if (m == nmc->nmc_mcur) {
1139 nmc->nmc_left -= (cnt - len);
1140 }
1141 cnt = len;
1142 }
1143 }
1144 if (mlen) {
1145 *mlen = cnt;
1146 }
1147
1148 /* trim any trailing data */
1149 if (m == nmc->nmc_mcur) {
1150 nmc->nmc_left = 0;
1151 }
1152 for (; m; m = mbuf_next(m)) {
1153 mbuf_setlen(m, 0);
1154 }
1155
1156 return 0;
1157 }
1158
1159 int
nfsm_chain_add_fattr(struct nfsrv_descript * nd,struct nfsm_chain * nmc,struct vnode_attr * vap)1160 nfsm_chain_add_fattr(
1161 struct nfsrv_descript *nd,
1162 struct nfsm_chain *nmc,
1163 struct vnode_attr *vap)
1164 {
1165 int error = 0;
1166
1167 // XXX Should we assert here that all fields are supported?
1168
1169 nfsm_chain_add_32(error, nmc, vtonfs_type(vap->va_type, nd->nd_vers));
1170 if (nd->nd_vers == NFS_VER3) {
1171 nfsm_chain_add_32(error, nmc, vap->va_mode & 07777);
1172 } else {
1173 nfsm_chain_add_32(error, nmc, vtonfsv2_mode(vap->va_type, vap->va_mode));
1174 }
1175 nfsm_chain_add_32(error, nmc, vap->va_nlink);
1176 nfsm_chain_add_32(error, nmc, vap->va_uid);
1177 nfsm_chain_add_32(error, nmc, vap->va_gid);
1178 if (nd->nd_vers == NFS_VER3) {
1179 nfsm_chain_add_64(error, nmc, vap->va_data_size);
1180 nfsm_chain_add_64(error, nmc, vap->va_data_alloc);
1181 nfsm_chain_add_32(error, nmc, major(vap->va_rdev));
1182 nfsm_chain_add_32(error, nmc, minor(vap->va_rdev));
1183 nfsm_chain_add_64(error, nmc, vap->va_fsid);
1184 nfsm_chain_add_64(error, nmc, vap->va_fileid);
1185 } else {
1186 nfsm_chain_add_32(error, nmc, vap->va_data_size);
1187 nfsm_chain_add_32(error, nmc, NFS_FABLKSIZE);
1188 if (vap->va_type == VFIFO) {
1189 nfsm_chain_add_32(error, nmc, 0xffffffff);
1190 } else {
1191 nfsm_chain_add_32(error, nmc, vap->va_rdev);
1192 }
1193 nfsm_chain_add_32(error, nmc, vap->va_data_alloc / NFS_FABLKSIZE);
1194 nfsm_chain_add_32(error, nmc, vap->va_fsid);
1195 nfsm_chain_add_32(error, nmc, vap->va_fileid);
1196 }
1197 nfsm_chain_add_time(error, nmc, nd->nd_vers, &vap->va_access_time);
1198 nfsm_chain_add_time(error, nmc, nd->nd_vers, &vap->va_modify_time);
1199 nfsm_chain_add_time(error, nmc, nd->nd_vers, &vap->va_change_time);
1200
1201 return error;
1202 }
1203
1204 int
nfsm_chain_get_sattr(struct nfsrv_descript * nd,struct nfsm_chain * nmc,struct vnode_attr * vap)1205 nfsm_chain_get_sattr(
1206 struct nfsrv_descript *nd,
1207 struct nfsm_chain *nmc,
1208 struct vnode_attr *vap)
1209 {
1210 int error = 0;
1211 uint32_t val = 0;
1212 uint64_t val64 = 0;
1213 struct timespec now;
1214
1215 if (nd->nd_vers == NFS_VER2) {
1216 /*
1217 * There is/was a bug in the Sun client that puts 0xffff in the mode
1218 * field of sattr when it should put in 0xffffffff. The u_short
1219 * doesn't sign extend. So check the low order 2 bytes for 0xffff.
1220 */
1221 nfsm_chain_get_32(error, nmc, val);
1222 if ((val & 0xffff) != 0xffff) {
1223 VATTR_SET(vap, va_mode, val & 07777);
1224 /* save the "type" bits for NFSv2 create */
1225 VATTR_SET(vap, va_type, IFTOVT(val));
1226 VATTR_CLEAR_ACTIVE(vap, va_type);
1227 }
1228 nfsm_chain_get_32(error, nmc, val);
1229 if (val != (uint32_t)-1) {
1230 VATTR_SET(vap, va_uid, val);
1231 }
1232 nfsm_chain_get_32(error, nmc, val);
1233 if (val != (uint32_t)-1) {
1234 VATTR_SET(vap, va_gid, val);
1235 }
1236 /* save the "size" bits for NFSv2 create (even if they appear unset) */
1237 nfsm_chain_get_32(error, nmc, val);
1238 VATTR_SET(vap, va_data_size, val);
1239 if (val == (uint32_t)-1) {
1240 VATTR_CLEAR_ACTIVE(vap, va_data_size);
1241 }
1242 nfsm_chain_get_time(error, nmc, NFS_VER2,
1243 vap->va_access_time.tv_sec,
1244 vap->va_access_time.tv_nsec);
1245 if (vap->va_access_time.tv_sec != -1) {
1246 VATTR_SET_ACTIVE(vap, va_access_time);
1247 }
1248 nfsm_chain_get_time(error, nmc, NFS_VER2,
1249 vap->va_modify_time.tv_sec,
1250 vap->va_modify_time.tv_nsec);
1251 if (vap->va_modify_time.tv_sec != -1) {
1252 VATTR_SET_ACTIVE(vap, va_modify_time);
1253 }
1254 return error;
1255 }
1256
1257 /* NFSv3 */
1258 nfsm_chain_get_32(error, nmc, val);
1259 if (val) {
1260 nfsm_chain_get_32(error, nmc, val);
1261 VATTR_SET(vap, va_mode, val & 07777);
1262 }
1263 nfsm_chain_get_32(error, nmc, val);
1264 if (val) {
1265 nfsm_chain_get_32(error, nmc, val);
1266 VATTR_SET(vap, va_uid, val);
1267 }
1268 nfsm_chain_get_32(error, nmc, val);
1269 if (val) {
1270 nfsm_chain_get_32(error, nmc, val);
1271 VATTR_SET(vap, va_gid, val);
1272 }
1273 nfsm_chain_get_32(error, nmc, val);
1274 if (val) {
1275 nfsm_chain_get_64(error, nmc, val64);
1276 VATTR_SET(vap, va_data_size, val64);
1277 }
1278 nanotime(&now);
1279 nfsm_chain_get_32(error, nmc, val);
1280 switch (val) {
1281 case NFS_TIME_SET_TO_CLIENT:
1282 nfsm_chain_get_time(error, nmc, nd->nd_vers,
1283 vap->va_access_time.tv_sec,
1284 vap->va_access_time.tv_nsec);
1285 VATTR_SET_ACTIVE(vap, va_access_time);
1286 vap->va_vaflags &= ~VA_UTIMES_NULL;
1287 break;
1288 case NFS_TIME_SET_TO_SERVER:
1289 VATTR_SET(vap, va_access_time, now);
1290 vap->va_vaflags |= VA_UTIMES_NULL;
1291 break;
1292 }
1293 nfsm_chain_get_32(error, nmc, val);
1294 switch (val) {
1295 case NFS_TIME_SET_TO_CLIENT:
1296 nfsm_chain_get_time(error, nmc, nd->nd_vers,
1297 vap->va_modify_time.tv_sec,
1298 vap->va_modify_time.tv_nsec);
1299 VATTR_SET_ACTIVE(vap, va_modify_time);
1300 vap->va_vaflags &= ~VA_UTIMES_NULL;
1301 break;
1302 case NFS_TIME_SET_TO_SERVER:
1303 VATTR_SET(vap, va_modify_time, now);
1304 if (!VATTR_IS_ACTIVE(vap, va_access_time)) {
1305 vap->va_vaflags |= VA_UTIMES_NULL;
1306 }
1307 break;
1308 }
1309
1310 return error;
1311 }
1312
1313 /*
1314 * Compare two security flavor structs
1315 */
1316 int
nfsrv_cmp_secflavs(struct nfs_sec * sf1,struct nfs_sec * sf2)1317 nfsrv_cmp_secflavs(struct nfs_sec *sf1, struct nfs_sec *sf2)
1318 {
1319 int i;
1320
1321 if (sf1->count != sf2->count) {
1322 return 1;
1323 }
1324 for (i = 0; i < sf1->count; i++) {
1325 if (sf1->flavors[i] != sf2->flavors[i]) {
1326 return 1;
1327 }
1328 }
1329 return 0;
1330 }
1331
1332 /*
1333 * Build hash lists of net addresses and hang them off the NFS export.
1334 * Called by nfsrv_export() to set up the lists of export addresses.
1335 */
1336 int
nfsrv_hang_addrlist(struct nfs_export * nx,struct user_nfs_export_args * unxa)1337 nfsrv_hang_addrlist(struct nfs_export *nx, struct user_nfs_export_args *unxa)
1338 {
1339 struct nfs_export_net_args nxna;
1340 struct nfs_netopt *no, *rn_no;
1341 struct radix_node_head *rnh;
1342 struct radix_node *rn;
1343 struct sockaddr *saddr, *smask;
1344 struct domain *dom;
1345 size_t i, ss_minsize;
1346 int error;
1347 unsigned int net;
1348 user_addr_t uaddr;
1349 kauth_cred_t cred;
1350
1351 uaddr = unxa->nxa_nets;
1352 ss_minsize = sizeof(((struct sockaddr_storage *)0)->ss_len) + sizeof(((struct sockaddr_storage *)0)->ss_family);
1353 for (net = 0; net < unxa->nxa_netcount; net++, uaddr += sizeof(nxna)) {
1354 error = copyin(uaddr, &nxna, sizeof(nxna));
1355 if (error) {
1356 return error;
1357 }
1358
1359 if (nxna.nxna_addr.ss_len > sizeof(struct sockaddr_storage) ||
1360 (nxna.nxna_addr.ss_len != 0 && nxna.nxna_addr.ss_len < ss_minsize) ||
1361 nxna.nxna_mask.ss_len > sizeof(struct sockaddr_storage) ||
1362 (nxna.nxna_mask.ss_len != 0 && nxna.nxna_mask.ss_len < ss_minsize) ||
1363 nxna.nxna_addr.ss_family > AF_MAX ||
1364 nxna.nxna_mask.ss_family > AF_MAX) {
1365 return EINVAL;
1366 }
1367
1368 if (nxna.nxna_flags & (NX_MAPROOT | NX_MAPALL)) {
1369 struct posix_cred temp_pcred;
1370 bzero(&temp_pcred, sizeof(temp_pcred));
1371 temp_pcred.cr_uid = nxna.nxna_cred.cr_uid;
1372 temp_pcred.cr_ngroups = nxna.nxna_cred.cr_ngroups;
1373 for (i = 0; i < (size_t)nxna.nxna_cred.cr_ngroups && i < NGROUPS; i++) {
1374 temp_pcred.cr_groups[i] = nxna.nxna_cred.cr_groups[i];
1375 }
1376 cred = posix_cred_create(&temp_pcred);
1377 if (!IS_VALID_CRED(cred)) {
1378 return ENOMEM;
1379 }
1380 } else {
1381 cred = NOCRED;
1382 }
1383
1384 if (nxna.nxna_addr.ss_len == 0) {
1385 /* No address means this is a default/world export */
1386 if (nx->nx_flags & NX_DEFAULTEXPORT) {
1387 if (IS_VALID_CRED(cred)) {
1388 kauth_cred_unref(&cred);
1389 }
1390 return EEXIST;
1391 }
1392 nx->nx_flags |= NX_DEFAULTEXPORT;
1393 nx->nx_defopt.nxo_flags = nxna.nxna_flags;
1394 nx->nx_defopt.nxo_cred = cred;
1395 bcopy(&nxna.nxna_sec, &nx->nx_defopt.nxo_sec, sizeof(struct nfs_sec));
1396 nx->nx_expcnt++;
1397 continue;
1398 }
1399
1400 no = kalloc_type(struct nfs_netopt, Z_WAITOK | Z_ZERO | Z_NOFAIL);
1401 no->no_opt.nxo_flags = nxna.nxna_flags;
1402 no->no_opt.nxo_cred = cred;
1403 bcopy(&nxna.nxna_sec, &no->no_opt.nxo_sec, sizeof(struct nfs_sec));
1404
1405 if (nxna.nxna_addr.ss_len) {
1406 no->no_addr = kalloc_data(nxna.nxna_addr.ss_len, M_WAITOK);
1407 bcopy(&nxna.nxna_addr, no->no_addr, nxna.nxna_addr.ss_len);
1408 }
1409 saddr = no->no_addr;
1410
1411 if (nxna.nxna_mask.ss_len) {
1412 no->no_mask = kalloc_data(nxna.nxna_mask.ss_len, M_WAITOK);
1413 bcopy(&nxna.nxna_mask, no->no_mask, nxna.nxna_mask.ss_len);
1414 }
1415 smask = no->no_mask;
1416
1417 sa_family_t family = saddr->sa_family;
1418 if ((rnh = nx->nx_rtable[family]) == 0) {
1419 /*
1420 * Seems silly to initialize every AF when most are not
1421 * used, do so on demand here
1422 */
1423 TAILQ_FOREACH(dom, &domains, dom_entry) {
1424 if (dom->dom_family == family && dom->dom_rtattach) {
1425 dom->dom_rtattach((void **)&nx->nx_rtable[family],
1426 dom->dom_rtoffset);
1427 break;
1428 }
1429 }
1430 if ((rnh = nx->nx_rtable[family]) == 0) {
1431 if (IS_VALID_CRED(cred)) {
1432 kauth_cred_unref(&cred);
1433 }
1434 nfs_netopt_free(no);
1435 return ENOBUFS;
1436 }
1437 }
1438 rn = (*rnh->rnh_addaddr)((caddr_t)saddr, (caddr_t)smask, rnh, no->no_rnodes);
1439 if (rn == 0) {
1440 /*
1441 * One of the reasons that rnh_addaddr may fail is that
1442 * the entry already exists. To check for this case, we
1443 * look up the entry to see if it is there. If so, we
1444 * do not need to make a new entry but do continue.
1445 *
1446 * XXX should this be rnh_lookup() instead?
1447 */
1448 int matched = 0;
1449 rn = (*rnh->rnh_matchaddr)((caddr_t)saddr, rnh);
1450 rn_no = (struct nfs_netopt *)rn;
1451 if (rn != 0 && (rn->rn_flags & RNF_ROOT) == 0 &&
1452 (rn_no->no_opt.nxo_flags == nxna.nxna_flags) &&
1453 (!nfsrv_cmp_secflavs(&rn_no->no_opt.nxo_sec, &nxna.nxna_sec))) {
1454 kauth_cred_t cred2 = rn_no->no_opt.nxo_cred;
1455 if (cred == cred2) {
1456 /* creds are same (or both NULL) */
1457 matched = 1;
1458 } else if (cred && cred2 && (kauth_cred_getuid(cred) == kauth_cred_getuid(cred2))) {
1459 /*
1460 * Now compare the effective and
1461 * supplementary groups...
1462 *
1463 * Note: This comparison, as written,
1464 * does not correctly indicate that
1465 * the groups are equivalent, since
1466 * other than the first supplementary
1467 * group, which is also the effective
1468 * group, order on the remaining groups
1469 * doesn't matter, and this is an
1470 * ordered compare.
1471 */
1472 gid_t groups[NGROUPS];
1473 gid_t groups2[NGROUPS];
1474 size_t groupcount = NGROUPS;
1475 size_t group2count = NGROUPS;
1476
1477 if (!kauth_cred_getgroups(cred, groups, &groupcount) &&
1478 !kauth_cred_getgroups(cred2, groups2, &group2count) &&
1479 groupcount == group2count) {
1480 for (i = 0; i < group2count; i++) {
1481 if (groups[i] != groups2[i]) {
1482 break;
1483 }
1484 }
1485 if (i >= group2count || i >= NGROUPS) {
1486 matched = 1;
1487 }
1488 }
1489 }
1490 }
1491 if (IS_VALID_CRED(cred)) {
1492 kauth_cred_unref(&cred);
1493 }
1494 nfs_netopt_free(no);
1495 if (matched) {
1496 continue;
1497 }
1498 return EPERM;
1499 }
1500 nx->nx_expcnt++;
1501 }
1502
1503 return 0;
1504 }
1505
1506 /*
1507 * In order to properly track an export's netopt count, we need to pass
1508 * an additional argument to nfsrv_free_netopt() so that it can decrement
1509 * the export's netopt count.
1510 */
1511 struct nfsrv_free_netopt_arg {
1512 uint32_t *cnt;
1513 struct radix_node_head *rnh;
1514 };
1515
1516 int
nfsrv_free_netopt(struct radix_node * rn,void * w)1517 nfsrv_free_netopt(struct radix_node *rn, void *w)
1518 {
1519 struct nfsrv_free_netopt_arg *fna = (struct nfsrv_free_netopt_arg *)w;
1520 struct radix_node_head *rnh = fna->rnh;
1521 uint32_t *cnt = fna->cnt;
1522 struct nfs_netopt *nno = (struct nfs_netopt *)rn;
1523
1524 (*rnh->rnh_deladdr)(rn_get_key(rn), rn_get_mask(rn), rnh);
1525 if (IS_VALID_CRED(nno->no_opt.nxo_cred)) {
1526 kauth_cred_unref(&nno->no_opt.nxo_cred);
1527 }
1528 nfs_netopt_free(nno);
1529 *cnt -= 1;
1530 return 0;
1531 }
1532
1533 /*
1534 * Free the net address hash lists that are hanging off the mount points.
1535 */
1536 int
nfsrv_free_addrlist(struct nfs_export * nx,struct user_nfs_export_args * unxa)1537 nfsrv_free_addrlist(struct nfs_export *nx, struct user_nfs_export_args *unxa)
1538 {
1539 struct nfs_export_net_args nxna;
1540 struct radix_node_head *rnh;
1541 struct radix_node *rn;
1542 struct nfsrv_free_netopt_arg fna;
1543 struct nfs_netopt *nno;
1544 size_t ss_minsize;
1545 user_addr_t uaddr;
1546 unsigned int net;
1547 int i, error;
1548
1549 if (!unxa || !unxa->nxa_netcount) {
1550 /* delete everything */
1551 for (i = 0; i <= AF_MAX; i++) {
1552 if ((rnh = nx->nx_rtable[i])) {
1553 fna.rnh = rnh;
1554 fna.cnt = &nx->nx_expcnt;
1555 (*rnh->rnh_walktree)(rnh, nfsrv_free_netopt, (caddr_t)&fna);
1556 zfree(radix_node_head_zone, rnh);
1557 nx->nx_rtable[i] = 0;
1558 }
1559 }
1560 return 0;
1561 }
1562
1563 /* delete only the exports specified */
1564 uaddr = unxa->nxa_nets;
1565 ss_minsize = sizeof(((struct sockaddr_storage *)0)->ss_len) + sizeof(((struct sockaddr_storage *)0)->ss_family);
1566 for (net = 0; net < unxa->nxa_netcount; net++, uaddr += sizeof(nxna)) {
1567 error = copyin(uaddr, &nxna, sizeof(nxna));
1568 if (error) {
1569 return error;
1570 }
1571
1572 if (nxna.nxna_addr.ss_len == 0) {
1573 /* No address means this is a default/world export */
1574 if (nx->nx_flags & NX_DEFAULTEXPORT) {
1575 nx->nx_flags &= ~NX_DEFAULTEXPORT;
1576 if (IS_VALID_CRED(nx->nx_defopt.nxo_cred)) {
1577 kauth_cred_unref(&nx->nx_defopt.nxo_cred);
1578 }
1579 nx->nx_expcnt--;
1580 }
1581 continue;
1582 }
1583
1584 if (nxna.nxna_addr.ss_len > sizeof(struct sockaddr_storage) ||
1585 (nxna.nxna_addr.ss_len != 0 && nxna.nxna_addr.ss_len < ss_minsize) ||
1586 nxna.nxna_addr.ss_family > AF_MAX) {
1587 printf("nfsrv_free_addrlist: invalid socket address (%u)\n", net);
1588 continue;
1589 }
1590
1591 if (nxna.nxna_mask.ss_len > sizeof(struct sockaddr_storage) ||
1592 (nxna.nxna_mask.ss_len != 0 && nxna.nxna_mask.ss_len < ss_minsize) ||
1593 nxna.nxna_mask.ss_family > AF_MAX) {
1594 printf("nfsrv_free_addrlist: invalid socket mask (%u)\n", net);
1595 continue;
1596 }
1597
1598 if ((rnh = nx->nx_rtable[nxna.nxna_addr.ss_family]) == 0) {
1599 /* AF not initialized? */
1600 if (!(unxa->nxa_flags & NXA_ADD)) {
1601 printf("nfsrv_free_addrlist: address not found (0)\n");
1602 }
1603 continue;
1604 }
1605
1606 rn = (*rnh->rnh_lookup)(&nxna.nxna_addr,
1607 nxna.nxna_mask.ss_len ? &nxna.nxna_mask : NULL, rnh);
1608 if (!rn || (rn->rn_flags & RNF_ROOT)) {
1609 if (!(unxa->nxa_flags & NXA_ADD)) {
1610 printf("nfsrv_free_addrlist: address not found (1)\n");
1611 }
1612 continue;
1613 }
1614
1615 (*rnh->rnh_deladdr)(rn_get_key(rn), rn_get_mask(rn), rnh);
1616 nno = (struct nfs_netopt *)rn;
1617 if (IS_VALID_CRED(nno->no_opt.nxo_cred)) {
1618 kauth_cred_unref(&nno->no_opt.nxo_cred);
1619 }
1620 nfs_netopt_free(nno);
1621
1622 nx->nx_expcnt--;
1623 if (nx->nx_expcnt == ((nx->nx_flags & NX_DEFAULTEXPORT) ? 1 : 0)) {
1624 /* no more entries in rnh, so free it up */
1625 zfree(radix_node_head_zone, rnh);
1626 nx->nx_rtable[nxna.nxna_addr.ss_family] = 0;
1627 }
1628 }
1629
1630 return 0;
1631 }
1632
1633 void enablequotas(struct mount *mp, vfs_context_t ctx); // XXX
1634
1635 static int
nfsrv_export_compare(char * path1,char * path2)1636 nfsrv_export_compare(char *path1, char *path2)
1637 {
1638 mount_t mp1 = NULL, mp2 = NULL;
1639
1640 if (strncmp(path1, path2, MAXPATHLEN) == 0) {
1641 return 0;
1642 }
1643
1644 mp1 = nfsrv_getvfs_by_mntonname(path1);
1645 if (mp1) {
1646 vfs_unbusy(mp1);
1647 mp2 = nfsrv_getvfs_by_mntonname(path2);
1648 if (mp2) {
1649 vfs_unbusy(mp2);
1650 if (mp1 == mp2) {
1651 return 0;
1652 }
1653 }
1654 }
1655 return 1;
1656 }
1657
1658 int
nfsrv_export(struct user_nfs_export_args * unxa,vfs_context_t ctx)1659 nfsrv_export(struct user_nfs_export_args *unxa, vfs_context_t ctx)
1660 {
1661 int error = 0;
1662 size_t pathlen, nxfs_pathlen;
1663 struct nfs_exportfs *nxfs, *nxfs2, *nxfs3;
1664 struct nfs_export *nx, *nx2, *nx3;
1665 struct nfs_filehandle nfh;
1666 struct nameidata mnd, xnd;
1667 vnode_t mvp = NULL, xvp = NULL;
1668 mount_t mp = NULL;
1669 char path[MAXPATHLEN], *nxfs_path;
1670 int expisroot;
1671
1672 if (unxa->nxa_flags == NXA_CHECK) {
1673 /* just check if the path is an NFS-exportable file system */
1674 error = copyinstr(unxa->nxa_fspath, path, MAXPATHLEN, &pathlen);
1675 if (error) {
1676 return error;
1677 }
1678 NDINIT(&mnd, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
1679 UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
1680 error = namei(&mnd);
1681 if (error) {
1682 return error;
1683 }
1684 mvp = mnd.ni_vp;
1685 mp = vnode_mount(mvp);
1686 /* make sure it's the root of a file system */
1687 if (!vnode_isvroot(mvp)) {
1688 error = EINVAL;
1689 }
1690 /* make sure the file system is NFS-exportable */
1691 if (!error) {
1692 nfh.nfh_len = NFSV3_MAX_FID_SIZE;
1693 error = VFS_VPTOFH(mvp, (int*)&nfh.nfh_len, &nfh.nfh_fid[0], NULL);
1694 }
1695 if (!error && (nfh.nfh_len > (int)NFSV3_MAX_FID_SIZE)) {
1696 error = EIO;
1697 }
1698 if (!error && !(mp->mnt_vtable->vfc_vfsflags & VFC_VFSREADDIR_EXTENDED)) {
1699 error = EISDIR;
1700 }
1701 vnode_put(mvp);
1702 nameidone(&mnd);
1703 return error;
1704 }
1705
1706 /* all other operations: must be super user */
1707 if ((error = vfs_context_suser(ctx))) {
1708 return error;
1709 }
1710
1711 if (unxa->nxa_flags & NXA_DELETE_ALL) {
1712 /* delete all exports on all file systems */
1713 lck_rw_lock_exclusive(&nfsrv_export_rwlock);
1714 while ((nxfs = LIST_FIRST(&nfsrv_exports))) {
1715 mp = vfs_getvfs_by_mntonname(nxfs->nxfs_path);
1716 if (mp) {
1717 vfs_clearflags(mp, MNT_EXPORTED);
1718 mount_iterdrop(mp);
1719 mp = NULL;
1720 }
1721 /* delete all exports on this file system */
1722 while ((nx = LIST_FIRST(&nxfs->nxfs_exports))) {
1723 LIST_REMOVE(nx, nx_next);
1724 LIST_REMOVE(nx, nx_hash);
1725 /* delete all netopts for this export */
1726 nfsrv_free_addrlist(nx, NULL);
1727 nx->nx_flags &= ~NX_DEFAULTEXPORT;
1728 if (IS_VALID_CRED(nx->nx_defopt.nxo_cred)) {
1729 kauth_cred_unref(&nx->nx_defopt.nxo_cred);
1730 }
1731 /* free active user list for this export */
1732 nfsrv_free_user_list(&nx->nx_user_list);
1733 kfree_data_addr(nx->nx_path);
1734 kfree_type(struct nfs_export, nx);
1735 }
1736 LIST_REMOVE(nxfs, nxfs_next);
1737 kfree_data_addr(nxfs->nxfs_path);
1738 kfree_type(struct nfs_exportfs, nxfs);
1739 }
1740 if (nfsrv_export_hashtbl) {
1741 /* all exports deleted, clean up export hash table */
1742 hashdestroy(nfsrv_export_hashtbl, M_TEMP, nfsrv_export_hash);
1743 nfsrv_export_hash = 0;
1744 nfsrv_export_hashtbl = NULL;
1745 }
1746 lck_rw_done(&nfsrv_export_rwlock);
1747 return 0;
1748 }
1749
1750 error = copyinstr(unxa->nxa_fspath, path, MAXPATHLEN, &pathlen);
1751 if (error) {
1752 return error;
1753 }
1754
1755 lck_rw_lock_exclusive(&nfsrv_export_rwlock);
1756
1757 /* init export hash table if not already */
1758 if (!nfsrv_export_hashtbl) {
1759 if (nfsrv_export_hash_size <= 0) {
1760 nfsrv_export_hash_size = NFSRVEXPHASHSZ;
1761 }
1762 nfsrv_export_hashtbl = hashinit(nfsrv_export_hash_size, M_TEMP, &nfsrv_export_hash);
1763 }
1764
1765 // first check if we've already got an exportfs with the given ID
1766 LIST_FOREACH(nxfs, &nfsrv_exports, nxfs_next) {
1767 if (nxfs->nxfs_id == unxa->nxa_fsid) {
1768 break;
1769 }
1770 }
1771 if (nxfs) {
1772 /* verify exported FS path matches given path */
1773 if (nfsrv_export_compare(path, nxfs->nxfs_path)) {
1774 error = EEXIST;
1775 goto unlock_out;
1776 }
1777 if ((unxa->nxa_flags & (NXA_ADD | NXA_OFFLINE)) == NXA_ADD) {
1778 /* find exported FS root vnode */
1779 NDINIT(&mnd, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
1780 UIO_SYSSPACE, CAST_USER_ADDR_T(nxfs->nxfs_path), ctx);
1781 error = namei(&mnd);
1782 if (error) {
1783 goto unlock_out;
1784 }
1785 mvp = mnd.ni_vp;
1786 /* make sure it's (still) the root of a file system */
1787 if (!vnode_isvroot(mvp)) {
1788 error = EINVAL;
1789 goto out;
1790 }
1791 /* if adding, verify that the mount is still what we expect */
1792 mp = nfsrv_getvfs_by_mntonname(nxfs->nxfs_path);
1793 if (mp) {
1794 mount_ref(mp, 0);
1795 vfs_unbusy(mp);
1796 }
1797 /* sanity check: this should be same mount */
1798 if (mp != vnode_mount(mvp)) {
1799 error = EINVAL;
1800 goto out;
1801 }
1802 }
1803 } else {
1804 /* no current exported file system with that ID */
1805 if (!(unxa->nxa_flags & NXA_ADD)) {
1806 error = ENOENT;
1807 goto unlock_out;
1808 }
1809
1810 /* find exported FS root vnode */
1811 NDINIT(&mnd, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
1812 UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
1813 error = namei(&mnd);
1814 if (error) {
1815 if (!(unxa->nxa_flags & NXA_OFFLINE)) {
1816 goto unlock_out;
1817 }
1818 } else {
1819 mvp = mnd.ni_vp;
1820 /* make sure it's the root of a file system */
1821 if (!vnode_isvroot(mvp)) {
1822 /* bail if not marked offline */
1823 if (!(unxa->nxa_flags & NXA_OFFLINE)) {
1824 error = EINVAL;
1825 goto out;
1826 }
1827 vnode_put(mvp);
1828 nameidone(&mnd);
1829 mvp = NULL;
1830 } else {
1831 mp = vnode_mount(mvp);
1832 mount_ref(mp, 0);
1833
1834 /* make sure the file system is NFS-exportable */
1835 nfh.nfh_len = NFSV3_MAX_FID_SIZE;
1836 error = VFS_VPTOFH(mvp, (int*)&nfh.nfh_len, &nfh.nfh_fid[0], NULL);
1837 if (!error && (nfh.nfh_len > (int)NFSV3_MAX_FID_SIZE)) {
1838 error = EIO;
1839 }
1840 if (!error && !(mp->mnt_vtable->vfc_vfsflags & VFC_VFSREADDIR_EXTENDED)) {
1841 error = EISDIR;
1842 }
1843 if (error) {
1844 goto out;
1845 }
1846 }
1847 }
1848
1849 /* add an exportfs for it */
1850 nxfs = kalloc_type(struct nfs_exportfs, Z_WAITOK | Z_ZERO | Z_NOFAIL);
1851 nxfs->nxfs_id = unxa->nxa_fsid;
1852 if (mp) {
1853 nxfs_path = mp->mnt_vfsstat.f_mntonname;
1854 nxfs_pathlen = sizeof(mp->mnt_vfsstat.f_mntonname);
1855 } else {
1856 nxfs_path = path;
1857 nxfs_pathlen = pathlen;
1858 }
1859 nxfs->nxfs_path = kalloc_data(nxfs_pathlen, Z_WAITOK);
1860 if (!nxfs->nxfs_path) {
1861 kfree_type(struct nfs_exportfs, nxfs);
1862 error = ENOMEM;
1863 goto out;
1864 }
1865 bcopy(nxfs_path, nxfs->nxfs_path, nxfs_pathlen);
1866 /* insert into list in reverse-sorted order */
1867 nxfs3 = NULL;
1868 LIST_FOREACH(nxfs2, &nfsrv_exports, nxfs_next) {
1869 if (strncmp(nxfs->nxfs_path, nxfs2->nxfs_path, MAXPATHLEN) > 0) {
1870 break;
1871 }
1872 nxfs3 = nxfs2;
1873 }
1874 if (nxfs2) {
1875 LIST_INSERT_BEFORE(nxfs2, nxfs, nxfs_next);
1876 } else if (nxfs3) {
1877 LIST_INSERT_AFTER(nxfs3, nxfs, nxfs_next);
1878 } else {
1879 LIST_INSERT_HEAD(&nfsrv_exports, nxfs, nxfs_next);
1880 }
1881
1882 /* make sure any quotas are enabled before we export the file system */
1883 if (mp) {
1884 enablequotas(mp, ctx);
1885 }
1886 }
1887
1888 if (unxa->nxa_exppath) {
1889 error = copyinstr(unxa->nxa_exppath, path, MAXPATHLEN, &pathlen);
1890 if (error) {
1891 goto out;
1892 }
1893 LIST_FOREACH(nx, &nxfs->nxfs_exports, nx_next) {
1894 if (nx->nx_id == unxa->nxa_expid) {
1895 break;
1896 }
1897 }
1898 if (nx) {
1899 /* verify exported FS path matches given path */
1900 if (strncmp(path, nx->nx_path, MAXPATHLEN)) {
1901 error = EEXIST;
1902 goto out;
1903 }
1904 } else {
1905 /* no current export with that ID */
1906 if (!(unxa->nxa_flags & NXA_ADD)) {
1907 error = ENOENT;
1908 goto out;
1909 }
1910 /* add an export for it */
1911 nx = kalloc_type(struct nfs_export, Z_WAITOK | Z_ZERO | Z_NOFAIL);
1912 nx->nx_id = unxa->nxa_expid;
1913 nx->nx_fs = nxfs;
1914 microtime(&nx->nx_exptime);
1915 nx->nx_path = kalloc_data(pathlen, Z_WAITOK);
1916 if (!nx->nx_path) {
1917 error = ENOMEM;
1918 kfree_type(struct nfs_export, nx);
1919 nx = NULL;
1920 goto out1;
1921 }
1922 bcopy(path, nx->nx_path, pathlen);
1923 /* initialize the active user list */
1924 nfsrv_init_user_list(&nx->nx_user_list);
1925 /* insert into list in reverse-sorted order */
1926 nx3 = NULL;
1927 LIST_FOREACH(nx2, &nxfs->nxfs_exports, nx_next) {
1928 if (strncmp(nx->nx_path, nx2->nx_path, MAXPATHLEN) > 0) {
1929 break;
1930 }
1931 nx3 = nx2;
1932 }
1933 if (nx2) {
1934 LIST_INSERT_BEFORE(nx2, nx, nx_next);
1935 } else if (nx3) {
1936 LIST_INSERT_AFTER(nx3, nx, nx_next);
1937 } else {
1938 LIST_INSERT_HEAD(&nxfs->nxfs_exports, nx, nx_next);
1939 }
1940 /* insert into hash */
1941 LIST_INSERT_HEAD(NFSRVEXPHASH(nxfs->nxfs_id, nx->nx_id), nx, nx_hash);
1942
1943 /*
1944 * We don't allow/support nested exports. Check if the new entry
1945 * nests with the entries before and after or if there's an
1946 * entry for the file system root and subdirs.
1947 */
1948 error = 0;
1949 if ((nx3 && !strncmp(nx3->nx_path, nx->nx_path, pathlen - 1) &&
1950 (nx3->nx_path[pathlen - 1] == '/')) ||
1951 (nx2 && !strncmp(nx2->nx_path, nx->nx_path, strlen(nx2->nx_path)) &&
1952 (nx->nx_path[strlen(nx2->nx_path)] == '/'))) {
1953 error = EINVAL;
1954 }
1955 if (!error) {
1956 /* check export conflict with fs root export and vice versa */
1957 expisroot = !nx->nx_path[0] ||
1958 ((nx->nx_path[0] == '.') && !nx->nx_path[1]);
1959 LIST_FOREACH(nx2, &nxfs->nxfs_exports, nx_next) {
1960 if (expisroot) {
1961 if (nx2 != nx) {
1962 break;
1963 }
1964 } else if (!nx2->nx_path[0]) {
1965 break;
1966 } else if ((nx2->nx_path[0] == '.') && !nx2->nx_path[1]) {
1967 break;
1968 }
1969 }
1970 if (nx2) {
1971 error = EINVAL;
1972 }
1973 }
1974 if (error) {
1975 /*
1976 * Don't actually return an error because mountd is
1977 * probably about to delete the conflicting export.
1978 * This can happen when a new export momentarily conflicts
1979 * with an old export while the transition is being made.
1980 * Theoretically, mountd could be written to avoid this
1981 * transient situation - but it would greatly increase the
1982 * complexity of mountd for very little overall benefit.
1983 */
1984 printf("nfsrv_export: warning: nested exports: %s/%s\n",
1985 nxfs->nxfs_path, nx->nx_path);
1986 error = 0;
1987 }
1988 nx->nx_fh.nfh_xh.nxh_flags = NXHF_INVALIDFH;
1989 }
1990 /* make sure file handle is set up */
1991 if ((nx->nx_fh.nfh_xh.nxh_version != htonl(NFS_FH_VERSION)) ||
1992 (nx->nx_fh.nfh_xh.nxh_flags & NXHF_INVALIDFH)) {
1993 /* try to set up export root file handle */
1994 nx->nx_fh.nfh_xh.nxh_version = htonl(NFS_FH_VERSION);
1995 nx->nx_fh.nfh_xh.nxh_fsid = htonl(nx->nx_fs->nxfs_id);
1996 nx->nx_fh.nfh_xh.nxh_expid = htonl(nx->nx_id);
1997 nx->nx_fh.nfh_xh.nxh_flags = 0;
1998 nx->nx_fh.nfh_xh.nxh_reserved = 0;
1999 nx->nx_fh.nfh_fhp = (u_char*)&nx->nx_fh.nfh_xh;
2000 bzero(&nx->nx_fh.nfh_fid[0], NFSV2_MAX_FID_SIZE);
2001 if (mvp) {
2002 /* find export root vnode */
2003 if (!nx->nx_path[0] || ((nx->nx_path[0] == '.') && !nx->nx_path[1])) {
2004 /* exporting file system's root directory */
2005 xvp = mvp;
2006 vnode_get(xvp);
2007 } else {
2008 NDINIT(&xnd, LOOKUP, OP_LOOKUP, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
2009 xnd.ni_pathlen = (uint32_t)pathlen - 1; // pathlen max value is equal to MAXPATHLEN
2010 xnd.ni_cnd.cn_nameptr = xnd.ni_cnd.cn_pnbuf = path;
2011 xnd.ni_startdir = mvp;
2012 xnd.ni_usedvp = mvp;
2013 xnd.ni_rootdir = rootvnode;
2014 while ((error = lookup(&xnd)) == ERECYCLE) {
2015 xnd.ni_cnd.cn_flags = LOCKLEAF;
2016 xnd.ni_cnd.cn_nameptr = xnd.ni_cnd.cn_pnbuf;
2017 xnd.ni_usedvp = xnd.ni_dvp = xnd.ni_startdir = mvp;
2018 }
2019 if (error) {
2020 goto out1;
2021 }
2022 xvp = xnd.ni_vp;
2023 }
2024
2025 if (vnode_vtype(xvp) != VDIR) {
2026 error = EINVAL;
2027 vnode_put(xvp);
2028 goto out1;
2029 }
2030
2031 /* grab file handle */
2032 nx->nx_fh.nfh_len = NFSV3_MAX_FID_SIZE;
2033 error = VFS_VPTOFH(xvp, (int*)&nx->nx_fh.nfh_len, &nx->nx_fh.nfh_fid[0], NULL);
2034 if (!error && (nx->nx_fh.nfh_len > (int)NFSV3_MAX_FID_SIZE)) {
2035 error = EIO;
2036 } else {
2037 nx->nx_fh.nfh_xh.nxh_fidlen = nx->nx_fh.nfh_len;
2038 nx->nx_fh.nfh_len += sizeof(nx->nx_fh.nfh_xh);
2039 }
2040
2041 vnode_put(xvp);
2042 if (error) {
2043 goto out1;
2044 }
2045 } else {
2046 nx->nx_fh.nfh_xh.nxh_flags = NXHF_INVALIDFH;
2047 nx->nx_fh.nfh_xh.nxh_fidlen = 0;
2048 nx->nx_fh.nfh_len = sizeof(nx->nx_fh.nfh_xh);
2049 }
2050 }
2051 } else {
2052 nx = NULL;
2053 }
2054
2055 /* perform the export changes */
2056 if (unxa->nxa_flags & NXA_DELETE) {
2057 if (!nx) {
2058 /* delete all exports on this file system */
2059 while ((nx = LIST_FIRST(&nxfs->nxfs_exports))) {
2060 LIST_REMOVE(nx, nx_next);
2061 LIST_REMOVE(nx, nx_hash);
2062 /* delete all netopts for this export */
2063 nfsrv_free_addrlist(nx, NULL);
2064 nx->nx_flags &= ~NX_DEFAULTEXPORT;
2065 if (IS_VALID_CRED(nx->nx_defopt.nxo_cred)) {
2066 kauth_cred_unref(&nx->nx_defopt.nxo_cred);
2067 }
2068 /* delete active user list for this export */
2069 nfsrv_free_user_list(&nx->nx_user_list);
2070 kfree_data_addr(nx->nx_path);
2071 kfree_type(struct nfs_export, nx);
2072 }
2073 goto out1;
2074 } else if (!unxa->nxa_netcount) {
2075 /* delete all netopts for this export */
2076 nfsrv_free_addrlist(nx, NULL);
2077 nx->nx_flags &= ~NX_DEFAULTEXPORT;
2078 if (IS_VALID_CRED(nx->nx_defopt.nxo_cred)) {
2079 kauth_cred_unref(&nx->nx_defopt.nxo_cred);
2080 }
2081 } else {
2082 /* delete only the netopts for the given addresses */
2083 error = nfsrv_free_addrlist(nx, unxa);
2084 if (error) {
2085 goto out1;
2086 }
2087 }
2088 }
2089 if (unxa->nxa_flags & NXA_ADD) {
2090 /*
2091 * If going offline set the export time so that when
2092 * coming back on line we will present a new write verifier
2093 * to the client.
2094 */
2095 if (unxa->nxa_flags & NXA_OFFLINE) {
2096 microtime(&nx->nx_exptime);
2097 }
2098
2099 error = nfsrv_hang_addrlist(nx, unxa);
2100 if (!error && mp) {
2101 vfs_setflags(mp, MNT_EXPORTED);
2102 }
2103 }
2104
2105 out1:
2106 if (nx && !nx->nx_expcnt) {
2107 /* export has no export options */
2108 LIST_REMOVE(nx, nx_next);
2109 LIST_REMOVE(nx, nx_hash);
2110 /* delete active user list for this export */
2111 nfsrv_free_user_list(&nx->nx_user_list);
2112 kfree_data_addr(nx->nx_path);
2113 kfree_type(struct nfs_export, nx);
2114 }
2115 if (LIST_EMPTY(&nxfs->nxfs_exports)) {
2116 /* exported file system has no more exports */
2117 LIST_REMOVE(nxfs, nxfs_next);
2118 kfree_data_addr(nxfs->nxfs_path);
2119 kfree_type(struct nfs_exportfs, nxfs);
2120 if (mp) {
2121 vfs_clearflags(mp, MNT_EXPORTED);
2122 }
2123 }
2124
2125 out:
2126 if (mvp) {
2127 vnode_put(mvp);
2128 nameidone(&mnd);
2129 }
2130 unlock_out:
2131 if (mp) {
2132 mount_drop(mp, 0);
2133 }
2134 lck_rw_done(&nfsrv_export_rwlock);
2135 return error;
2136 }
2137
2138 /*
2139 * Check if there is a least one export that will allow this address.
2140 *
2141 * Return 0, if there is an export that will allow this address,
2142 * else return EACCES
2143 */
2144 int
nfsrv_check_exports_allow_address(mbuf_t nam)2145 nfsrv_check_exports_allow_address(mbuf_t nam)
2146 {
2147 struct nfs_exportfs *nxfs;
2148 struct nfs_export *nx;
2149 struct nfs_export_options *nxo = NULL;
2150
2151 if (nam == NULL) {
2152 return EACCES;
2153 }
2154
2155 lck_rw_lock_shared(&nfsrv_export_rwlock);
2156 LIST_FOREACH(nxfs, &nfsrv_exports, nxfs_next) {
2157 LIST_FOREACH(nx, &nxfs->nxfs_exports, nx_next) {
2158 /* A little optimizing by checking for the default first */
2159 if (nx->nx_flags & NX_DEFAULTEXPORT) {
2160 nxo = &nx->nx_defopt;
2161 }
2162 if (nxo || (nxo = nfsrv_export_lookup(nx, nam))) {
2163 goto found;
2164 }
2165 }
2166 }
2167 found:
2168 lck_rw_done(&nfsrv_export_rwlock);
2169
2170 return nxo ? 0 : EACCES;
2171 }
2172
2173 struct nfs_export_options *
nfsrv_export_lookup(struct nfs_export * nx,mbuf_t nam)2174 nfsrv_export_lookup(struct nfs_export *nx, mbuf_t nam)
2175 {
2176 struct nfs_export_options *nxo = NULL;
2177 struct nfs_netopt *no = NULL;
2178 struct radix_node_head *rnh;
2179 struct sockaddr *saddr;
2180
2181 /* Lookup in the export list first. */
2182 if (nam != NULL) {
2183 saddr = SA(mtod(nam, caddr_t));
2184 if (saddr->sa_family > AF_MAX) {
2185 /* Bogus sockaddr? Don't match anything. */
2186 return NULL;
2187 }
2188 rnh = nx->nx_rtable[saddr->sa_family];
2189 if (rnh != NULL) {
2190 no = (struct nfs_netopt *)
2191 (*rnh->rnh_matchaddr)((caddr_t)saddr, rnh);
2192 if (no && no->no_rnodes->rn_flags & RNF_ROOT) {
2193 no = NULL;
2194 }
2195 if (no) {
2196 nxo = &no->no_opt;
2197 }
2198 }
2199 }
2200 /* If no address match, use the default if it exists. */
2201 if ((nxo == NULL) && (nx->nx_flags & NX_DEFAULTEXPORT)) {
2202 nxo = &nx->nx_defopt;
2203 }
2204 return nxo;
2205 }
2206
2207 /* find an export for the given handle */
2208 struct nfs_export *
nfsrv_fhtoexport(struct nfs_filehandle * nfhp)2209 nfsrv_fhtoexport(struct nfs_filehandle *nfhp)
2210 {
2211 struct nfs_exphandle *nxh = (struct nfs_exphandle*)nfhp->nfh_fhp;
2212 struct nfs_export *nx;
2213 uint32_t fsid, expid;
2214
2215 if (!nfsrv_export_hashtbl) {
2216 return NULL;
2217 }
2218 fsid = ntohl(nxh->nxh_fsid);
2219 expid = ntohl(nxh->nxh_expid);
2220 nx = NFSRVEXPHASH(fsid, expid)->lh_first;
2221 for (; nx; nx = LIST_NEXT(nx, nx_hash)) {
2222 if (nx->nx_fs->nxfs_id != fsid) {
2223 continue;
2224 }
2225 if (nx->nx_id != expid) {
2226 continue;
2227 }
2228 break;
2229 }
2230 return nx;
2231 }
2232
2233 struct nfsrv_getvfs_by_mntonname_callback_args {
2234 const char *path; /* IN */
2235 mount_t mp; /* OUT */
2236 };
2237
2238 static int
nfsrv_getvfs_by_mntonname_callback(mount_t mp,void * v)2239 nfsrv_getvfs_by_mntonname_callback(mount_t mp, void *v)
2240 {
2241 struct nfsrv_getvfs_by_mntonname_callback_args * const args = v;
2242 char real_mntonname[MAXPATHLEN];
2243 size_t pathbuflen = MAXPATHLEN;
2244 vnode_t rvp;
2245 int error;
2246
2247 error = VFS_ROOT(mp, &rvp, vfs_context_current());
2248 if (error) {
2249 goto out;
2250 }
2251 error = vn_getpath_ext(rvp, NULLVP, real_mntonname, &pathbuflen,
2252 VN_GETPATH_FSENTER | VN_GETPATH_NO_FIRMLINK);
2253 vnode_put(rvp);
2254 if (error) {
2255 goto out;
2256 }
2257 if (strcmp(args->path, real_mntonname) == 0) {
2258 error = vfs_busy(mp, LK_NOWAIT);
2259 if (error == 0) {
2260 args->mp = mp;
2261 }
2262 return VFS_RETURNED_DONE;
2263 }
2264 out:
2265 return VFS_RETURNED;
2266 }
2267
2268 static mount_t
nfsrv_getvfs_by_mntonname(char * path)2269 nfsrv_getvfs_by_mntonname(char *path)
2270 {
2271 struct nfsrv_getvfs_by_mntonname_callback_args args = {
2272 .path = path,
2273 .mp = NULL,
2274 };
2275 mount_t mp;
2276 int error;
2277
2278 mp = vfs_getvfs_by_mntonname(path);
2279 if (mp) {
2280 error = vfs_busy(mp, LK_NOWAIT);
2281 mount_iterdrop(mp);
2282 if (error) {
2283 mp = NULL;
2284 }
2285 } else if (vfs_iterate(0, nfsrv_getvfs_by_mntonname_callback,
2286 &args) == 0) {
2287 mp = args.mp;
2288 }
2289 return mp;
2290 }
2291
2292 /*
2293 * nfsrv_fhtovp() - convert FH to vnode and export info
2294 */
2295 int
nfsrv_fhtovp(struct nfs_filehandle * nfhp,struct nfsrv_descript * nd,vnode_t * vpp,struct nfs_export ** nxp,struct nfs_export_options ** nxop)2296 nfsrv_fhtovp(
2297 struct nfs_filehandle *nfhp,
2298 struct nfsrv_descript *nd,
2299 vnode_t *vpp,
2300 struct nfs_export **nxp,
2301 struct nfs_export_options **nxop)
2302 {
2303 struct nfs_exphandle *nxh = (struct nfs_exphandle*)nfhp->nfh_fhp;
2304 struct nfs_export_options *nxo;
2305 u_char *fidp;
2306 int error;
2307 struct mount *mp;
2308 mbuf_t nam = NULL;
2309 uint32_t v;
2310 int i, valid;
2311
2312 *vpp = NULL;
2313 *nxp = NULL;
2314 *nxop = NULL;
2315
2316 if (nd != NULL) {
2317 nam = nd->nd_nam;
2318 }
2319
2320 v = ntohl(nxh->nxh_version);
2321 if (v != NFS_FH_VERSION) {
2322 /* file handle format not supported */
2323 return ESTALE;
2324 }
2325 if (nfhp->nfh_len > NFSV3_MAX_FH_SIZE) {
2326 return EBADRPC;
2327 }
2328 if (nfhp->nfh_len < (int)sizeof(struct nfs_exphandle)) {
2329 return ESTALE;
2330 }
2331 v = ntohs(nxh->nxh_flags);
2332 if (v & NXHF_INVALIDFH) {
2333 return ESTALE;
2334 }
2335
2336 *nxp = nfsrv_fhtoexport(nfhp);
2337 if (!*nxp) {
2338 return ESTALE;
2339 }
2340
2341 /* Get the export option structure for this <export, client> tuple. */
2342 *nxop = nxo = nfsrv_export_lookup(*nxp, nam);
2343 if (nam && (*nxop == NULL)) {
2344 return EACCES;
2345 }
2346
2347 if (nd != NULL) {
2348 /* Validate the security flavor of the request */
2349 for (i = 0, valid = 0; i < nxo->nxo_sec.count; i++) {
2350 if (nd->nd_sec == nxo->nxo_sec.flavors[i]) {
2351 valid = 1;
2352 break;
2353 }
2354 }
2355 if (!valid) {
2356 /*
2357 * RFC 2623 section 2.3.2 recommends no authentication
2358 * requirement for certain NFS procedures used for mounting.
2359 * This allows an unauthenticated superuser on the client
2360 * to do mounts for the benefit of authenticated users.
2361 */
2362 if (nd->nd_vers == NFS_VER2) {
2363 if (nd->nd_procnum == NFSV2PROC_GETATTR ||
2364 nd->nd_procnum == NFSV2PROC_STATFS) {
2365 valid = 1;
2366 }
2367 }
2368 if (nd->nd_vers == NFS_VER3) {
2369 if (nd->nd_procnum == NFSPROC_FSINFO) {
2370 valid = 1;
2371 }
2372 }
2373
2374 if (!valid) {
2375 return NFSERR_AUTHERR | AUTH_REJECTCRED;
2376 }
2377 }
2378 }
2379
2380 if (nxo && (nxo->nxo_flags & NX_OFFLINE)) {
2381 return (nd == NULL || nd->nd_vers == NFS_VER2) ? ESTALE : NFSERR_TRYLATER;
2382 }
2383
2384 /* find mount structure */
2385 mp = nfsrv_getvfs_by_mntonname((*nxp)->nx_fs->nxfs_path);
2386 if (!mp) {
2387 /*
2388 * We have an export, but no mount?
2389 * Perhaps the export just hasn't been marked offline yet.
2390 */
2391 return (nd == NULL || nd->nd_vers == NFS_VER2) ? ESTALE : NFSERR_TRYLATER;
2392 }
2393
2394 fidp = nfhp->nfh_fhp + sizeof(*nxh);
2395 error = VFS_FHTOVP(mp, nxh->nxh_fidlen, fidp, vpp, NULL);
2396 vfs_unbusy(mp);
2397 if (error) {
2398 return error;
2399 }
2400 /* vnode pointer should be good at this point or ... */
2401 if (*vpp == NULL) {
2402 return ESTALE;
2403 }
2404 return 0;
2405 }
2406
2407 /*
2408 * nfsrv_credcheck() - check/map credentials according
2409 * to given export options.
2410 */
2411 int
nfsrv_credcheck(struct nfsrv_descript * nd,vfs_context_t ctx,__unused struct nfs_export * nx,struct nfs_export_options * nxo)2412 nfsrv_credcheck(
2413 struct nfsrv_descript *nd,
2414 vfs_context_t ctx,
2415 __unused struct nfs_export *nx,
2416 struct nfs_export_options *nxo)
2417 {
2418 if (nxo && nxo->nxo_cred) {
2419 if ((nxo->nxo_flags & NX_MAPALL) ||
2420 ((nxo->nxo_flags & NX_MAPROOT) && !suser(nd->nd_cr, NULL))) {
2421 kauth_cred_ref(nxo->nxo_cred);
2422 kauth_cred_unref(&nd->nd_cr);
2423 nd->nd_cr = nxo->nxo_cred;
2424 }
2425 }
2426 ctx->vc_ucred = nd->nd_cr;
2427 return 0;
2428 }
2429
2430 /*
2431 * nfsrv_vptofh() - convert vnode to file handle for given export
2432 *
2433 * If the caller is passing in a vnode for a ".." directory entry,
2434 * they can pass a directory NFS file handle (dnfhp) which will be
2435 * checked against the root export file handle. If it matches, we
2436 * refuse to provide the file handle for the out-of-export directory.
2437 */
2438 int
nfsrv_vptofh(struct nfs_export * nx,int nfsvers,struct nfs_filehandle * dnfhp,vnode_t vp,vfs_context_t ctx,struct nfs_filehandle * nfhp)2439 nfsrv_vptofh(
2440 struct nfs_export *nx,
2441 int nfsvers,
2442 struct nfs_filehandle *dnfhp,
2443 vnode_t vp,
2444 vfs_context_t ctx,
2445 struct nfs_filehandle *nfhp)
2446 {
2447 int error;
2448 uint32_t maxfidsize;
2449
2450 nfhp->nfh_fhp = (u_char*)&nfhp->nfh_xh;
2451 nfhp->nfh_xh.nxh_version = htonl(NFS_FH_VERSION);
2452 nfhp->nfh_xh.nxh_fsid = htonl(nx->nx_fs->nxfs_id);
2453 nfhp->nfh_xh.nxh_expid = htonl(nx->nx_id);
2454 nfhp->nfh_xh.nxh_flags = 0;
2455 nfhp->nfh_xh.nxh_reserved = 0;
2456
2457 if (nfsvers == NFS_VER2) {
2458 bzero(&nfhp->nfh_fid[0], NFSV2_MAX_FID_SIZE);
2459 }
2460
2461 /* if directory FH matches export root, return invalid FH */
2462 if (dnfhp && nfsrv_fhmatch(dnfhp, &nx->nx_fh)) {
2463 if (nfsvers == NFS_VER2) {
2464 nfhp->nfh_len = NFSX_V2FH;
2465 } else {
2466 nfhp->nfh_len = sizeof(nfhp->nfh_xh);
2467 }
2468 nfhp->nfh_xh.nxh_fidlen = 0;
2469 nfhp->nfh_xh.nxh_flags = htons(NXHF_INVALIDFH);
2470 return 0;
2471 }
2472
2473 if (nfsvers == NFS_VER2) {
2474 maxfidsize = NFSV2_MAX_FID_SIZE;
2475 } else {
2476 maxfidsize = NFSV3_MAX_FID_SIZE;
2477 }
2478 nfhp->nfh_len = maxfidsize;
2479
2480 error = VFS_VPTOFH(vp, (int*)&nfhp->nfh_len, &nfhp->nfh_fid[0], ctx);
2481 if (error) {
2482 return error;
2483 }
2484 if (nfhp->nfh_len > maxfidsize) {
2485 return EOVERFLOW;
2486 }
2487 nfhp->nfh_xh.nxh_fidlen = nfhp->nfh_len;
2488 nfhp->nfh_len += sizeof(nfhp->nfh_xh);
2489 if ((nfsvers == NFS_VER2) && (nfhp->nfh_len < NFSX_V2FH)) {
2490 nfhp->nfh_len = NFSX_V2FH;
2491 }
2492
2493 return 0;
2494 }
2495
2496 /*
2497 * Compare two file handles to see it they're the same.
2498 * Note that we don't use nfh_len because that may include
2499 * padding in an NFSv2 file handle.
2500 */
2501 int
nfsrv_fhmatch(struct nfs_filehandle * fh1,struct nfs_filehandle * fh2)2502 nfsrv_fhmatch(struct nfs_filehandle *fh1, struct nfs_filehandle *fh2)
2503 {
2504 struct nfs_exphandle *nxh1, *nxh2;
2505 int len1, len2;
2506
2507 nxh1 = (struct nfs_exphandle *)fh1->nfh_fhp;
2508 nxh2 = (struct nfs_exphandle *)fh2->nfh_fhp;
2509 len1 = sizeof(fh1->nfh_xh) + nxh1->nxh_fidlen;
2510 len2 = sizeof(fh2->nfh_xh) + nxh2->nxh_fidlen;
2511 if (len1 != len2) {
2512 return 0;
2513 }
2514 if (bcmp(nxh1, nxh2, len1)) {
2515 return 0;
2516 }
2517 return 1;
2518 }
2519
2520 /*
2521 * Functions for dealing with active user lists
2522 */
2523
2524 /*
2525 * Search the hash table for a user node with a matching IP address and uid field.
2526 * If found, the node's tm_last timestamp is updated and the node is returned.
2527 *
2528 * If not found, a new node is allocated (or reclaimed via LRU), initialized, and returned.
2529 * Returns NULL if a new node could not be allocated OR saddr length exceeds sizeof(unode->sock).
2530 *
2531 * The list's user_mutex lock MUST be held.
2532 */
2533 struct nfs_user_stat_node *
nfsrv_get_user_stat_node(struct nfs_active_user_list * list,struct sockaddr * saddr,uid_t uid)2534 nfsrv_get_user_stat_node(struct nfs_active_user_list *list, struct sockaddr *saddr, uid_t uid)
2535 {
2536 struct nfs_user_stat_node *unode;
2537 struct timeval now;
2538 struct nfs_user_stat_hashtbl_head *head;
2539
2540 /* seach the hash table */
2541 head = NFS_USER_STAT_HASH(list->user_hashtbl, uid);
2542 LIST_FOREACH(unode, head, hash_link) {
2543 if ((uid == unode->uid) && (nfs_sockaddr_cmp(saddr, (struct sockaddr*)&unode->sock) == 0)) {
2544 /* found matching node */
2545 break;
2546 }
2547 }
2548
2549 if (unode) {
2550 /* found node in the hash table, now update lru position */
2551 TAILQ_REMOVE(&list->user_lru, unode, lru_link);
2552 TAILQ_INSERT_TAIL(&list->user_lru, unode, lru_link);
2553
2554 /* update time stamp */
2555 microtime(&now);
2556 unode->tm_last = (uint32_t)now.tv_sec;
2557 return unode;
2558 }
2559
2560 if (saddr->sa_len > sizeof(((struct nfs_user_stat_node *)0)->sock)) {
2561 /* saddr length exceeds maximum value */
2562 return NULL;
2563 }
2564
2565 if (list->node_count < nfsrv_user_stat_max_nodes) {
2566 /* Allocate a new node */
2567 unode = kalloc_type(struct nfs_user_stat_node,
2568 Z_WAITOK | Z_ZERO | Z_NOFAIL);
2569
2570 /* increment node count */
2571 OSAddAtomic(1, &nfsrv_user_stat_node_count);
2572 list->node_count++;
2573 } else {
2574 /* reuse the oldest node in the lru list */
2575 unode = TAILQ_FIRST(&list->user_lru);
2576
2577 if (!unode) {
2578 return NULL;
2579 }
2580
2581 /* Remove the node */
2582 TAILQ_REMOVE(&list->user_lru, unode, lru_link);
2583 LIST_REMOVE(unode, hash_link);
2584 }
2585
2586 /* Initialize the node */
2587 unode->uid = uid;
2588 bcopy(saddr, &unode->sock, MIN(saddr->sa_len, sizeof(unode->sock)));
2589 microtime(&now);
2590 unode->ops = 0;
2591 unode->bytes_read = 0;
2592 unode->bytes_written = 0;
2593 unode->tm_start = (uint32_t)now.tv_sec;
2594 unode->tm_last = (uint32_t)now.tv_sec;
2595
2596 /* insert the node */
2597 TAILQ_INSERT_TAIL(&list->user_lru, unode, lru_link);
2598 LIST_INSERT_HEAD(head, unode, hash_link);
2599
2600 return unode;
2601 }
2602
2603 void
nfsrv_update_user_stat(struct nfs_export * nx,struct nfsrv_descript * nd,uid_t uid,u_int ops,u_int rd_bytes,u_int wr_bytes)2604 nfsrv_update_user_stat(struct nfs_export *nx, struct nfsrv_descript *nd, uid_t uid, u_int ops, u_int rd_bytes, u_int wr_bytes)
2605 {
2606 struct nfs_user_stat_node *unode;
2607 struct nfs_active_user_list *ulist;
2608 struct sockaddr *saddr;
2609
2610 if ((!nfsrv_user_stat_enabled) || (!nx) || (!nd) || (!nd->nd_nam)) {
2611 return;
2612 }
2613
2614 saddr = SA(mtod(nd->nd_nam, caddr_t));
2615
2616 /* check address family before going any further */
2617 if ((saddr->sa_family != AF_INET) && (saddr->sa_family != AF_INET6)) {
2618 return;
2619 }
2620
2621 ulist = &nx->nx_user_list;
2622
2623 /* lock the active user list */
2624 lck_mtx_lock(&ulist->user_mutex);
2625
2626 /* get the user node */
2627 unode = nfsrv_get_user_stat_node(ulist, saddr, uid);
2628
2629 if (!unode) {
2630 lck_mtx_unlock(&ulist->user_mutex);
2631 return;
2632 }
2633
2634 /* update counters */
2635 unode->ops += ops;
2636 unode->bytes_read += rd_bytes;
2637 unode->bytes_written += wr_bytes;
2638
2639 /* done */
2640 lck_mtx_unlock(&ulist->user_mutex);
2641 }
2642
2643 /* initialize an active user list */
2644 void
nfsrv_init_user_list(struct nfs_active_user_list * ulist)2645 nfsrv_init_user_list(struct nfs_active_user_list *ulist)
2646 {
2647 uint i;
2648
2649 /* initialize the lru */
2650 TAILQ_INIT(&ulist->user_lru);
2651
2652 /* initialize the hash table */
2653 for (i = 0; i < NFS_USER_STAT_HASH_SIZE; i++) {
2654 LIST_INIT(&ulist->user_hashtbl[i]);
2655 }
2656 ulist->node_count = 0;
2657
2658 lck_mtx_init(&ulist->user_mutex, &nfsrv_active_user_mutex_group, LCK_ATTR_NULL);
2659 }
2660
2661 /* Free all nodes in an active user list */
2662 void
nfsrv_free_user_list(struct nfs_active_user_list * ulist)2663 nfsrv_free_user_list(struct nfs_active_user_list *ulist)
2664 {
2665 struct nfs_user_stat_node *unode;
2666
2667 if (!ulist) {
2668 return;
2669 }
2670
2671 while ((unode = TAILQ_FIRST(&ulist->user_lru))) {
2672 /* Remove node and free */
2673 TAILQ_REMOVE(&ulist->user_lru, unode, lru_link);
2674 LIST_REMOVE(unode, hash_link);
2675 kfree_type(struct nfs_user_stat_node, unode);
2676
2677 /* decrement node count */
2678 OSAddAtomic(-1, &nfsrv_user_stat_node_count);
2679 }
2680 ulist->node_count = 0;
2681
2682 lck_mtx_destroy(&ulist->user_mutex, &nfsrv_active_user_mutex_group);
2683 }
2684
2685 /* Reclaim old expired user nodes from active user lists. */
2686 void
nfsrv_active_user_list_reclaim(void)2687 nfsrv_active_user_list_reclaim(void)
2688 {
2689 struct nfs_exportfs *nxfs;
2690 struct nfs_export *nx;
2691 struct nfs_active_user_list *ulist;
2692 struct nfs_user_stat_hashtbl_head oldlist;
2693 struct nfs_user_stat_node *unode, *unode_next;
2694 struct timeval now;
2695 long tstale;
2696
2697 LIST_INIT(&oldlist);
2698
2699 lck_rw_lock_shared(&nfsrv_export_rwlock);
2700 microtime(&now);
2701 tstale = now.tv_sec - nfsrv_user_stat_max_idle_sec;
2702 LIST_FOREACH(nxfs, &nfsrv_exports, nxfs_next) {
2703 LIST_FOREACH(nx, &nxfs->nxfs_exports, nx_next) {
2704 /* Scan through all user nodes of this export */
2705 ulist = &nx->nx_user_list;
2706 lck_mtx_lock(&ulist->user_mutex);
2707 for (unode = TAILQ_FIRST(&ulist->user_lru); unode; unode = unode_next) {
2708 unode_next = TAILQ_NEXT(unode, lru_link);
2709
2710 /* check if this node has expired */
2711 if (unode->tm_last >= tstale) {
2712 break;
2713 }
2714
2715 /* Remove node from the active user list */
2716 TAILQ_REMOVE(&ulist->user_lru, unode, lru_link);
2717 LIST_REMOVE(unode, hash_link);
2718
2719 /* Add node to temp list */
2720 LIST_INSERT_HEAD(&oldlist, unode, hash_link);
2721
2722 /* decrement node count */
2723 OSAddAtomic(-1, &nfsrv_user_stat_node_count);
2724 ulist->node_count--;
2725 }
2726 /* can unlock this export's list now */
2727 lck_mtx_unlock(&ulist->user_mutex);
2728 }
2729 }
2730 lck_rw_done(&nfsrv_export_rwlock);
2731
2732 /* Free expired nodes */
2733 while ((unode = LIST_FIRST(&oldlist))) {
2734 LIST_REMOVE(unode, hash_link);
2735 kfree_type(struct nfs_user_stat_node, unode);
2736 }
2737 }
2738
2739 /*
2740 * Maps errno values to nfs error numbers.
2741 * Use NFSERR_IO as the catch all for ones not specifically defined in
2742 * RFC 1094.
2743 */
2744 static u_char nfsrv_v2errmap[] = {
2745 NFSERR_PERM, NFSERR_NOENT, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2746 NFSERR_NXIO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2747 NFSERR_IO, NFSERR_IO, NFSERR_ACCES, NFSERR_IO, NFSERR_IO,
2748 NFSERR_IO, NFSERR_EXIST, NFSERR_IO, NFSERR_NODEV, NFSERR_NOTDIR,
2749 NFSERR_ISDIR, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2750 NFSERR_IO, NFSERR_FBIG, NFSERR_NOSPC, NFSERR_IO, NFSERR_ROFS,
2751 NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2752 NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2753 NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2754 NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2755 NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2756 NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
2757 NFSERR_IO, NFSERR_IO, NFSERR_NAMETOL, NFSERR_IO, NFSERR_IO,
2758 NFSERR_NOTEMPTY, NFSERR_IO, NFSERR_IO, NFSERR_DQUOT, NFSERR_STALE,
2759 };
2760
2761 /*
2762 * Maps errno values to nfs error numbers.
2763 * Although it is not obvious whether or not NFS clients really care if
2764 * a returned error value is in the specified list for the procedure, the
2765 * safest thing to do is filter them appropriately. For Version 2, the
2766 * X/Open XNFS document is the only specification that defines error values
2767 * for each RPC (The RFC simply lists all possible error values for all RPCs),
2768 * so I have decided to not do this for Version 2.
2769 * The first entry is the default error return and the rest are the valid
2770 * errors for that RPC in increasing numeric order.
2771 */
2772 static short nfsv3err_null[] = {
2773 0,
2774 0,
2775 };
2776
2777 static short nfsv3err_getattr[] = {
2778 NFSERR_IO,
2779 NFSERR_IO,
2780 NFSERR_STALE,
2781 NFSERR_BADHANDLE,
2782 NFSERR_SERVERFAULT,
2783 NFSERR_TRYLATER,
2784 0,
2785 };
2786
2787 static short nfsv3err_setattr[] = {
2788 NFSERR_IO,
2789 NFSERR_PERM,
2790 NFSERR_IO,
2791 NFSERR_ACCES,
2792 NFSERR_INVAL,
2793 NFSERR_NOSPC,
2794 NFSERR_ROFS,
2795 NFSERR_DQUOT,
2796 NFSERR_STALE,
2797 NFSERR_BADHANDLE,
2798 NFSERR_NOT_SYNC,
2799 NFSERR_SERVERFAULT,
2800 NFSERR_TRYLATER,
2801 0,
2802 };
2803
2804 static short nfsv3err_lookup[] = {
2805 NFSERR_IO,
2806 NFSERR_NOENT,
2807 NFSERR_IO,
2808 NFSERR_ACCES,
2809 NFSERR_NOTDIR,
2810 NFSERR_NAMETOL,
2811 NFSERR_STALE,
2812 NFSERR_BADHANDLE,
2813 NFSERR_SERVERFAULT,
2814 NFSERR_TRYLATER,
2815 0,
2816 };
2817
2818 static short nfsv3err_access[] = {
2819 NFSERR_IO,
2820 NFSERR_IO,
2821 NFSERR_STALE,
2822 NFSERR_BADHANDLE,
2823 NFSERR_SERVERFAULT,
2824 NFSERR_TRYLATER,
2825 0,
2826 };
2827
2828 static short nfsv3err_readlink[] = {
2829 NFSERR_IO,
2830 NFSERR_IO,
2831 NFSERR_ACCES,
2832 NFSERR_INVAL,
2833 NFSERR_STALE,
2834 NFSERR_BADHANDLE,
2835 NFSERR_NOTSUPP,
2836 NFSERR_SERVERFAULT,
2837 NFSERR_TRYLATER,
2838 0,
2839 };
2840
2841 static short nfsv3err_read[] = {
2842 NFSERR_IO,
2843 NFSERR_IO,
2844 NFSERR_NXIO,
2845 NFSERR_ACCES,
2846 NFSERR_INVAL,
2847 NFSERR_STALE,
2848 NFSERR_BADHANDLE,
2849 NFSERR_SERVERFAULT,
2850 NFSERR_TRYLATER,
2851 0,
2852 };
2853
2854 static short nfsv3err_write[] = {
2855 NFSERR_IO,
2856 NFSERR_IO,
2857 NFSERR_ACCES,
2858 NFSERR_INVAL,
2859 NFSERR_FBIG,
2860 NFSERR_NOSPC,
2861 NFSERR_ROFS,
2862 NFSERR_DQUOT,
2863 NFSERR_STALE,
2864 NFSERR_BADHANDLE,
2865 NFSERR_SERVERFAULT,
2866 NFSERR_TRYLATER,
2867 0,
2868 };
2869
2870 static short nfsv3err_create[] = {
2871 NFSERR_IO,
2872 NFSERR_IO,
2873 NFSERR_ACCES,
2874 NFSERR_EXIST,
2875 NFSERR_NOTDIR,
2876 NFSERR_NOSPC,
2877 NFSERR_ROFS,
2878 NFSERR_NAMETOL,
2879 NFSERR_DQUOT,
2880 NFSERR_STALE,
2881 NFSERR_BADHANDLE,
2882 NFSERR_NOTSUPP,
2883 NFSERR_SERVERFAULT,
2884 NFSERR_TRYLATER,
2885 0,
2886 };
2887
2888 static short nfsv3err_mkdir[] = {
2889 NFSERR_IO,
2890 NFSERR_IO,
2891 NFSERR_ACCES,
2892 NFSERR_EXIST,
2893 NFSERR_NOTDIR,
2894 NFSERR_NOSPC,
2895 NFSERR_ROFS,
2896 NFSERR_NAMETOL,
2897 NFSERR_DQUOT,
2898 NFSERR_STALE,
2899 NFSERR_BADHANDLE,
2900 NFSERR_NOTSUPP,
2901 NFSERR_SERVERFAULT,
2902 NFSERR_TRYLATER,
2903 0,
2904 };
2905
2906 static short nfsv3err_symlink[] = {
2907 NFSERR_IO,
2908 NFSERR_IO,
2909 NFSERR_ACCES,
2910 NFSERR_EXIST,
2911 NFSERR_NOTDIR,
2912 NFSERR_NOSPC,
2913 NFSERR_ROFS,
2914 NFSERR_NAMETOL,
2915 NFSERR_DQUOT,
2916 NFSERR_STALE,
2917 NFSERR_BADHANDLE,
2918 NFSERR_NOTSUPP,
2919 NFSERR_SERVERFAULT,
2920 NFSERR_TRYLATER,
2921 0,
2922 };
2923
2924 static short nfsv3err_mknod[] = {
2925 NFSERR_IO,
2926 NFSERR_IO,
2927 NFSERR_ACCES,
2928 NFSERR_EXIST,
2929 NFSERR_NOTDIR,
2930 NFSERR_NOSPC,
2931 NFSERR_ROFS,
2932 NFSERR_NAMETOL,
2933 NFSERR_DQUOT,
2934 NFSERR_STALE,
2935 NFSERR_BADHANDLE,
2936 NFSERR_NOTSUPP,
2937 NFSERR_SERVERFAULT,
2938 NFSERR_BADTYPE,
2939 NFSERR_TRYLATER,
2940 0,
2941 };
2942
2943 static short nfsv3err_remove[] = {
2944 NFSERR_IO,
2945 NFSERR_NOENT,
2946 NFSERR_IO,
2947 NFSERR_ACCES,
2948 NFSERR_NOTDIR,
2949 NFSERR_ISDIR,
2950 NFSERR_ROFS,
2951 NFSERR_NAMETOL,
2952 NFSERR_STALE,
2953 NFSERR_BADHANDLE,
2954 NFSERR_SERVERFAULT,
2955 NFSERR_TRYLATER,
2956 0,
2957 };
2958
2959 static short nfsv3err_rmdir[] = {
2960 NFSERR_IO,
2961 NFSERR_NOENT,
2962 NFSERR_IO,
2963 NFSERR_ACCES,
2964 NFSERR_EXIST,
2965 NFSERR_NOTDIR,
2966 NFSERR_INVAL,
2967 NFSERR_ROFS,
2968 NFSERR_NAMETOL,
2969 NFSERR_NOTEMPTY,
2970 NFSERR_STALE,
2971 NFSERR_BADHANDLE,
2972 NFSERR_NOTSUPP,
2973 NFSERR_SERVERFAULT,
2974 NFSERR_TRYLATER,
2975 0,
2976 };
2977
2978 static short nfsv3err_rename[] = {
2979 NFSERR_IO,
2980 NFSERR_NOENT,
2981 NFSERR_IO,
2982 NFSERR_ACCES,
2983 NFSERR_EXIST,
2984 NFSERR_XDEV,
2985 NFSERR_NOTDIR,
2986 NFSERR_ISDIR,
2987 NFSERR_INVAL,
2988 NFSERR_NOSPC,
2989 NFSERR_ROFS,
2990 NFSERR_MLINK,
2991 NFSERR_NAMETOL,
2992 NFSERR_NOTEMPTY,
2993 NFSERR_DQUOT,
2994 NFSERR_STALE,
2995 NFSERR_BADHANDLE,
2996 NFSERR_NOTSUPP,
2997 NFSERR_SERVERFAULT,
2998 NFSERR_TRYLATER,
2999 0,
3000 };
3001
3002 static short nfsv3err_link[] = {
3003 NFSERR_IO,
3004 NFSERR_IO,
3005 NFSERR_ACCES,
3006 NFSERR_EXIST,
3007 NFSERR_XDEV,
3008 NFSERR_NOTDIR,
3009 NFSERR_ISDIR,
3010 NFSERR_INVAL,
3011 NFSERR_NOSPC,
3012 NFSERR_ROFS,
3013 NFSERR_MLINK,
3014 NFSERR_NAMETOL,
3015 NFSERR_DQUOT,
3016 NFSERR_STALE,
3017 NFSERR_BADHANDLE,
3018 NFSERR_NOTSUPP,
3019 NFSERR_SERVERFAULT,
3020 NFSERR_TRYLATER,
3021 0,
3022 };
3023
3024 static short nfsv3err_readdir[] = {
3025 NFSERR_IO,
3026 NFSERR_IO,
3027 NFSERR_ACCES,
3028 NFSERR_NOTDIR,
3029 NFSERR_STALE,
3030 NFSERR_BADHANDLE,
3031 NFSERR_BAD_COOKIE,
3032 NFSERR_TOOSMALL,
3033 NFSERR_SERVERFAULT,
3034 NFSERR_TRYLATER,
3035 0,
3036 };
3037
3038 static short nfsv3err_readdirplus[] = {
3039 NFSERR_IO,
3040 NFSERR_IO,
3041 NFSERR_ACCES,
3042 NFSERR_NOTDIR,
3043 NFSERR_STALE,
3044 NFSERR_BADHANDLE,
3045 NFSERR_BAD_COOKIE,
3046 NFSERR_NOTSUPP,
3047 NFSERR_TOOSMALL,
3048 NFSERR_SERVERFAULT,
3049 NFSERR_TRYLATER,
3050 0,
3051 };
3052
3053 static short nfsv3err_fsstat[] = {
3054 NFSERR_IO,
3055 NFSERR_IO,
3056 NFSERR_STALE,
3057 NFSERR_BADHANDLE,
3058 NFSERR_SERVERFAULT,
3059 NFSERR_TRYLATER,
3060 0,
3061 };
3062
3063 static short nfsv3err_fsinfo[] = {
3064 NFSERR_STALE,
3065 NFSERR_STALE,
3066 NFSERR_BADHANDLE,
3067 NFSERR_SERVERFAULT,
3068 NFSERR_TRYLATER,
3069 0,
3070 };
3071
3072 static short nfsv3err_pathconf[] = {
3073 NFSERR_STALE,
3074 NFSERR_STALE,
3075 NFSERR_BADHANDLE,
3076 NFSERR_SERVERFAULT,
3077 NFSERR_TRYLATER,
3078 0,
3079 };
3080
3081 static short nfsv3err_commit[] = {
3082 NFSERR_IO,
3083 NFSERR_IO,
3084 NFSERR_STALE,
3085 NFSERR_BADHANDLE,
3086 NFSERR_SERVERFAULT,
3087 NFSERR_BADTYPE,
3088 NFSERR_TRYLATER,
3089 0,
3090 };
3091
3092 static short *nfsrv_v3errmap[] = {
3093 nfsv3err_null,
3094 nfsv3err_getattr,
3095 nfsv3err_setattr,
3096 nfsv3err_lookup,
3097 nfsv3err_access,
3098 nfsv3err_readlink,
3099 nfsv3err_read,
3100 nfsv3err_write,
3101 nfsv3err_create,
3102 nfsv3err_mkdir,
3103 nfsv3err_symlink,
3104 nfsv3err_mknod,
3105 nfsv3err_remove,
3106 nfsv3err_rmdir,
3107 nfsv3err_rename,
3108 nfsv3err_link,
3109 nfsv3err_readdir,
3110 nfsv3err_readdirplus,
3111 nfsv3err_fsstat,
3112 nfsv3err_fsinfo,
3113 nfsv3err_pathconf,
3114 nfsv3err_commit,
3115 };
3116
3117 /*
3118 * Map errnos to NFS error numbers. For Version 3 also filter out error
3119 * numbers not specified for the associated procedure.
3120 */
3121 int
nfsrv_errmap(struct nfsrv_descript * nd,int err)3122 nfsrv_errmap(struct nfsrv_descript *nd, int err)
3123 {
3124 short *defaulterrp, *errp;
3125
3126 if (nd->nd_vers == NFS_VER2) {
3127 if (err <= (int)sizeof(nfsrv_v2errmap)) {
3128 return (int)nfsrv_v2errmap[err - 1];
3129 }
3130 return NFSERR_IO;
3131 }
3132 /* NFSv3 */
3133 if (nd->nd_procnum > NFSPROC_COMMIT) {
3134 return err & 0xffff;
3135 }
3136 errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
3137 while (*++errp) {
3138 if (*errp == err) {
3139 return err;
3140 } else if (*errp > err) {
3141 break;
3142 }
3143 }
3144 return (int)*defaulterrp;
3145 }
3146
3147 #endif /* CONFIG_NFS_SERVER */
3148