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