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