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