1 /* $NetBSD: nfs_boot.c,v 1.79 2009/03/04 06:56:25 nisimura Exp $ */ 2 3 /*- 4 * Copyright (c) 1995, 1997 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Adam Glass and Gordon W. Ross. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Support for NFS diskless booting, specifically getting information 34 * about where to mount root from, what pathnames, etc. 35 */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: nfs_boot.c,v 1.79 2009/03/04 06:56:25 nisimura Exp $"); 39 40 #ifdef _KERNEL_OPT 41 #include "opt_nfs.h" 42 #include "opt_tftproot.h" 43 #include "opt_nfs_boot.h" 44 #endif 45 46 #include <sys/param.h> 47 #include <sys/systm.h> 48 #include <sys/kernel.h> 49 #include <sys/device.h> 50 #include <sys/ioctl.h> 51 #include <sys/proc.h> 52 #include <sys/mount.h> 53 #include <sys/mbuf.h> 54 #include <sys/reboot.h> 55 #include <sys/socket.h> 56 #include <sys/socketvar.h> 57 58 #include <net/if.h> 59 #include <net/route.h> 60 #include <net/if_ether.h> 61 #include <net/if_types.h> 62 63 #include <netinet/in.h> 64 #include <netinet/if_inarp.h> 65 66 #include <nfs/rpcv2.h> 67 #include <nfs/krpc.h> 68 #include <nfs/xdr_subs.h> 69 70 #include <nfs/nfsproto.h> 71 #include <nfs/nfs.h> 72 #include <nfs/nfsmount.h> 73 #include <nfs/nfsdiskless.h> 74 75 /* 76 * There are three implementations of NFS diskless boot. 77 * One implementation uses BOOTP (RFC951, RFC1048), 78 * Sun RPC/bootparams or static configuration. See the 79 * files: 80 * nfs_bootdhcp.c: BOOTP (RFC951, RFC1048) 81 * nfs_bootparam.c: Sun RPC/bootparams 82 * nfs_bootstatic.c: honour config(1) description 83 */ 84 #if defined(NFS_BOOT_BOOTP) || defined(NFS_BOOT_DHCP) 85 int nfs_boot_rfc951 = 1; /* BOOTP enabled (default) */ 86 #endif 87 #ifdef NFS_BOOT_BOOTPARAM 88 int nfs_boot_bootparam = 1; /* BOOTPARAM enabled (default) */ 89 #endif 90 #ifdef NFS_BOOT_BOOTSTATIC 91 int nfs_boot_bootstatic = 1; /* BOOTSTATIC enabled (default) */ 92 #endif 93 94 /* mountd RPC */ 95 static int md_mount(struct sockaddr_in *mdsin, char *path, 96 struct nfs_args *argp, struct lwp *l); 97 98 static int nfs_boot_delroute(struct rtentry *, void *); 99 static void nfs_boot_defrt(struct in_addr *); 100 static int nfs_boot_getfh(struct nfs_dlmount *ndm, struct lwp *); 101 102 103 /* 104 * Called with an empty nfs_diskless struct to be filled in. 105 * Find an interface, determine its ip address (etc.) and 106 * save all the boot parameters in the nfs_diskless struct. 107 */ 108 int 109 nfs_boot_init(struct nfs_diskless *nd, struct lwp *lwp) 110 { 111 struct ifnet *ifp; 112 int error = 0; 113 int flags; 114 115 /* Explicitly necessary or build fails 116 * due to unused variable, otherwise. 117 */ 118 flags = 0; 119 120 /* 121 * Find the network interface. 122 */ 123 ifp = ifunit(device_xname(root_device)); 124 if (ifp == NULL) { 125 printf("nfs_boot: '%s' not found\n", 126 device_xname(root_device)); 127 return (ENXIO); 128 } 129 nd->nd_ifp = ifp; 130 131 error = EADDRNOTAVAIL; /* ??? */ 132 #if defined(NFS_BOOT_BOOTSTATIC) 133 if (error && nfs_boot_bootstatic) { 134 printf("nfs_boot: trying static\n"); 135 error = nfs_bootstatic(nd, lwp, &flags); 136 } 137 #endif 138 #if defined(NFS_BOOT_BOOTP) || defined(NFS_BOOT_DHCP) 139 if (error && nfs_boot_rfc951) { 140 #if defined(NFS_BOOT_DHCP) 141 printf("nfs_boot: trying DHCP/BOOTP\n"); 142 #else 143 printf("nfs_boot: trying BOOTP\n"); 144 #endif 145 error = nfs_bootdhcp(nd, lwp, &flags); 146 } 147 #endif 148 #ifdef NFS_BOOT_BOOTPARAM 149 if (error && nfs_boot_bootparam) { 150 printf("nfs_boot: trying RARP (and RPC/bootparam)\n"); 151 error = nfs_bootparam(nd, lwp, &flags); 152 } 153 #endif 154 if (error) 155 return (error); 156 157 /* 158 * If the gateway address is set, add a default route. 159 * (The mountd RPCs may go across a gateway.) 160 */ 161 if (nd->nd_gwip.s_addr) 162 nfs_boot_defrt(&nd->nd_gwip); 163 164 #ifdef TFTPROOT 165 if (nd->nd_nomount) 166 goto out; 167 #endif 168 /* 169 * Now fetch the NFS file handles as appropriate. 170 */ 171 error = nfs_boot_getfh(&nd->nd_root, lwp); 172 173 if (error) 174 nfs_boot_cleanup(nd, lwp); 175 176 #ifdef TFTPROOT 177 out: 178 #endif 179 return (error); 180 } 181 182 void 183 nfs_boot_cleanup(struct nfs_diskless *nd, struct lwp *lwp) 184 { 185 186 nfs_boot_deladdress(nd->nd_ifp, lwp, nd->nd_myip.s_addr); 187 nfs_boot_ifupdown(nd->nd_ifp, lwp, 0); 188 nfs_boot_flushrt(nd->nd_ifp); 189 } 190 191 int 192 nfs_boot_ifupdown(struct ifnet *ifp, struct lwp *lwp, int up) 193 { 194 struct socket *so; 195 struct ifreq ireq; 196 int error; 197 198 memset(&ireq, 0, sizeof(ireq)); 199 memcpy(ireq.ifr_name, ifp->if_xname, IFNAMSIZ); 200 201 /* 202 * Get a socket to use for various things in here. 203 * After this, use "goto out" to cleanup and return. 204 */ 205 error = socreate(AF_INET, &so, SOCK_DGRAM, 0, lwp, NULL); 206 if (error) { 207 printf("ifupdown: socreate, error=%d\n", error); 208 return (error); 209 } 210 211 /* 212 * Bring up the interface. (just set the "up" flag) 213 * Get the old interface flags and or IFF_UP into them so 214 * things like media selection flags are not clobbered. 215 */ 216 error = ifioctl(so, SIOCGIFFLAGS, (void *)&ireq, lwp); 217 if (error) { 218 printf("ifupdown: GIFFLAGS, error=%d\n", error); 219 goto out; 220 } 221 if (up) 222 ireq.ifr_flags |= IFF_UP; 223 else 224 ireq.ifr_flags &= ~IFF_UP; 225 error = ifioctl(so, SIOCSIFFLAGS, &ireq, lwp); 226 if (error) { 227 printf("ifupdown: SIFFLAGS, error=%d\n", error); 228 goto out; 229 } 230 231 if (up) 232 /* give the link some time to get up */ 233 tsleep(nfs_boot_ifupdown, PZERO, "nfsbif", 3 * hz); 234 out: 235 soclose(so); 236 return (error); 237 } 238 239 int 240 nfs_boot_setaddress(struct ifnet *ifp, struct lwp *lwp, 241 uint32_t addr, uint32_t netmask, uint32_t braddr) 242 { 243 struct socket *so; 244 struct ifaliasreq iareq; 245 struct sockaddr_in *sin; 246 int error; 247 248 /* 249 * Get a socket to use for various things in here. 250 * After this, use "goto out" to cleanup and return. 251 */ 252 error = socreate(AF_INET, &so, SOCK_DGRAM, 0, lwp, NULL); 253 if (error) { 254 printf("setaddress: socreate, error=%d\n", error); 255 return (error); 256 } 257 258 memset(&iareq, 0, sizeof(iareq)); 259 memcpy(iareq.ifra_name, ifp->if_xname, IFNAMSIZ); 260 261 /* Set the I/F address */ 262 sin = (struct sockaddr_in *)&iareq.ifra_addr; 263 sin->sin_len = sizeof(*sin); 264 sin->sin_family = AF_INET; 265 sin->sin_addr.s_addr = addr; 266 267 /* Set the netmask */ 268 if (netmask != INADDR_ANY) { 269 sin = (struct sockaddr_in *)&iareq.ifra_mask; 270 sin->sin_len = sizeof(*sin); 271 sin->sin_family = AF_INET; 272 sin->sin_addr.s_addr = netmask; 273 } /* else leave subnetmask unspecified (len=0) */ 274 275 /* Set the broadcast addr. */ 276 if (braddr != INADDR_ANY) { 277 sin = (struct sockaddr_in *)&iareq.ifra_broadaddr; 278 sin->sin_len = sizeof(*sin); 279 sin->sin_family = AF_INET; 280 sin->sin_addr.s_addr = braddr; 281 } /* else leave broadcast addr unspecified (len=0) */ 282 283 error = ifioctl(so, SIOCAIFADDR, (void *)&iareq, lwp); 284 if (error) { 285 printf("setaddress, error=%d\n", error); 286 goto out; 287 } 288 289 /* give the link some time to get up */ 290 tsleep(nfs_boot_setaddress, PZERO, "nfsbtd", 3 * hz); 291 out: 292 soclose(so); 293 return (error); 294 } 295 296 int 297 nfs_boot_deladdress(struct ifnet *ifp, struct lwp *lwp, uint32_t addr) 298 { 299 struct socket *so; 300 struct ifreq ifr; 301 struct sockaddr_in sin; 302 struct in_addr ia = {.s_addr = addr}; 303 int error; 304 305 /* 306 * Get a socket to use for various things in here. 307 * After this, use "goto out" to cleanup and return. 308 */ 309 error = socreate(AF_INET, &so, SOCK_DGRAM, 0, lwp, NULL); 310 if (error) { 311 printf("deladdress: socreate, error=%d\n", error); 312 return (error); 313 } 314 315 memset(&ifr, 0, sizeof(ifr)); 316 memcpy(ifr.ifr_name, ifp->if_xname, IFNAMSIZ); 317 318 sockaddr_in_init(&sin, &ia, 0); 319 ifreq_setaddr(SIOCDIFADDR, &ifr, sintocsa(&sin)); 320 321 error = ifioctl(so, SIOCDIFADDR, &ifr, lwp); 322 if (error) { 323 printf("deladdress, error=%d\n", error); 324 goto out; 325 } 326 327 out: 328 soclose(so); 329 return (error); 330 } 331 332 int 333 nfs_boot_setrecvtimo(struct socket *so) 334 { 335 struct timeval tv; 336 337 tv.tv_sec = 1; 338 tv.tv_usec = 0; 339 340 return (so_setsockopt(NULL, so, SOL_SOCKET, SO_RCVTIMEO, &tv, 341 sizeof(tv))); 342 } 343 344 int 345 nfs_boot_enbroadcast(struct socket *so) 346 { 347 int32_t on; 348 349 on = 1; 350 return (so_setsockopt(NULL, so, SOL_SOCKET, SO_BROADCAST, &on, 351 sizeof(on))); 352 } 353 354 int 355 nfs_boot_sobind_ipport(struct socket *so, uint16_t port, struct lwp *l) 356 { 357 struct mbuf *m; 358 struct sockaddr_in *sin; 359 int error; 360 361 m = m_getclr(M_WAIT, MT_SONAME); 362 sin = mtod(m, struct sockaddr_in *); 363 sin->sin_len = m->m_len = sizeof(*sin); 364 sin->sin_family = AF_INET; 365 sin->sin_addr.s_addr = INADDR_ANY; 366 sin->sin_port = htons(port); 367 error = sobind(so, m, l); 368 m_freem(m); 369 return (error); 370 } 371 372 /* 373 * What is the longest we will wait before re-sending a request? 374 * Note this is also the frequency of "timeout" messages. 375 * The re-send loop counts up linearly to this maximum, so the 376 * first complaint will happen after (1+2+3+4+5)=15 seconds. 377 */ 378 #define MAX_RESEND_DELAY 5 /* seconds */ 379 #define TOTAL_TIMEOUT 30 /* seconds */ 380 381 int 382 nfs_boot_sendrecv(struct socket *so, struct mbuf *nam, 383 int (*sndproc)(struct mbuf *, void *, int), 384 struct mbuf *snd, 385 int (*rcvproc)(struct mbuf *, void *), 386 struct mbuf **rcv, struct mbuf **from_p, 387 void *context, struct lwp *lwp) 388 { 389 int error, rcvflg, timo, secs, waited; 390 struct mbuf *m, *from; 391 struct uio uio; 392 393 /* Free at end if not null. */ 394 from = NULL; 395 396 /* 397 * Send it, repeatedly, until a reply is received, 398 * but delay each re-send by an increasing amount. 399 * If the delay hits the maximum, start complaining. 400 */ 401 waited = timo = 0; 402 send_again: 403 waited += timo; 404 if (waited >= TOTAL_TIMEOUT) 405 return (ETIMEDOUT); 406 407 /* Determine new timeout. */ 408 if (timo < MAX_RESEND_DELAY) 409 timo++; 410 else 411 printf("nfs_boot: timeout...\n"); 412 413 if (sndproc) { 414 error = (*sndproc)(snd, context, waited); 415 if (error) 416 goto out; 417 } 418 419 /* Send request (or re-send). */ 420 m = m_copypacket(snd, M_WAIT); 421 if (m == NULL) { 422 error = ENOBUFS; 423 goto out; 424 } 425 error = (*so->so_send)(so, nam, NULL, m, NULL, 0, lwp); 426 if (error) { 427 printf("nfs_boot: sosend: %d\n", error); 428 goto out; 429 } 430 m = NULL; 431 432 /* 433 * Wait for up to timo seconds for a reply. 434 * The socket receive timeout was set to 1 second. 435 */ 436 437 secs = timo; 438 for (;;) { 439 if (from) { 440 m_freem(from); 441 from = NULL; 442 } 443 if (m) { 444 m_freem(m); 445 m = NULL; 446 } 447 uio.uio_resid = 1 << 16; /* ??? */ 448 rcvflg = 0; 449 error = (*so->so_receive)(so, &from, &uio, &m, NULL, &rcvflg); 450 if (error == EWOULDBLOCK) { 451 if (--secs <= 0) 452 goto send_again; 453 continue; 454 } 455 if (error) 456 goto out; 457 #ifdef DIAGNOSTIC 458 if (!m || !(m->m_flags & M_PKTHDR) 459 || (1 << 16) - uio.uio_resid != m->m_pkthdr.len) 460 panic("nfs_boot_sendrecv: return size"); 461 #endif 462 463 if ((*rcvproc)(m, context)) 464 continue; 465 466 if (rcv) 467 *rcv = m; 468 else 469 m_freem(m); 470 if (from_p) { 471 *from_p = from; 472 from = NULL; 473 } 474 break; 475 } 476 out: 477 if (from) m_freem(from); 478 return (error); 479 } 480 481 /* 482 * Install a default route to the passed IP address. 483 */ 484 static void 485 nfs_boot_defrt(struct in_addr *gw_ip) 486 { 487 struct sockaddr dst, gw, mask; 488 struct sockaddr_in *sin; 489 int error; 490 491 /* Destination: (default) */ 492 memset((void *)&dst, 0, sizeof(dst)); 493 dst.sa_len = sizeof(dst); 494 dst.sa_family = AF_INET; 495 /* Gateway: */ 496 memset((void *)&gw, 0, sizeof(gw)); 497 sin = (struct sockaddr_in *)&gw; 498 sin->sin_len = sizeof(*sin); 499 sin->sin_family = AF_INET; 500 sin->sin_addr.s_addr = gw_ip->s_addr; 501 /* Mask: (zero length) */ 502 /* XXX - Just pass a null pointer? */ 503 memset(&mask, 0, sizeof(mask)); 504 505 /* add, dest, gw, mask, flags, 0 */ 506 error = rtrequest(RTM_ADD, &dst, &gw, &mask, 507 (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL); 508 if (error) { 509 printf("nfs_boot: add route, error=%d\n", error); 510 error = 0; 511 } 512 } 513 514 static int 515 nfs_boot_delroute(struct rtentry *rt, void *w) 516 { 517 int error; 518 519 if ((void *)rt->rt_ifp != w) 520 return 0; 521 522 error = rtrequest(RTM_DELETE, rt_getkey(rt), NULL, rt_mask(rt), 0, 523 NULL); 524 if (error != 0) 525 printf("%s: del route, error=%d\n", __func__, error); 526 527 return 0; 528 } 529 530 void 531 nfs_boot_flushrt(struct ifnet *ifp) 532 { 533 534 rt_walktree(AF_INET, nfs_boot_delroute, ifp); 535 } 536 537 /* 538 * Get an initial NFS file handle using Sun RPC/mountd. 539 * Separate function because we used to call it twice. 540 * (once for root and once for swap) 541 * 542 * ndm output 543 */ 544 static int 545 nfs_boot_getfh(struct nfs_dlmount *ndm, struct lwp *l) 546 { 547 struct nfs_args *args; 548 struct sockaddr_in *sin; 549 char *pathname; 550 int error; 551 u_int16_t port; 552 553 args = &ndm->ndm_args; 554 555 /* Initialize mount args. */ 556 memset((void *) args, 0, sizeof(*args)); 557 args->addr = &ndm->ndm_saddr; 558 args->addrlen = args->addr->sa_len; 559 #ifdef NFS_BOOT_TCP 560 args->sotype = SOCK_STREAM; 561 #else 562 args->sotype = SOCK_DGRAM; 563 #endif 564 args->fh = ndm->ndm_fh; 565 args->hostname = ndm->ndm_host; 566 args->flags = NFSMNT_NOCONN | NFSMNT_RESVPORT; 567 568 #ifndef NFS_V2_ONLY 569 args->flags |= NFSMNT_NFSV3; 570 #endif 571 #ifdef NFS_BOOT_OPTIONS 572 args->flags |= NFS_BOOT_OPTIONS; 573 #endif 574 #ifdef NFS_BOOT_RWSIZE 575 /* 576 * Reduce rsize,wsize for interfaces that consistently 577 * drop fragments of long UDP messages. (i.e. wd8003). 578 * You can always change these later via remount. 579 */ 580 args->flags |= NFSMNT_WSIZE | NFSMNT_RSIZE; 581 args->wsize = NFS_BOOT_RWSIZE; 582 args->rsize = NFS_BOOT_RWSIZE; 583 #endif 584 585 /* 586 * Find the pathname part of the "server:pathname" 587 * string left in ndm->ndm_host by nfs_boot_init. 588 */ 589 pathname = strchr(ndm->ndm_host, ':'); 590 if (pathname == 0) { 591 printf("nfs_boot: getfh - no pathname\n"); 592 return (EIO); 593 } 594 pathname++; 595 596 /* 597 * Get file handle using RPC to mountd/mount 598 */ 599 sin = (struct sockaddr_in *)&ndm->ndm_saddr; 600 error = md_mount(sin, pathname, args, l); 601 if (error) { 602 printf("nfs_boot: mountd `%s', error=%d\n", 603 ndm->ndm_host, error); 604 return (error); 605 } 606 607 /* Set port number for NFS use. */ 608 /* XXX: NFS port is always 2049, right? */ 609 retry: 610 error = krpc_portmap(sin, NFS_PROG, 611 (args->flags & NFSMNT_NFSV3) ? NFS_VER3 : NFS_VER2, 612 (args->sotype == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP, 613 &port, l); 614 if (port == htons(0)) 615 error = EIO; 616 if (error) { 617 if (args->sotype == SOCK_STREAM) { 618 args->sotype = SOCK_DGRAM; 619 goto retry; 620 } 621 printf("nfs_boot: portmap NFS, error=%d\n", error); 622 return (error); 623 } 624 sin->sin_port = port; 625 return (0); 626 } 627 628 629 /* 630 * RPC: mountd/mount 631 * Given a server pathname, get an NFS file handle. 632 * Also, sets sin->sin_port to the NFS service port. 633 * 634 * mdsin mountd server address 635 */ 636 static int 637 md_mount(struct sockaddr_in *mdsin, char *path, 638 struct nfs_args *argp, struct lwp *lwp) 639 { 640 /* The RPC structures */ 641 struct rdata { 642 u_int32_t errno; 643 union { 644 u_int8_t v2fh[NFSX_V2FH]; 645 struct { 646 u_int32_t fhlen; 647 u_int8_t fh[1]; 648 } v3fh; 649 } fh; 650 } *rdata; 651 struct mbuf *m; 652 u_int8_t *fh; 653 int minlen, error; 654 int mntver; 655 656 mntver = (argp->flags & NFSMNT_NFSV3) ? 3 : 2; 657 do { 658 /* 659 * Get port number for MOUNTD. 660 */ 661 error = krpc_portmap(mdsin, RPCPROG_MNT, mntver, 662 IPPROTO_UDP, &mdsin->sin_port, lwp); 663 if (error) 664 continue; 665 666 /* This mbuf is consumed by krpc_call. */ 667 m = xdr_string_encode(path, strlen(path)); 668 if (m == NULL) 669 return ENOMEM; 670 671 /* Do RPC to mountd. */ 672 error = krpc_call(mdsin, RPCPROG_MNT, mntver, 673 RPCMNT_MOUNT, &m, NULL, lwp); 674 if (error != EPROGMISMATCH) 675 break; 676 /* Try lower version of mountd. */ 677 } while (--mntver >= 1); 678 if (error) { 679 printf("nfs_boot: mountd error=%d\n", error); 680 return error; 681 } 682 if (mntver != 3) 683 argp->flags &= ~NFSMNT_NFSV3; 684 685 /* The reply might have only the errno. */ 686 if (m->m_len < 4) 687 goto bad; 688 /* Have at least errno, so check that. */ 689 rdata = mtod(m, struct rdata *); 690 error = fxdr_unsigned(u_int32_t, rdata->errno); 691 if (error) 692 goto out; 693 694 /* Have errno==0, so the fh must be there. */ 695 if (mntver == 3) { 696 argp->fhsize = fxdr_unsigned(u_int32_t, rdata->fh.v3fh.fhlen); 697 if (argp->fhsize > NFSX_V3FHMAX) 698 goto bad; 699 minlen = 2 * sizeof(u_int32_t) + argp->fhsize; 700 } else { 701 argp->fhsize = NFSX_V2FH; 702 minlen = sizeof(u_int32_t) + argp->fhsize; 703 } 704 705 if (m->m_len < minlen) { 706 m = m_pullup(m, minlen); 707 if (m == NULL) 708 return(EBADRPC); 709 rdata = mtod(m, struct rdata *); 710 } 711 712 fh = (mntver == 3) ? 713 rdata->fh.v3fh.fh : rdata->fh.v2fh; 714 memcpy(argp->fh, fh, argp->fhsize); 715 716 goto out; 717 718 bad: 719 error = EBADRPC; 720 721 out: 722 m_freem(m); 723 return error; 724 } 725