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