1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1988 AT&T */ 28 /* All Rights Reserved */ 29 30 #include <sys/utsname.h> 31 #include <sys/types.h> 32 #include <sys/socket.h> 33 #include <netinet/in.h> 34 #include <rpc/types.h> 35 #include <rpc/auth.h> 36 #include <sys/t_lock.h> 37 #include <netdb.h> 38 #include "clnt.h" 39 #include <rpc/xdr.h> 40 #include <rpc/rpc_msg.h> 41 #include <rpc/rpc.h> 42 #include "brpc.h" 43 #include "auth_inet.h" 44 #include "pmap.h" 45 #include <rpcsvc/nfs_prot.h> 46 #include <rpcsvc/nfs4_prot.h> 47 #include "nfs_inet.h" 48 #include <rpcsvc/bootparam.h> 49 #include <dhcp_impl.h> 50 #include <rpcsvc/mount.h> 51 #include <sys/promif.h> 52 #include <sys/salib.h> 53 #include "socket_inet.h" 54 #include "ipv4.h" 55 #include "mac.h" 56 #include <sys/bootdebug.h> 57 #include <errno.h> 58 #include "dhcpv4.h" 59 #include <sys/mntent.h> 60 61 /* ARP timeout in milliseconds for BOOTP/RARP */ 62 #define ARP_INETBOOT_TIMEOUT 1000 63 64 struct nfs_file roothandle; /* root file handle */ 65 static char root_hostname[SYS_NMLN]; /* server hostname */ 66 static char my_hostname[MAXHOSTNAMELEN]; 67 static char root_pathbuf[NFS_MAXPATHLEN]; /* the root's path */ 68 static char root_boot_file[NFS_MAXPATHLEN]; /* optional boot file */ 69 static struct sockaddr_in root_to; /* server sock ip */ 70 /* in network order */ 71 CLIENT *root_CLIENT = NULL; /* CLIENT handle */ 72 int dontroute = FALSE; /* In case rarp/bootparams was selected */ 73 char rootopts[MAX_PATH_LEN]; 74 static gid_t fake_gids = 1; /* fake gids list for auth_unix */ 75 76 extern void set_default_filename(char *); /* boot.c */ 77 78 /* 79 * xdr routines used by mount. 80 */ 81 82 bool_t 83 xdr_fhstatus(XDR *xdrs, struct fhstatus *fhsp) 84 { 85 if (!xdr_int(xdrs, (int *)&fhsp->fhs_status)) 86 return (FALSE); 87 if (fhsp->fhs_status == 0) { 88 return (xdr_fhandle(xdrs, fhsp->fhstatus_u.fhs_fhandle)); 89 } 90 return (TRUE); 91 } 92 93 bool_t 94 xdr_fhandle(XDR *xdrs, fhandle fhp) 95 { 96 return (xdr_opaque(xdrs, (char *)fhp, NFS_FHSIZE)); 97 } 98 99 bool_t 100 xdr_path(XDR *xdrs, char **pathp) 101 { 102 return (xdr_string(xdrs, pathp, MNTPATHLEN)); 103 } 104 105 bool_t 106 xdr_fhandle3(XDR *xdrs, fhandle3 *objp) 107 { 108 return (xdr_bytes(xdrs, (char **)&objp->fhandle3_val, 109 (uint_t *)&objp->fhandle3_len, FHSIZE3)); 110 } 111 112 bool_t 113 xdr_mountstat3(XDR *xdrs, mountstat3 *objp) 114 { 115 return (xdr_enum(xdrs, (enum_t *)objp)); 116 } 117 118 bool_t 119 xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp) 120 { 121 if (!xdr_fhandle3(xdrs, &objp->fhandle)) 122 return (FALSE); 123 return (xdr_array(xdrs, (char **)&objp->auth_flavors.auth_flavors_val, 124 (uint_t *)&objp->auth_flavors.auth_flavors_len, ~0, 125 sizeof (int), (xdrproc_t)xdr_int)); 126 } 127 128 bool_t 129 xdr_mountres3(XDR *xdrs, mountres3 *objp) 130 { 131 if (!xdr_mountstat3(xdrs, &objp->fhs_status)) 132 return (FALSE); 133 if (objp->fhs_status == MNT_OK) 134 return (xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo)); 135 return (TRUE); 136 } 137 138 static int 139 nfsmountroot(char *path, struct nfs_file *filep) 140 { 141 int rexmit; 142 int resp_wait; 143 enum clnt_stat status; 144 struct fhstatus root_tmp; /* to pass to rpc/xdr */ 145 146 /* 147 * Wait up to 16 secs for first response, retransmitting expon. 148 */ 149 rexmit = 0; /* default retransmission interval */ 150 resp_wait = 16; 151 152 do { 153 status = brpc_call((rpcprog_t)MOUNTPROG, (rpcvers_t)MOUNTVERS, 154 (rpcproc_t)MOUNTPROC_MNT, xdr_path, (caddr_t)&path, 155 xdr_fhstatus, (caddr_t)&(root_tmp), rexmit, resp_wait, 156 &root_to, NULL, AUTH_UNIX); 157 if (status == RPC_TIMEDOUT) { 158 dprintf("boot: %s:%s mount server not responding.\n", 159 root_hostname, path); 160 } 161 rexmit = resp_wait; 162 resp_wait = 0; /* use default wait time. */ 163 } while (status == RPC_TIMEDOUT); 164 165 if ((status != RPC_SUCCESS) || (root_tmp.fhs_status != 0)) { 166 nfs_error(root_tmp.fhs_status); 167 root_to.sin_port = 0; 168 return (-1); 169 } 170 171 /* 172 * Since the mount succeeded, we'll mark the filep's 173 * status as NFS_OK, and its type as NFDIR. If these 174 * points aren't the case, then we wouldn't be here. 175 */ 176 bcopy(&root_tmp.fhstatus_u.fhs_fhandle, &filep->fh.fh2, FHSIZE); 177 filep->ftype.type2 = NFDIR; 178 filep->version = NFS_VERSION; 179 nfs_readsize = nfs_readsize < NFS_MAXDATA ? nfs_readsize : NFS_MAXDATA; 180 /* 181 * Set a reasonable lower limit on readsize 182 */ 183 nfs_readsize = (nfs_readsize != 0 && nfs_readsize < 512) ? 184 512 : nfs_readsize; 185 return (0); 186 } 187 188 int 189 setup_root_vars(void) 190 { 191 size_t buflen; 192 uint16_t readsize; 193 194 /* 195 * Root server name. Required. 196 */ 197 buflen = sizeof (root_hostname); 198 if (dhcp_getinfo(DSYM_VENDOR, VS_NFSMNT_ROOTSRVR_NAME, 0, 199 root_hostname, &buflen)) { 200 root_hostname[buflen] = '\0'; 201 } else { 202 dprintf("BOUND: Missing Root Server Name Option\n"); 203 errno = EINVAL; 204 return (-1); 205 } 206 207 /* 208 * Root server IP. Required. 209 */ 210 buflen = sizeof (root_to.sin_addr); 211 if (!dhcp_getinfo(DSYM_VENDOR, VS_NFSMNT_ROOTSRVR_IP, 0, 212 &root_to.sin_addr, &buflen)) { 213 dprintf("BOUND: Missing Root Server IP Option\n"); 214 errno = EINVAL; 215 return (-1); 216 } 217 218 /* 219 * Root path Required. 220 */ 221 buflen = sizeof (root_pathbuf); 222 if (dhcp_getinfo(DSYM_VENDOR, VS_NFSMNT_ROOTPATH, 0, 223 root_pathbuf, &buflen)) { 224 root_pathbuf[buflen] = '\0'; 225 } else { 226 dprintf("BOUND: Missing Root Path Option\n"); 227 errno = EINVAL; 228 return (-1); 229 } 230 231 /* 232 * Optional Bootfile path. 233 */ 234 buflen = sizeof (root_boot_file); 235 if (dhcp_getinfo(DSYM_VENDOR, VS_NFSMNT_BOOTFILE, 0, 236 root_boot_file, &buflen)) { 237 root_boot_file[buflen] = '\0'; 238 dprintf("BOUND: Optional Boot File is: %s\n", root_boot_file); 239 } 240 241 /* if we got a boot file name, use it as the default */ 242 if (root_boot_file[0] != '\0') 243 set_default_filename(root_boot_file); 244 245 /* 246 * Set the NFS read size. The mount code will adjust it to 247 * the maximum size. 248 */ 249 buflen = sizeof (readsize); 250 if (dhcp_getinfo(DSYM_VENDOR, VS_BOOT_NFS_READSIZE, 0, 251 &readsize, &buflen)) { 252 nfs_readsize = ntohs(readsize); 253 if (boothowto & RB_VERBOSE) { 254 printf("Boot NFS read size: %d\n", nfs_readsize); 255 } 256 } 257 258 /* 259 * Optional rootopts. 260 */ 261 buflen = sizeof (rootopts); 262 if (dhcp_getinfo(DSYM_VENDOR, VS_NFSMNT_ROOTOPTS, 0, 263 rootopts, &buflen)) { 264 rootopts[buflen] = '\0'; 265 dprintf("BOUND: Optional Rootopts is: %s\n", rootopts); 266 } 267 268 return (0); 269 } 270 271 static void 272 mnt3_error(enum mountstat3 status) 273 { 274 if (!(boothowto & RB_DEBUG)) 275 return; 276 277 switch (status) { 278 case MNT_OK: 279 printf("Mount: No error.\n"); 280 break; 281 case MNT3ERR_PERM: 282 printf("Mount: Not owner.\n"); 283 break; 284 case MNT3ERR_NOENT: 285 printf("Mount: No such file or directory.\n"); 286 break; 287 case MNT3ERR_IO: 288 printf("Mount: I/O error.\n"); 289 break; 290 case MNT3ERR_ACCES: 291 printf("Mount: Permission denied.\n"); 292 break; 293 case MNT3ERR_NOTDIR: 294 printf("Mount: Not a directory.\n"); 295 break; 296 case MNT3ERR_INVAL: 297 printf("Mount: Invalid argument.\n"); 298 break; 299 case MNT3ERR_NAMETOOLONG: 300 printf("Mount: File name too long.\n"); 301 break; 302 case MNT3ERR_NOTSUPP: 303 printf("Mount: Operation not supported.\n"); 304 break; 305 case MNT3ERR_SERVERFAULT: 306 printf("Mount: Server fault.\n"); 307 break; 308 default: 309 printf("Mount: unknown error.\n"); 310 break; 311 } 312 } 313 314 static int 315 nfs3mountroot(char *path, struct nfs_file *filep) 316 { 317 int rexmit; 318 int resp_wait; 319 struct mountres3 res3; 320 enum clnt_stat status; 321 322 /* 323 * Wait up to 16 secs for first response, retransmitting expon. 324 */ 325 rexmit = 0; /* default retransmission interval */ 326 resp_wait = 16; 327 328 /* 329 * Try to mount using V3 330 */ 331 do { 332 bzero(&res3, sizeof (struct mountres3)); 333 334 status = brpc_call((rpcprog_t)MOUNTPROG, (rpcvers_t)MOUNTVERS3, 335 (rpcproc_t)MOUNTPROC_MNT, xdr_path, (caddr_t)&path, 336 xdr_mountres3, (caddr_t)&res3, rexmit, resp_wait, 337 &root_to, NULL, AUTH_UNIX); 338 339 if (status != RPC_TIMEDOUT) 340 break; 341 342 dprintf("boot: %s:%s mount server not responding.\n", 343 root_hostname, path); 344 345 rexmit = resp_wait; 346 resp_wait = 0; /* use default wait time. */ 347 348 xdr_free(xdr_mountres3, (caddr_t)&res3); 349 } while (status == RPC_TIMEDOUT); 350 351 if ((status != RPC_SUCCESS) || (res3.fhs_status != MNT_OK)) { 352 mnt3_error(res3.fhs_status); 353 root_to.sin_port = 0; 354 return (-1); 355 } 356 357 /* 358 * Since the mount succeeded, we'll mark the filep's 359 * status as NFS_OK, and its type as NF3DIR. If these 360 * points aren't the case, then we wouldn't be here. 361 */ 362 filep->fh.fh3.len = res3.mountres3_u.mountinfo.fhandle.fhandle3_len; 363 bcopy(res3.mountres3_u.mountinfo.fhandle.fhandle3_val, 364 filep->fh.fh3.data, 365 filep->fh.fh3.len); 366 filep->ftype.type3 = NF3DIR; 367 filep->version = NFS_V3; 368 /* 369 * Hardwire in a known reasonable upper limit of 32K 370 */ 371 nfs_readsize = nfs_readsize < 32 * 1024 ? nfs_readsize : 32 * 1024; 372 /* 373 * Set a reasonable lower limit on readsize 374 */ 375 nfs_readsize = (nfs_readsize != 0 && nfs_readsize < 512) ? 376 512 : nfs_readsize; 377 xdr_free(xdr_mountres3, (caddr_t)&res3); 378 return (0); 379 } 380 381 /* 382 * Setup v4 client for inetboot 383 */ 384 static int 385 nfs4init(char *path, uint16_t nfs_port) 386 { 387 struct timeval wait; 388 int fd = -1; 389 int error = 0; 390 enum clnt_stat rpc_stat; 391 struct nfs_file rootpath; 392 393 wait.tv_sec = RPC_RCVWAIT_MSEC / 1000; 394 wait.tv_usec = 0; 395 396 /* 397 * If we haven't explicitly set the port number, set to the standard 398 * 2049 and don't cause a rpcbind request. 399 */ 400 if (nfs_port == 0) 401 nfs_port = 2049; 402 403 root_to.sin_port = htons(nfs_port); 404 405 /* 406 * Support TCP only 407 */ 408 root_CLIENT = clntbtcp_create(&root_to, NFS_PROGRAM, 409 NFS_V4, wait, &fd, 410 NFS4BUF_SIZE, NFS4BUF_SIZE); 411 412 if (root_CLIENT == NULL) { 413 root_to.sin_port = 0; 414 return (-1); 415 } 416 417 root_CLIENT->cl_auth = 418 authunix_create(my_hostname, 0, 1, 1, &fake_gids); 419 420 /* 421 * Send NULL proc the server first to see if V4 exists 422 */ 423 rpc_stat = CLNT_CALL(root_CLIENT, NFSPROC4_NULL, xdr_void, NULL, 424 xdr_void, NULL, wait); 425 426 if (rpc_stat != RPC_SUCCESS) { 427 dprintf("boot: NULL proc failed NFSv4 service not available\n"); 428 AUTH_DESTROY(root_CLIENT->cl_auth); 429 CLNT_DESTROY(root_CLIENT); 430 root_to.sin_port = 0; 431 return (-1); 432 } 433 434 /* 435 * Do a lookup to get to the root_path. This is nice since it can 436 * handle multicomponent lookups. 437 */ 438 roothandle.version = NFS_V4; 439 roothandle.ftype.type4 = NF4DIR; 440 roothandle.fh.fh4.len = 0; /* Force a PUTROOTFH */ 441 roothandle.offset = (uint_t)0; /* it's a directory! */ 442 error = lookup(path, &rootpath, TRUE); 443 444 if (error) { 445 printf("boot: lookup %s failed\n", path); 446 return (-1); 447 } 448 roothandle = rootpath; /* structure copy */ 449 450 /* 451 * Hardwire in a known reasonable upper limit of 32K 452 */ 453 nfs_readsize = nfs_readsize < 32 * 1024 ? nfs_readsize : 32 * 1024; 454 /* 455 * Set a reasonable lower limit on readsize 456 */ 457 nfs_readsize = (nfs_readsize != 0 && nfs_readsize < 512) ? 458 512 : nfs_readsize; 459 460 return (0); 461 } 462 463 static int 464 atoi(const char *p) 465 { 466 int n; 467 int c, neg = 0; 468 469 if (!isdigit(c = *p)) { 470 while (c == ' ' || c == '\t' || c == '\n') 471 c = *++p; 472 switch (c) { 473 case '-': 474 neg++; 475 /* FALLTHROUGH */ 476 case '+': 477 c = *++p; 478 } 479 if (!isdigit(c)) 480 return (0); 481 } 482 for (n = '0' - c; isdigit(c = *++p); ) { 483 n *= 10; /* two steps to avoid unnecessary overflow */ 484 n += '0' - c; /* accum neg to avoid surprises at MAX */ 485 } 486 return (neg ? n : -n); 487 } 488 489 /* 490 * Parse suboptions from a string. 491 * Same as getsubopt(3C). 492 */ 493 static int 494 getsubopt(char **optionsp, char * const *tokens, char **valuep) 495 { 496 char *s = *optionsp, *p; 497 int i; 498 size_t optlen; 499 500 *valuep = NULL; 501 if (*s == '\0') 502 return (-1); 503 p = strchr(s, ','); /* find next option */ 504 if (p == NULL) { 505 p = s + strlen(s); 506 } else { 507 *p++ = '\0'; /* mark end and point to next */ 508 } 509 *optionsp = p; /* point to next option */ 510 p = strchr(s, '='); /* find value */ 511 if (p == NULL) { 512 optlen = strlen(s); 513 *valuep = NULL; 514 } else { 515 optlen = p - s; 516 *valuep = ++p; 517 } 518 for (i = 0; tokens[i] != NULL; i++) { 519 if ((optlen == strlen(tokens[i])) && 520 (strncmp(s, tokens[i], optlen) == 0)) 521 return (i); 522 } 523 /* no match, point value at option and return error */ 524 *valuep = s; 525 return (-1); 526 } 527 528 /* 529 * The only interesting NFS mount options for initiating the kernel 530 * all others are ignored. 531 */ 532 static char *optlist[] = { 533 #define OPT_RSIZE 0 534 MNTOPT_RSIZE, 535 #define OPT_TIMEO 1 536 MNTOPT_TIMEO, 537 #define OPT_VERS 2 538 MNTOPT_VERS, 539 #define OPT_PROTO 3 540 MNTOPT_PROTO, 541 #define OPT_PORT 4 542 MNTOPT_PORT, 543 NULL 544 }; 545 546 /* 547 * This routine will open a device as it is known by the V2 OBP. It 548 * then goes thru the stuff necessary to initialize the network device, 549 * get our network parameters, (using DHCP or rarp/bootparams), and 550 * finally actually go and get the root filehandle. Sound like fun? 551 * Suuurrrree. Take a look. 552 * 553 * Returns 0 if things worked. -1 if we crashed and burned. 554 */ 555 int 556 boot_nfs_mountroot(char *str) 557 { 558 int status; 559 enum clnt_stat rpc_stat; 560 char *root_path = &root_pathbuf[0]; /* to make XDR happy */ 561 struct timeval wait; 562 int fd; 563 int bufsize; 564 char *opts, *val; 565 int nfs_version = 0; 566 int istcp = 1; 567 int nfs_port = 0; /* Cause pmap to get port */ 568 struct sockaddr_in tmp_addr; /* throw away */ 569 570 if (root_CLIENT != NULL) { 571 AUTH_DESTROY(root_CLIENT->cl_auth); 572 CLNT_DESTROY(root_CLIENT); 573 root_CLIENT = NULL; 574 } 575 576 root_to.sin_family = AF_INET; 577 root_to.sin_addr.s_addr = htonl(INADDR_ANY); 578 root_to.sin_port = htons(0); 579 580 mac_init(str); 581 582 (void) ipv4_setpromiscuous(TRUE); 583 584 if (get_netconfig_strategy() == NCT_BOOTP_DHCP) { 585 if (boothowto & RB_VERBOSE) 586 printf("Using BOOTP/DHCP...\n"); 587 if (dhcp() != 0 || setup_root_vars() != 0) { 588 (void) ipv4_setpromiscuous(FALSE); 589 if (boothowto & RB_VERBOSE) 590 printf("BOOTP/DHCP configuration failed!\n"); 591 return (-1); 592 } 593 594 /* now that we have an IP address, turn off promiscuous mode */ 595 (void) ipv4_setpromiscuous(FALSE); 596 } else { 597 /* Use RARP/BOOTPARAMS. RARP will try forever... */ 598 if (boothowto & RB_VERBOSE) 599 printf("Using RARP/BOOTPARAMS...\n"); 600 mac_call_rarp(); 601 602 /* 603 * Since there is no way to determine our netmask, and therefore 604 * figure out if the router we got is useful, we assume all 605 * services are local. Use DHCP if this bothers you. 606 */ 607 dontroute = TRUE; 608 /* 609 * We are trying to keep the ARP response 610 * timeout on the lower side with BOOTP/RARP. 611 * We are doing this for BOOTP/RARP where policy 612 * doesn't allow to route the packets outside 613 * the subnet as it has no idea about the 614 * netmask. By doing so, we are reducing 615 * ARP response timeout for any packet destined 616 * for outside booting clients subnet. Client can 617 * not expect such ARP replies and will finally 618 * timeout after a long delay. This would cause 619 * booting client to get stalled for a longer 620 * time. We can not avoid accepting any outside 621 * subnet packets accidentally destined for the 622 * booting client. 623 */ 624 mac_set_arp_timeout(ARP_INETBOOT_TIMEOUT); 625 626 /* now that we have an IP address, turn off promiscuous mode */ 627 (void) ipv4_setpromiscuous(FALSE); 628 629 /* get our hostname */ 630 if (whoami() == FALSE) 631 return (-1); 632 633 /* get our bootparams. */ 634 if (getfile("root", root_hostname, &root_to.sin_addr, 635 root_pathbuf) == FALSE) 636 return (-1); 637 638 /* get our rootopts. */ 639 (void) getfile("rootopts", root_hostname, &tmp_addr.sin_addr, 640 rootopts); 641 } 642 643 /* mount root */ 644 if (boothowto & RB_VERBOSE) { 645 printf("root server: %s (%s)\n", root_hostname, 646 inet_ntoa(root_to.sin_addr)); 647 printf("root directory: %s\n", root_pathbuf); 648 } 649 650 /* 651 * Assumes we've configured the stack and thus know our 652 * IP address/hostname, either by using DHCP or rarp/bootparams. 653 */ 654 (void) gethostname(my_hostname, sizeof (my_hostname)); 655 656 wait.tv_sec = RPC_RCVWAIT_MSEC / 1000; 657 wait.tv_usec = 0; 658 659 /* 660 * Parse out the interesting root options, if an invalid 661 * or unknown option is provided, silently ignore it and 662 * use the defaults. 663 */ 664 opts = rootopts; 665 while (*opts) { 666 int ival; 667 switch (getsubopt(&opts, optlist, &val)) { 668 case OPT_RSIZE: 669 if (val == NULL || !isdigit(*val)) 670 break; 671 nfs_readsize = atoi(val); 672 break; 673 case OPT_TIMEO: 674 if (val == NULL || !isdigit(*val)) 675 break; 676 ival = atoi(val); 677 wait.tv_sec = ival / 10; 678 wait.tv_usec = (ival % 10) * 100000; 679 break; 680 case OPT_VERS: 681 if (val == NULL || !isdigit(*val)) 682 break; 683 nfs_version = atoi(val); 684 break; 685 case OPT_PROTO: 686 if (val == NULL || isdigit(*val)) 687 break; 688 if ((strncmp(val, "udp", 3) == 0)) 689 istcp = 0; 690 else 691 istcp = 1; /* must be tcp */ 692 break; 693 case OPT_PORT: 694 if (val == NULL || !isdigit(*val)) 695 break; 696 nfs_port = atoi(val); 697 698 /* 699 * Currently nfs_dlinet.c doesn't support setting 700 * the root NFS port. Delete this when it does. 701 */ 702 nfs_port = 0; 703 break; 704 default: 705 /* 706 * Unknown options are silently ignored 707 */ 708 break; 709 } 710 } 711 712 /* 713 * If version is set, then try that version first. 714 */ 715 switch (nfs_version) { 716 case NFS_VERSION: 717 if (nfsmountroot(root_path, &roothandle) == 0) 718 goto domount; 719 break; 720 case NFS_V3: 721 if (nfs3mountroot(root_path, &roothandle) == 0) 722 goto domount; 723 break; 724 case NFS_V4: 725 /* 726 * With v4 we skip the mount and go straight to 727 * setting the root filehandle. Because of this we 728 * do things slightly differently and obtain our 729 * client handle first. 730 */ 731 if (istcp && nfs4init(root_path, nfs_port) == 0) { 732 /* 733 * If v4 init succeeded then we are done. Just return. 734 */ 735 return (0); 736 } 737 } 738 739 /* 740 * If there was no chosen version or the chosen version failed 741 * try all versions in order, this may still fail to boot 742 * at the kernel level if the options are not right, but be 743 * generous at this early stage. 744 */ 745 if (istcp && nfs4init(root_path, nfs_port) == 0) { 746 /* 747 * If v4 init succeeded then we are done. Just return. 748 */ 749 return (0); 750 } 751 752 if (nfs3mountroot(root_path, &roothandle) == 0) 753 goto domount; 754 755 if ((status = nfsmountroot(root_path, &roothandle)) != 0) 756 return (status); 757 758 domount: 759 /* 760 * Only v2 and v3 go on from here. 761 */ 762 roothandle.offset = (uint_t)0; /* it's a directory! */ 763 root_to.sin_port = htons(nfs_port); /* NFS is next after mount */ 764 765 /* 766 * Create the CLIENT handle for NFS operations 767 */ 768 if (roothandle.version == NFS_VERSION) 769 bufsize = NFSBUF_SIZE; 770 else 771 bufsize = NFS3BUF_SIZE; 772 773 /* 774 * First try TCP then UDP (unless UDP asked for explicitly), if mountd 775 * alows this version but neither transport is available we are stuck. 776 */ 777 if (istcp) { 778 fd = -1; 779 root_CLIENT = clntbtcp_create(&root_to, NFS_PROGRAM, 780 roothandle.version, wait, &fd, bufsize, bufsize); 781 if (root_CLIENT != NULL) { 782 root_CLIENT->cl_auth = 783 authunix_create(my_hostname, 0, 1, 1, &fake_gids); 784 /* 785 * Send NULL proc, check if the server really exists 786 */ 787 rpc_stat = CLNT_CALL(root_CLIENT, 0, 788 xdr_void, NULL, xdr_void, NULL, wait); 789 790 if (rpc_stat == RPC_SUCCESS) 791 return (0); 792 793 AUTH_DESTROY(root_CLIENT->cl_auth); 794 CLNT_DESTROY(root_CLIENT); 795 root_CLIENT = NULL; 796 } 797 /* Fall through to UDP case */ 798 } 799 800 fd = -1; 801 root_CLIENT = clntbudp_bufcreate(&root_to, NFS_PROGRAM, 802 roothandle.version, wait, &fd, bufsize, bufsize); 803 if (root_CLIENT == NULL) 804 return (-1); 805 806 root_CLIENT->cl_auth = 807 authunix_create(my_hostname, 0, 1, 1, &fake_gids); 808 /* 809 * Send NULL proc, check if the server really exists 810 */ 811 rpc_stat = CLNT_CALL(root_CLIENT, 0, 812 xdr_void, NULL, xdr_void, NULL, wait); 813 814 if (rpc_stat == RPC_SUCCESS) 815 return (0); 816 817 AUTH_DESTROY(root_CLIENT->cl_auth); 818 CLNT_DESTROY(root_CLIENT); 819 root_CLIENT = NULL; 820 return (-1); 821 } 822