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