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