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