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