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