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