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