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