xref: /illumos-gate/usr/src/stand/lib/fs/nfs/mount.c (revision 7c478bd9)
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