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