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