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