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