xref: /titanic_52/usr/src/cmd/fs.d/nfs/mount/mount.c (revision 2cb27123907a098a777e39eebc349d73e99a518f)
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  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*
30  * University Copyright- Copyright (c) 1982, 1986, 1988
31  * The Regents of the University of California
32  * All Rights Reserved
33  *
34  * University Acknowledgment- Portions of this document are derived from
35  * software developed by the University of California, Berkeley, and its
36  * contributors.
37  */
38 
39 #pragma ident	"%Z%%M%	%I%	%E% SMI"
40 
41 /*
42  * nfs mount
43  */
44 
45 #define	NFSCLIENT
46 #include <locale.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <memory.h>
50 #include <stdarg.h>
51 #include <unistd.h>
52 #include <ctype.h>
53 #include <stdlib.h>
54 #include <signal.h>
55 #include <sys/param.h>
56 #include <rpc/rpc.h>
57 #include <errno.h>
58 #include <sys/stat.h>
59 #include <netdb.h>
60 #include <sys/mount.h>
61 #include <sys/mntent.h>
62 #include <sys/mnttab.h>
63 #include <nfs/nfs.h>
64 #include <nfs/mount.h>
65 #include <rpcsvc/mount.h>
66 #include <sys/pathconf.h>
67 #include <netdir.h>
68 #include <netconfig.h>
69 #include <sys/sockio.h>
70 #include <net/if.h>
71 #include <syslog.h>
72 #include <fslib.h>
73 #include <deflt.h>
74 #include <sys/wait.h>
75 #include "replica.h"
76 #include <netinet/in.h>
77 #include <nfs/nfs_sec.h>
78 #include <rpcsvc/daemon_utils.h>
79 #include <priv.h>
80 #include <tsol/label.h>
81 #include "nfs_subr.h"
82 #include "webnfs.h"
83 #include <rpcsvc/nfs4_prot.h>
84 
85 #ifndef	NFS_VERSMAX
86 #define	NFS_VERSMAX	4
87 #endif
88 #ifndef	NFS_VERSMIN
89 #define	NFS_VERSMIN	2
90 #endif
91 
92 #define	RET_OK		0
93 #define	RET_RETRY	32
94 #define	RET_ERR		33
95 #define	RET_MNTERR	1000
96 #define	ERR_PROTO_NONE		0
97 #define	ERR_PROTO_INVALID	901
98 #define	ERR_PROTO_UNSUPP	902
99 #define	ERR_NETPATH		903
100 #define	ERR_NOHOST		904
101 #define	ERR_RPCERROR		905
102 
103 typedef struct err_ret {
104 	int error_type;
105 	int error_value;
106 } err_ret_t;
107 
108 #define	SET_ERR_RET(errst, etype, eval) \
109 	if (errst) { \
110 		(errst)->error_type = etype; \
111 		(errst)->error_value = eval; \
112 	}
113 
114 /* number of transports to try */
115 #define	MNT_PREF_LISTLEN	2
116 #define	FIRST_TRY		1
117 #define	SECOND_TRY		2
118 
119 #define	BIGRETRY	10000
120 
121 /* maximum length of RPC header for NFS messages */
122 #define	NFS_RPC_HDR	432
123 
124 #define	NFS_ARGS_EXTB_secdata(args, secdata) \
125 	{ (args)->nfs_args_ext = NFS_ARGS_EXTB, \
126 	(args)->nfs_ext_u.nfs_extB.secdata = secdata; }
127 
128 extern int __clnt_bindresvport();
129 extern char *nfs_get_qop_name();
130 extern AUTH * nfs_create_ah();
131 extern enum snego_stat nfs_sec_nego();
132 
133 static void usage(void);
134 static int retry(struct mnttab *, int);
135 static int set_args(int *, struct nfs_args *, char *, struct mnttab *);
136 static int get_fh_via_pub(struct nfs_args *, char *, char *, bool_t, bool_t,
137 	int *, struct netconfig **, ushort_t);
138 static int get_fh(struct nfs_args *, char *, char *, int *, bool_t,
139 	struct netconfig **, ushort_t);
140 static int make_secure(struct nfs_args *, char *, struct netconfig *,
141 	bool_t, rpcvers_t);
142 static int mount_nfs(struct mnttab *, int, err_ret_t *);
143 static int getaddr_nfs(struct nfs_args *, char *, struct netconfig **,
144 		    bool_t, char *, ushort_t, err_ret_t *, bool_t);
145 static void pr_err(const char *fmt, ...);
146 static void usage(void);
147 static struct netbuf *get_addr(char *, rpcprog_t, rpcvers_t,
148 	struct netconfig **, char *, ushort_t, struct t_info *,
149 	caddr_t *, bool_t, char *, err_ret_t *);
150 
151 static struct netbuf *get_the_addr(char *, rpcprog_t, rpcvers_t,
152 	struct netconfig *, ushort_t, struct t_info *, caddr_t *,
153 	bool_t, char *, err_ret_t *);
154 
155 extern int self_check(char *);
156 
157 static void read_default(void);
158 
159 static char typename[64];
160 
161 static int bg = 0;
162 static int backgrounded = 0;
163 static int posix = 0;
164 static int retries = BIGRETRY;
165 static ushort_t nfs_port = 0;
166 static char *nfs_proto = NULL;
167 
168 static int mflg = 0;
169 static int Oflg = 0;	/* Overlay mounts */
170 static int qflg = 0;	/* quiet - don't print warnings on bad options */
171 
172 static char *fstype = MNTTYPE_NFS;
173 
174 static seconfig_t nfs_sec;
175 static int sec_opt = 0;	/* any security option ? */
176 static bool_t snego_done;
177 static void sigusr1(int);
178 
179 /*
180  * list of support services needed
181  */
182 static char	*service_list[] = { STATD, LOCKD, NULL };
183 static char	*service_list_v4[] = { STATD, LOCKD, NFS4CBD, NFSMAPID, NULL };
184 
185 /*
186  * These two variables control the NFS version number to be used.
187  *
188  * nfsvers defaults to 0 which means to use the highest number that
189  * both the client and the server support.  It can also be set to
190  * a particular value, either 2, 3, or 4 to indicate the version
191  * number of choice.  If the server (or the client) do not support
192  * the version indicated, then the mount attempt will be failed.
193  *
194  * nfsvers_to_use is the actual version number found to use.  It
195  * is determined in get_fh by pinging the various versions of the
196  * NFS service on the server to see which responds positively.
197  *
198  * nfsretry_vers is the version number set when we retry the mount
199  * command with the version decremented from nfsvers_to_use.
200  * nfsretry_vers is set from nfsvers_to_use when we retry the mount
201  * for errors other than RPC errors; it helps un know why we are
202  * retrying. It is an indication that the retry is due to
203  * non-RPC errors.
204  */
205 static rpcvers_t nfsvers = 0;
206 static rpcvers_t nfsvers_to_use = 0;
207 static rpcvers_t nfsretry_vers = 0;
208 
209 /*
210  * There are the defaults (range) for the client when determining
211  * which NFS version to use when probing the server (see above).
212  * These will only be used when the vers mount option is not used and
213  * these may be reset if /etc/default/nfs is configured to do so.
214  */
215 static rpcvers_t vers_max_default = NFS_VERSMAX_DEFAULT;
216 static rpcvers_t vers_min_default = NFS_VERSMIN_DEFAULT;
217 
218 /*
219  * This variable controls whether to try the public file handle.
220  */
221 static bool_t public_opt;
222 
223 int
224 main(int argc, char *argv[])
225 {
226 	struct mnttab mnt;
227 	extern char *optarg;
228 	extern int optind;
229 	char optbuf[MAX_MNTOPT_STR];
230 	int ro = 0;
231 	int r;
232 	int c;
233 	char *myname;
234 	err_ret_t retry_error;
235 
236 	(void) setlocale(LC_ALL, "");
237 #if !defined(TEXT_DOMAIN)
238 #define	TEXT_DOMAIN	"SYS_TEST"
239 #endif
240 	(void) textdomain(TEXT_DOMAIN);
241 
242 	myname = strrchr(argv[0], '/');
243 	myname = myname ? myname + 1 : argv[0];
244 	(void) snprintf(typename, sizeof (typename), "%s %s",
245 	    MNTTYPE_NFS, myname);
246 	argv[0] = typename;
247 
248 	mnt.mnt_mntopts = optbuf;
249 	(void) strcpy(optbuf, "rw");
250 
251 	/*
252 	 * Set options
253 	 */
254 	while ((c = getopt(argc, argv, "ro:mOq")) != EOF) {
255 		switch (c) {
256 		case 'r':
257 			ro++;
258 			break;
259 		case 'o':
260 			if (strlen(optarg) >= MAX_MNTOPT_STR) {
261 				pr_err(gettext("option string too long"));
262 				return (RET_ERR);
263 			}
264 			(void) strcpy(mnt.mnt_mntopts, optarg);
265 #ifdef LATER					/* XXX */
266 			if (strstr(optarg, MNTOPT_REMOUNT)) {
267 				/*
268 				 * If remount is specified, only rw is allowed.
269 				 */
270 				if ((strcmp(optarg, MNTOPT_REMOUNT) != 0) &&
271 				    (strcmp(optarg, "remount,rw") != 0) &&
272 				    (strcmp(optarg, "rw,remount") != 0)) {
273 					pr_err(gettext("Invalid options\n"));
274 					exit(RET_ERR);
275 				}
276 			}
277 #endif /* LATER */				/* XXX */
278 			break;
279 		case 'm':
280 			mflg++;
281 			break;
282 		case 'O':
283 			Oflg++;
284 			break;
285 		case 'q':
286 			qflg++;
287 			break;
288 		default:
289 			usage();
290 			exit(RET_ERR);
291 		}
292 	}
293 	if (argc - optind != 2) {
294 		usage();
295 		exit(RET_ERR);
296 	}
297 
298 	mnt.mnt_special = argv[optind];
299 	mnt.mnt_mountp = argv[optind+1];
300 
301 	if (!priv_ineffect(PRIV_SYS_MOUNT) ||
302 	    !priv_ineffect(PRIV_NET_PRIVADDR)) {
303 		pr_err(gettext("insufficient privileges\n"));
304 		exit(RET_ERR);
305 	}
306 
307 	/*
308 	 * On a labeled system, allow read-down nfs mounts if privileged
309 	 * (PRIV_NET_MAC_AWARE) to do so.  Otherwise, ignore the error
310 	 * and "mount equal label only" behavior will result.
311 	 */
312 	if (is_system_labeled())
313 		(void) setpflags(NET_MAC_AWARE, 1);
314 
315 	/*
316 	 * Read the defaults file to see if the min/max versions have
317 	 * been set and therefore would override the encoded defaults.
318 	 * Then check to make sure that if they were set that the
319 	 * values are reasonable.
320 	 */
321 	read_default();
322 	if (vers_min_default > vers_max_default ||
323 		vers_min_default < NFS_VERSMIN ||
324 		vers_max_default > NFS_VERSMAX) {
325 		pr_err("%s %s\n%s %s\n",
326 			gettext("Incorrect configuration of client\'s"),
327 			NFSADMIN,
328 			gettext("NFS_CLIENT_VERSMIN or NFS_CLIENT_VERSMAX"),
329 			gettext("is either out of range or overlaps."));
330 	}
331 
332 	SET_ERR_RET(&retry_error, ERR_PROTO_NONE, 0);
333 	r = mount_nfs(&mnt, ro, &retry_error);
334 	if (r == RET_RETRY && retries) {
335 		/*
336 		 * Check the error code from the last mount attempt if it was
337 		 * an RPC error, then retry as is. Otherwise we retry with the
338 		 * nfsretry_vers set. It is set by decrementing nfsvers_to_use.
339 		 * If we are retrying with nfsretry_vers then we don't print any
340 		 * retry messages, since we are not retrying due to an RPC
341 		 * error.
342 		 */
343 		if (retry_error.error_type) {
344 			if (retry_error.error_type != ERR_RPCERROR) {
345 				nfsretry_vers = nfsvers_to_use =
346 				    nfsvers_to_use - 1;
347 				if (nfsretry_vers < NFS_VERSMIN)
348 					return (r);
349 			}
350 		}
351 
352 		r = retry(&mnt, ro);
353 	}
354 	/*
355 	 * exit(r);
356 	 */
357 	return (r);
358 }
359 
360 static void
361 pr_err(const char *fmt, ...)
362 {
363 	va_list ap;
364 
365 	va_start(ap, fmt);
366 	if (backgrounded != 0) {
367 		(void) vsyslog(LOG_ERR, fmt, ap);
368 	} else {
369 		(void) fprintf(stderr, "%s: ", typename);
370 		(void) vfprintf(stderr, fmt, ap);
371 		(void) fflush(stderr);
372 	}
373 	va_end(ap);
374 }
375 
376 static void
377 usage()
378 {
379 	(void) fprintf(stderr,
380 	    gettext("Usage: nfs mount [-r] [-o opts] [server:]path dir\n"));
381 	exit(RET_ERR);
382 }
383 
384 static int
385 mount_nfs(struct mnttab *mntp, int ro, err_ret_t *retry_error)
386 {
387 	struct nfs_args *args = NULL, *argp = NULL, *prev_argp = NULL;
388 	struct netconfig *nconf = NULL;
389 	struct replica *list = NULL;
390 	int mntflags = 0;
391 	int i, r, n;
392 	int oldvers = 0, vers = 0;
393 	int last_error = RET_OK;
394 	int replicated = 0;
395 	char *p;
396 	bool_t url;
397 	bool_t use_pubfh;
398 	char *special = NULL;
399 	char *oldpath = NULL;
400 	char *newpath = NULL;
401 	char *service;
402 	pid_t pi;
403 	struct flock f;
404 	char *saveopts = NULL;
405 	char **sl = NULL;
406 
407 	mntp->mnt_fstype = MNTTYPE_NFS;
408 
409 	if (ro) {
410 		mntflags |= MS_RDONLY;
411 		/* convert "rw"->"ro" */
412 		if (p = strstr(mntp->mnt_mntopts, "rw")) {
413 			if (*(p+2) == ',' || *(p+2) == '\0')
414 				*(p+1) = 'o';
415 		}
416 	}
417 
418 	if (Oflg)
419 		mntflags |= MS_OVERLAY;
420 
421 	list = parse_replica(mntp->mnt_special, &n);
422 	if (list == NULL) {
423 		if (n < 0)
424 			pr_err(gettext("nfs file system; use [host:]path\n"));
425 		else
426 			pr_err(gettext("no memory\n"));
427 		return (RET_ERR);
428 	}
429 
430 	replicated = (n > 1);
431 
432 	/*
433 	 * There are some free() calls at the bottom of this loop, so be
434 	 * careful about adding continue statements.
435 	 */
436 	for (i = 0; i < n; i++) {
437 		char *path;
438 		char *host;
439 		ushort_t port;
440 
441 		argp = (struct nfs_args *)malloc(sizeof (*argp));
442 		if (argp == NULL) {
443 			pr_err(gettext("no memory\n"));
444 			last_error = RET_ERR;
445 			goto out;
446 		}
447 		memset(argp, 0, sizeof (*argp));
448 
449 		memset(&nfs_sec, 0, sizeof (nfs_sec));
450 		sec_opt = 0;
451 		use_pubfh = FALSE;
452 		url = FALSE;
453 		port = 0;
454 		snego_done = FALSE;
455 
456 		/*
457 		 * Looking for resources of the form
458 		 *	nfs://server_host[:port_number]/path_name
459 		 */
460 		if (strcmp(list[i].host, "nfs") == 0 && strncmp(list[i].path,
461 		    "//", 2) == 0) {
462 			char *sport, *cb;
463 			url = TRUE;
464 			oldpath = strdup(list[i].path);
465 			if (oldpath == NULL) {
466 				pr_err(gettext("memory allocation failure\n"));
467 				last_error = RET_ERR;
468 				goto out;
469 			}
470 			host = list[i].path+2;
471 			path = strchr(host, '/');
472 
473 			if (path == NULL) {
474 				pr_err(gettext(
475 				    "illegal nfs url syntax\n"));
476 				last_error = RET_ERR;
477 				goto out;
478 			}
479 
480 			*path = '\0';
481 			if (*host == '[') {
482 				cb = strchr(host, ']');
483 				if (cb == NULL) {
484 					pr_err(gettext(
485 						"illegal nfs url syntax\n"));
486 					last_error = RET_ERR;
487 					goto out;
488 				} else {
489 					*cb = '\0';
490 					host++;
491 					cb++;
492 					if (*cb == ':')
493 						port = htons((ushort_t)
494 							atoi(cb+1));
495 				}
496 			} else {
497 				sport = strchr(host, ':');
498 
499 				if (sport != NULL && sport < path) {
500 					*sport = '\0';
501 					port = htons((ushort_t)atoi(sport+1));
502 				}
503 			}
504 
505 			path++;
506 			if (*path == '\0')
507 				path = ".";
508 
509 		} else {
510 			host = list[i].host;
511 			path = list[i].path;
512 		}
513 
514 		if (r = set_args(&mntflags, argp, host, mntp)) {
515 			last_error = r;
516 			goto out;
517 		}
518 
519 		if (public_opt == TRUE)
520 			use_pubfh = TRUE;
521 
522 		if (port == 0) {
523 			port = nfs_port;
524 		} else if (nfs_port != 0 && nfs_port != port) {
525 			pr_err(gettext(
526 			    "port (%u) in nfs URL not the same"
527 			    " as port (%u) in port option\n"),
528 			    (unsigned int)ntohs(port),
529 			    (unsigned int)ntohs(nfs_port));
530 			last_error = RET_ERR;
531 			goto out;
532 		}
533 
534 
535 		if (replicated && !(mntflags & MS_RDONLY)) {
536 			pr_err(gettext(
537 				"replicated mounts must be read-only\n"));
538 			last_error = RET_ERR;
539 			goto out;
540 		}
541 
542 		if (replicated && (argp->flags & NFSMNT_SOFT)) {
543 			pr_err(gettext(
544 				"replicated mounts must not be soft\n"));
545 			last_error = RET_ERR;
546 			goto out;
547 		}
548 
549 		oldvers = vers;
550 		nconf = NULL;
551 
552 		r = RET_ERR;
553 
554 		/*
555 		 * If -o public was specified, and/or a URL was specified,
556 		 * then try the public file handle method.
557 		 */
558 		if ((use_pubfh == TRUE) || (url == TRUE)) {
559 			r = get_fh_via_pub(argp, host, path, url, use_pubfh,
560 				&vers, &nconf, port);
561 
562 			if (r != RET_OK) {
563 				/*
564 				 * If -o public was specified, then return the
565 				 * error now.
566 				 */
567 				if (use_pubfh == TRUE) {
568 					last_error = r;
569 					goto out;
570 				}
571 			} else
572 				use_pubfh = TRUE;
573 			argp->flags |= NFSMNT_PUBLIC;
574 		}
575 
576 		if ((r != RET_OK) || (vers == NFS_V4)) {
577 			bool_t loud_on_mnt_err;
578 
579 			/*
580 			 * This can happen if -o public is not specified,
581 			 * special is a URL, and server doesn't support
582 			 * public file handle.
583 			 */
584 			if (url) {
585 				URLparse(path);
586 			}
587 
588 			/*
589 			 * If the path portion of the URL didn't have
590 			 * a leading / then there is good possibility
591 			 * that a mount without a leading slash will
592 			 * fail.
593 			 */
594 			if (url == TRUE && *path != '/')
595 				loud_on_mnt_err = FALSE;
596 			else
597 				loud_on_mnt_err = TRUE;
598 
599 			r = get_fh(argp, host, path, &vers,
600 				loud_on_mnt_err, &nconf, port);
601 
602 			if (r != RET_OK) {
603 
604 				/*
605 				 * If there was no leading / and the path was
606 				 * derived from a URL, then try again
607 				 * with a leading /.
608 				 */
609 				if ((r == RET_MNTERR) &&
610 				    (loud_on_mnt_err == FALSE)) {
611 
612 					newpath = malloc(strlen(path)+2);
613 
614 					if (newpath == NULL) {
615 						pr_err(gettext("memory "
616 						    "allocation failure\n"));
617 						last_error = RET_ERR;
618 						goto out;
619 					}
620 
621 					strcpy(newpath, "/");
622 					strcat(newpath, path);
623 
624 					r = get_fh(argp, host, newpath, &vers,
625 						TRUE, &nconf, port);
626 
627 					if (r == RET_OK)
628 						path = newpath;
629 				}
630 
631 				/*
632 				 * map exit code back to RET_ERR.
633 				 */
634 				if (r == RET_MNTERR)
635 					r = RET_ERR;
636 
637 				if (r != RET_OK) {
638 
639 					if (replicated) {
640 						if (argp->fh)
641 							free(argp->fh);
642 						if (argp->pathconf)
643 							free(argp->pathconf);
644 						free(argp);
645 						goto cont;
646 					}
647 
648 					last_error = r;
649 					goto out;
650 				}
651 			}
652 		}
653 
654 		if (oldvers && vers != oldvers) {
655 			pr_err(
656 			    gettext("replicas must have the same version\n"));
657 			last_error = RET_ERR;
658 			goto out;
659 		}
660 
661 		/*
662 		 * decide whether to use remote host's
663 		 * lockd or do local locking
664 		 */
665 		if (!(argp->flags & NFSMNT_LLOCK) && vers == NFS_VERSION &&
666 		    remote_lock(host, argp->fh)) {
667 			(void) fprintf(stderr, gettext(
668 			    "WARNING: No network locking on %s:%s:"),
669 			    host, path);
670 			(void) fprintf(stderr, gettext(
671 			    " contact admin to install server change\n"));
672 				argp->flags |= NFSMNT_LLOCK;
673 		}
674 
675 		if (self_check(host))
676 			argp->flags |= NFSMNT_LOOPBACK;
677 
678 		if (use_pubfh == FALSE) {
679 			/*
680 			 * Call to get_fh() above may have obtained the
681 			 * netconfig info and NULL proc'd the server.
682 			 * This would be the case with v4
683 			 */
684 			if (!(argp->flags & NFSMNT_KNCONF)) {
685 				nconf = NULL;
686 				if (r = getaddr_nfs(argp, host, &nconf,
687 					FALSE, path, port, retry_error,
688 					TRUE)) {
689 						last_error = r;
690 						goto out;
691 				}
692 			}
693 		}
694 
695 		if (make_secure(argp, host, nconf, use_pubfh, vers) < 0) {
696 			last_error = RET_ERR;
697 			goto out;
698 		}
699 
700 		if ((url == TRUE) && (use_pubfh == FALSE)) {
701 			/*
702 			 * Convert the special from
703 			 *	nfs://host/path
704 			 * to
705 			 *	host:path
706 			 */
707 			if (convert_special(&special, host, oldpath, path,
708 			    mntp->mnt_special) == -1) {
709 				(void) fprintf(stderr, gettext(
710 				    "could not convert URL nfs:%s to %s:%s\n"),
711 				    oldpath, host, path);
712 				last_error = RET_ERR;
713 				goto out;
714 			} else {
715 				mntp->mnt_special = special;
716 			}
717 		}
718 
719 		if (prev_argp == NULL)
720 			args = argp;
721 		else
722 			prev_argp->nfs_ext_u.nfs_extB.next = argp;
723 		prev_argp = argp;
724 
725 cont:
726 		if (oldpath != NULL) {
727 			free(oldpath);
728 			oldpath = NULL;
729 		}
730 
731 		if (newpath != NULL) {
732 			free(newpath);
733 			newpath = NULL;
734 		}
735 	}
736 
737 	argp = NULL;
738 
739 	if (args == NULL) {
740 		last_error = RET_RETRY;
741 		goto out;
742 	}
743 
744 	/* Determine which services are appropriate for the NFS version */
745 	if (strcmp(fstype, MNTTYPE_NFS4) == 0)
746 		sl = service_list_v4;
747 	else
748 		sl = service_list;
749 
750 	/*
751 	 * enable services as needed.
752 	 */
753 	_check_services(sl);
754 
755 	mntflags |= MS_DATA | MS_OPTIONSTR;
756 
757 	if (mflg)
758 		mntflags |= MS_NOMNTTAB;
759 
760 	if (!qflg)
761 		saveopts = strdup(mntp->mnt_mntopts);
762 
763 	if (mount(mntp->mnt_special, mntp->mnt_mountp, mntflags, fstype, args,
764 		sizeof (*args), mntp->mnt_mntopts, MAX_MNTOPT_STR) < 0) {
765 		if (errno != ENOENT) {
766 			pr_err(gettext("mount: %s: %s\n"),
767 				mntp->mnt_mountp, strerror(errno));
768 		} else {
769 			struct stat sb;
770 			if (stat(mntp->mnt_mountp, &sb) < 0 && errno == ENOENT)
771 				pr_err(gettext("mount: %s: %s\n"),
772 					mntp->mnt_mountp, strerror(ENOENT));
773 			else
774 				pr_err("%s: %s\n", mntp->mnt_special,
775 					strerror(ENOENT));
776 		}
777 
778 		last_error = RET_ERR;
779 		goto out;
780 	}
781 
782 	if (!qflg && saveopts != NULL) {
783 		cmp_requested_to_actual_options(saveopts, mntp->mnt_mntopts,
784 		    mntp->mnt_special, mntp->mnt_mountp);
785 	}
786 
787 out:
788 	if (saveopts != NULL)
789 		free(saveopts);
790 	if (special != NULL)
791 		free(special);
792 	if (oldpath != NULL)
793 		free(oldpath);
794 	if (newpath != NULL)
795 		free(newpath);
796 
797 	free_replica(list, n);
798 
799 	if (argp != NULL) {
800 		/*
801 		 * If we had a new entry which was not added to the
802 		 * list yet, then add it now that it can be freed.
803 		 */
804 		if (prev_argp == NULL)
805 			args = argp;
806 		else
807 			prev_argp->nfs_ext_u.nfs_extB.next = argp;
808 	}
809 	argp = args;
810 	while (argp != NULL) {
811 		if (argp->fh)
812 			free(argp->fh);
813 		if (argp->pathconf)
814 			free(argp->pathconf);
815 		if (argp->knconf)
816 			free(argp->knconf);
817 		if (argp->addr) {
818 			free(argp->addr->buf);
819 			free(argp->addr);
820 		}
821 		nfs_free_secdata(argp->nfs_ext_u.nfs_extB.secdata);
822 		if (argp->syncaddr) {
823 			free(argp->syncaddr->buf);
824 			free(argp->syncaddr);
825 		}
826 		if (argp->netname)
827 			free(argp->netname);
828 		prev_argp = argp;
829 		argp = argp->nfs_ext_u.nfs_extB.next;
830 		free(prev_argp);
831 	}
832 
833 	return (last_error);
834 }
835 
836 /*
837  * These options are duplicated in uts/common/fs/nfs/nfs_dlinet.c
838  * Changes must be made to both lists.
839  */
840 static char *optlist[] = {
841 #define	OPT_RO		0
842 	MNTOPT_RO,
843 #define	OPT_RW		1
844 	MNTOPT_RW,
845 #define	OPT_QUOTA	2
846 	MNTOPT_QUOTA,
847 #define	OPT_NOQUOTA	3
848 	MNTOPT_NOQUOTA,
849 #define	OPT_SOFT	4
850 	MNTOPT_SOFT,
851 #define	OPT_HARD	5
852 	MNTOPT_HARD,
853 #define	OPT_SUID	6
854 	MNTOPT_SUID,
855 #define	OPT_NOSUID	7
856 	MNTOPT_NOSUID,
857 #define	OPT_GRPID	8
858 	MNTOPT_GRPID,
859 #define	OPT_REMOUNT	9
860 	MNTOPT_REMOUNT,
861 #define	OPT_NOSUB	10
862 	MNTOPT_NOSUB,
863 #define	OPT_INTR	11
864 	MNTOPT_INTR,
865 #define	OPT_NOINTR	12
866 	MNTOPT_NOINTR,
867 #define	OPT_PORT	13
868 	MNTOPT_PORT,
869 #define	OPT_SECURE	14
870 	MNTOPT_SECURE,
871 #define	OPT_RSIZE	15
872 	MNTOPT_RSIZE,
873 #define	OPT_WSIZE	16
874 	MNTOPT_WSIZE,
875 #define	OPT_TIMEO	17
876 	MNTOPT_TIMEO,
877 #define	OPT_RETRANS	18
878 	MNTOPT_RETRANS,
879 #define	OPT_ACTIMEO	19
880 	MNTOPT_ACTIMEO,
881 #define	OPT_ACREGMIN	20
882 	MNTOPT_ACREGMIN,
883 #define	OPT_ACREGMAX	21
884 	MNTOPT_ACREGMAX,
885 #define	OPT_ACDIRMIN	22
886 	MNTOPT_ACDIRMIN,
887 #define	OPT_ACDIRMAX	23
888 	MNTOPT_ACDIRMAX,
889 #define	OPT_BG		24
890 	MNTOPT_BG,
891 #define	OPT_FG		25
892 	MNTOPT_FG,
893 #define	OPT_RETRY	26
894 	MNTOPT_RETRY,
895 #define	OPT_NOAC	27
896 	MNTOPT_NOAC,
897 #define	OPT_NOCTO	28
898 	MNTOPT_NOCTO,
899 #define	OPT_LLOCK	29
900 	MNTOPT_LLOCK,
901 #define	OPT_POSIX	30
902 	MNTOPT_POSIX,
903 #define	OPT_VERS	31
904 	MNTOPT_VERS,
905 #define	OPT_PROTO	32
906 	MNTOPT_PROTO,
907 #define	OPT_SEMISOFT	33
908 	MNTOPT_SEMISOFT,
909 #define	OPT_NOPRINT	34
910 	MNTOPT_NOPRINT,
911 #define	OPT_SEC		35
912 	MNTOPT_SEC,
913 #define	OPT_LARGEFILES	36
914 	MNTOPT_LARGEFILES,
915 #define	OPT_NOLARGEFILES 37
916 	MNTOPT_NOLARGEFILES,
917 #define	OPT_PUBLIC	38
918 	MNTOPT_PUBLIC,
919 #define	OPT_DIRECTIO	39
920 	MNTOPT_FORCEDIRECTIO,
921 #define	OPT_NODIRECTIO	40
922 	MNTOPT_NOFORCEDIRECTIO,
923 #define	OPT_XATTR	41
924 	MNTOPT_XATTR,
925 #define	OPT_NOXATTR	42
926 	MNTOPT_NOXATTR,
927 #define	OPT_DEVICES	43
928 	MNTOPT_DEVICES,
929 #define	OPT_NODEVICES	44
930 	MNTOPT_NODEVICES,
931 #define	OPT_SETUID	45
932 	MNTOPT_SETUID,
933 #define	OPT_NOSETUID	46
934 	MNTOPT_NOSETUID,
935 #define	OPT_EXEC	47
936 	MNTOPT_EXEC,
937 #define	OPT_NOEXEC	48
938 	MNTOPT_NOEXEC,
939 	NULL
940 };
941 
942 #define	bad(val) (val == NULL || !isdigit(*val))
943 
944 static int
945 set_args(int *mntflags, struct nfs_args *args, char *fshost, struct mnttab *mnt)
946 {
947 	char *saveopt, *optstr, *opts, *newopts, *val;
948 	int largefiles = 0;
949 	int invalid = 0;
950 	int attrpref = 0;
951 	int optlen;
952 
953 	args->flags = NFSMNT_INT;	/* default is "intr" */
954 	args->flags |= NFSMNT_HOSTNAME;
955 	args->flags |= NFSMNT_NEWARGS;	/* using extented nfs_args structure */
956 	args->hostname = fshost;
957 
958 	optstr = opts = strdup(mnt->mnt_mntopts);
959 	/* sizeof (MNTOPT_XXX) includes one extra byte we may need for "," */
960 	optlen = strlen(mnt->mnt_mntopts) + sizeof (MNTOPT_XATTR) + 1;
961 	if (optlen > MAX_MNTOPT_STR) {
962 		pr_err(gettext("option string too long"));
963 		return (RET_ERR);
964 	}
965 	newopts = malloc(optlen);
966 	if (opts == NULL || newopts == NULL) {
967 		pr_err(gettext("no memory"));
968 		if (opts)
969 			free(opts);
970 		if (newopts)
971 			free(newopts);
972 		return (RET_ERR);
973 	}
974 	newopts[0] = '\0';
975 
976 	while (*opts) {
977 		invalid = 0;
978 		saveopt = opts;
979 		switch (getsubopt(&opts, optlist, &val)) {
980 		case OPT_RO:
981 			*mntflags |= MS_RDONLY;
982 			break;
983 		case OPT_RW:
984 			*mntflags &= ~(MS_RDONLY);
985 			break;
986 		case OPT_QUOTA:
987 		case OPT_NOQUOTA:
988 			break;
989 		case OPT_SOFT:
990 			args->flags |= NFSMNT_SOFT;
991 			args->flags &= ~(NFSMNT_SEMISOFT);
992 			break;
993 		case OPT_SEMISOFT:
994 			args->flags |= NFSMNT_SOFT;
995 			args->flags |= NFSMNT_SEMISOFT;
996 			break;
997 		case OPT_HARD:
998 			args->flags &= ~(NFSMNT_SOFT);
999 			args->flags &= ~(NFSMNT_SEMISOFT);
1000 			break;
1001 		case OPT_SUID:
1002 			*mntflags &= ~(MS_NOSUID);
1003 			break;
1004 		case OPT_NOSUID:
1005 			*mntflags |= MS_NOSUID;
1006 			break;
1007 		case OPT_GRPID:
1008 			args->flags |= NFSMNT_GRPID;
1009 			break;
1010 		case OPT_REMOUNT:
1011 			*mntflags |= MS_REMOUNT;
1012 			break;
1013 		case OPT_INTR:
1014 			args->flags |= NFSMNT_INT;
1015 			break;
1016 		case OPT_NOINTR:
1017 			args->flags &= ~(NFSMNT_INT);
1018 			break;
1019 		case OPT_NOAC:
1020 			args->flags |= NFSMNT_NOAC;
1021 			break;
1022 		case OPT_PORT:
1023 			if (bad(val))
1024 				goto badopt;
1025 			nfs_port = htons((ushort_t)atoi(val));
1026 			break;
1027 
1028 		case OPT_SECURE:
1029 			if (nfs_getseconfig_byname("dh", &nfs_sec)) {
1030 			    pr_err(gettext("can not get \"dh\" from %s\n"),
1031 						NFSSEC_CONF);
1032 			    goto badopt;
1033 			}
1034 			sec_opt++;
1035 			break;
1036 
1037 		case OPT_NOCTO:
1038 			args->flags |= NFSMNT_NOCTO;
1039 			break;
1040 
1041 		case OPT_RSIZE:
1042 			args->flags |= NFSMNT_RSIZE;
1043 			if (bad(val))
1044 				goto badopt;
1045 			args->rsize = atoi(val);
1046 			break;
1047 		case OPT_WSIZE:
1048 			args->flags |= NFSMNT_WSIZE;
1049 			if (bad(val))
1050 				goto badopt;
1051 			args->wsize = atoi(val);
1052 			break;
1053 		case OPT_TIMEO:
1054 			args->flags |= NFSMNT_TIMEO;
1055 			if (bad(val))
1056 				goto badopt;
1057 			args->timeo = atoi(val);
1058 			break;
1059 		case OPT_RETRANS:
1060 			args->flags |= NFSMNT_RETRANS;
1061 			if (bad(val))
1062 				goto badopt;
1063 			args->retrans = atoi(val);
1064 			break;
1065 		case OPT_ACTIMEO:
1066 			args->flags |= NFSMNT_ACDIRMAX;
1067 			args->flags |= NFSMNT_ACREGMAX;
1068 			args->flags |= NFSMNT_ACDIRMIN;
1069 			args->flags |= NFSMNT_ACREGMIN;
1070 			if (bad(val))
1071 				goto badopt;
1072 			args->acdirmin = args->acregmin = args->acdirmax
1073 				= args->acregmax = atoi(val);
1074 			break;
1075 		case OPT_ACREGMIN:
1076 			args->flags |= NFSMNT_ACREGMIN;
1077 			if (bad(val))
1078 				goto badopt;
1079 			args->acregmin = atoi(val);
1080 			break;
1081 		case OPT_ACREGMAX:
1082 			args->flags |= NFSMNT_ACREGMAX;
1083 			if (bad(val))
1084 				goto badopt;
1085 			args->acregmax = atoi(val);
1086 			break;
1087 		case OPT_ACDIRMIN:
1088 			args->flags |= NFSMNT_ACDIRMIN;
1089 			if (bad(val))
1090 				goto badopt;
1091 			args->acdirmin = atoi(val);
1092 			break;
1093 		case OPT_ACDIRMAX:
1094 			args->flags |= NFSMNT_ACDIRMAX;
1095 			if (bad(val))
1096 				goto badopt;
1097 			args->acdirmax = atoi(val);
1098 			break;
1099 		case OPT_BG:
1100 			bg++;
1101 			break;
1102 		case OPT_FG:
1103 			bg = 0;
1104 			break;
1105 		case OPT_RETRY:
1106 			if (bad(val))
1107 				goto badopt;
1108 			retries = atoi(val);
1109 			break;
1110 		case OPT_LLOCK:
1111 			args->flags |= NFSMNT_LLOCK;
1112 			break;
1113 		case OPT_POSIX:
1114 			posix = 1;
1115 			break;
1116 		case OPT_VERS:
1117 			if (bad(val))
1118 				goto badopt;
1119 			nfsvers = (rpcvers_t)atoi(val);
1120 			break;
1121 		case OPT_PROTO:
1122 			if (val == NULL)
1123 				goto badopt;
1124 
1125 			nfs_proto = (char *)malloc(strlen(val)+1);
1126 			if (!nfs_proto) {
1127 				pr_err(gettext("no memory"));
1128 				return (RET_ERR);
1129 			}
1130 
1131 			(void) strncpy(nfs_proto, val, strlen(val)+1);
1132 			break;
1133 		case OPT_NOPRINT:
1134 			args->flags |= NFSMNT_NOPRINT;
1135 			break;
1136 		case OPT_LARGEFILES:
1137 			largefiles = 1;
1138 			break;
1139 		case OPT_NOLARGEFILES:
1140 			pr_err(gettext("NFS can't support \"nolargefiles\"\n"));
1141 			free(optstr);
1142 			return (RET_ERR);
1143 
1144 		case OPT_SEC:
1145 			if (nfs_getseconfig_byname(val, &nfs_sec)) {
1146 			    pr_err(gettext("can not get \"%s\" from %s\n"),
1147 						val, NFSSEC_CONF);
1148 			    return (RET_ERR);
1149 			}
1150 			sec_opt++;
1151 			break;
1152 
1153 		case OPT_PUBLIC:
1154 			public_opt = TRUE;
1155 			break;
1156 
1157 		case OPT_DIRECTIO:
1158 			args->flags |= NFSMNT_DIRECTIO;
1159 			break;
1160 
1161 		case OPT_NODIRECTIO:
1162 			args->flags &= ~(NFSMNT_DIRECTIO);
1163 			break;
1164 
1165 		case OPT_XATTR:
1166 		case OPT_NOXATTR:
1167 			/*
1168 			 * VFS options; just need to get them into the
1169 			 * new mount option string and note we've seen them
1170 			 */
1171 			attrpref = 1;
1172 			break;
1173 		default:
1174 			/*
1175 			 * Note that this could be a valid OPT_* option so
1176 			 * we can't use "val" but need to use "saveopt".
1177 			 */
1178 			if (fsisstdopt(saveopt))
1179 				break;
1180 			invalid = 1;
1181 			if (!qflg)
1182 				(void) fprintf(stderr, gettext(
1183 				    "mount: %s on %s - WARNING unknown option"
1184 				    " \"%s\"\n"), mnt->mnt_special,
1185 				    mnt->mnt_mountp, saveopt);
1186 			break;
1187 		}
1188 		if (!invalid) {
1189 			if (newopts[0])
1190 				strcat(newopts, ",");
1191 			strcat(newopts, saveopt);
1192 		}
1193 	}
1194 	/* Default is to turn extended attrs on */
1195 	if (!attrpref) {
1196 		if (newopts[0])
1197 			strcat(newopts, ",");
1198 		strcat(newopts, MNTOPT_XATTR);
1199 	}
1200 	strcpy(mnt->mnt_mntopts, newopts);
1201 	free(newopts);
1202 	free(optstr);
1203 
1204 	/* ensure that only one secure mode is requested */
1205 	if (sec_opt > 1) {
1206 		pr_err(gettext("Security options conflict\n"));
1207 		return (RET_ERR);
1208 	}
1209 
1210 	/* ensure that the user isn't trying to get large files over V2 */
1211 	if (nfsvers == NFS_VERSION && largefiles) {
1212 		pr_err(gettext("NFS V2 can't support \"largefiles\"\n"));
1213 		return (RET_ERR);
1214 	}
1215 
1216 	if (nfsvers == NFS_V4 &&
1217 	    nfs_proto != NULL &&
1218 	    strncasecmp(nfs_proto, NC_UDP, strlen(NC_UDP)) == 0) {
1219 		pr_err(gettext("NFS V4 does not support %s\n"), nfs_proto);
1220 		return (RET_ERR);
1221 	}
1222 
1223 	return (RET_OK);
1224 
1225 badopt:
1226 	pr_err(gettext("invalid option: \"%s\"\n"), saveopt);
1227 	free(optstr);
1228 	return (RET_ERR);
1229 }
1230 
1231 static int
1232 make_secure(struct nfs_args *args, char *hostname, struct netconfig *nconf,
1233 	bool_t use_pubfh, rpcvers_t vers)
1234 {
1235 	sec_data_t *secdata;
1236 	int flags;
1237 	struct netbuf *syncaddr = NULL;
1238 	struct nd_addrlist *retaddrs = NULL;
1239 	char netname[MAXNETNAMELEN+1];
1240 
1241 	/*
1242 	 * check to see if any secure mode is requested.
1243 	 * if not, use default security mode.
1244 	 */
1245 	if (!snego_done && !sec_opt) {
1246 		/*
1247 		 *  Get default security mode.
1248 		 *  AUTH_UNIX has been the default choice for a long time.
1249 		 *  The better NFS security service becomes, the better chance
1250 		 *  we will set stronger security service as the default NFS
1251 		 *  security mode.
1252 		 *
1253 		 */
1254 	    if (nfs_getseconfig_default(&nfs_sec)) {
1255 		pr_err(gettext("error getting default security entry\n"));
1256 		return (-1);
1257 	    }
1258 	    args->flags |= NFSMNT_SECDEFAULT;
1259 	}
1260 
1261 	/*
1262 	 * Get the network address for the time service on the server.
1263 	 * If an RPC based time service is not available then try the
1264 	 * IP time service.
1265 	 *
1266 	 * This is for AUTH_DH processing. We will also pass down syncaddr
1267 	 * and netname for NFS V4 even if AUTH_DH is not requested right now.
1268 	 * NFS V4 does security negotiation in the kernel via SECINFO.
1269 	 * These information might be needed later in the kernel.
1270 	 *
1271 	 * Eventurally, we want to move this code to nfs_clnt_secdata()
1272 	 * when autod_nfs.c and mount.c can share the same get_the_addr()
1273 	 * routine.
1274 	 */
1275 	flags = 0;
1276 	syncaddr = NULL;
1277 
1278 	if (nfs_sec.sc_rpcnum == AUTH_DH || vers == NFS_V4) {
1279 		/*
1280 		 * If using the public fh or nfsv4, we will not contact the
1281 		 * remote RPCBINDer, since it is possibly behind a firewall.
1282 		 */
1283 		if (use_pubfh == FALSE && vers != NFS_V4) {
1284 			syncaddr = get_the_addr(hostname, RPCBPROG, RPCBVERS,
1285 				nconf, 0, NULL, NULL, FALSE, NULL, NULL);
1286 		}
1287 
1288 		if (syncaddr != NULL) {
1289 			/* for flags in sec_data */
1290 			flags |= AUTH_F_RPCTIMESYNC;
1291 		} else {
1292 			struct nd_hostserv hs;
1293 			int error;
1294 
1295 			hs.h_host = hostname;
1296 			hs.h_serv = "timserver";
1297 
1298 			error = netdir_getbyname(nconf, &hs, &retaddrs);
1299 
1300 			if (error != ND_OK && (nfs_sec.sc_rpcnum == AUTH_DH)) {
1301 			    pr_err(gettext("%s: secure: no time service\n"),
1302 						hostname);
1303 			    return (-1);
1304 			}
1305 
1306 			if (error == ND_OK)
1307 				syncaddr = retaddrs->n_addrs;
1308 
1309 			/*
1310 			 * For NFS_V4 if AUTH_DH is negotiated later in the
1311 			 * kernel thru SECINFO, it will need syncaddr
1312 			 * and netname data.
1313 			 */
1314 			if (vers == NFS_V4 && syncaddr &&
1315 				    host2netname(netname, hostname, NULL)) {
1316 			    args->syncaddr = malloc(sizeof (struct netbuf));
1317 			    args->syncaddr->buf = malloc(syncaddr->len);
1318 			    (void) memcpy(args->syncaddr->buf, syncaddr->buf,
1319 							syncaddr->len);
1320 			    args->syncaddr->len = syncaddr->len;
1321 			    args->syncaddr->maxlen = syncaddr->maxlen;
1322 			    args->netname = strdup(netname);
1323 			    args->flags |= NFSMNT_SECURE;
1324 			}
1325 		}
1326 	}
1327 
1328 	/*
1329 	 * For the initial chosen flavor (any flavor defined in nfssec.conf),
1330 	 * the data will be stored in the sec_data structure via
1331 	 * nfs_clnt_secdata() and be passed to the kernel via nfs_args_*
1332 	 * extended data structure.
1333 	 */
1334 	if (!(secdata = nfs_clnt_secdata(&nfs_sec, hostname, args->knconf,
1335 					syncaddr, flags))) {
1336 		pr_err(gettext("errors constructing security related data\n"));
1337 		if (flags & AUTH_F_RPCTIMESYNC) {
1338 			free(syncaddr->buf);
1339 			free(syncaddr);
1340 		} else if (retaddrs)
1341 			netdir_free((void *)retaddrs, ND_ADDRLIST);
1342 		return (-1);
1343 	}
1344 
1345 	NFS_ARGS_EXTB_secdata(args, secdata);
1346 	if (flags & AUTH_F_RPCTIMESYNC) {
1347 		free(syncaddr->buf);
1348 		free(syncaddr);
1349 	} else if (retaddrs)
1350 		netdir_free((void *)retaddrs, ND_ADDRLIST);
1351 	return (0);
1352 }
1353 
1354 /*
1355  * Get the network address on "hostname" for program "prog"
1356  * with version "vers" by using the nconf configuration data
1357  * passed in.
1358  *
1359  * If the address of a netconfig pointer is null then
1360  * information is not sufficient and no netbuf will be returned.
1361  *
1362  * Finally, ping the null procedure of that service.
1363  *
1364  * A similar routine is also defined in ../../autofs/autod_nfs.c.
1365  * This is a potential routine to move to ../lib for common usage.
1366  */
1367 static struct netbuf *
1368 get_the_addr(char *hostname, ulong_t prog, ulong_t vers,
1369 	struct netconfig *nconf, ushort_t port, struct t_info *tinfo,
1370 	caddr_t *fhp, bool_t get_pubfh, char *fspath, err_ret_t *error)
1371 {
1372 	struct netbuf *nb = NULL;
1373 	struct t_bind *tbind = NULL;
1374 	CLIENT *cl = NULL;
1375 	struct timeval tv;
1376 	int fd = -1;
1377 	AUTH *ah = NULL;
1378 	AUTH *new_ah = NULL;
1379 	struct snego_t snego;
1380 
1381 	if (nconf == NULL)
1382 		return (NULL);
1383 
1384 	if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) == -1)
1385 		    goto done;
1386 
1387 	/* LINTED pointer alignment */
1388 	if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR))
1389 		== NULL)
1390 		goto done;
1391 
1392 	/*
1393 	 * In the case of public filehandle usage or NFSv4 we want to
1394 	 * avoid use of the rpcbind/portmap protocol
1395 	 */
1396 	if ((get_pubfh == TRUE) || (vers == NFS_V4)) {
1397 		struct nd_hostserv hs;
1398 		struct nd_addrlist *retaddrs;
1399 		int retval;
1400 		hs.h_host = hostname;
1401 
1402 		/* NFS where vers==4 does not support UDP */
1403 		if (vers == NFS_V4 &&
1404 		    strncasecmp(nconf->nc_proto, NC_UDP,
1405 				strlen(NC_UDP)) == 0) {
1406 			SET_ERR_RET(error, ERR_PROTO_UNSUPP, 0);
1407 			goto done;
1408 		}
1409 
1410 		if (port == 0)
1411 			hs.h_serv = "nfs";
1412 		else
1413 			hs.h_serv = NULL;
1414 
1415 		if ((retval = netdir_getbyname(nconf, &hs, &retaddrs))
1416 		    != ND_OK) {
1417 			/*
1418 			 * Carefully set the error value here. Want to signify
1419 			 * that the error was an unknown host.
1420 			 */
1421 			if (retval == ND_NOHOST) {
1422 				SET_ERR_RET(error, ERR_NOHOST, retval);
1423 			}
1424 
1425 			goto done;
1426 		}
1427 		memcpy(tbind->addr.buf, retaddrs->n_addrs->buf,
1428 			retaddrs->n_addrs->len);
1429 		tbind->addr.len = retaddrs->n_addrs->len;
1430 		netdir_free((void *)retaddrs, ND_ADDRLIST);
1431 		(void) netdir_options(nconf, ND_SET_RESERVEDPORT, fd, NULL);
1432 
1433 	} else {
1434 		if (rpcb_getaddr(prog, vers, nconf, &tbind->addr,
1435 		    hostname) == FALSE) {
1436 			goto done;
1437 		}
1438 	}
1439 
1440 	if (port) {
1441 		/* LINTED pointer alignment */
1442 		if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
1443 			((struct sockaddr_in *)tbind->addr.buf)->sin_port
1444 				= port;
1445 		else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
1446 			((struct sockaddr_in6 *)tbind->addr.buf)->sin6_port
1447 				= port;
1448 
1449 	}
1450 
1451 	cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0);
1452 	if (cl == NULL) {
1453 		/*
1454 		 * clnt_tli_create() returns either RPC_SYSTEMERROR,
1455 		 * RPC_UNKNOWNPROTO or RPC_TLIERROR. The RPC_TLIERROR translates
1456 		 * to "Misc. TLI error". This is not too helpful. Most likely
1457 		 * the connection to the remote server timed out, so this
1458 		 * error is at least less perplexing.
1459 		 * See: usr/src/cmd/rpcinfo/rpcinfo.c
1460 		 */
1461 		if (rpc_createerr.cf_stat == RPC_TLIERROR) {
1462 			SET_ERR_RET(error, ERR_RPCERROR, RPC_PMAPFAILURE);
1463 		} else {
1464 			SET_ERR_RET(error, ERR_RPCERROR, rpc_createerr.cf_stat);
1465 		}
1466 		goto done;
1467 	}
1468 
1469 	ah = authsys_create_default();
1470 	if (ah != NULL)
1471 		cl->cl_auth = ah;
1472 
1473 	tv.tv_sec = 5;
1474 	tv.tv_usec = 0;
1475 
1476 	(void) clnt_control(cl, CLSET_TIMEOUT, (char *)&tv);
1477 
1478 	if ((get_pubfh == TRUE) && (vers != NFS_V4)) {
1479 	    enum snego_stat sec;
1480 
1481 	    if (!snego_done) {
1482 		/*
1483 		 * negotiate sec flavor.
1484 		 */
1485 		snego.cnt = 0;
1486 		if ((sec = nfs_sec_nego(vers, cl, fspath, &snego)) ==
1487 			SNEGO_SUCCESS) {
1488 		    int jj;
1489 
1490 		/*
1491 		 * check if server supports the one
1492 		 * specified in the sec= option.
1493 		 */
1494 		    if (sec_opt) {
1495 			for (jj = 0; jj < snego.cnt; jj++) {
1496 			    if (snego.array[jj] == nfs_sec.sc_nfsnum) {
1497 				snego_done = TRUE;
1498 				break;
1499 			    }
1500 			}
1501 		    }
1502 
1503 		/*
1504 		 * find a common sec flavor
1505 		 */
1506 		    if (!snego_done) {
1507 			if (sec_opt) {
1508 			    pr_err(gettext(
1509 				"Server does not support the security"
1510 				" flavor specified.\n"));
1511 			}
1512 			for (jj = 0; jj < snego.cnt; jj++) {
1513 			    if (!nfs_getseconfig_bynumber(snego.array[jj],
1514 				&nfs_sec)) {
1515 				snego_done = TRUE;
1516 				if (sec_opt) {
1517 				    pr_err(gettext(
1518 					"Security flavor %d was negotiated and"
1519 					" will be used.\n"),
1520 					nfs_sec.sc_nfsnum);
1521 				}
1522 				break;
1523 			    }
1524 			}
1525 		    }
1526 		    if (!snego_done)
1527 			return (NULL);
1528 
1529 		/*
1530 		 * Now that the flavor has been
1531 		 * negotiated, get the fh.
1532 		 *
1533 		 * First, create an auth handle using the negotiated
1534 		 * sec flavor in the next lookup to
1535 		 * fetch the filehandle.
1536 		 */
1537 		    new_ah = nfs_create_ah(cl, hostname, &nfs_sec);
1538 		    if (new_ah == NULL)
1539 			goto done;
1540 		    cl->cl_auth = new_ah;
1541 		} else if (sec == SNEGO_ARRAY_TOO_SMALL || sec ==
1542 		    SNEGO_FAILURE) {
1543 			goto done;
1544 		}
1545 		/*
1546 		 * Note that if sec == SNEGO_DEF_VALID
1547 		 * default sec flavor is acceptable.
1548 		 * Use it to get the filehandle.
1549 		 */
1550 	    }
1551 
1552 	    if (vers == NFS_VERSION) {
1553 		wnl_diropargs arg;
1554 		wnl_diropres *res;
1555 
1556 		memset((char *)&arg.dir, 0, sizeof (wnl_fh));
1557 		arg.name = fspath;
1558 		res = wnlproc_lookup_2(&arg, cl);
1559 
1560 		if (res == NULL || res->status != NFS_OK)
1561 		    goto done;
1562 		*fhp = malloc(sizeof (wnl_fh));
1563 
1564 		if (*fhp == NULL) {
1565 		    pr_err(gettext("no memory\n"));
1566 		    goto done;
1567 		}
1568 
1569 		memcpy((char *)*fhp,
1570 		    (char *)&res->wnl_diropres_u.wnl_diropres.file,
1571 		    sizeof (wnl_fh));
1572 	    } else {
1573 		WNL_LOOKUP3args arg;
1574 		WNL_LOOKUP3res *res;
1575 		nfs_fh3 *fh3p;
1576 
1577 		memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3));
1578 		arg.what.name = fspath;
1579 		res = wnlproc3_lookup_3(&arg, cl);
1580 
1581 		if (res == NULL || res->status != NFS3_OK)
1582 		    goto done;
1583 
1584 		fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
1585 
1586 		if (fh3p == NULL) {
1587 		    pr_err(gettext("no memory\n"));
1588 		    CLNT_FREERES(cl, xdr_WNL_LOOKUP3res, (char *)res);
1589 		    goto done;
1590 		}
1591 
1592 		fh3p->fh3_length =
1593 		    res->WNL_LOOKUP3res_u.res_ok.object.data.data_len;
1594 		memcpy(fh3p->fh3_u.data,
1595 		    res->WNL_LOOKUP3res_u.res_ok.object.data.data_val,
1596 		    fh3p->fh3_length);
1597 
1598 		*fhp = (caddr_t)fh3p;
1599 
1600 		CLNT_FREERES(cl, xdr_WNL_LOOKUP3res, (char *)res);
1601 	    }
1602 	} else {
1603 		void *res;
1604 		struct rpc_err r_err;
1605 
1606 		if (vers == NFS_VERSION)
1607 		    res = wnlproc_null_2(NULL, cl);
1608 		else if (vers == NFS_V3)
1609 		    res = wnlproc3_null_3(NULL, cl);
1610 		else
1611 		    res = wnlproc4_null_4(NULL, cl);
1612 
1613 		if (res == NULL) {
1614 			clnt_geterr(cl, &r_err);
1615 			if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
1616 				switch (r_err.re_status) {
1617 				case RPC_TLIERROR:
1618 				case RPC_CANTRECV:
1619 				case RPC_CANTSEND:
1620 					r_err.re_status = RPC_PROGVERSMISMATCH;
1621 				}
1622 			}
1623 			SET_ERR_RET(error, ERR_RPCERROR, r_err.re_status);
1624 			goto done;
1625 		}
1626 	}
1627 
1628 	/*
1629 	 * Make a copy of the netbuf to return
1630 	 */
1631 	nb = (struct netbuf *)malloc(sizeof (*nb));
1632 	if (nb == NULL) {
1633 		pr_err(gettext("no memory\n"));
1634 		goto done;
1635 	}
1636 	*nb = tbind->addr;
1637 	nb->buf = (char *)malloc(nb->maxlen);
1638 	if (nb->buf == NULL) {
1639 		pr_err(gettext("no memory\n"));
1640 		free(nb);
1641 		nb = NULL;
1642 		goto done;
1643 	}
1644 	(void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
1645 
1646 done:
1647 	if (cl) {
1648 	    if (ah != NULL) {
1649 		if (new_ah != NULL)
1650 		    AUTH_DESTROY(ah);
1651 		AUTH_DESTROY(cl->cl_auth);
1652 		cl->cl_auth = NULL;
1653 	    }
1654 	    clnt_destroy(cl);
1655 	    cl = NULL;
1656 	}
1657 	if (tbind) {
1658 		t_free((char *)tbind, T_BIND);
1659 		tbind = NULL;
1660 	}
1661 	if (fd >= 0)
1662 		(void) t_close(fd);
1663 	return (nb);
1664 }
1665 
1666 /*
1667  * Get a network address on "hostname" for program "prog"
1668  * with version "vers".  If the port number is specified (non zero)
1669  * then try for a TCP/UDP transport and set the port number of the
1670  * resulting IP address.
1671  *
1672  * If the address of a netconfig pointer was passed and
1673  * if it's not null, use it as the netconfig otherwise
1674  * assign the address of the netconfig that was used to
1675  * establish contact with the service.
1676  *
1677  * A similar routine is also defined in ../../autofs/autod_nfs.c.
1678  * This is a potential routine to move to ../lib for common usage.
1679  *
1680  * "error" refers to a more descriptive term when get_addr fails
1681  * and returns NULL: ERR_PROTO_NONE if no error introduced by
1682  * -o proto option, ERR_NETPATH if error found in NETPATH
1683  * environment variable, ERR_PROTO_INVALID if an unrecognized
1684  * protocol is specified by user, and ERR_PROTO_UNSUPP for a
1685  * recognized but invalid protocol (eg. ticlts, ticots, etc.).
1686  * "error" is ignored if get_addr returns non-NULL result.
1687  *
1688  */
1689 static struct netbuf *
1690 get_addr(char *hostname, ulong_t prog, ulong_t vers, struct netconfig **nconfp,
1691 	char *proto, ushort_t port, struct t_info *tinfo, caddr_t *fhp,
1692 	bool_t get_pubfh, char *fspath, err_ret_t *error)
1693 {
1694 	struct netbuf *nb = NULL;
1695 	struct netconfig *nconf = NULL;
1696 	NCONF_HANDLE *nc = NULL;
1697 	int nthtry = FIRST_TRY;
1698 	err_ret_t errsave_nohost, errsave_rpcerr;
1699 
1700 	SET_ERR_RET(&errsave_nohost, ERR_PROTO_NONE, 0);
1701 	SET_ERR_RET(&errsave_rpcerr, ERR_PROTO_NONE, 0);
1702 
1703 	SET_ERR_RET(error, ERR_PROTO_NONE, 0);
1704 
1705 	if (nconfp && *nconfp)
1706 		return (get_the_addr(hostname, prog, vers, *nconfp, port,
1707 			tinfo, fhp, get_pubfh, fspath, error));
1708 	/*
1709 	 * No nconf passed in.
1710 	 *
1711 	 * Try to get a nconf from /etc/netconfig filtered by
1712 	 * the NETPATH environment variable.
1713 	 * First search for COTS, second for CLTS unless proto
1714 	 * is specified.  When we retry, we reset the
1715 	 * netconfig list so that we would search the whole list
1716 	 * all over again.
1717 	 */
1718 
1719 	if ((nc = setnetpath()) == NULL) {
1720 		/* should only return an error if problems with NETPATH */
1721 		/* In which case you are hosed */
1722 		SET_ERR_RET(error, ERR_NETPATH, 0);
1723 		goto done;
1724 	}
1725 
1726 	/*
1727 	 * If proto is specified, then only search for the match,
1728 	 * otherwise try COTS first, if failed, try CLTS.
1729 	 */
1730 	if (proto) {
1731 		/* no matching proto name */
1732 		SET_ERR_RET(error, ERR_PROTO_INVALID, 0);
1733 
1734 		while (nconf = getnetpath(nc)) {
1735 			if (strcmp(nconf->nc_netid, proto))
1736 				continue;
1737 
1738 			/* may be unsupported */
1739 			SET_ERR_RET(error, ERR_PROTO_UNSUPP, 0);
1740 
1741 			if ((port != 0) &&
1742 				((strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
1743 				strcmp(nconf->nc_protofmly, NC_INET6) == 0) &&
1744 				(strcmp(nconf->nc_proto, NC_TCP) != 0 &&
1745 				strcmp(nconf->nc_proto, NC_UDP) != 0)))
1746 
1747 				continue;
1748 
1749 			else {
1750 				nb = get_the_addr(hostname, prog,
1751 					vers, nconf, port, tinfo,
1752 						fhp, get_pubfh, fspath, error);
1753 
1754 				if (nb != NULL)
1755 					break;
1756 
1757 				/* nb is NULL - deal with errors */
1758 				if (error) {
1759 					if (error->error_type == ERR_NOHOST)
1760 						SET_ERR_RET(&errsave_nohost,
1761 							error->error_type,
1762 							error->error_value);
1763 					if (error->error_type == ERR_RPCERROR)
1764 						SET_ERR_RET(&errsave_rpcerr,
1765 							error->error_type,
1766 							error->error_value);
1767 				}
1768 				/*
1769 				 * continue with same protocol
1770 				 * selection
1771 				 */
1772 				continue;
1773 			}
1774 		} /* end of while */
1775 
1776 		if (nconf == NULL)
1777 			goto done;
1778 
1779 		if ((nb = get_the_addr(hostname, prog, vers, nconf, port,
1780 				tinfo, fhp, get_pubfh, fspath, error)) == NULL)
1781 			goto done;
1782 
1783 
1784 	} else {
1785 retry:
1786 		SET_ERR_RET(error, ERR_NETPATH, 0);
1787 		while (nconf = getnetpath(nc)) {
1788 			SET_ERR_RET(error, ERR_PROTO_NONE, 0);
1789 			if (nconf->nc_flag & NC_VISIBLE) {
1790 				if (nthtry == FIRST_TRY) {
1791 					if ((nconf->nc_semantics ==
1792 						NC_TPI_COTS_ORD) ||
1793 					    (nconf->nc_semantics ==
1794 						NC_TPI_COTS)) {
1795 
1796 						if (port == 0)
1797 							break;
1798 
1799 						if ((strcmp(nconf->nc_protofmly,
1800 							NC_INET) == 0 ||
1801 							strcmp(nconf->
1802 							nc_protofmly,
1803 							NC_INET6) == 0) &&
1804 						    (strcmp(nconf->nc_proto,
1805 							NC_TCP) == 0))
1806 
1807 							break;
1808 					}
1809 				}
1810 				if (nthtry == SECOND_TRY) {
1811 					if (nconf->nc_semantics ==
1812 						NC_TPI_CLTS) {
1813 						if (port == 0)
1814 							break;
1815 						if ((strcmp(nconf->nc_protofmly,
1816 							NC_INET) == 0 ||
1817 							strcmp(nconf->
1818 							nc_protofmly, NC_INET6)
1819 							== 0) &&
1820 							(strcmp(
1821 							nconf->nc_proto,
1822 							NC_UDP) == 0))
1823 							break;
1824 					}
1825 				}
1826 			}
1827 		} /* while */
1828 		if (nconf == NULL) {
1829 			if (++nthtry <= MNT_PREF_LISTLEN) {
1830 				endnetpath(nc);
1831 				if ((nc = setnetpath()) == NULL)
1832 					goto done;
1833 				goto retry;
1834 			} else
1835 				goto done;
1836 		} else {
1837 			if ((nb = get_the_addr(hostname, prog, vers, nconf,
1838 				port, tinfo, fhp, get_pubfh, fspath, error))
1839 				== NULL) {
1840 				/* nb is NULL - deal with errors */
1841 				if (error) {
1842 					if (error->error_type == ERR_NOHOST)
1843 						SET_ERR_RET(&errsave_nohost,
1844 							error->error_type,
1845 							error->error_value);
1846 					if (error->error_type == ERR_RPCERROR)
1847 						SET_ERR_RET(&errsave_rpcerr,
1848 							error->error_type,
1849 							error->error_value);
1850 				}
1851 				/*
1852 				 * Continue the same search path in the
1853 				 * netconfig db until no more matched
1854 				 * nconf (nconf == NULL).
1855 				 */
1856 				goto retry;
1857 			}
1858 		}
1859 	}
1860 	SET_ERR_RET(error, ERR_PROTO_NONE, 0);
1861 
1862 	/*
1863 	 * Got nconf and nb.  Now dup the netconfig structure (nconf)
1864 	 * and return it thru nconfp.
1865 	 */
1866 	*nconfp = getnetconfigent(nconf->nc_netid);
1867 	if (*nconfp == NULL) {
1868 		syslog(LOG_ERR, "no memory\n");
1869 		free(nb);
1870 		nb = NULL;
1871 	}
1872 done:
1873 	if (nc)
1874 		endnetpath(nc);
1875 
1876 	if (nb == NULL) {
1877 		/*
1878 		 * Check the saved errors. The RPC error has *
1879 		 * precedence over the no host error.
1880 		 */
1881 		if (errsave_nohost.error_type != ERR_PROTO_NONE)
1882 			SET_ERR_RET(error, errsave_nohost.error_type,
1883 					errsave_nohost.error_value);
1884 
1885 		if (errsave_rpcerr.error_type != ERR_PROTO_NONE)
1886 			SET_ERR_RET(error, errsave_rpcerr.error_type,
1887 					errsave_rpcerr.error_value);
1888 	}
1889 
1890 	return (nb);
1891 }
1892 
1893 /*
1894  * Get a file handle usinging multi-component lookup with the public
1895  * file handle.
1896  */
1897 static int
1898 get_fh_via_pub(struct nfs_args *args, char *fshost, char *fspath, bool_t url,
1899 	bool_t loud, int *versp, struct netconfig **nconfp, ushort_t port)
1900 {
1901 	uint_t vers_min;
1902 	uint_t vers_max;
1903 	int r;
1904 	char *path;
1905 
1906 	if (nfsvers != 0) {
1907 		vers_max = vers_min = nfsvers;
1908 	} else {
1909 		vers_max = vers_max_default;
1910 		vers_min = vers_min_default;
1911 	}
1912 
1913 	if (url == FALSE) {
1914 		path = malloc(strlen(fspath) + 2);
1915 		if (path == NULL) {
1916 			if (loud == TRUE)  {
1917 				pr_err(gettext("no memory\n"));
1918 			}
1919 			return (RET_ERR);
1920 		}
1921 
1922 		path[0] = (char)WNL_NATIVEPATH;
1923 		(void) strcpy(&path[1], fspath);
1924 
1925 	} else  {
1926 		path = fspath;
1927 	}
1928 
1929 	for (nfsvers_to_use = vers_max; nfsvers_to_use >= vers_min;
1930 	    nfsvers_to_use--) {
1931 		/*
1932 		 * getaddr_nfs will also fill in the fh for us.
1933 		 */
1934 		r = getaddr_nfs(args, fshost, nconfp,
1935 				TRUE, path, port, NULL, FALSE);
1936 
1937 		if (r == RET_OK) {
1938 			/*
1939 			 * Since we are using the public fh, and NLM is
1940 			 * not firewall friendly, use local locking.
1941 			 * Not the case for v4.
1942 			 */
1943 			*versp = nfsvers_to_use;
1944 			switch (nfsvers_to_use) {
1945 			case NFS_V4:
1946 				fstype = MNTTYPE_NFS4;
1947 				break;
1948 			case NFS_V3:
1949 				fstype = MNTTYPE_NFS3;
1950 				/* fall through to pick up llock option */
1951 			default:
1952 				args->flags |= NFSMNT_LLOCK;
1953 				break;
1954 			}
1955 			if (fspath != path)
1956 				free(path);
1957 
1958 			return (r);
1959 		}
1960 	}
1961 
1962 	if (fspath != path) {
1963 		free(path);
1964 	}
1965 
1966 	if (loud == TRUE) {
1967 		pr_err(gettext("Could not use public filehandle in request to"
1968 			" server %s\n"), fshost);
1969 	}
1970 
1971 	return (r);
1972 }
1973 
1974 /*
1975  * get fhandle of remote path from server's mountd
1976  */
1977 static int
1978 get_fh(struct nfs_args *args, char *fshost, char *fspath, int *versp,
1979 	bool_t loud_on_mnt_err, struct netconfig **nconfp, ushort_t port)
1980 {
1981 	static struct fhstatus fhs;
1982 	static struct mountres3 mountres3;
1983 	static struct pathcnf p;
1984 	nfs_fh3 *fh3p;
1985 	struct timeval timeout = { 25, 0};
1986 	CLIENT *cl;
1987 	enum clnt_stat rpc_stat;
1988 	rpcvers_t outvers = 0;
1989 	rpcvers_t vers_to_try;
1990 	rpcvers_t vers_min;
1991 	static int printed = 0;
1992 	int count, i, *auths;
1993 	char *msg;
1994 
1995 	switch (nfsvers) {
1996 	case 2: /* version 2 specified try that only */
1997 		vers_to_try = MOUNTVERS_POSIX;
1998 		vers_min = MOUNTVERS;
1999 		break;
2000 	case 3: /* version 3 specified try that only */
2001 		vers_to_try = MOUNTVERS3;
2002 		vers_min = MOUNTVERS3;
2003 		break;
2004 	case 4: /* version 4 specified try that only */
2005 		/*
2006 		 * This assignment is in the wrong version sequence.
2007 		 * The above are MOUNT program and this is NFS
2008 		 * program.  However, it happens to work out since the
2009 		 * two don't collide for NFSv4.
2010 		 */
2011 		vers_to_try = NFS_V4;
2012 		vers_min = NFS_V4;
2013 		break;
2014 	default: /* no version specified, start with default */
2015 		/*
2016 		 * If the retry version is set, use that. This will
2017 		 * be set if the last mount attempt returned any other
2018 		 * besides an RPC error.
2019 		 */
2020 		if (nfsretry_vers)
2021 			vers_to_try = nfsretry_vers;
2022 		else {
2023 			vers_to_try = vers_max_default;
2024 			vers_min = vers_min_default;
2025 		}
2026 
2027 		break;
2028 	}
2029 
2030 	/*
2031 	 * In the case of version 4, just NULL proc the server since
2032 	 * there is no MOUNT program.  If this fails, then decrease
2033 	 * vers_to_try and continue on with regular MOUNT program
2034 	 * processing.
2035 	 */
2036 	if (vers_to_try == NFS_V4) {
2037 		int savevers = nfsvers_to_use;
2038 		err_ret_t error;
2039 		int retval;
2040 		SET_ERR_RET(&error, ERR_PROTO_NONE, 0);
2041 
2042 		/* Let's hope for the best */
2043 		nfsvers_to_use = NFS_V4;
2044 		retval =
2045 			getaddr_nfs(args, fshost, nconfp, FALSE,
2046 				    fspath, port, &error, vers_min == NFS_V4);
2047 
2048 		if (retval == RET_OK) {
2049 			*versp = nfsvers_to_use = NFS_V4;
2050 			fstype = MNTTYPE_NFS4;
2051 			args->fh = strdup(fspath);
2052 			if (args->fh == NULL) {
2053 				pr_err(gettext("no memory\n"));
2054 				*versp = nfsvers_to_use = savevers;
2055 				return (RET_ERR);
2056 			}
2057 			return (RET_OK);
2058 		}
2059 		nfsvers_to_use = savevers;
2060 
2061 		vers_to_try--;
2062 		/* If no more versions to try, let the user know. */
2063 		if (vers_to_try < vers_min) {
2064 			return (retval);
2065 		}
2066 
2067 		/*
2068 		 * If we are here, there are more versions to try but
2069 		 * there has been an error of some sort.  If it is not
2070 		 * an RPC error (e.g. host unknown), we just stop and
2071 		 * return the error since the other versions would see
2072 		 * the same error as well.
2073 		 */
2074 		if (retval == RET_ERR && error.error_type != ERR_RPCERROR)
2075 			return (retval);
2076 	}
2077 
2078 	while ((cl = clnt_create_vers(fshost, MOUNTPROG, &outvers,
2079 			vers_min, vers_to_try, "datagram_v")) == NULL) {
2080 		if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST) {
2081 			pr_err(gettext("%s: %s\n"), fshost,
2082 			    clnt_spcreateerror(""));
2083 			return (RET_ERR);
2084 		}
2085 
2086 		/*
2087 		 * We don't want to downgrade version on lost packets
2088 		 */
2089 		if ((rpc_createerr.cf_stat == RPC_TIMEDOUT) ||
2090 			(rpc_createerr.cf_stat == RPC_PMAPFAILURE)) {
2091 			pr_err(gettext("%s: %s\n"), fshost,
2092 			    clnt_spcreateerror(""));
2093 			return (RET_RETRY);
2094 		}
2095 
2096 		/*
2097 		 * back off and try the previous version - patch to the
2098 		 * problem of version numbers not being contigous and
2099 		 * clnt_create_vers failing (SunOS4.1 clients & SGI servers)
2100 		 * The problem happens with most non-Sun servers who
2101 		 * don't support mountd protocol #2. So, in case the
2102 		 * call fails, we re-try the call anyway.
2103 		 */
2104 		vers_to_try--;
2105 		if (vers_to_try < vers_min) {
2106 			if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH) {
2107 				if (nfsvers == 0) {
2108 					pr_err(gettext(
2109 			"%s:%s: no applicable versions of NFS supported\n"),
2110 					    fshost, fspath);
2111 				} else {
2112 					pr_err(gettext(
2113 			"%s:%s: NFS Version %d not supported\n"),
2114 					    fshost, fspath, nfsvers);
2115 				}
2116 				return (RET_ERR);
2117 			}
2118 			if (!printed) {
2119 				pr_err(gettext("%s: %s\n"), fshost,
2120 				    clnt_spcreateerror(""));
2121 				printed = 1;
2122 			}
2123 			return (RET_RETRY);
2124 		}
2125 	}
2126 	if (posix && outvers < MOUNTVERS_POSIX) {
2127 		pr_err(gettext("%s: %s: no pathconf info\n"),
2128 		    fshost, clnt_sperror(cl, ""));
2129 		clnt_destroy(cl);
2130 		return (RET_ERR);
2131 	}
2132 
2133 	if (__clnt_bindresvport(cl) < 0) {
2134 		pr_err(gettext("Couldn't bind to reserved port\n"));
2135 		clnt_destroy(cl);
2136 		return (RET_RETRY);
2137 	}
2138 
2139 	if ((cl->cl_auth = authsys_create_default()) == NULL) {
2140 		pr_err(
2141 		    gettext("Couldn't create default authentication handle\n"));
2142 		clnt_destroy(cl);
2143 		return (RET_RETRY);
2144 	}
2145 
2146 	switch (outvers) {
2147 	case MOUNTVERS:
2148 	case MOUNTVERS_POSIX:
2149 		*versp = nfsvers_to_use = NFS_VERSION;
2150 		rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath,
2151 			(caddr_t)&fspath, xdr_fhstatus, (caddr_t)&fhs, timeout);
2152 		if (rpc_stat != RPC_SUCCESS) {
2153 			pr_err(gettext("%s:%s: server not responding %s\n"),
2154 			    fshost, fspath, clnt_sperror(cl, ""));
2155 			clnt_destroy(cl);
2156 			return (RET_RETRY);
2157 		}
2158 
2159 		if ((errno = fhs.fhs_status) != MNT_OK) {
2160 			if (loud_on_mnt_err) {
2161 			    if (errno == EACCES) {
2162 				pr_err(gettext("%s:%s: access denied\n"),
2163 				    fshost, fspath);
2164 			    } else {
2165 				pr_err(gettext("%s:%s: %s\n"), fshost, fspath,
2166 				    strerror(errno));
2167 			    }
2168 			}
2169 			clnt_destroy(cl);
2170 			return (RET_MNTERR);
2171 		}
2172 		args->fh = malloc(sizeof (fhs.fhstatus_u.fhs_fhandle));
2173 		if (args->fh == NULL) {
2174 			pr_err(gettext("no memory\n"));
2175 			return (RET_ERR);
2176 		}
2177 		memcpy((caddr_t)args->fh, (caddr_t)&fhs.fhstatus_u.fhs_fhandle,
2178 			sizeof (fhs.fhstatus_u.fhs_fhandle));
2179 		if (!errno && posix) {
2180 			rpc_stat = clnt_call(cl, MOUNTPROC_PATHCONF,
2181 				xdr_dirpath, (caddr_t)&fspath, xdr_ppathcnf,
2182 				(caddr_t)&p, timeout);
2183 			if (rpc_stat != RPC_SUCCESS) {
2184 				pr_err(gettext(
2185 				    "%s:%s: server not responding %s\n"),
2186 				    fshost, fspath, clnt_sperror(cl, ""));
2187 				free(args->fh);
2188 				clnt_destroy(cl);
2189 				return (RET_RETRY);
2190 			}
2191 			if (_PC_ISSET(_PC_ERROR, p.pc_mask)) {
2192 				pr_err(gettext(
2193 				    "%s:%s: no pathconf info\n"),
2194 				    fshost, fspath);
2195 				free(args->fh);
2196 				clnt_destroy(cl);
2197 				return (RET_ERR);
2198 			}
2199 			args->flags |= NFSMNT_POSIX;
2200 			args->pathconf = malloc(sizeof (p));
2201 			if (args->pathconf == NULL) {
2202 				pr_err(gettext("no memory\n"));
2203 				free(args->fh);
2204 				clnt_destroy(cl);
2205 				return (RET_ERR);
2206 			}
2207 			memcpy((caddr_t)args->pathconf, (caddr_t)&p,
2208 				sizeof (p));
2209 		}
2210 		break;
2211 
2212 	case MOUNTVERS3:
2213 		*versp = nfsvers_to_use = NFS_V3;
2214 		rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath,
2215 				(caddr_t)&fspath,
2216 				xdr_mountres3, (caddr_t)&mountres3, timeout);
2217 		if (rpc_stat != RPC_SUCCESS) {
2218 			pr_err(gettext("%s:%s: server not responding %s\n"),
2219 			    fshost, fspath, clnt_sperror(cl, ""));
2220 			clnt_destroy(cl);
2221 			return (RET_RETRY);
2222 		}
2223 
2224 		/*
2225 		 * Assume here that most of the MNT3ERR_*
2226 		 * codes map into E* errors.
2227 		 */
2228 		if ((errno = mountres3.fhs_status) != MNT_OK) {
2229 		    if (loud_on_mnt_err) {
2230 			switch (errno) {
2231 			case MNT3ERR_NAMETOOLONG:
2232 				msg = "path name is too long";
2233 				break;
2234 			case MNT3ERR_NOTSUPP:
2235 				msg = "operation not supported";
2236 				break;
2237 			case MNT3ERR_SERVERFAULT:
2238 				msg = "server fault";
2239 				break;
2240 			default:
2241 				msg = strerror(errno);
2242 				break;
2243 			}
2244 			pr_err(gettext("%s:%s: %s\n"), fshost, fspath, msg);
2245 		    }
2246 		    clnt_destroy(cl);
2247 		    return (RET_MNTERR);
2248 		}
2249 
2250 		fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
2251 		if (fh3p == NULL) {
2252 			pr_err(gettext("no memory\n"));
2253 			return (RET_ERR);
2254 		}
2255 		fh3p->fh3_length =
2256 			mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len;
2257 		(void) memcpy(fh3p->fh3_u.data,
2258 			mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val,
2259 			fh3p->fh3_length);
2260 		args->fh = (caddr_t)fh3p;
2261 		fstype = MNTTYPE_NFS3;
2262 
2263 		/*
2264 		 * Check the security flavor to be used.
2265 		 *
2266 		 * If "secure" or "sec=flavor" is a mount
2267 		 * option, check if the server supports the "flavor".
2268 		 * If the server does not support the flavor, return
2269 		 * error.
2270 		 *
2271 		 * If no mount option is given then use the first supported
2272 		 * security flavor (by the client) in the auth list returned
2273 		 * from the server.
2274 		 *
2275 		 */
2276 		auths =
2277 		mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val;
2278 		count =
2279 		mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len;
2280 
2281 		if (sec_opt) {
2282 			for (i = 0; i < count; i++) {
2283 				if (auths[i] == nfs_sec.sc_nfsnum)
2284 				    break;
2285 			}
2286 			if (i >= count) {
2287 				goto autherr;
2288 			}
2289 		} else {
2290 		    if (count > 0) {
2291 			for (i = 0; i < count; i++) {
2292 			    if (!nfs_getseconfig_bynumber(auths[i], &nfs_sec)) {
2293 				sec_opt++;
2294 				break;
2295 			    }
2296 			}
2297 			if (i >= count) {
2298 			    goto autherr;
2299 			}
2300 		    }
2301 		}
2302 		break;
2303 	default:
2304 		pr_err(gettext("%s:%s: Unknown MOUNT version %d\n"),
2305 		    fshost, fspath, outvers);
2306 		clnt_destroy(cl);
2307 		return (RET_ERR);
2308 	}
2309 
2310 	clnt_destroy(cl);
2311 	return (RET_OK);
2312 
2313 autherr:
2314 	pr_err(gettext(
2315 		"security mode does not match the server exporting %s:%s\n"),
2316 		fshost, fspath);
2317 	clnt_destroy(cl);
2318 	return (RET_ERR);
2319 }
2320 
2321 /*
2322  * Fill in the address for the server's NFS service and
2323  * fill in a knetconfig structure for the transport that
2324  * the service is available on.
2325  */
2326 static int
2327 getaddr_nfs(struct nfs_args *args, char *fshost, struct netconfig **nconfp,
2328 	    bool_t get_pubfh, char *fspath, ushort_t port, err_ret_t *error,
2329 	    bool_t print_rpcerror)
2330 {
2331 	struct stat sb;
2332 	struct netconfig *nconf;
2333 	struct knetconfig *knconfp;
2334 	static int printed = 0;
2335 	struct t_info tinfo;
2336 	err_ret_t addr_error;
2337 
2338 	SET_ERR_RET(error, ERR_PROTO_NONE, 0);
2339 	SET_ERR_RET(&addr_error, ERR_PROTO_NONE, 0);
2340 
2341 	if (nfs_proto) {
2342 		/*
2343 		 * If a proto is specified and its rdma try this. The kernel
2344 		 * will later do the reachablity test and fail form there
2345 		 * if rdma transport is not available to kernel rpc
2346 		 */
2347 		if (strcmp(nfs_proto, "rdma") == 0) {
2348 			args->addr = get_addr(fshost, NFS_PROGRAM,
2349 			    nfsvers_to_use, nconfp, NULL, port, &tinfo,
2350 			    &args->fh, get_pubfh, fspath, &addr_error);
2351 
2352 			args->flags |= NFSMNT_DORDMA;
2353 		} else {
2354 			args->addr = get_addr(fshost, NFS_PROGRAM,
2355 			    nfsvers_to_use, nconfp, nfs_proto, port, &tinfo,
2356 			    &args->fh, get_pubfh, fspath, &addr_error);
2357 		}
2358 	} else {
2359 		args->addr = get_addr(fshost, NFS_PROGRAM, nfsvers_to_use,
2360 		    nconfp, nfs_proto, port, &tinfo, &args->fh, get_pubfh,
2361 		    fspath, &addr_error);
2362 		/*
2363 		 * If no proto is specified set this flag.
2364 		 * Kernel mount code will try to use RDMA if its on the
2365 		 * system, otherwise it will keep on using the protocol
2366 		 * selected here, through the above get_addr call.
2367 		 */
2368 		if (nfs_proto == NULL)
2369 			args->flags |= NFSMNT_TRYRDMA;
2370 	}
2371 
2372 	if (args->addr == NULL) {
2373 		/*
2374 		 * We could have failed because the server had no public
2375 		 * file handle support. So don't print a message and don't
2376 		 * retry.
2377 		 */
2378 		if (get_pubfh == TRUE)
2379 			return (RET_ERR);
2380 
2381 		if (!printed) {
2382 			switch (addr_error.error_type) {
2383 			case 0:
2384 				printed = 1;
2385 				break;
2386 			case ERR_RPCERROR:
2387 				if (!print_rpcerror)
2388 					/* no error print at this time */
2389 					break;
2390 				pr_err(gettext("%s NFS service not"
2391 					    " available %s\n"), fshost,
2392 				    clnt_sperrno(addr_error.error_value));
2393 				printed = 1;
2394 				break;
2395 			case ERR_NETPATH:
2396 				pr_err(gettext("%s: Error in NETPATH.\n"),
2397 					fshost);
2398 				printed = 1;
2399 				break;
2400 			case ERR_PROTO_INVALID:
2401 				pr_err(gettext("%s: NFS service does not"
2402 					" recognize protocol: %s.\n"), fshost,
2403 					nfs_proto);
2404 				printed = 1;
2405 				break;
2406 			case ERR_PROTO_UNSUPP:
2407 				if (nfsvers || nfsvers_to_use == NFS_VERSMIN) {
2408 					/*
2409 					 * Don't set "printed" here. Since we
2410 					 * have to keep checking here till we
2411 					 * exhaust transport errors on all vers.
2412 					 *
2413 					 * Print this message if:
2414 					 * 1. After we have tried all versions
2415 					 *    of NFS and none support the asked
2416 					 *    transport.
2417 					 *
2418 					 * 2. If a version is specified and it
2419 					 *    does'nt support the asked
2420 					 *    transport.
2421 					 *
2422 					 * Otherwise we decrement the version
2423 					 * and retry below.
2424 					 */
2425 					pr_err(gettext("%s: NFS service does"
2426 						" not support protocol: %s.\n"),
2427 						fshost, nfs_proto);
2428 				}
2429 				break;
2430 			case ERR_NOHOST:
2431 				pr_err("%s: %s\n", fshost, "Unknown host");
2432 				printed = 1;
2433 				break;
2434 			default:
2435 				/* case ERR_PROTO_NONE falls through */
2436 				pr_err(gettext("%s: NFS service not responding"
2437 					"\n"), fshost);
2438 				printed = 1;
2439 				break;
2440 			}
2441 		}
2442 		SET_ERR_RET(error,
2443 			addr_error.error_type, addr_error.error_value);
2444 		if (addr_error.error_type == ERR_PROTO_NONE)
2445 			return (RET_RETRY);
2446 		else if (addr_error.error_type == ERR_RPCERROR &&
2447 			! IS_UNRECOVERABLE_RPC(addr_error.error_value)) {
2448 			return (RET_RETRY);
2449 		} else if (nfsvers == 0 && addr_error.error_type ==
2450 			ERR_PROTO_UNSUPP && nfsvers_to_use != NFS_VERSMIN) {
2451 			/*
2452 			 * If no version is specified, and the error is due
2453 			 * to an unsupported transport, then decrement the
2454 			 * version and retry.
2455 			 */
2456 			return (RET_RETRY);
2457 		} else
2458 			return (RET_ERR);
2459 	}
2460 	nconf = *nconfp;
2461 
2462 	if (stat(nconf->nc_device, &sb) < 0) {
2463 		pr_err(gettext("getaddr_nfs: couldn't stat: %s: %s\n"),
2464 		    nconf->nc_device, strerror(errno));
2465 		return (RET_ERR);
2466 	}
2467 
2468 	knconfp = (struct knetconfig *)malloc(sizeof (*knconfp));
2469 	if (!knconfp) {
2470 		pr_err(gettext("no memory\n"));
2471 		return (RET_ERR);
2472 	}
2473 	knconfp->knc_semantics = nconf->nc_semantics;
2474 	knconfp->knc_protofmly = nconf->nc_protofmly;
2475 	knconfp->knc_proto = nconf->nc_proto;
2476 	knconfp->knc_rdev = sb.st_rdev;
2477 
2478 	/* make sure we don't overload the transport */
2479 	if (tinfo.tsdu > 0 && tinfo.tsdu < NFS_MAXDATA + NFS_RPC_HDR) {
2480 		args->flags |= (NFSMNT_RSIZE | NFSMNT_WSIZE);
2481 		if (args->rsize == 0 || args->rsize > tinfo.tsdu - NFS_RPC_HDR)
2482 			args->rsize = tinfo.tsdu - NFS_RPC_HDR;
2483 		if (args->wsize == 0 || args->wsize > tinfo.tsdu - NFS_RPC_HDR)
2484 			args->wsize = tinfo.tsdu - NFS_RPC_HDR;
2485 	}
2486 
2487 	args->flags |= NFSMNT_KNCONF;
2488 	args->knconf = knconfp;
2489 	return (RET_OK);
2490 }
2491 
2492 static int
2493 retry(struct mnttab *mntp, int ro)
2494 {
2495 	int delay = 5;
2496 	int count = retries;
2497 	int r;
2498 
2499 	/*
2500 	 * Please see comments on nfsretry_vers in the beginning of this file
2501 	 * and in main() routine.
2502 	 */
2503 
2504 	if (bg) {
2505 		if (fork() > 0)
2506 			return (RET_OK);
2507 		pr_err(gettext("backgrounding: %s\n"), mntp->mnt_mountp);
2508 		backgrounded = 1;
2509 	} else {
2510 		if (!nfsretry_vers)
2511 			pr_err(gettext("retrying: %s\n"), mntp->mnt_mountp);
2512 	}
2513 
2514 	while (count--) {
2515 		if ((r = mount_nfs(mntp, ro, NULL)) == RET_OK) {
2516 			pr_err(gettext("%s: mounted OK\n"), mntp->mnt_mountp);
2517 			return (RET_OK);
2518 		}
2519 		if (r != RET_RETRY)
2520 			break;
2521 
2522 		if (count > 0) {
2523 		    (void) sleep(delay);
2524 		    delay *= 2;
2525 		    if (delay > 120)
2526 			    delay = 120;
2527 		}
2528 	}
2529 
2530 	if (!nfsretry_vers)
2531 		pr_err(gettext("giving up on: %s\n"), mntp->mnt_mountp);
2532 
2533 	return (RET_ERR);
2534 }
2535 
2536 /*
2537  * Read the /etc/default/nfs configuration file to determine if the
2538  * client has been configured for a new min/max for the NFS version to
2539  * use.
2540  */
2541 static void
2542 read_default(void)
2543 {
2544 	char *defval;
2545 	int errno;
2546 	int tmp;
2547 
2548 	/* Fail silently if error in opening the default nfs config file */
2549 	if ((defopen(NFSADMIN)) == 0) {
2550 		if ((defval = defread("NFS_CLIENT_VERSMIN=")) != NULL) {
2551 			errno = 0;
2552 			tmp = strtol(defval, (char **)NULL, 10);
2553 			if (errno == 0) {
2554 				vers_min_default = tmp;
2555 			}
2556 		}
2557 		if ((defval = defread("NFS_CLIENT_VERSMAX=")) != NULL) {
2558 			errno = 0;
2559 			tmp = strtol(defval, (char **)NULL, 10);
2560 			if (errno == 0) {
2561 				vers_max_default = tmp;
2562 			}
2563 		}
2564 		/* close defaults file */
2565 		defopen(NULL);
2566 	}
2567 }
2568 
2569 static void
2570 sigusr1(int s)
2571 {
2572 }
2573