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