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