xref: /onnv-gate/usr/src/cmd/fs.d/nfs/mount/mount.c (revision 12782:39cdfef4d699)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53378Srica  * Common Development and Distribution License (the "License").
63378Srica  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
215655Sth199096 
220Sstevel@tonic-gate /*
23*12782SMarcel.Telka@Sun.COM  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
270Sstevel@tonic-gate /*	  All Rights Reserved  	*/
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
310Sstevel@tonic-gate  * The Regents of the University of California
320Sstevel@tonic-gate  * All Rights Reserved
330Sstevel@tonic-gate  *
340Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
350Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
360Sstevel@tonic-gate  * contributors.
370Sstevel@tonic-gate  */
380Sstevel@tonic-gate 
390Sstevel@tonic-gate /*
400Sstevel@tonic-gate  * nfs mount
410Sstevel@tonic-gate  */
420Sstevel@tonic-gate 
430Sstevel@tonic-gate #define	NFSCLIENT
440Sstevel@tonic-gate #include <locale.h>
450Sstevel@tonic-gate #include <stdio.h>
460Sstevel@tonic-gate #include <string.h>
470Sstevel@tonic-gate #include <memory.h>
480Sstevel@tonic-gate #include <stdarg.h>
490Sstevel@tonic-gate #include <unistd.h>
500Sstevel@tonic-gate #include <ctype.h>
510Sstevel@tonic-gate #include <stdlib.h>
520Sstevel@tonic-gate #include <signal.h>
530Sstevel@tonic-gate #include <sys/param.h>
540Sstevel@tonic-gate #include <rpc/rpc.h>
550Sstevel@tonic-gate #include <errno.h>
560Sstevel@tonic-gate #include <sys/stat.h>
570Sstevel@tonic-gate #include <netdb.h>
580Sstevel@tonic-gate #include <sys/mount.h>
590Sstevel@tonic-gate #include <sys/mntent.h>
600Sstevel@tonic-gate #include <sys/mnttab.h>
610Sstevel@tonic-gate #include <nfs/nfs.h>
620Sstevel@tonic-gate #include <nfs/mount.h>
630Sstevel@tonic-gate #include <rpcsvc/mount.h>
640Sstevel@tonic-gate #include <sys/pathconf.h>
650Sstevel@tonic-gate #include <netdir.h>
660Sstevel@tonic-gate #include <netconfig.h>
670Sstevel@tonic-gate #include <sys/sockio.h>
680Sstevel@tonic-gate #include <net/if.h>
690Sstevel@tonic-gate #include <syslog.h>
700Sstevel@tonic-gate #include <fslib.h>
710Sstevel@tonic-gate #include <deflt.h>
720Sstevel@tonic-gate #include <sys/wait.h>
730Sstevel@tonic-gate #include "replica.h"
740Sstevel@tonic-gate #include <netinet/in.h>
750Sstevel@tonic-gate #include <nfs/nfs_sec.h>
760Sstevel@tonic-gate #include <rpcsvc/daemon_utils.h>
770Sstevel@tonic-gate #include <priv.h>
783378Srica #include <tsol/label.h>
790Sstevel@tonic-gate #include "nfs_subr.h"
800Sstevel@tonic-gate #include "webnfs.h"
810Sstevel@tonic-gate #include <rpcsvc/nfs4_prot.h>
82*12782SMarcel.Telka@Sun.COM #include <limits.h>
830Sstevel@tonic-gate 
845302Sth199096 #include <nfs/nfssys.h>
855302Sth199096 extern int _nfssys(enum nfssys_op, void *);
865302Sth199096 
870Sstevel@tonic-gate #ifndef	NFS_VERSMAX
880Sstevel@tonic-gate #define	NFS_VERSMAX	4
890Sstevel@tonic-gate #endif
900Sstevel@tonic-gate #ifndef	NFS_VERSMIN
910Sstevel@tonic-gate #define	NFS_VERSMIN	2
920Sstevel@tonic-gate #endif
930Sstevel@tonic-gate 
940Sstevel@tonic-gate #define	RET_OK		0
950Sstevel@tonic-gate #define	RET_RETRY	32
960Sstevel@tonic-gate #define	RET_ERR		33
970Sstevel@tonic-gate #define	RET_MNTERR	1000
980Sstevel@tonic-gate #define	ERR_PROTO_NONE		0
990Sstevel@tonic-gate #define	ERR_PROTO_INVALID	901
1000Sstevel@tonic-gate #define	ERR_PROTO_UNSUPP	902
1010Sstevel@tonic-gate #define	ERR_NETPATH		903
1020Sstevel@tonic-gate #define	ERR_NOHOST		904
1030Sstevel@tonic-gate #define	ERR_RPCERROR		905
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate typedef struct err_ret {
1060Sstevel@tonic-gate 	int error_type;
1070Sstevel@tonic-gate 	int error_value;
1080Sstevel@tonic-gate } err_ret_t;
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate #define	SET_ERR_RET(errst, etype, eval) \
1110Sstevel@tonic-gate 	if (errst) { \
1120Sstevel@tonic-gate 		(errst)->error_type = etype; \
1130Sstevel@tonic-gate 		(errst)->error_value = eval; \
1140Sstevel@tonic-gate 	}
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate /* number of transports to try */
1170Sstevel@tonic-gate #define	MNT_PREF_LISTLEN	2
1180Sstevel@tonic-gate #define	FIRST_TRY		1
1190Sstevel@tonic-gate #define	SECOND_TRY		2
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate #define	BIGRETRY	10000
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate /* maximum length of RPC header for NFS messages */
1240Sstevel@tonic-gate #define	NFS_RPC_HDR	432
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate #define	NFS_ARGS_EXTB_secdata(args, secdata) \
1270Sstevel@tonic-gate 	{ (args)->nfs_args_ext = NFS_ARGS_EXTB, \
1280Sstevel@tonic-gate 	(args)->nfs_ext_u.nfs_extB.secdata = secdata; }
1290Sstevel@tonic-gate 
1305655Sth199096 extern int __clnt_bindresvport(CLIENT *);
1310Sstevel@tonic-gate extern char *nfs_get_qop_name();
1320Sstevel@tonic-gate extern AUTH * nfs_create_ah();
1330Sstevel@tonic-gate extern enum snego_stat nfs_sec_nego();
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate static void usage(void);
1360Sstevel@tonic-gate static int retry(struct mnttab *, int);
1370Sstevel@tonic-gate static int set_args(int *, struct nfs_args *, char *, struct mnttab *);
1380Sstevel@tonic-gate static int get_fh_via_pub(struct nfs_args *, char *, char *, bool_t, bool_t,
1390Sstevel@tonic-gate 	int *, struct netconfig **, ushort_t);
1400Sstevel@tonic-gate static int get_fh(struct nfs_args *, char *, char *, int *, bool_t,
1410Sstevel@tonic-gate 	struct netconfig **, ushort_t);
1420Sstevel@tonic-gate static int make_secure(struct nfs_args *, char *, struct netconfig *,
1430Sstevel@tonic-gate 	bool_t, rpcvers_t);
144489Soa138391 static int mount_nfs(struct mnttab *, int, err_ret_t *);
1450Sstevel@tonic-gate static int getaddr_nfs(struct nfs_args *, char *, struct netconfig **,
1460Sstevel@tonic-gate 		    bool_t, char *, ushort_t, err_ret_t *, bool_t);
1470Sstevel@tonic-gate static void pr_err(const char *fmt, ...);
1480Sstevel@tonic-gate static void usage(void);
1490Sstevel@tonic-gate static struct netbuf *get_addr(char *, rpcprog_t, rpcvers_t,
1500Sstevel@tonic-gate 	struct netconfig **, char *, ushort_t, struct t_info *,
1510Sstevel@tonic-gate 	caddr_t *, bool_t, char *, err_ret_t *);
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate static struct netbuf *get_the_addr(char *, rpcprog_t, rpcvers_t,
1540Sstevel@tonic-gate 	struct netconfig *, ushort_t, struct t_info *, caddr_t *,
1550Sstevel@tonic-gate 	bool_t, char *, err_ret_t *);
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate extern int self_check(char *);
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate static void read_default(void);
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate static char typename[64];
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate static int bg = 0;
1640Sstevel@tonic-gate static int backgrounded = 0;
1650Sstevel@tonic-gate static int posix = 0;
1660Sstevel@tonic-gate static int retries = BIGRETRY;
1670Sstevel@tonic-gate static ushort_t nfs_port = 0;
1680Sstevel@tonic-gate static char *nfs_proto = NULL;
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate static int mflg = 0;
1710Sstevel@tonic-gate static int Oflg = 0;	/* Overlay mounts */
1720Sstevel@tonic-gate static int qflg = 0;	/* quiet - don't print warnings on bad options */
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate static char *fstype = MNTTYPE_NFS;
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate static seconfig_t nfs_sec;
1770Sstevel@tonic-gate static int sec_opt = 0;	/* any security option ? */
1780Sstevel@tonic-gate static bool_t snego_done;
1790Sstevel@tonic-gate static void sigusr1(int);
1800Sstevel@tonic-gate 
1815302Sth199096 extern void set_nfsv4_ephemeral_mount_to(void);
1825302Sth199096 
1830Sstevel@tonic-gate /*
1840Sstevel@tonic-gate  * list of support services needed
1850Sstevel@tonic-gate  */
1860Sstevel@tonic-gate static char	*service_list[] = { STATD, LOCKD, NULL };
1870Sstevel@tonic-gate static char	*service_list_v4[] = { STATD, LOCKD, NFS4CBD, NFSMAPID, NULL };
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate /*
1900Sstevel@tonic-gate  * These two variables control the NFS version number to be used.
1910Sstevel@tonic-gate  *
1920Sstevel@tonic-gate  * nfsvers defaults to 0 which means to use the highest number that
1930Sstevel@tonic-gate  * both the client and the server support.  It can also be set to
1940Sstevel@tonic-gate  * a particular value, either 2, 3, or 4 to indicate the version
1950Sstevel@tonic-gate  * number of choice.  If the server (or the client) do not support
1960Sstevel@tonic-gate  * the version indicated, then the mount attempt will be failed.
1970Sstevel@tonic-gate  *
1980Sstevel@tonic-gate  * nfsvers_to_use is the actual version number found to use.  It
1990Sstevel@tonic-gate  * is determined in get_fh by pinging the various versions of the
2000Sstevel@tonic-gate  * NFS service on the server to see which responds positively.
201489Soa138391  *
202489Soa138391  * nfsretry_vers is the version number set when we retry the mount
203489Soa138391  * command with the version decremented from nfsvers_to_use.
204489Soa138391  * nfsretry_vers is set from nfsvers_to_use when we retry the mount
205489Soa138391  * for errors other than RPC errors; it helps un know why we are
206489Soa138391  * retrying. It is an indication that the retry is due to
207489Soa138391  * non-RPC errors.
2080Sstevel@tonic-gate  */
2090Sstevel@tonic-gate static rpcvers_t nfsvers = 0;
2100Sstevel@tonic-gate static rpcvers_t nfsvers_to_use = 0;
211489Soa138391 static rpcvers_t nfsretry_vers = 0;
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate /*
2140Sstevel@tonic-gate  * There are the defaults (range) for the client when determining
2150Sstevel@tonic-gate  * which NFS version to use when probing the server (see above).
2160Sstevel@tonic-gate  * These will only be used when the vers mount option is not used and
2170Sstevel@tonic-gate  * these may be reset if /etc/default/nfs is configured to do so.
2180Sstevel@tonic-gate  */
2190Sstevel@tonic-gate static rpcvers_t vers_max_default = NFS_VERSMAX_DEFAULT;
2200Sstevel@tonic-gate static rpcvers_t vers_min_default = NFS_VERSMIN_DEFAULT;
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate /*
2230Sstevel@tonic-gate  * This variable controls whether to try the public file handle.
2240Sstevel@tonic-gate  */
2250Sstevel@tonic-gate static bool_t public_opt;
2260Sstevel@tonic-gate 
227249Sjwahlig int
228249Sjwahlig main(int argc, char *argv[])
2290Sstevel@tonic-gate {
2300Sstevel@tonic-gate 	struct mnttab mnt;
2310Sstevel@tonic-gate 	extern char *optarg;
2320Sstevel@tonic-gate 	extern int optind;
2330Sstevel@tonic-gate 	char optbuf[MAX_MNTOPT_STR];
2340Sstevel@tonic-gate 	int ro = 0;
2350Sstevel@tonic-gate 	int r;
2360Sstevel@tonic-gate 	int c;
2370Sstevel@tonic-gate 	char *myname;
238489Soa138391 	err_ret_t retry_error;
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
2410Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
2420Sstevel@tonic-gate #define	TEXT_DOMAIN	"SYS_TEST"
2430Sstevel@tonic-gate #endif
2440Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 	myname = strrchr(argv[0], '/');
2470Sstevel@tonic-gate 	myname = myname ? myname + 1 : argv[0];
2480Sstevel@tonic-gate 	(void) snprintf(typename, sizeof (typename), "%s %s",
2490Sstevel@tonic-gate 	    MNTTYPE_NFS, myname);
2500Sstevel@tonic-gate 	argv[0] = typename;
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate 	mnt.mnt_mntopts = optbuf;
2530Sstevel@tonic-gate 	(void) strcpy(optbuf, "rw");
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	/*
2560Sstevel@tonic-gate 	 * Set options
2570Sstevel@tonic-gate 	 */
2580Sstevel@tonic-gate 	while ((c = getopt(argc, argv, "ro:mOq")) != EOF) {
2590Sstevel@tonic-gate 		switch (c) {
2600Sstevel@tonic-gate 		case 'r':
2610Sstevel@tonic-gate 			ro++;
2620Sstevel@tonic-gate 			break;
2630Sstevel@tonic-gate 		case 'o':
2640Sstevel@tonic-gate 			if (strlen(optarg) >= MAX_MNTOPT_STR) {
2650Sstevel@tonic-gate 				pr_err(gettext("option string too long"));
2660Sstevel@tonic-gate 				return (RET_ERR);
2670Sstevel@tonic-gate 			}
2680Sstevel@tonic-gate 			(void) strcpy(mnt.mnt_mntopts, optarg);
2690Sstevel@tonic-gate #ifdef LATER					/* XXX */
2700Sstevel@tonic-gate 			if (strstr(optarg, MNTOPT_REMOUNT)) {
2710Sstevel@tonic-gate 				/*
2720Sstevel@tonic-gate 				 * If remount is specified, only rw is allowed.
2730Sstevel@tonic-gate 				 */
2740Sstevel@tonic-gate 				if ((strcmp(optarg, MNTOPT_REMOUNT) != 0) &&
2750Sstevel@tonic-gate 				    (strcmp(optarg, "remount,rw") != 0) &&
2760Sstevel@tonic-gate 				    (strcmp(optarg, "rw,remount") != 0)) {
2770Sstevel@tonic-gate 					pr_err(gettext("Invalid options\n"));
2780Sstevel@tonic-gate 					exit(RET_ERR);
2790Sstevel@tonic-gate 				}
2800Sstevel@tonic-gate 			}
2810Sstevel@tonic-gate #endif /* LATER */				/* XXX */
2820Sstevel@tonic-gate 			break;
2830Sstevel@tonic-gate 		case 'm':
2840Sstevel@tonic-gate 			mflg++;
2850Sstevel@tonic-gate 			break;
2860Sstevel@tonic-gate 		case 'O':
2870Sstevel@tonic-gate 			Oflg++;
2880Sstevel@tonic-gate 			break;
2890Sstevel@tonic-gate 		case 'q':
2900Sstevel@tonic-gate 			qflg++;
2910Sstevel@tonic-gate 			break;
2920Sstevel@tonic-gate 		default:
2930Sstevel@tonic-gate 			usage();
2940Sstevel@tonic-gate 			exit(RET_ERR);
2950Sstevel@tonic-gate 		}
2960Sstevel@tonic-gate 	}
2970Sstevel@tonic-gate 	if (argc - optind != 2) {
2980Sstevel@tonic-gate 		usage();
2990Sstevel@tonic-gate 		exit(RET_ERR);
3000Sstevel@tonic-gate 	}
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	mnt.mnt_special = argv[optind];
3030Sstevel@tonic-gate 	mnt.mnt_mountp = argv[optind+1];
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate 	if (!priv_ineffect(PRIV_SYS_MOUNT) ||
3060Sstevel@tonic-gate 	    !priv_ineffect(PRIV_NET_PRIVADDR)) {
3070Sstevel@tonic-gate 		pr_err(gettext("insufficient privileges\n"));
3080Sstevel@tonic-gate 		exit(RET_ERR);
3090Sstevel@tonic-gate 	}
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate 	/*
3123378Srica 	 * On a labeled system, allow read-down nfs mounts if privileged
3133378Srica 	 * (PRIV_NET_MAC_AWARE) to do so.  Otherwise, ignore the error
3143378Srica 	 * and "mount equal label only" behavior will result.
3153378Srica 	 */
3163378Srica 	if (is_system_labeled())
3173378Srica 		(void) setpflags(NET_MAC_AWARE, 1);
3183378Srica 
3193378Srica 	/*
3200Sstevel@tonic-gate 	 * Read the defaults file to see if the min/max versions have
3210Sstevel@tonic-gate 	 * been set and therefore would override the encoded defaults.
3220Sstevel@tonic-gate 	 * Then check to make sure that if they were set that the
3230Sstevel@tonic-gate 	 * values are reasonable.
3240Sstevel@tonic-gate 	 */
3250Sstevel@tonic-gate 	read_default();
3260Sstevel@tonic-gate 	if (vers_min_default > vers_max_default ||
3275302Sth199096 	    vers_min_default < NFS_VERSMIN ||
3285302Sth199096 	    vers_max_default > NFS_VERSMAX) {
3290Sstevel@tonic-gate 		pr_err("%s %s\n%s %s\n",
3305302Sth199096 		    gettext("Incorrect configuration of client\'s"),
3315302Sth199096 		    NFSADMIN,
3325302Sth199096 		    gettext("NFS_CLIENT_VERSMIN or NFS_CLIENT_VERSMAX"),
3335302Sth199096 		    gettext("is either out of range or overlaps."));
3340Sstevel@tonic-gate 	}
3350Sstevel@tonic-gate 
336489Soa138391 	SET_ERR_RET(&retry_error, ERR_PROTO_NONE, 0);
337489Soa138391 	r = mount_nfs(&mnt, ro, &retry_error);
338489Soa138391 	if (r == RET_RETRY && retries) {
339489Soa138391 		/*
340489Soa138391 		 * Check the error code from the last mount attempt if it was
341489Soa138391 		 * an RPC error, then retry as is. Otherwise we retry with the
342489Soa138391 		 * nfsretry_vers set. It is set by decrementing nfsvers_to_use.
343489Soa138391 		 * If we are retrying with nfsretry_vers then we don't print any
344489Soa138391 		 * retry messages, since we are not retrying due to an RPC
345489Soa138391 		 * error.
346489Soa138391 		 */
347489Soa138391 		if (retry_error.error_type) {
348489Soa138391 			if (retry_error.error_type != ERR_RPCERROR) {
349489Soa138391 				nfsretry_vers = nfsvers_to_use =
350489Soa138391 				    nfsvers_to_use - 1;
351489Soa138391 				if (nfsretry_vers < NFS_VERSMIN)
352489Soa138391 					return (r);
353489Soa138391 			}
354489Soa138391 		}
355489Soa138391 
3560Sstevel@tonic-gate 		r = retry(&mnt, ro);
357489Soa138391 	}
3580Sstevel@tonic-gate 	/*
3590Sstevel@tonic-gate 	 * exit(r);
3600Sstevel@tonic-gate 	 */
3610Sstevel@tonic-gate 	return (r);
3620Sstevel@tonic-gate }
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate static void
3650Sstevel@tonic-gate pr_err(const char *fmt, ...)
3660Sstevel@tonic-gate {
3670Sstevel@tonic-gate 	va_list ap;
3680Sstevel@tonic-gate 
3690Sstevel@tonic-gate 	va_start(ap, fmt);
3700Sstevel@tonic-gate 	if (backgrounded != 0) {
3710Sstevel@tonic-gate 		(void) vsyslog(LOG_ERR, fmt, ap);
3720Sstevel@tonic-gate 	} else {
3730Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: ", typename);
3740Sstevel@tonic-gate 		(void) vfprintf(stderr, fmt, ap);
3750Sstevel@tonic-gate 		(void) fflush(stderr);
3760Sstevel@tonic-gate 	}
3770Sstevel@tonic-gate 	va_end(ap);
3780Sstevel@tonic-gate }
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate static void
3810Sstevel@tonic-gate usage()
3820Sstevel@tonic-gate {
3830Sstevel@tonic-gate 	(void) fprintf(stderr,
3840Sstevel@tonic-gate 	    gettext("Usage: nfs mount [-r] [-o opts] [server:]path dir\n"));
3850Sstevel@tonic-gate 	exit(RET_ERR);
3860Sstevel@tonic-gate }
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate static int
389489Soa138391 mount_nfs(struct mnttab *mntp, int ro, err_ret_t *retry_error)
3900Sstevel@tonic-gate {
3910Sstevel@tonic-gate 	struct nfs_args *args = NULL, *argp = NULL, *prev_argp = NULL;
3920Sstevel@tonic-gate 	struct netconfig *nconf = NULL;
3930Sstevel@tonic-gate 	struct replica *list = NULL;
3940Sstevel@tonic-gate 	int mntflags = 0;
3950Sstevel@tonic-gate 	int i, r, n;
3960Sstevel@tonic-gate 	int oldvers = 0, vers = 0;
3970Sstevel@tonic-gate 	int last_error = RET_OK;
3980Sstevel@tonic-gate 	int replicated = 0;
3990Sstevel@tonic-gate 	char *p;
4000Sstevel@tonic-gate 	bool_t url;
4010Sstevel@tonic-gate 	bool_t use_pubfh;
4020Sstevel@tonic-gate 	char *special = NULL;
4030Sstevel@tonic-gate 	char *oldpath = NULL;
4040Sstevel@tonic-gate 	char *newpath = NULL;
4050Sstevel@tonic-gate 	char *service;
4060Sstevel@tonic-gate 	pid_t pi;
4070Sstevel@tonic-gate 	struct flock f;
4080Sstevel@tonic-gate 	char *saveopts = NULL;
4090Sstevel@tonic-gate 	char **sl = NULL;
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate 	mntp->mnt_fstype = MNTTYPE_NFS;
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 	if (ro) {
4140Sstevel@tonic-gate 		mntflags |= MS_RDONLY;
4150Sstevel@tonic-gate 		/* convert "rw"->"ro" */
4160Sstevel@tonic-gate 		if (p = strstr(mntp->mnt_mntopts, "rw")) {
4170Sstevel@tonic-gate 			if (*(p+2) == ',' || *(p+2) == '\0')
4180Sstevel@tonic-gate 				*(p+1) = 'o';
4190Sstevel@tonic-gate 		}
4200Sstevel@tonic-gate 	}
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate 	if (Oflg)
4230Sstevel@tonic-gate 		mntflags |= MS_OVERLAY;
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate 	list = parse_replica(mntp->mnt_special, &n);
4260Sstevel@tonic-gate 	if (list == NULL) {
4270Sstevel@tonic-gate 		if (n < 0)
4280Sstevel@tonic-gate 			pr_err(gettext("nfs file system; use [host:]path\n"));
4290Sstevel@tonic-gate 		else
4300Sstevel@tonic-gate 			pr_err(gettext("no memory\n"));
4310Sstevel@tonic-gate 		return (RET_ERR);
4320Sstevel@tonic-gate 	}
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 	replicated = (n > 1);
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	/*
4370Sstevel@tonic-gate 	 * There are some free() calls at the bottom of this loop, so be
4380Sstevel@tonic-gate 	 * careful about adding continue statements.
4390Sstevel@tonic-gate 	 */
4400Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
4410Sstevel@tonic-gate 		char *path;
4420Sstevel@tonic-gate 		char *host;
4430Sstevel@tonic-gate 		ushort_t port;
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 		argp = (struct nfs_args *)malloc(sizeof (*argp));
4460Sstevel@tonic-gate 		if (argp == NULL) {
4470Sstevel@tonic-gate 			pr_err(gettext("no memory\n"));
4480Sstevel@tonic-gate 			last_error = RET_ERR;
4490Sstevel@tonic-gate 			goto out;
4500Sstevel@tonic-gate 		}
4510Sstevel@tonic-gate 		memset(argp, 0, sizeof (*argp));
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 		memset(&nfs_sec, 0, sizeof (nfs_sec));
4540Sstevel@tonic-gate 		sec_opt = 0;
4550Sstevel@tonic-gate 		use_pubfh = FALSE;
4560Sstevel@tonic-gate 		url = FALSE;
4570Sstevel@tonic-gate 		port = 0;
4580Sstevel@tonic-gate 		snego_done = FALSE;
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate 		/*
4610Sstevel@tonic-gate 		 * Looking for resources of the form
4620Sstevel@tonic-gate 		 *	nfs://server_host[:port_number]/path_name
4630Sstevel@tonic-gate 		 */
4640Sstevel@tonic-gate 		if (strcmp(list[i].host, "nfs") == 0 && strncmp(list[i].path,
4650Sstevel@tonic-gate 		    "//", 2) == 0) {
4660Sstevel@tonic-gate 			char *sport, *cb;
4670Sstevel@tonic-gate 			url = TRUE;
4680Sstevel@tonic-gate 			oldpath = strdup(list[i].path);
4690Sstevel@tonic-gate 			if (oldpath == NULL) {
4700Sstevel@tonic-gate 				pr_err(gettext("memory allocation failure\n"));
4710Sstevel@tonic-gate 				last_error = RET_ERR;
4720Sstevel@tonic-gate 				goto out;
4730Sstevel@tonic-gate 			}
4740Sstevel@tonic-gate 			host = list[i].path+2;
4750Sstevel@tonic-gate 			path = strchr(host, '/');
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate 			if (path == NULL) {
4780Sstevel@tonic-gate 				pr_err(gettext(
4790Sstevel@tonic-gate 				    "illegal nfs url syntax\n"));
4800Sstevel@tonic-gate 				last_error = RET_ERR;
4810Sstevel@tonic-gate 				goto out;
4820Sstevel@tonic-gate 			}
4830Sstevel@tonic-gate 
4840Sstevel@tonic-gate 			*path = '\0';
4850Sstevel@tonic-gate 			if (*host == '[') {
4860Sstevel@tonic-gate 				cb = strchr(host, ']');
4870Sstevel@tonic-gate 				if (cb == NULL) {
4880Sstevel@tonic-gate 					pr_err(gettext(
4895302Sth199096 					    "illegal nfs url syntax\n"));
4900Sstevel@tonic-gate 					last_error = RET_ERR;
4910Sstevel@tonic-gate 					goto out;
4920Sstevel@tonic-gate 				} else {
4930Sstevel@tonic-gate 					*cb = '\0';
4940Sstevel@tonic-gate 					host++;
4950Sstevel@tonic-gate 					cb++;
4960Sstevel@tonic-gate 					if (*cb == ':')
4970Sstevel@tonic-gate 						port = htons((ushort_t)
4985302Sth199096 						    atoi(cb+1));
4990Sstevel@tonic-gate 				}
5000Sstevel@tonic-gate 			} else {
5010Sstevel@tonic-gate 				sport = strchr(host, ':');
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate 				if (sport != NULL && sport < path) {
5040Sstevel@tonic-gate 					*sport = '\0';
5050Sstevel@tonic-gate 					port = htons((ushort_t)atoi(sport+1));
5060Sstevel@tonic-gate 				}
5070Sstevel@tonic-gate 			}
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 			path++;
5100Sstevel@tonic-gate 			if (*path == '\0')
5110Sstevel@tonic-gate 				path = ".";
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 		} else {
5140Sstevel@tonic-gate 			host = list[i].host;
5150Sstevel@tonic-gate 			path = list[i].path;
5160Sstevel@tonic-gate 		}
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 		if (r = set_args(&mntflags, argp, host, mntp)) {
5190Sstevel@tonic-gate 			last_error = r;
5200Sstevel@tonic-gate 			goto out;
5210Sstevel@tonic-gate 		}
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 		if (public_opt == TRUE)
5240Sstevel@tonic-gate 			use_pubfh = TRUE;
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 		if (port == 0) {
5270Sstevel@tonic-gate 			port = nfs_port;
5280Sstevel@tonic-gate 		} else if (nfs_port != 0 && nfs_port != port) {
5290Sstevel@tonic-gate 			pr_err(gettext(
5300Sstevel@tonic-gate 			    "port (%u) in nfs URL not the same"
5310Sstevel@tonic-gate 			    " as port (%u) in port option\n"),
5320Sstevel@tonic-gate 			    (unsigned int)ntohs(port),
5330Sstevel@tonic-gate 			    (unsigned int)ntohs(nfs_port));
5340Sstevel@tonic-gate 			last_error = RET_ERR;
5350Sstevel@tonic-gate 			goto out;
5360Sstevel@tonic-gate 		}
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 		if (replicated && !(mntflags & MS_RDONLY)) {
5400Sstevel@tonic-gate 			pr_err(gettext(
5415302Sth199096 			    "replicated mounts must be read-only\n"));
5420Sstevel@tonic-gate 			last_error = RET_ERR;
5430Sstevel@tonic-gate 			goto out;
5440Sstevel@tonic-gate 		}
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate 		if (replicated && (argp->flags & NFSMNT_SOFT)) {
5470Sstevel@tonic-gate 			pr_err(gettext(
5485302Sth199096 			    "replicated mounts must not be soft\n"));
5490Sstevel@tonic-gate 			last_error = RET_ERR;
5500Sstevel@tonic-gate 			goto out;
5510Sstevel@tonic-gate 		}
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate 		oldvers = vers;
5540Sstevel@tonic-gate 		nconf = NULL;
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate 		r = RET_ERR;
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 		/*
5590Sstevel@tonic-gate 		 * If -o public was specified, and/or a URL was specified,
5600Sstevel@tonic-gate 		 * then try the public file handle method.
5610Sstevel@tonic-gate 		 */
5620Sstevel@tonic-gate 		if ((use_pubfh == TRUE) || (url == TRUE)) {
5630Sstevel@tonic-gate 			r = get_fh_via_pub(argp, host, path, url, use_pubfh,
5645302Sth199096 			    &vers, &nconf, port);
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 			if (r != RET_OK) {
5670Sstevel@tonic-gate 				/*
5680Sstevel@tonic-gate 				 * If -o public was specified, then return the
5690Sstevel@tonic-gate 				 * error now.
5700Sstevel@tonic-gate 				 */
5710Sstevel@tonic-gate 				if (use_pubfh == TRUE) {
5720Sstevel@tonic-gate 					last_error = r;
5730Sstevel@tonic-gate 					goto out;
5740Sstevel@tonic-gate 				}
5750Sstevel@tonic-gate 			} else
5760Sstevel@tonic-gate 				use_pubfh = TRUE;
5770Sstevel@tonic-gate 			argp->flags |= NFSMNT_PUBLIC;
5780Sstevel@tonic-gate 		}
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 		if ((r != RET_OK) || (vers == NFS_V4)) {
5810Sstevel@tonic-gate 			bool_t loud_on_mnt_err;
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 			/*
5840Sstevel@tonic-gate 			 * This can happen if -o public is not specified,
5850Sstevel@tonic-gate 			 * special is a URL, and server doesn't support
5860Sstevel@tonic-gate 			 * public file handle.
5870Sstevel@tonic-gate 			 */
5880Sstevel@tonic-gate 			if (url) {
5890Sstevel@tonic-gate 				URLparse(path);
5900Sstevel@tonic-gate 			}
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate 			/*
5930Sstevel@tonic-gate 			 * If the path portion of the URL didn't have
5940Sstevel@tonic-gate 			 * a leading / then there is good possibility
5950Sstevel@tonic-gate 			 * that a mount without a leading slash will
5960Sstevel@tonic-gate 			 * fail.
5970Sstevel@tonic-gate 			 */
5980Sstevel@tonic-gate 			if (url == TRUE && *path != '/')
5990Sstevel@tonic-gate 				loud_on_mnt_err = FALSE;
6000Sstevel@tonic-gate 			else
6010Sstevel@tonic-gate 				loud_on_mnt_err = TRUE;
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate 			r = get_fh(argp, host, path, &vers,
6045302Sth199096 			    loud_on_mnt_err, &nconf, port);
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate 			if (r != RET_OK) {
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate 				/*
6090Sstevel@tonic-gate 				 * If there was no leading / and the path was
6100Sstevel@tonic-gate 				 * derived from a URL, then try again
6110Sstevel@tonic-gate 				 * with a leading /.
6120Sstevel@tonic-gate 				 */
6130Sstevel@tonic-gate 				if ((r == RET_MNTERR) &&
6140Sstevel@tonic-gate 				    (loud_on_mnt_err == FALSE)) {
6150Sstevel@tonic-gate 
6160Sstevel@tonic-gate 					newpath = malloc(strlen(path)+2);
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate 					if (newpath == NULL) {
6190Sstevel@tonic-gate 						pr_err(gettext("memory "
6200Sstevel@tonic-gate 						    "allocation failure\n"));
6210Sstevel@tonic-gate 						last_error = RET_ERR;
6220Sstevel@tonic-gate 						goto out;
6230Sstevel@tonic-gate 					}
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 					strcpy(newpath, "/");
6260Sstevel@tonic-gate 					strcat(newpath, path);
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate 					r = get_fh(argp, host, newpath, &vers,
6295302Sth199096 					    TRUE, &nconf, port);
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate 					if (r == RET_OK)
6320Sstevel@tonic-gate 						path = newpath;
6330Sstevel@tonic-gate 				}
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 				/*
6360Sstevel@tonic-gate 				 * map exit code back to RET_ERR.
6370Sstevel@tonic-gate 				 */
6380Sstevel@tonic-gate 				if (r == RET_MNTERR)
6390Sstevel@tonic-gate 					r = RET_ERR;
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 				if (r != RET_OK) {
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate 					if (replicated) {
6440Sstevel@tonic-gate 						if (argp->fh)
6450Sstevel@tonic-gate 							free(argp->fh);
6460Sstevel@tonic-gate 						if (argp->pathconf)
6470Sstevel@tonic-gate 							free(argp->pathconf);
6480Sstevel@tonic-gate 						free(argp);
6490Sstevel@tonic-gate 						goto cont;
6500Sstevel@tonic-gate 					}
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate 					last_error = r;
6530Sstevel@tonic-gate 					goto out;
6540Sstevel@tonic-gate 				}
6550Sstevel@tonic-gate 			}
6560Sstevel@tonic-gate 		}
6570Sstevel@tonic-gate 
6580Sstevel@tonic-gate 		if (oldvers && vers != oldvers) {
6590Sstevel@tonic-gate 			pr_err(
6600Sstevel@tonic-gate 			    gettext("replicas must have the same version\n"));
6610Sstevel@tonic-gate 			last_error = RET_ERR;
6620Sstevel@tonic-gate 			goto out;
6630Sstevel@tonic-gate 		}
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 		/*
6660Sstevel@tonic-gate 		 * decide whether to use remote host's
6670Sstevel@tonic-gate 		 * lockd or do local locking
6680Sstevel@tonic-gate 		 */
6690Sstevel@tonic-gate 		if (!(argp->flags & NFSMNT_LLOCK) && vers == NFS_VERSION &&
6700Sstevel@tonic-gate 		    remote_lock(host, argp->fh)) {
6710Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
6720Sstevel@tonic-gate 			    "WARNING: No network locking on %s:%s:"),
6730Sstevel@tonic-gate 			    host, path);
6740Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
6750Sstevel@tonic-gate 			    " contact admin to install server change\n"));
6765302Sth199096 			argp->flags |= NFSMNT_LLOCK;
6770Sstevel@tonic-gate 		}
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate 		if (self_check(host))
6800Sstevel@tonic-gate 			argp->flags |= NFSMNT_LOOPBACK;
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 		if (use_pubfh == FALSE) {
6830Sstevel@tonic-gate 			/*
6840Sstevel@tonic-gate 			 * Call to get_fh() above may have obtained the
6850Sstevel@tonic-gate 			 * netconfig info and NULL proc'd the server.
6860Sstevel@tonic-gate 			 * This would be the case with v4
6870Sstevel@tonic-gate 			 */
6880Sstevel@tonic-gate 			if (!(argp->flags & NFSMNT_KNCONF)) {
6890Sstevel@tonic-gate 				nconf = NULL;
6900Sstevel@tonic-gate 				if (r = getaddr_nfs(argp, host, &nconf,
6915302Sth199096 				    FALSE, path, port, retry_error,
6925302Sth199096 				    TRUE)) {
693489Soa138391 						last_error = r;
694489Soa138391 						goto out;
6950Sstevel@tonic-gate 				}
6960Sstevel@tonic-gate 			}
6970Sstevel@tonic-gate 		}
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate 		if (make_secure(argp, host, nconf, use_pubfh, vers) < 0) {
7000Sstevel@tonic-gate 			last_error = RET_ERR;
7010Sstevel@tonic-gate 			goto out;
7020Sstevel@tonic-gate 		}
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate 		if ((url == TRUE) && (use_pubfh == FALSE)) {
7050Sstevel@tonic-gate 			/*
7060Sstevel@tonic-gate 			 * Convert the special from
7070Sstevel@tonic-gate 			 *	nfs://host/path
7080Sstevel@tonic-gate 			 * to
7090Sstevel@tonic-gate 			 *	host:path
7100Sstevel@tonic-gate 			 */
7110Sstevel@tonic-gate 			if (convert_special(&special, host, oldpath, path,
7120Sstevel@tonic-gate 			    mntp->mnt_special) == -1) {
7130Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(
7140Sstevel@tonic-gate 				    "could not convert URL nfs:%s to %s:%s\n"),
7150Sstevel@tonic-gate 				    oldpath, host, path);
7160Sstevel@tonic-gate 				last_error = RET_ERR;
7170Sstevel@tonic-gate 				goto out;
7180Sstevel@tonic-gate 			} else {
7190Sstevel@tonic-gate 				mntp->mnt_special = special;
7200Sstevel@tonic-gate 			}
7210Sstevel@tonic-gate 		}
7220Sstevel@tonic-gate 
7230Sstevel@tonic-gate 		if (prev_argp == NULL)
7240Sstevel@tonic-gate 			args = argp;
7250Sstevel@tonic-gate 		else
7260Sstevel@tonic-gate 			prev_argp->nfs_ext_u.nfs_extB.next = argp;
7270Sstevel@tonic-gate 		prev_argp = argp;
7280Sstevel@tonic-gate 
7290Sstevel@tonic-gate cont:
7300Sstevel@tonic-gate 		if (oldpath != NULL) {
7310Sstevel@tonic-gate 			free(oldpath);
7320Sstevel@tonic-gate 			oldpath = NULL;
7330Sstevel@tonic-gate 		}
7340Sstevel@tonic-gate 
7350Sstevel@tonic-gate 		if (newpath != NULL) {
7360Sstevel@tonic-gate 			free(newpath);
7370Sstevel@tonic-gate 			newpath = NULL;
7380Sstevel@tonic-gate 		}
7390Sstevel@tonic-gate 	}
7400Sstevel@tonic-gate 
7410Sstevel@tonic-gate 	argp = NULL;
7420Sstevel@tonic-gate 
7430Sstevel@tonic-gate 	if (args == NULL) {
7440Sstevel@tonic-gate 		last_error = RET_RETRY;
7450Sstevel@tonic-gate 		goto out;
7460Sstevel@tonic-gate 	}
7470Sstevel@tonic-gate 
7480Sstevel@tonic-gate 	/* Determine which services are appropriate for the NFS version */
7490Sstevel@tonic-gate 	if (strcmp(fstype, MNTTYPE_NFS4) == 0)
7500Sstevel@tonic-gate 		sl = service_list_v4;
7510Sstevel@tonic-gate 	else
7520Sstevel@tonic-gate 		sl = service_list;
7530Sstevel@tonic-gate 
7540Sstevel@tonic-gate 	/*
7550Sstevel@tonic-gate 	 * enable services as needed.
7560Sstevel@tonic-gate 	 */
7570Sstevel@tonic-gate 	_check_services(sl);
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate 	mntflags |= MS_DATA | MS_OPTIONSTR;
7600Sstevel@tonic-gate 
7610Sstevel@tonic-gate 	if (mflg)
7620Sstevel@tonic-gate 		mntflags |= MS_NOMNTTAB;
7630Sstevel@tonic-gate 
7640Sstevel@tonic-gate 	if (!qflg)
7650Sstevel@tonic-gate 		saveopts = strdup(mntp->mnt_mntopts);
7660Sstevel@tonic-gate 
7675302Sth199096 	/*
7685302Sth199096 	 * And make sure that we have the ephemeral mount_to
7695302Sth199096 	 * set for this zone.
7705302Sth199096 	 */
7715302Sth199096 	set_nfsv4_ephemeral_mount_to();
7725302Sth199096 
7730Sstevel@tonic-gate 	if (mount(mntp->mnt_special, mntp->mnt_mountp, mntflags, fstype, args,
7745302Sth199096 	    sizeof (*args), mntp->mnt_mntopts, MAX_MNTOPT_STR) < 0) {
7750Sstevel@tonic-gate 		if (errno != ENOENT) {
7760Sstevel@tonic-gate 			pr_err(gettext("mount: %s: %s\n"),
7775302Sth199096 			    mntp->mnt_mountp, strerror(errno));
7780Sstevel@tonic-gate 		} else {
7790Sstevel@tonic-gate 			struct stat sb;
7800Sstevel@tonic-gate 			if (stat(mntp->mnt_mountp, &sb) < 0 && errno == ENOENT)
7810Sstevel@tonic-gate 				pr_err(gettext("mount: %s: %s\n"),
7825302Sth199096 				    mntp->mnt_mountp, strerror(ENOENT));
7830Sstevel@tonic-gate 			else
7840Sstevel@tonic-gate 				pr_err("%s: %s\n", mntp->mnt_special,
7855302Sth199096 				    strerror(ENOENT));
7860Sstevel@tonic-gate 		}
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate 		last_error = RET_ERR;
7890Sstevel@tonic-gate 		goto out;
7900Sstevel@tonic-gate 	}
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate 	if (!qflg && saveopts != NULL) {
7930Sstevel@tonic-gate 		cmp_requested_to_actual_options(saveopts, mntp->mnt_mntopts,
7940Sstevel@tonic-gate 		    mntp->mnt_special, mntp->mnt_mountp);
7950Sstevel@tonic-gate 	}
7960Sstevel@tonic-gate 
7970Sstevel@tonic-gate out:
7980Sstevel@tonic-gate 	if (saveopts != NULL)
7990Sstevel@tonic-gate 		free(saveopts);
8000Sstevel@tonic-gate 	if (special != NULL)
8010Sstevel@tonic-gate 		free(special);
8020Sstevel@tonic-gate 	if (oldpath != NULL)
8030Sstevel@tonic-gate 		free(oldpath);
8040Sstevel@tonic-gate 	if (newpath != NULL)
8050Sstevel@tonic-gate 		free(newpath);
8060Sstevel@tonic-gate 
8070Sstevel@tonic-gate 	free_replica(list, n);
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 	if (argp != NULL) {
8100Sstevel@tonic-gate 		/*
8110Sstevel@tonic-gate 		 * If we had a new entry which was not added to the
8120Sstevel@tonic-gate 		 * list yet, then add it now that it can be freed.
8130Sstevel@tonic-gate 		 */
8140Sstevel@tonic-gate 		if (prev_argp == NULL)
8150Sstevel@tonic-gate 			args = argp;
8160Sstevel@tonic-gate 		else
8170Sstevel@tonic-gate 			prev_argp->nfs_ext_u.nfs_extB.next = argp;
8180Sstevel@tonic-gate 	}
8190Sstevel@tonic-gate 	argp = args;
8200Sstevel@tonic-gate 	while (argp != NULL) {
8210Sstevel@tonic-gate 		if (argp->fh)
8220Sstevel@tonic-gate 			free(argp->fh);
8230Sstevel@tonic-gate 		if (argp->pathconf)
8240Sstevel@tonic-gate 			free(argp->pathconf);
8250Sstevel@tonic-gate 		if (argp->knconf)
8260Sstevel@tonic-gate 			free(argp->knconf);
8270Sstevel@tonic-gate 		if (argp->addr) {
8280Sstevel@tonic-gate 			free(argp->addr->buf);
8290Sstevel@tonic-gate 			free(argp->addr);
8300Sstevel@tonic-gate 		}
8310Sstevel@tonic-gate 		nfs_free_secdata(argp->nfs_ext_u.nfs_extB.secdata);
8320Sstevel@tonic-gate 		if (argp->syncaddr) {
8330Sstevel@tonic-gate 			free(argp->syncaddr->buf);
8340Sstevel@tonic-gate 			free(argp->syncaddr);
8350Sstevel@tonic-gate 		}
8360Sstevel@tonic-gate 		if (argp->netname)
8370Sstevel@tonic-gate 			free(argp->netname);
8380Sstevel@tonic-gate 		prev_argp = argp;
8390Sstevel@tonic-gate 		argp = argp->nfs_ext_u.nfs_extB.next;
8400Sstevel@tonic-gate 		free(prev_argp);
8410Sstevel@tonic-gate 	}
8420Sstevel@tonic-gate 
8430Sstevel@tonic-gate 	return (last_error);
8440Sstevel@tonic-gate }
8450Sstevel@tonic-gate 
8460Sstevel@tonic-gate /*
8470Sstevel@tonic-gate  * These options are duplicated in uts/common/fs/nfs/nfs_dlinet.c
8480Sstevel@tonic-gate  * Changes must be made to both lists.
8490Sstevel@tonic-gate  */
8500Sstevel@tonic-gate static char *optlist[] = {
8510Sstevel@tonic-gate #define	OPT_RO		0
8520Sstevel@tonic-gate 	MNTOPT_RO,
8530Sstevel@tonic-gate #define	OPT_RW		1
8540Sstevel@tonic-gate 	MNTOPT_RW,
8550Sstevel@tonic-gate #define	OPT_QUOTA	2
8560Sstevel@tonic-gate 	MNTOPT_QUOTA,
8570Sstevel@tonic-gate #define	OPT_NOQUOTA	3
8580Sstevel@tonic-gate 	MNTOPT_NOQUOTA,
8590Sstevel@tonic-gate #define	OPT_SOFT	4
8600Sstevel@tonic-gate 	MNTOPT_SOFT,
8610Sstevel@tonic-gate #define	OPT_HARD	5
8620Sstevel@tonic-gate 	MNTOPT_HARD,
8630Sstevel@tonic-gate #define	OPT_SUID	6
8640Sstevel@tonic-gate 	MNTOPT_SUID,
8650Sstevel@tonic-gate #define	OPT_NOSUID	7
8660Sstevel@tonic-gate 	MNTOPT_NOSUID,
8670Sstevel@tonic-gate #define	OPT_GRPID	8
8680Sstevel@tonic-gate 	MNTOPT_GRPID,
8690Sstevel@tonic-gate #define	OPT_REMOUNT	9
8700Sstevel@tonic-gate 	MNTOPT_REMOUNT,
8710Sstevel@tonic-gate #define	OPT_NOSUB	10
8720Sstevel@tonic-gate 	MNTOPT_NOSUB,
8730Sstevel@tonic-gate #define	OPT_INTR	11
8740Sstevel@tonic-gate 	MNTOPT_INTR,
8750Sstevel@tonic-gate #define	OPT_NOINTR	12
8760Sstevel@tonic-gate 	MNTOPT_NOINTR,
8770Sstevel@tonic-gate #define	OPT_PORT	13
8780Sstevel@tonic-gate 	MNTOPT_PORT,
8790Sstevel@tonic-gate #define	OPT_SECURE	14
8800Sstevel@tonic-gate 	MNTOPT_SECURE,
8810Sstevel@tonic-gate #define	OPT_RSIZE	15
8820Sstevel@tonic-gate 	MNTOPT_RSIZE,
8830Sstevel@tonic-gate #define	OPT_WSIZE	16
8840Sstevel@tonic-gate 	MNTOPT_WSIZE,
8850Sstevel@tonic-gate #define	OPT_TIMEO	17
8860Sstevel@tonic-gate 	MNTOPT_TIMEO,
8870Sstevel@tonic-gate #define	OPT_RETRANS	18
8880Sstevel@tonic-gate 	MNTOPT_RETRANS,
8890Sstevel@tonic-gate #define	OPT_ACTIMEO	19
8900Sstevel@tonic-gate 	MNTOPT_ACTIMEO,
8910Sstevel@tonic-gate #define	OPT_ACREGMIN	20
8920Sstevel@tonic-gate 	MNTOPT_ACREGMIN,
8930Sstevel@tonic-gate #define	OPT_ACREGMAX	21
8940Sstevel@tonic-gate 	MNTOPT_ACREGMAX,
8950Sstevel@tonic-gate #define	OPT_ACDIRMIN	22
8960Sstevel@tonic-gate 	MNTOPT_ACDIRMIN,
8970Sstevel@tonic-gate #define	OPT_ACDIRMAX	23
8980Sstevel@tonic-gate 	MNTOPT_ACDIRMAX,
8990Sstevel@tonic-gate #define	OPT_BG		24
9000Sstevel@tonic-gate 	MNTOPT_BG,
9010Sstevel@tonic-gate #define	OPT_FG		25
9020Sstevel@tonic-gate 	MNTOPT_FG,
9030Sstevel@tonic-gate #define	OPT_RETRY	26
9040Sstevel@tonic-gate 	MNTOPT_RETRY,
9050Sstevel@tonic-gate #define	OPT_NOAC	27
9060Sstevel@tonic-gate 	MNTOPT_NOAC,
9070Sstevel@tonic-gate #define	OPT_NOCTO	28
9080Sstevel@tonic-gate 	MNTOPT_NOCTO,
9090Sstevel@tonic-gate #define	OPT_LLOCK	29
9100Sstevel@tonic-gate 	MNTOPT_LLOCK,
9110Sstevel@tonic-gate #define	OPT_POSIX	30
9120Sstevel@tonic-gate 	MNTOPT_POSIX,
9130Sstevel@tonic-gate #define	OPT_VERS	31
9140Sstevel@tonic-gate 	MNTOPT_VERS,
9150Sstevel@tonic-gate #define	OPT_PROTO	32
9160Sstevel@tonic-gate 	MNTOPT_PROTO,
9170Sstevel@tonic-gate #define	OPT_SEMISOFT	33
9180Sstevel@tonic-gate 	MNTOPT_SEMISOFT,
9190Sstevel@tonic-gate #define	OPT_NOPRINT	34
9200Sstevel@tonic-gate 	MNTOPT_NOPRINT,
9210Sstevel@tonic-gate #define	OPT_SEC		35
9220Sstevel@tonic-gate 	MNTOPT_SEC,
9230Sstevel@tonic-gate #define	OPT_LARGEFILES	36
9240Sstevel@tonic-gate 	MNTOPT_LARGEFILES,
9250Sstevel@tonic-gate #define	OPT_NOLARGEFILES 37
9260Sstevel@tonic-gate 	MNTOPT_NOLARGEFILES,
9270Sstevel@tonic-gate #define	OPT_PUBLIC	38
9280Sstevel@tonic-gate 	MNTOPT_PUBLIC,
9290Sstevel@tonic-gate #define	OPT_DIRECTIO	39
9300Sstevel@tonic-gate 	MNTOPT_FORCEDIRECTIO,
9310Sstevel@tonic-gate #define	OPT_NODIRECTIO	40
9320Sstevel@tonic-gate 	MNTOPT_NOFORCEDIRECTIO,
9330Sstevel@tonic-gate #define	OPT_XATTR	41
9340Sstevel@tonic-gate 	MNTOPT_XATTR,
9350Sstevel@tonic-gate #define	OPT_NOXATTR	42
9360Sstevel@tonic-gate 	MNTOPT_NOXATTR,
9370Sstevel@tonic-gate #define	OPT_DEVICES	43
9380Sstevel@tonic-gate 	MNTOPT_DEVICES,
9390Sstevel@tonic-gate #define	OPT_NODEVICES	44
9400Sstevel@tonic-gate 	MNTOPT_NODEVICES,
9410Sstevel@tonic-gate #define	OPT_SETUID	45
9420Sstevel@tonic-gate 	MNTOPT_SETUID,
9430Sstevel@tonic-gate #define	OPT_NOSETUID	46
9440Sstevel@tonic-gate 	MNTOPT_NOSETUID,
9450Sstevel@tonic-gate #define	OPT_EXEC	47
9460Sstevel@tonic-gate 	MNTOPT_EXEC,
9470Sstevel@tonic-gate #define	OPT_NOEXEC	48
9480Sstevel@tonic-gate 	MNTOPT_NOEXEC,
9490Sstevel@tonic-gate 	NULL
9500Sstevel@tonic-gate };
9510Sstevel@tonic-gate 
952*12782SMarcel.Telka@Sun.COM static int
953*12782SMarcel.Telka@Sun.COM convert_int(int *val, char *str)
954*12782SMarcel.Telka@Sun.COM {
955*12782SMarcel.Telka@Sun.COM 	long lval;
956*12782SMarcel.Telka@Sun.COM 
957*12782SMarcel.Telka@Sun.COM 	if (str == NULL || !isdigit(*str))
958*12782SMarcel.Telka@Sun.COM 		return (-1);
959*12782SMarcel.Telka@Sun.COM 
960*12782SMarcel.Telka@Sun.COM 	lval = strtol(str, &str, 10);
961*12782SMarcel.Telka@Sun.COM 	if (*str != '\0' || lval > INT_MAX)
962*12782SMarcel.Telka@Sun.COM 		return (-2);
963*12782SMarcel.Telka@Sun.COM 
964*12782SMarcel.Telka@Sun.COM 	*val = (int)lval;
965*12782SMarcel.Telka@Sun.COM 	return (0);
966*12782SMarcel.Telka@Sun.COM }
9670Sstevel@tonic-gate 
9680Sstevel@tonic-gate static int
9690Sstevel@tonic-gate set_args(int *mntflags, struct nfs_args *args, char *fshost, struct mnttab *mnt)
9700Sstevel@tonic-gate {
9710Sstevel@tonic-gate 	char *saveopt, *optstr, *opts, *newopts, *val;
972*12782SMarcel.Telka@Sun.COM 	int num;
9730Sstevel@tonic-gate 	int largefiles = 0;
9740Sstevel@tonic-gate 	int invalid = 0;
9750Sstevel@tonic-gate 	int attrpref = 0;
9760Sstevel@tonic-gate 	int optlen;
9770Sstevel@tonic-gate 
9780Sstevel@tonic-gate 	args->flags = NFSMNT_INT;	/* default is "intr" */
9790Sstevel@tonic-gate 	args->flags |= NFSMNT_HOSTNAME;
9800Sstevel@tonic-gate 	args->flags |= NFSMNT_NEWARGS;	/* using extented nfs_args structure */
9810Sstevel@tonic-gate 	args->hostname = fshost;
9820Sstevel@tonic-gate 
9830Sstevel@tonic-gate 	optstr = opts = strdup(mnt->mnt_mntopts);
9840Sstevel@tonic-gate 	/* sizeof (MNTOPT_XXX) includes one extra byte we may need for "," */
9850Sstevel@tonic-gate 	optlen = strlen(mnt->mnt_mntopts) + sizeof (MNTOPT_XATTR) + 1;
9860Sstevel@tonic-gate 	if (optlen > MAX_MNTOPT_STR) {
9870Sstevel@tonic-gate 		pr_err(gettext("option string too long"));
9880Sstevel@tonic-gate 		return (RET_ERR);
9890Sstevel@tonic-gate 	}
9900Sstevel@tonic-gate 	newopts = malloc(optlen);
9910Sstevel@tonic-gate 	if (opts == NULL || newopts == NULL) {
9920Sstevel@tonic-gate 		pr_err(gettext("no memory"));
9930Sstevel@tonic-gate 		if (opts)
9940Sstevel@tonic-gate 			free(opts);
9950Sstevel@tonic-gate 		if (newopts)
9960Sstevel@tonic-gate 			free(newopts);
9970Sstevel@tonic-gate 		return (RET_ERR);
9980Sstevel@tonic-gate 	}
9990Sstevel@tonic-gate 	newopts[0] = '\0';
10000Sstevel@tonic-gate 
10010Sstevel@tonic-gate 	while (*opts) {
10020Sstevel@tonic-gate 		invalid = 0;
10030Sstevel@tonic-gate 		saveopt = opts;
10040Sstevel@tonic-gate 		switch (getsubopt(&opts, optlist, &val)) {
10050Sstevel@tonic-gate 		case OPT_RO:
10060Sstevel@tonic-gate 			*mntflags |= MS_RDONLY;
10070Sstevel@tonic-gate 			break;
10080Sstevel@tonic-gate 		case OPT_RW:
10090Sstevel@tonic-gate 			*mntflags &= ~(MS_RDONLY);
10100Sstevel@tonic-gate 			break;
10110Sstevel@tonic-gate 		case OPT_QUOTA:
10120Sstevel@tonic-gate 		case OPT_NOQUOTA:
10130Sstevel@tonic-gate 			break;
10140Sstevel@tonic-gate 		case OPT_SOFT:
10150Sstevel@tonic-gate 			args->flags |= NFSMNT_SOFT;
10160Sstevel@tonic-gate 			args->flags &= ~(NFSMNT_SEMISOFT);
10170Sstevel@tonic-gate 			break;
10180Sstevel@tonic-gate 		case OPT_SEMISOFT:
10190Sstevel@tonic-gate 			args->flags |= NFSMNT_SOFT;
10200Sstevel@tonic-gate 			args->flags |= NFSMNT_SEMISOFT;
10210Sstevel@tonic-gate 			break;
10220Sstevel@tonic-gate 		case OPT_HARD:
10230Sstevel@tonic-gate 			args->flags &= ~(NFSMNT_SOFT);
10240Sstevel@tonic-gate 			args->flags &= ~(NFSMNT_SEMISOFT);
10250Sstevel@tonic-gate 			break;
10260Sstevel@tonic-gate 		case OPT_SUID:
10270Sstevel@tonic-gate 			*mntflags &= ~(MS_NOSUID);
10280Sstevel@tonic-gate 			break;
10290Sstevel@tonic-gate 		case OPT_NOSUID:
10300Sstevel@tonic-gate 			*mntflags |= MS_NOSUID;
10310Sstevel@tonic-gate 			break;
10320Sstevel@tonic-gate 		case OPT_GRPID:
10330Sstevel@tonic-gate 			args->flags |= NFSMNT_GRPID;
10340Sstevel@tonic-gate 			break;
10350Sstevel@tonic-gate 		case OPT_REMOUNT:
10360Sstevel@tonic-gate 			*mntflags |= MS_REMOUNT;
10370Sstevel@tonic-gate 			break;
10380Sstevel@tonic-gate 		case OPT_INTR:
10390Sstevel@tonic-gate 			args->flags |= NFSMNT_INT;
10400Sstevel@tonic-gate 			break;
10410Sstevel@tonic-gate 		case OPT_NOINTR:
10420Sstevel@tonic-gate 			args->flags &= ~(NFSMNT_INT);
10430Sstevel@tonic-gate 			break;
10440Sstevel@tonic-gate 		case OPT_NOAC:
10450Sstevel@tonic-gate 			args->flags |= NFSMNT_NOAC;
10460Sstevel@tonic-gate 			break;
10470Sstevel@tonic-gate 		case OPT_PORT:
1048*12782SMarcel.Telka@Sun.COM 			if (convert_int(&num, val) != 0)
10490Sstevel@tonic-gate 				goto badopt;
1050*12782SMarcel.Telka@Sun.COM 			nfs_port = htons((ushort_t)num);
10510Sstevel@tonic-gate 			break;
10520Sstevel@tonic-gate 
10530Sstevel@tonic-gate 		case OPT_SECURE:
10540Sstevel@tonic-gate 			if (nfs_getseconfig_byname("dh", &nfs_sec)) {
10555302Sth199096 				pr_err(gettext("can not get \"dh\" from %s\n"),
10565302Sth199096 				    NFSSEC_CONF);
10575302Sth199096 				goto badopt;
10580Sstevel@tonic-gate 			}
10590Sstevel@tonic-gate 			sec_opt++;
10600Sstevel@tonic-gate 			break;
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 		case OPT_NOCTO:
10630Sstevel@tonic-gate 			args->flags |= NFSMNT_NOCTO;
10640Sstevel@tonic-gate 			break;
10650Sstevel@tonic-gate 
10660Sstevel@tonic-gate 		case OPT_RSIZE:
1067*12782SMarcel.Telka@Sun.COM 			if (convert_int(&args->rsize, val) != 0)
1068*12782SMarcel.Telka@Sun.COM 				goto badopt;
10690Sstevel@tonic-gate 			args->flags |= NFSMNT_RSIZE;
10700Sstevel@tonic-gate 			break;
10710Sstevel@tonic-gate 		case OPT_WSIZE:
1072*12782SMarcel.Telka@Sun.COM 			if (convert_int(&args->wsize, val) != 0)
1073*12782SMarcel.Telka@Sun.COM 				goto badopt;
10740Sstevel@tonic-gate 			args->flags |= NFSMNT_WSIZE;
10750Sstevel@tonic-gate 			break;
10760Sstevel@tonic-gate 		case OPT_TIMEO:
1077*12782SMarcel.Telka@Sun.COM 			if (convert_int(&args->timeo, val) != 0)
1078*12782SMarcel.Telka@Sun.COM 				goto badopt;
10790Sstevel@tonic-gate 			args->flags |= NFSMNT_TIMEO;
10800Sstevel@tonic-gate 			break;
10810Sstevel@tonic-gate 		case OPT_RETRANS:
1082*12782SMarcel.Telka@Sun.COM 			if (convert_int(&args->retrans, val) != 0)
1083*12782SMarcel.Telka@Sun.COM 				goto badopt;
10840Sstevel@tonic-gate 			args->flags |= NFSMNT_RETRANS;
10850Sstevel@tonic-gate 			break;
10860Sstevel@tonic-gate 		case OPT_ACTIMEO:
1087*12782SMarcel.Telka@Sun.COM 			if (convert_int(&args->acregmax, val) != 0)
1088*12782SMarcel.Telka@Sun.COM 				goto badopt;
1089*12782SMarcel.Telka@Sun.COM 			args->acdirmin = args->acregmin = args->acdirmax
1090*12782SMarcel.Telka@Sun.COM 			    = args->acregmax;
10910Sstevel@tonic-gate 			args->flags |= NFSMNT_ACDIRMAX;
10920Sstevel@tonic-gate 			args->flags |= NFSMNT_ACREGMAX;
10930Sstevel@tonic-gate 			args->flags |= NFSMNT_ACDIRMIN;
10940Sstevel@tonic-gate 			args->flags |= NFSMNT_ACREGMIN;
10950Sstevel@tonic-gate 			break;
10960Sstevel@tonic-gate 		case OPT_ACREGMIN:
1097*12782SMarcel.Telka@Sun.COM 			if (convert_int(&args->acregmin, val) != 0)
1098*12782SMarcel.Telka@Sun.COM 				goto badopt;
10990Sstevel@tonic-gate 			args->flags |= NFSMNT_ACREGMIN;
11000Sstevel@tonic-gate 			break;
11010Sstevel@tonic-gate 		case OPT_ACREGMAX:
1102*12782SMarcel.Telka@Sun.COM 			if (convert_int(&args->acregmax, val) != 0)
1103*12782SMarcel.Telka@Sun.COM 				goto badopt;
11040Sstevel@tonic-gate 			args->flags |= NFSMNT_ACREGMAX;
11050Sstevel@tonic-gate 			break;
11060Sstevel@tonic-gate 		case OPT_ACDIRMIN:
1107*12782SMarcel.Telka@Sun.COM 			if (convert_int(&args->acdirmin, val) != 0)
1108*12782SMarcel.Telka@Sun.COM 				goto badopt;
11090Sstevel@tonic-gate 			args->flags |= NFSMNT_ACDIRMIN;
11100Sstevel@tonic-gate 			break;
11110Sstevel@tonic-gate 		case OPT_ACDIRMAX:
1112*12782SMarcel.Telka@Sun.COM 			if (convert_int(&args->acdirmax, val) != 0)
1113*12782SMarcel.Telka@Sun.COM 				goto badopt;
11140Sstevel@tonic-gate 			args->flags |= NFSMNT_ACDIRMAX;
11150Sstevel@tonic-gate 			break;
11160Sstevel@tonic-gate 		case OPT_BG:
11170Sstevel@tonic-gate 			bg++;
11180Sstevel@tonic-gate 			break;
11190Sstevel@tonic-gate 		case OPT_FG:
11200Sstevel@tonic-gate 			bg = 0;
11210Sstevel@tonic-gate 			break;
11220Sstevel@tonic-gate 		case OPT_RETRY:
1123*12782SMarcel.Telka@Sun.COM 			if (convert_int(&retries, val) != 0)
11240Sstevel@tonic-gate 				goto badopt;
11250Sstevel@tonic-gate 			break;
11260Sstevel@tonic-gate 		case OPT_LLOCK:
11270Sstevel@tonic-gate 			args->flags |= NFSMNT_LLOCK;
11280Sstevel@tonic-gate 			break;
11290Sstevel@tonic-gate 		case OPT_POSIX:
11300Sstevel@tonic-gate 			posix = 1;
11310Sstevel@tonic-gate 			break;
11320Sstevel@tonic-gate 		case OPT_VERS:
1133*12782SMarcel.Telka@Sun.COM 			if (convert_int(&num, val) != 0)
11340Sstevel@tonic-gate 				goto badopt;
1135*12782SMarcel.Telka@Sun.COM 			nfsvers = (rpcvers_t)num;
11360Sstevel@tonic-gate 			break;
11370Sstevel@tonic-gate 		case OPT_PROTO:
113877Soa138391 			if (val == NULL)
113977Soa138391 				goto badopt;
114077Soa138391 
11410Sstevel@tonic-gate 			nfs_proto = (char *)malloc(strlen(val)+1);
114277Soa138391 			if (!nfs_proto) {
114377Soa138391 				pr_err(gettext("no memory"));
114477Soa138391 				return (RET_ERR);
114577Soa138391 			}
114677Soa138391 
114777Soa138391 			(void) strncpy(nfs_proto, val, strlen(val)+1);
11480Sstevel@tonic-gate 			break;
11495302Sth199096 
11500Sstevel@tonic-gate 		case OPT_NOPRINT:
11510Sstevel@tonic-gate 			args->flags |= NFSMNT_NOPRINT;
11520Sstevel@tonic-gate 			break;
11535302Sth199096 
11540Sstevel@tonic-gate 		case OPT_LARGEFILES:
11550Sstevel@tonic-gate 			largefiles = 1;
11560Sstevel@tonic-gate 			break;
11575302Sth199096 
11580Sstevel@tonic-gate 		case OPT_NOLARGEFILES:
11590Sstevel@tonic-gate 			pr_err(gettext("NFS can't support \"nolargefiles\"\n"));
11600Sstevel@tonic-gate 			free(optstr);
11610Sstevel@tonic-gate 			return (RET_ERR);
11620Sstevel@tonic-gate 
11630Sstevel@tonic-gate 		case OPT_SEC:
11645885Sdougm 			if (val == NULL) {
11655885Sdougm 				pr_err(gettext(
11665885Sdougm 				    "\"sec\" option requires argument\n"));
11675885Sdougm 				return (RET_ERR);
11685885Sdougm 			}
11690Sstevel@tonic-gate 			if (nfs_getseconfig_byname(val, &nfs_sec)) {
11705302Sth199096 				pr_err(gettext("can not get \"%s\" from %s\n"),
11715302Sth199096 				    val, NFSSEC_CONF);
11725302Sth199096 				return (RET_ERR);
11730Sstevel@tonic-gate 			}
11740Sstevel@tonic-gate 			sec_opt++;
11750Sstevel@tonic-gate 			break;
11760Sstevel@tonic-gate 
11770Sstevel@tonic-gate 		case OPT_PUBLIC:
11780Sstevel@tonic-gate 			public_opt = TRUE;
11790Sstevel@tonic-gate 			break;
11800Sstevel@tonic-gate 
11810Sstevel@tonic-gate 		case OPT_DIRECTIO:
11820Sstevel@tonic-gate 			args->flags |= NFSMNT_DIRECTIO;
11830Sstevel@tonic-gate 			break;
11840Sstevel@tonic-gate 
11850Sstevel@tonic-gate 		case OPT_NODIRECTIO:
11860Sstevel@tonic-gate 			args->flags &= ~(NFSMNT_DIRECTIO);
11870Sstevel@tonic-gate 			break;
11880Sstevel@tonic-gate 
11890Sstevel@tonic-gate 		case OPT_XATTR:
11900Sstevel@tonic-gate 		case OPT_NOXATTR:
11910Sstevel@tonic-gate 			/*
11920Sstevel@tonic-gate 			 * VFS options; just need to get them into the
11930Sstevel@tonic-gate 			 * new mount option string and note we've seen them
11940Sstevel@tonic-gate 			 */
11950Sstevel@tonic-gate 			attrpref = 1;
11960Sstevel@tonic-gate 			break;
11970Sstevel@tonic-gate 		default:
11980Sstevel@tonic-gate 			/*
11990Sstevel@tonic-gate 			 * Note that this could be a valid OPT_* option so
12000Sstevel@tonic-gate 			 * we can't use "val" but need to use "saveopt".
12010Sstevel@tonic-gate 			 */
12020Sstevel@tonic-gate 			if (fsisstdopt(saveopt))
12030Sstevel@tonic-gate 				break;
12040Sstevel@tonic-gate 			invalid = 1;
12050Sstevel@tonic-gate 			if (!qflg)
12060Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(
12070Sstevel@tonic-gate 				    "mount: %s on %s - WARNING unknown option"
12080Sstevel@tonic-gate 				    " \"%s\"\n"), mnt->mnt_special,
12090Sstevel@tonic-gate 				    mnt->mnt_mountp, saveopt);
12100Sstevel@tonic-gate 			break;
12110Sstevel@tonic-gate 		}
12120Sstevel@tonic-gate 		if (!invalid) {
12130Sstevel@tonic-gate 			if (newopts[0])
12140Sstevel@tonic-gate 				strcat(newopts, ",");
12150Sstevel@tonic-gate 			strcat(newopts, saveopt);
12160Sstevel@tonic-gate 		}
12170Sstevel@tonic-gate 	}
12180Sstevel@tonic-gate 	/* Default is to turn extended attrs on */
12190Sstevel@tonic-gate 	if (!attrpref) {
12200Sstevel@tonic-gate 		if (newopts[0])
12210Sstevel@tonic-gate 			strcat(newopts, ",");
12220Sstevel@tonic-gate 		strcat(newopts, MNTOPT_XATTR);
12230Sstevel@tonic-gate 	}
12240Sstevel@tonic-gate 	strcpy(mnt->mnt_mntopts, newopts);
12250Sstevel@tonic-gate 	free(newopts);
12260Sstevel@tonic-gate 	free(optstr);
12270Sstevel@tonic-gate 
12280Sstevel@tonic-gate 	/* ensure that only one secure mode is requested */
12290Sstevel@tonic-gate 	if (sec_opt > 1) {
12300Sstevel@tonic-gate 		pr_err(gettext("Security options conflict\n"));
12310Sstevel@tonic-gate 		return (RET_ERR);
12320Sstevel@tonic-gate 	}
12330Sstevel@tonic-gate 
12340Sstevel@tonic-gate 	/* ensure that the user isn't trying to get large files over V2 */
12350Sstevel@tonic-gate 	if (nfsvers == NFS_VERSION && largefiles) {
12360Sstevel@tonic-gate 		pr_err(gettext("NFS V2 can't support \"largefiles\"\n"));
12370Sstevel@tonic-gate 		return (RET_ERR);
12380Sstevel@tonic-gate 	}
12390Sstevel@tonic-gate 
12400Sstevel@tonic-gate 	if (nfsvers == NFS_V4 &&
12410Sstevel@tonic-gate 	    nfs_proto != NULL &&
12420Sstevel@tonic-gate 	    strncasecmp(nfs_proto, NC_UDP, strlen(NC_UDP)) == 0) {
12430Sstevel@tonic-gate 		pr_err(gettext("NFS V4 does not support %s\n"), nfs_proto);
12440Sstevel@tonic-gate 		return (RET_ERR);
12450Sstevel@tonic-gate 	}
12460Sstevel@tonic-gate 
12470Sstevel@tonic-gate 	return (RET_OK);
12480Sstevel@tonic-gate 
12490Sstevel@tonic-gate badopt:
12500Sstevel@tonic-gate 	pr_err(gettext("invalid option: \"%s\"\n"), saveopt);
12510Sstevel@tonic-gate 	free(optstr);
12520Sstevel@tonic-gate 	return (RET_ERR);
12530Sstevel@tonic-gate }
12540Sstevel@tonic-gate 
12550Sstevel@tonic-gate static int
12560Sstevel@tonic-gate make_secure(struct nfs_args *args, char *hostname, struct netconfig *nconf,
12570Sstevel@tonic-gate 	bool_t use_pubfh, rpcvers_t vers)
12580Sstevel@tonic-gate {
12590Sstevel@tonic-gate 	sec_data_t *secdata;
12600Sstevel@tonic-gate 	int flags;
12610Sstevel@tonic-gate 	struct netbuf *syncaddr = NULL;
12620Sstevel@tonic-gate 	struct nd_addrlist *retaddrs = NULL;
12630Sstevel@tonic-gate 	char netname[MAXNETNAMELEN+1];
12640Sstevel@tonic-gate 
12650Sstevel@tonic-gate 	/*
12660Sstevel@tonic-gate 	 * check to see if any secure mode is requested.
12670Sstevel@tonic-gate 	 * if not, use default security mode.
12680Sstevel@tonic-gate 	 */
12690Sstevel@tonic-gate 	if (!snego_done && !sec_opt) {
12700Sstevel@tonic-gate 		/*
12715302Sth199096 		 * Get default security mode.
12725302Sth199096 		 * AUTH_UNIX has been the default choice for a long time.
12735302Sth199096 		 * The better NFS security service becomes, the better chance
12745302Sth199096 		 * we will set stronger security service as the default NFS
12755302Sth199096 		 * security mode.
12760Sstevel@tonic-gate 		 */
12775302Sth199096 		if (nfs_getseconfig_default(&nfs_sec)) {
12785302Sth199096 			pr_err(gettext("error getting default"
12795302Sth199096 			    " security entry\n"));
12805302Sth199096 			return (-1);
12815302Sth199096 		}
12825302Sth199096 		args->flags |= NFSMNT_SECDEFAULT;
12830Sstevel@tonic-gate 	}
12840Sstevel@tonic-gate 
12850Sstevel@tonic-gate 	/*
12860Sstevel@tonic-gate 	 * Get the network address for the time service on the server.
12870Sstevel@tonic-gate 	 * If an RPC based time service is not available then try the
12880Sstevel@tonic-gate 	 * IP time service.
12890Sstevel@tonic-gate 	 *
12900Sstevel@tonic-gate 	 * This is for AUTH_DH processing. We will also pass down syncaddr
12910Sstevel@tonic-gate 	 * and netname for NFS V4 even if AUTH_DH is not requested right now.
12920Sstevel@tonic-gate 	 * NFS V4 does security negotiation in the kernel via SECINFO.
12930Sstevel@tonic-gate 	 * These information might be needed later in the kernel.
12940Sstevel@tonic-gate 	 *
12950Sstevel@tonic-gate 	 * Eventurally, we want to move this code to nfs_clnt_secdata()
12960Sstevel@tonic-gate 	 * when autod_nfs.c and mount.c can share the same get_the_addr()
12970Sstevel@tonic-gate 	 * routine.
12980Sstevel@tonic-gate 	 */
12990Sstevel@tonic-gate 	flags = 0;
13000Sstevel@tonic-gate 	syncaddr = NULL;
13010Sstevel@tonic-gate 
13020Sstevel@tonic-gate 	if (nfs_sec.sc_rpcnum == AUTH_DH || vers == NFS_V4) {
13030Sstevel@tonic-gate 		/*
13040Sstevel@tonic-gate 		 * If using the public fh or nfsv4, we will not contact the
13050Sstevel@tonic-gate 		 * remote RPCBINDer, since it is possibly behind a firewall.
13060Sstevel@tonic-gate 		 */
13075302Sth199096 		if (use_pubfh == FALSE && vers != NFS_V4)
13080Sstevel@tonic-gate 			syncaddr = get_the_addr(hostname, RPCBPROG, RPCBVERS,
13095302Sth199096 			    nconf, 0, NULL, NULL, FALSE, NULL, NULL);
13100Sstevel@tonic-gate 
13110Sstevel@tonic-gate 		if (syncaddr != NULL) {
13120Sstevel@tonic-gate 			/* for flags in sec_data */
13130Sstevel@tonic-gate 			flags |= AUTH_F_RPCTIMESYNC;
13140Sstevel@tonic-gate 		} else {
13150Sstevel@tonic-gate 			struct nd_hostserv hs;
13160Sstevel@tonic-gate 			int error;
13170Sstevel@tonic-gate 
13180Sstevel@tonic-gate 			hs.h_host = hostname;
13190Sstevel@tonic-gate 			hs.h_serv = "timserver";
13200Sstevel@tonic-gate 
13210Sstevel@tonic-gate 			error = netdir_getbyname(nconf, &hs, &retaddrs);
13220Sstevel@tonic-gate 
13230Sstevel@tonic-gate 			if (error != ND_OK && (nfs_sec.sc_rpcnum == AUTH_DH)) {
13245302Sth199096 				pr_err(gettext("%s: secure: no time service\n"),
13255302Sth199096 				    hostname);
13265302Sth199096 				return (-1);
13270Sstevel@tonic-gate 			}
13280Sstevel@tonic-gate 
13290Sstevel@tonic-gate 			if (error == ND_OK)
13300Sstevel@tonic-gate 				syncaddr = retaddrs->n_addrs;
13310Sstevel@tonic-gate 
13320Sstevel@tonic-gate 			/*
13330Sstevel@tonic-gate 			 * For NFS_V4 if AUTH_DH is negotiated later in the
13340Sstevel@tonic-gate 			 * kernel thru SECINFO, it will need syncaddr
13350Sstevel@tonic-gate 			 * and netname data.
13360Sstevel@tonic-gate 			 */
13370Sstevel@tonic-gate 			if (vers == NFS_V4 && syncaddr &&
13385302Sth199096 			    host2netname(netname, hostname, NULL)) {
13395302Sth199096 				args->syncaddr = malloc(sizeof (struct netbuf));
13405302Sth199096 				args->syncaddr->buf = malloc(syncaddr->len);
13415302Sth199096 				(void) memcpy(args->syncaddr->buf,
13425302Sth199096 				    syncaddr->buf, syncaddr->len);
13435302Sth199096 				args->syncaddr->len = syncaddr->len;
13445302Sth199096 				args->syncaddr->maxlen = syncaddr->maxlen;
13455302Sth199096 				args->netname = strdup(netname);
13465302Sth199096 				args->flags |= NFSMNT_SECURE;
13470Sstevel@tonic-gate 			}
13480Sstevel@tonic-gate 		}
13490Sstevel@tonic-gate 	}
13500Sstevel@tonic-gate 
13510Sstevel@tonic-gate 	/*
13520Sstevel@tonic-gate 	 * For the initial chosen flavor (any flavor defined in nfssec.conf),
13530Sstevel@tonic-gate 	 * the data will be stored in the sec_data structure via
13540Sstevel@tonic-gate 	 * nfs_clnt_secdata() and be passed to the kernel via nfs_args_*
13550Sstevel@tonic-gate 	 * extended data structure.
13560Sstevel@tonic-gate 	 */
13570Sstevel@tonic-gate 	if (!(secdata = nfs_clnt_secdata(&nfs_sec, hostname, args->knconf,
13585302Sth199096 	    syncaddr, flags))) {
13590Sstevel@tonic-gate 		pr_err(gettext("errors constructing security related data\n"));
13600Sstevel@tonic-gate 		if (flags & AUTH_F_RPCTIMESYNC) {
13610Sstevel@tonic-gate 			free(syncaddr->buf);
13620Sstevel@tonic-gate 			free(syncaddr);
13630Sstevel@tonic-gate 		} else if (retaddrs)
13640Sstevel@tonic-gate 			netdir_free((void *)retaddrs, ND_ADDRLIST);
13650Sstevel@tonic-gate 		return (-1);
13660Sstevel@tonic-gate 	}
13670Sstevel@tonic-gate 
13680Sstevel@tonic-gate 	NFS_ARGS_EXTB_secdata(args, secdata);
13690Sstevel@tonic-gate 	if (flags & AUTH_F_RPCTIMESYNC) {
13700Sstevel@tonic-gate 		free(syncaddr->buf);
13710Sstevel@tonic-gate 		free(syncaddr);
13720Sstevel@tonic-gate 	} else if (retaddrs)
13730Sstevel@tonic-gate 		netdir_free((void *)retaddrs, ND_ADDRLIST);
13740Sstevel@tonic-gate 	return (0);
13750Sstevel@tonic-gate }
13760Sstevel@tonic-gate 
13770Sstevel@tonic-gate /*
13780Sstevel@tonic-gate  * Get the network address on "hostname" for program "prog"
13790Sstevel@tonic-gate  * with version "vers" by using the nconf configuration data
13800Sstevel@tonic-gate  * passed in.
13810Sstevel@tonic-gate  *
13820Sstevel@tonic-gate  * If the address of a netconfig pointer is null then
13830Sstevel@tonic-gate  * information is not sufficient and no netbuf will be returned.
13840Sstevel@tonic-gate  *
13850Sstevel@tonic-gate  * Finally, ping the null procedure of that service.
13860Sstevel@tonic-gate  *
13870Sstevel@tonic-gate  * A similar routine is also defined in ../../autofs/autod_nfs.c.
13880Sstevel@tonic-gate  * This is a potential routine to move to ../lib for common usage.
13890Sstevel@tonic-gate  */
13900Sstevel@tonic-gate static struct netbuf *
13910Sstevel@tonic-gate get_the_addr(char *hostname, ulong_t prog, ulong_t vers,
13920Sstevel@tonic-gate 	struct netconfig *nconf, ushort_t port, struct t_info *tinfo,
13930Sstevel@tonic-gate 	caddr_t *fhp, bool_t get_pubfh, char *fspath, err_ret_t *error)
13940Sstevel@tonic-gate {
13950Sstevel@tonic-gate 	struct netbuf *nb = NULL;
13960Sstevel@tonic-gate 	struct t_bind *tbind = NULL;
13970Sstevel@tonic-gate 	CLIENT *cl = NULL;
13980Sstevel@tonic-gate 	struct timeval tv;
13990Sstevel@tonic-gate 	int fd = -1;
14000Sstevel@tonic-gate 	AUTH *ah = NULL;
14010Sstevel@tonic-gate 	AUTH *new_ah = NULL;
14020Sstevel@tonic-gate 	struct snego_t snego;
14030Sstevel@tonic-gate 
14040Sstevel@tonic-gate 	if (nconf == NULL)
14050Sstevel@tonic-gate 		return (NULL);
14060Sstevel@tonic-gate 
14070Sstevel@tonic-gate 	if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) == -1)
14085302Sth199096 		goto done;
14090Sstevel@tonic-gate 
14100Sstevel@tonic-gate 	/* LINTED pointer alignment */
14110Sstevel@tonic-gate 	if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR))
14125302Sth199096 	    == NULL)
14130Sstevel@tonic-gate 		goto done;
14140Sstevel@tonic-gate 
14150Sstevel@tonic-gate 	/*
14160Sstevel@tonic-gate 	 * In the case of public filehandle usage or NFSv4 we want to
14170Sstevel@tonic-gate 	 * avoid use of the rpcbind/portmap protocol
14180Sstevel@tonic-gate 	 */
14190Sstevel@tonic-gate 	if ((get_pubfh == TRUE) || (vers == NFS_V4)) {
14200Sstevel@tonic-gate 		struct nd_hostserv hs;
14210Sstevel@tonic-gate 		struct nd_addrlist *retaddrs;
14220Sstevel@tonic-gate 		int retval;
14230Sstevel@tonic-gate 		hs.h_host = hostname;
14240Sstevel@tonic-gate 
14250Sstevel@tonic-gate 		/* NFS where vers==4 does not support UDP */
14260Sstevel@tonic-gate 		if (vers == NFS_V4 &&
14270Sstevel@tonic-gate 		    strncasecmp(nconf->nc_proto, NC_UDP,
14285302Sth199096 		    strlen(NC_UDP)) == 0) {
142977Soa138391 			SET_ERR_RET(error, ERR_PROTO_UNSUPP, 0);
14300Sstevel@tonic-gate 			goto done;
14310Sstevel@tonic-gate 		}
14320Sstevel@tonic-gate 
14330Sstevel@tonic-gate 		if (port == 0)
14340Sstevel@tonic-gate 			hs.h_serv = "nfs";
14350Sstevel@tonic-gate 		else
14360Sstevel@tonic-gate 			hs.h_serv = NULL;
14370Sstevel@tonic-gate 
14380Sstevel@tonic-gate 		if ((retval = netdir_getbyname(nconf, &hs, &retaddrs))
14390Sstevel@tonic-gate 		    != ND_OK) {
14400Sstevel@tonic-gate 			/*
14410Sstevel@tonic-gate 			 * Carefully set the error value here. Want to signify
14420Sstevel@tonic-gate 			 * that the error was an unknown host.
14430Sstevel@tonic-gate 			 */
14440Sstevel@tonic-gate 			if (retval == ND_NOHOST) {
14450Sstevel@tonic-gate 				SET_ERR_RET(error, ERR_NOHOST, retval);
14460Sstevel@tonic-gate 			}
14470Sstevel@tonic-gate 
14480Sstevel@tonic-gate 			goto done;
14490Sstevel@tonic-gate 		}
14500Sstevel@tonic-gate 		memcpy(tbind->addr.buf, retaddrs->n_addrs->buf,
14515302Sth199096 		    retaddrs->n_addrs->len);
14520Sstevel@tonic-gate 		tbind->addr.len = retaddrs->n_addrs->len;
14530Sstevel@tonic-gate 		netdir_free((void *)retaddrs, ND_ADDRLIST);
14540Sstevel@tonic-gate 		(void) netdir_options(nconf, ND_SET_RESERVEDPORT, fd, NULL);
14550Sstevel@tonic-gate 
14560Sstevel@tonic-gate 	} else {
14570Sstevel@tonic-gate 		if (rpcb_getaddr(prog, vers, nconf, &tbind->addr,
14580Sstevel@tonic-gate 		    hostname) == FALSE) {
14590Sstevel@tonic-gate 			goto done;
14600Sstevel@tonic-gate 		}
14610Sstevel@tonic-gate 	}
14620Sstevel@tonic-gate 
14630Sstevel@tonic-gate 	if (port) {
14640Sstevel@tonic-gate 		/* LINTED pointer alignment */
14650Sstevel@tonic-gate 		if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
14660Sstevel@tonic-gate 			((struct sockaddr_in *)tbind->addr.buf)->sin_port
14675302Sth199096 			    = port;
14680Sstevel@tonic-gate 		else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
14690Sstevel@tonic-gate 			((struct sockaddr_in6 *)tbind->addr.buf)->sin6_port
14705302Sth199096 			    = port;
14710Sstevel@tonic-gate 
14720Sstevel@tonic-gate 	}
14730Sstevel@tonic-gate 
14740Sstevel@tonic-gate 	cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0);
14750Sstevel@tonic-gate 	if (cl == NULL) {
147677Soa138391 		/*
147777Soa138391 		 * clnt_tli_create() returns either RPC_SYSTEMERROR,
147877Soa138391 		 * RPC_UNKNOWNPROTO or RPC_TLIERROR. The RPC_TLIERROR translates
147977Soa138391 		 * to "Misc. TLI error". This is not too helpful. Most likely
148077Soa138391 		 * the connection to the remote server timed out, so this
148177Soa138391 		 * error is at least less perplexing.
148277Soa138391 		 * See: usr/src/cmd/rpcinfo/rpcinfo.c
148377Soa138391 		 */
148477Soa138391 		if (rpc_createerr.cf_stat == RPC_TLIERROR) {
148577Soa138391 			SET_ERR_RET(error, ERR_RPCERROR, RPC_PMAPFAILURE);
148677Soa138391 		} else {
148777Soa138391 			SET_ERR_RET(error, ERR_RPCERROR, rpc_createerr.cf_stat);
148877Soa138391 		}
14890Sstevel@tonic-gate 		goto done;
14900Sstevel@tonic-gate 	}
14910Sstevel@tonic-gate 
14920Sstevel@tonic-gate 	ah = authsys_create_default();
14930Sstevel@tonic-gate 	if (ah != NULL)
14940Sstevel@tonic-gate 		cl->cl_auth = ah;
14950Sstevel@tonic-gate 
14960Sstevel@tonic-gate 	tv.tv_sec = 5;
14970Sstevel@tonic-gate 	tv.tv_usec = 0;
14980Sstevel@tonic-gate 
14990Sstevel@tonic-gate 	(void) clnt_control(cl, CLSET_TIMEOUT, (char *)&tv);
15000Sstevel@tonic-gate 
15010Sstevel@tonic-gate 	if ((get_pubfh == TRUE) && (vers != NFS_V4)) {
15025302Sth199096 		enum snego_stat sec;
15030Sstevel@tonic-gate 
15045302Sth199096 		if (!snego_done) {
15055302Sth199096 			/*
15065302Sth199096 			 * negotiate sec flavor.
15075302Sth199096 			 */
15085302Sth199096 			snego.cnt = 0;
15095302Sth199096 			if ((sec = nfs_sec_nego(vers, cl, fspath, &snego)) ==
15105302Sth199096 			    SNEGO_SUCCESS) {
15115302Sth199096 				int jj;
15120Sstevel@tonic-gate 
15135302Sth199096 				/*
15145302Sth199096 				 * check if server supports the one
15155302Sth199096 				 * specified in the sec= option.
15165302Sth199096 				 */
15170Sstevel@tonic-gate 				if (sec_opt) {
15185302Sth199096 					for (jj = 0; jj < snego.cnt; jj++) {
15195302Sth199096 						if (snego.array[jj] ==
15205302Sth199096 						    nfs_sec.sc_nfsnum) {
15215302Sth199096 							snego_done = TRUE;
15225302Sth199096 							break;
15235302Sth199096 						}
15245302Sth199096 					}
15250Sstevel@tonic-gate 				}
15265302Sth199096 
15275302Sth199096 				/*
15285302Sth199096 				 * find a common sec flavor
15295302Sth199096 				 */
15305302Sth199096 				if (!snego_done) {
15315302Sth199096 					if (sec_opt) {
15325302Sth199096 						pr_err(gettext(
15335302Sth199096 						    "Server does not support"
15345302Sth199096 						    " the security flavor"
15355302Sth199096 						    " specified.\n"));
15365302Sth199096 					}
15370Sstevel@tonic-gate 
15385302Sth199096 					for (jj = 0; jj < snego.cnt; jj++) {
15395302Sth199096 						if (!nfs_getseconfig_bynumber(
15405302Sth199096 						    snego.array[jj],
15415302Sth199096 						    &nfs_sec)) {
15425302Sth199096 							snego_done = TRUE;
15435302Sth199096 #define	EMSG80SUX "Security flavor %d was negotiated and will be used.\n"
15445302Sth199096 							if (sec_opt)
15455302Sth199096 								pr_err(gettext(
15465302Sth199096 								    EMSG80SUX),
15475302Sth199096 								    nfs_sec.
15485302Sth199096 								    sc_nfsnum);
15495302Sth199096 							break;
15505302Sth199096 						}
15515302Sth199096 					}
15525302Sth199096 				}
15535302Sth199096 
15545302Sth199096 				if (!snego_done)
15555302Sth199096 					return (NULL);
15560Sstevel@tonic-gate 
15575302Sth199096 				/*
15585302Sth199096 				 * Now that the flavor has been
15595302Sth199096 				 * negotiated, get the fh.
15605302Sth199096 				 *
15615302Sth199096 				 * First, create an auth handle using the
15625302Sth199096 				 * negotiated sec flavor in the next lookup to
15635302Sth199096 				 * fetch the filehandle.
15645302Sth199096 				 */
15655302Sth199096 				new_ah = nfs_create_ah(cl, hostname, &nfs_sec);
15665302Sth199096 				if (new_ah == NULL)
15675302Sth199096 					goto done;
15685302Sth199096 				cl->cl_auth = new_ah;
15695302Sth199096 			} else if (sec == SNEGO_ARRAY_TOO_SMALL || sec ==
15705302Sth199096 			    SNEGO_FAILURE) {
15715302Sth199096 				goto done;
15725302Sth199096 			}
15730Sstevel@tonic-gate 
15745302Sth199096 			/*
15755302Sth199096 			 * Note that if sec == SNEGO_DEF_VALID
15765302Sth199096 			 * default sec flavor is acceptable.
15775302Sth199096 			 * Use it to get the filehandle.
15785302Sth199096 			 */
15790Sstevel@tonic-gate 		}
15800Sstevel@tonic-gate 
15815302Sth199096 		if (vers == NFS_VERSION) {
15825302Sth199096 			wnl_diropargs arg;
15837693SVallish.Vaidyeshwara@Sun.COM 			wnl_diropres res;
15845302Sth199096 
15855302Sth199096 			memset((char *)&arg.dir, 0, sizeof (wnl_fh));
15865302Sth199096 			arg.name = fspath;
15877693SVallish.Vaidyeshwara@Sun.COM 			memset((char *)&res, 0, sizeof (wnl_diropres));
15887693SVallish.Vaidyeshwara@Sun.COM 			if (wnlproc_lookup_2(&arg, &res, cl) !=
15897693SVallish.Vaidyeshwara@Sun.COM 			    RPC_SUCCESS || res.status != NFS_OK)
15907693SVallish.Vaidyeshwara@Sun.COM 				goto done;
15915302Sth199096 
15925302Sth199096 			*fhp = malloc(sizeof (wnl_fh));
15930Sstevel@tonic-gate 
15945302Sth199096 			if (*fhp == NULL) {
15955302Sth199096 				pr_err(gettext("no memory\n"));
15965302Sth199096 				goto done;
15975302Sth199096 			}
15980Sstevel@tonic-gate 
15995302Sth199096 			memcpy((char *)*fhp,
16007693SVallish.Vaidyeshwara@Sun.COM 			    (char *)&res.wnl_diropres_u.wnl_diropres.file,
16015302Sth199096 			    sizeof (wnl_fh));
16025302Sth199096 		} else {
16035302Sth199096 			WNL_LOOKUP3args arg;
16047693SVallish.Vaidyeshwara@Sun.COM 			WNL_LOOKUP3res res;
16055302Sth199096 			nfs_fh3 *fh3p;
16060Sstevel@tonic-gate 
16075302Sth199096 			memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3));
16085302Sth199096 			arg.what.name = fspath;
16097693SVallish.Vaidyeshwara@Sun.COM 			memset((char *)&res, 0, sizeof (WNL_LOOKUP3res));
16107693SVallish.Vaidyeshwara@Sun.COM 			if (wnlproc3_lookup_3(&arg, &res, cl) !=
16117693SVallish.Vaidyeshwara@Sun.COM 			    RPC_SUCCESS || res.status != NFS3_OK)
16125302Sth199096 				goto done;
16135302Sth199096 
16145302Sth199096 			fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
16150Sstevel@tonic-gate 
16165302Sth199096 			if (fh3p == NULL) {
16175302Sth199096 				pr_err(gettext("no memory\n"));
16185302Sth199096 				goto done;
16195302Sth199096 			}
16200Sstevel@tonic-gate 
16215302Sth199096 			fh3p->fh3_length =
16227693SVallish.Vaidyeshwara@Sun.COM 			    res.WNL_LOOKUP3res_u.res_ok.object.data.data_len;
16235302Sth199096 			memcpy(fh3p->fh3_u.data,
16248650SVallish.Vaidyeshwara@Sun.COM 			    res.WNL_LOOKUP3res_u.res_ok.object.data.data_val,
16255302Sth199096 			    fh3p->fh3_length);
16260Sstevel@tonic-gate 
16275302Sth199096 			*fhp = (caddr_t)fh3p;
16285302Sth199096 		}
16290Sstevel@tonic-gate 	} else {
16300Sstevel@tonic-gate 		struct rpc_err r_err;
16317693SVallish.Vaidyeshwara@Sun.COM 		enum clnt_stat rc;
16320Sstevel@tonic-gate 
16337693SVallish.Vaidyeshwara@Sun.COM 		/*
16347693SVallish.Vaidyeshwara@Sun.COM 		 * NULL procedures need not have an argument or
16357693SVallish.Vaidyeshwara@Sun.COM 		 * result param.
16367693SVallish.Vaidyeshwara@Sun.COM 		 */
16370Sstevel@tonic-gate 		if (vers == NFS_VERSION)
16387693SVallish.Vaidyeshwara@Sun.COM 			rc = wnlproc_null_2(NULL, NULL, cl);
16390Sstevel@tonic-gate 		else if (vers == NFS_V3)
16407693SVallish.Vaidyeshwara@Sun.COM 			rc = wnlproc3_null_3(NULL, NULL, cl);
16410Sstevel@tonic-gate 		else
16427693SVallish.Vaidyeshwara@Sun.COM 			rc = wnlproc4_null_4(NULL, NULL, cl);
16430Sstevel@tonic-gate 
16447693SVallish.Vaidyeshwara@Sun.COM 		if (rc != RPC_SUCCESS) {
16450Sstevel@tonic-gate 			clnt_geterr(cl, &r_err);
16460Sstevel@tonic-gate 			if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
16470Sstevel@tonic-gate 				switch (r_err.re_status) {
16480Sstevel@tonic-gate 				case RPC_TLIERROR:
16490Sstevel@tonic-gate 				case RPC_CANTRECV:
16500Sstevel@tonic-gate 				case RPC_CANTSEND:
16510Sstevel@tonic-gate 					r_err.re_status = RPC_PROGVERSMISMATCH;
16520Sstevel@tonic-gate 				}
16530Sstevel@tonic-gate 			}
16540Sstevel@tonic-gate 			SET_ERR_RET(error, ERR_RPCERROR, r_err.re_status);
16550Sstevel@tonic-gate 			goto done;
16560Sstevel@tonic-gate 		}
16570Sstevel@tonic-gate 	}
16580Sstevel@tonic-gate 
16590Sstevel@tonic-gate 	/*
16600Sstevel@tonic-gate 	 * Make a copy of the netbuf to return
16610Sstevel@tonic-gate 	 */
16620Sstevel@tonic-gate 	nb = (struct netbuf *)malloc(sizeof (*nb));
16630Sstevel@tonic-gate 	if (nb == NULL) {
16640Sstevel@tonic-gate 		pr_err(gettext("no memory\n"));
16650Sstevel@tonic-gate 		goto done;
16660Sstevel@tonic-gate 	}
16670Sstevel@tonic-gate 	*nb = tbind->addr;
16680Sstevel@tonic-gate 	nb->buf = (char *)malloc(nb->maxlen);
16690Sstevel@tonic-gate 	if (nb->buf == NULL) {
16700Sstevel@tonic-gate 		pr_err(gettext("no memory\n"));
16710Sstevel@tonic-gate 		free(nb);
16720Sstevel@tonic-gate 		nb = NULL;
16730Sstevel@tonic-gate 		goto done;
16740Sstevel@tonic-gate 	}
16750Sstevel@tonic-gate 	(void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
16760Sstevel@tonic-gate 
16770Sstevel@tonic-gate done:
16780Sstevel@tonic-gate 	if (cl) {
16795302Sth199096 		if (ah != NULL) {
16805302Sth199096 			if (new_ah != NULL)
16815302Sth199096 				AUTH_DESTROY(ah);
16825302Sth199096 			AUTH_DESTROY(cl->cl_auth);
16835302Sth199096 			cl->cl_auth = NULL;
16845302Sth199096 		}
16855302Sth199096 		clnt_destroy(cl);
16865302Sth199096 		cl = NULL;
16870Sstevel@tonic-gate 	}
16880Sstevel@tonic-gate 	if (tbind) {
16890Sstevel@tonic-gate 		t_free((char *)tbind, T_BIND);
16900Sstevel@tonic-gate 		tbind = NULL;
16910Sstevel@tonic-gate 	}
16920Sstevel@tonic-gate 	if (fd >= 0)
16930Sstevel@tonic-gate 		(void) t_close(fd);
16940Sstevel@tonic-gate 	return (nb);
16950Sstevel@tonic-gate }
16960Sstevel@tonic-gate 
16975302Sth199096 static int
16985302Sth199096 check_nconf(struct netconfig *nconf, int nthtry, int *valid_proto)
16995302Sth199096 {
17005302Sth199096 	int	try_test = 0;
17015302Sth199096 	int	valid_family;
17025302Sth199096 	char	*proto = NULL;
17035302Sth199096 
17045302Sth199096 
17055302Sth199096 	if (nthtry == FIRST_TRY) {
17065302Sth199096 		try_test = ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
17075302Sth199096 		    (nconf->nc_semantics == NC_TPI_COTS));
17085302Sth199096 		proto = NC_TCP;
17095302Sth199096 	} else if (nthtry == SECOND_TRY) {
17105302Sth199096 		try_test = (nconf->nc_semantics == NC_TPI_CLTS);
17115302Sth199096 		proto = NC_UDP;
17125302Sth199096 	}
17135302Sth199096 
17145302Sth199096 	if (proto &&
17155302Sth199096 	    (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
17165302Sth199096 	    strcmp(nconf->nc_protofmly, NC_INET6) == 0) &&
17175302Sth199096 	    (strcmp(nconf->nc_proto, proto) == 0))
17185302Sth199096 		*valid_proto = TRUE;
17195302Sth199096 	else
17205302Sth199096 		*valid_proto = FALSE;
17215302Sth199096 
17225302Sth199096 	return (try_test);
17235302Sth199096 }
17245302Sth199096 
17250Sstevel@tonic-gate /*
17260Sstevel@tonic-gate  * Get a network address on "hostname" for program "prog"
17270Sstevel@tonic-gate  * with version "vers".  If the port number is specified (non zero)
17280Sstevel@tonic-gate  * then try for a TCP/UDP transport and set the port number of the
17290Sstevel@tonic-gate  * resulting IP address.
17300Sstevel@tonic-gate  *
17310Sstevel@tonic-gate  * If the address of a netconfig pointer was passed and
17320Sstevel@tonic-gate  * if it's not null, use it as the netconfig otherwise
17330Sstevel@tonic-gate  * assign the address of the netconfig that was used to
17340Sstevel@tonic-gate  * establish contact with the service.
17350Sstevel@tonic-gate  *
17360Sstevel@tonic-gate  * A similar routine is also defined in ../../autofs/autod_nfs.c.
17370Sstevel@tonic-gate  * This is a potential routine to move to ../lib for common usage.
17380Sstevel@tonic-gate  *
17390Sstevel@tonic-gate  * "error" refers to a more descriptive term when get_addr fails
17400Sstevel@tonic-gate  * and returns NULL: ERR_PROTO_NONE if no error introduced by
17410Sstevel@tonic-gate  * -o proto option, ERR_NETPATH if error found in NETPATH
17420Sstevel@tonic-gate  * environment variable, ERR_PROTO_INVALID if an unrecognized
17430Sstevel@tonic-gate  * protocol is specified by user, and ERR_PROTO_UNSUPP for a
17440Sstevel@tonic-gate  * recognized but invalid protocol (eg. ticlts, ticots, etc.).
17450Sstevel@tonic-gate  * "error" is ignored if get_addr returns non-NULL result.
17460Sstevel@tonic-gate  *
17470Sstevel@tonic-gate  */
17480Sstevel@tonic-gate static struct netbuf *
17490Sstevel@tonic-gate get_addr(char *hostname, ulong_t prog, ulong_t vers, struct netconfig **nconfp,
17500Sstevel@tonic-gate 	char *proto, ushort_t port, struct t_info *tinfo, caddr_t *fhp,
17510Sstevel@tonic-gate 	bool_t get_pubfh, char *fspath, err_ret_t *error)
17520Sstevel@tonic-gate {
17530Sstevel@tonic-gate 	struct netbuf *nb = NULL;
17540Sstevel@tonic-gate 	struct netconfig *nconf = NULL;
17550Sstevel@tonic-gate 	NCONF_HANDLE *nc = NULL;
17560Sstevel@tonic-gate 	int nthtry = FIRST_TRY;
17570Sstevel@tonic-gate 	err_ret_t errsave_nohost, errsave_rpcerr;
17580Sstevel@tonic-gate 
17590Sstevel@tonic-gate 	SET_ERR_RET(&errsave_nohost, ERR_PROTO_NONE, 0);
17600Sstevel@tonic-gate 	SET_ERR_RET(&errsave_rpcerr, ERR_PROTO_NONE, 0);
17610Sstevel@tonic-gate 
17620Sstevel@tonic-gate 	SET_ERR_RET(error, ERR_PROTO_NONE, 0);
17630Sstevel@tonic-gate 
17640Sstevel@tonic-gate 	if (nconfp && *nconfp)
17650Sstevel@tonic-gate 		return (get_the_addr(hostname, prog, vers, *nconfp, port,
17665302Sth199096 		    tinfo, fhp, get_pubfh, fspath, error));
17670Sstevel@tonic-gate 	/*
17680Sstevel@tonic-gate 	 * No nconf passed in.
17690Sstevel@tonic-gate 	 *
17700Sstevel@tonic-gate 	 * Try to get a nconf from /etc/netconfig filtered by
17710Sstevel@tonic-gate 	 * the NETPATH environment variable.
17720Sstevel@tonic-gate 	 * First search for COTS, second for CLTS unless proto
17730Sstevel@tonic-gate 	 * is specified.  When we retry, we reset the
17740Sstevel@tonic-gate 	 * netconfig list so that we would search the whole list
17750Sstevel@tonic-gate 	 * all over again.
17760Sstevel@tonic-gate 	 */
17770Sstevel@tonic-gate 
17780Sstevel@tonic-gate 	if ((nc = setnetpath()) == NULL) {
17790Sstevel@tonic-gate 		/* should only return an error if problems with NETPATH */
17800Sstevel@tonic-gate 		/* In which case you are hosed */
17810Sstevel@tonic-gate 		SET_ERR_RET(error, ERR_NETPATH, 0);
17820Sstevel@tonic-gate 		goto done;
17830Sstevel@tonic-gate 	}
17840Sstevel@tonic-gate 
17850Sstevel@tonic-gate 	/*
17860Sstevel@tonic-gate 	 * If proto is specified, then only search for the match,
17870Sstevel@tonic-gate 	 * otherwise try COTS first, if failed, try CLTS.
17880Sstevel@tonic-gate 	 */
17890Sstevel@tonic-gate 	if (proto) {
17900Sstevel@tonic-gate 		/* no matching proto name */
17910Sstevel@tonic-gate 		SET_ERR_RET(error, ERR_PROTO_INVALID, 0);
17920Sstevel@tonic-gate 
17930Sstevel@tonic-gate 		while (nconf = getnetpath(nc)) {
17940Sstevel@tonic-gate 			if (strcmp(nconf->nc_netid, proto))
17950Sstevel@tonic-gate 				continue;
17960Sstevel@tonic-gate 
17970Sstevel@tonic-gate 			/* may be unsupported */
17980Sstevel@tonic-gate 			SET_ERR_RET(error, ERR_PROTO_UNSUPP, 0);
17990Sstevel@tonic-gate 
18000Sstevel@tonic-gate 			if ((port != 0) &&
18015302Sth199096 			    ((strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
18025302Sth199096 			    strcmp(nconf->nc_protofmly, NC_INET6) == 0) &&
18035302Sth199096 			    (strcmp(nconf->nc_proto, NC_TCP) != 0 &&
18045302Sth199096 			    strcmp(nconf->nc_proto, NC_UDP) != 0))) {
18050Sstevel@tonic-gate 				continue;
18065302Sth199096 			} else {
18070Sstevel@tonic-gate 				nb = get_the_addr(hostname, prog,
18085302Sth199096 				    vers, nconf, port, tinfo,
18095302Sth199096 				    fhp, get_pubfh, fspath, error);
18100Sstevel@tonic-gate 
18110Sstevel@tonic-gate 				if (nb != NULL)
18120Sstevel@tonic-gate 					break;
18130Sstevel@tonic-gate 
18140Sstevel@tonic-gate 				/* nb is NULL - deal with errors */
18150Sstevel@tonic-gate 				if (error) {
18160Sstevel@tonic-gate 					if (error->error_type == ERR_NOHOST)
18170Sstevel@tonic-gate 						SET_ERR_RET(&errsave_nohost,
18185302Sth199096 						    error->error_type,
18195302Sth199096 						    error->error_value);
18200Sstevel@tonic-gate 					if (error->error_type == ERR_RPCERROR)
18210Sstevel@tonic-gate 						SET_ERR_RET(&errsave_rpcerr,
18225302Sth199096 						    error->error_type,
18235302Sth199096 						    error->error_value);
18240Sstevel@tonic-gate 				}
18250Sstevel@tonic-gate 				/*
18260Sstevel@tonic-gate 				 * continue with same protocol
18270Sstevel@tonic-gate 				 * selection
18280Sstevel@tonic-gate 				 */
18290Sstevel@tonic-gate 				continue;
18300Sstevel@tonic-gate 			}
18310Sstevel@tonic-gate 		} /* end of while */
18320Sstevel@tonic-gate 
18330Sstevel@tonic-gate 		if (nconf == NULL)
18340Sstevel@tonic-gate 			goto done;
18350Sstevel@tonic-gate 
18360Sstevel@tonic-gate 		if ((nb = get_the_addr(hostname, prog, vers, nconf, port,
18375302Sth199096 		    tinfo, fhp, get_pubfh, fspath, error)) == NULL)
18380Sstevel@tonic-gate 			goto done;
18390Sstevel@tonic-gate 	} else {
18400Sstevel@tonic-gate retry:
18410Sstevel@tonic-gate 		SET_ERR_RET(error, ERR_NETPATH, 0);
18420Sstevel@tonic-gate 		while (nconf = getnetpath(nc)) {
18430Sstevel@tonic-gate 			SET_ERR_RET(error, ERR_PROTO_NONE, 0);
18445302Sth199096 
18450Sstevel@tonic-gate 			if (nconf->nc_flag & NC_VISIBLE) {
18465302Sth199096 				int	valid_proto;
18470Sstevel@tonic-gate 
18485302Sth199096 				if (check_nconf(nconf,
18495302Sth199096 				    nthtry, &valid_proto)) {
18505302Sth199096 					if (port == 0)
18515302Sth199096 						break;
18525302Sth199096 
18535302Sth199096 					if (valid_proto == TRUE)
18545302Sth199096 						break;
18550Sstevel@tonic-gate 				}
18560Sstevel@tonic-gate 			}
18570Sstevel@tonic-gate 		} /* while */
18580Sstevel@tonic-gate 		if (nconf == NULL) {
18590Sstevel@tonic-gate 			if (++nthtry <= MNT_PREF_LISTLEN) {
18600Sstevel@tonic-gate 				endnetpath(nc);
18610Sstevel@tonic-gate 				if ((nc = setnetpath()) == NULL)
18620Sstevel@tonic-gate 					goto done;
18630Sstevel@tonic-gate 				goto retry;
18640Sstevel@tonic-gate 			} else
18650Sstevel@tonic-gate 				goto done;
18660Sstevel@tonic-gate 		} else {
18670Sstevel@tonic-gate 			if ((nb = get_the_addr(hostname, prog, vers, nconf,
18685302Sth199096 			    port, tinfo, fhp, get_pubfh, fspath, error))
18695302Sth199096 			    == NULL) {
18700Sstevel@tonic-gate 				/* nb is NULL - deal with errors */
18710Sstevel@tonic-gate 				if (error) {
18720Sstevel@tonic-gate 					if (error->error_type == ERR_NOHOST)
18730Sstevel@tonic-gate 						SET_ERR_RET(&errsave_nohost,
18745302Sth199096 						    error->error_type,
18755302Sth199096 						    error->error_value);
18760Sstevel@tonic-gate 					if (error->error_type == ERR_RPCERROR)
18770Sstevel@tonic-gate 						SET_ERR_RET(&errsave_rpcerr,
18785302Sth199096 						    error->error_type,
18795302Sth199096 						    error->error_value);
18800Sstevel@tonic-gate 				}
18810Sstevel@tonic-gate 				/*
18820Sstevel@tonic-gate 				 * Continue the same search path in the
18830Sstevel@tonic-gate 				 * netconfig db until no more matched
18840Sstevel@tonic-gate 				 * nconf (nconf == NULL).
18850Sstevel@tonic-gate 				 */
18860Sstevel@tonic-gate 				goto retry;
18870Sstevel@tonic-gate 			}
18880Sstevel@tonic-gate 		}
18890Sstevel@tonic-gate 	}
18900Sstevel@tonic-gate 	SET_ERR_RET(error, ERR_PROTO_NONE, 0);
18910Sstevel@tonic-gate 
18920Sstevel@tonic-gate 	/*
18930Sstevel@tonic-gate 	 * Got nconf and nb.  Now dup the netconfig structure (nconf)
18940Sstevel@tonic-gate 	 * and return it thru nconfp.
18950Sstevel@tonic-gate 	 */
18960Sstevel@tonic-gate 	*nconfp = getnetconfigent(nconf->nc_netid);
18970Sstevel@tonic-gate 	if (*nconfp == NULL) {
18980Sstevel@tonic-gate 		syslog(LOG_ERR, "no memory\n");
18990Sstevel@tonic-gate 		free(nb);
19000Sstevel@tonic-gate 		nb = NULL;
19010Sstevel@tonic-gate 	}
19020Sstevel@tonic-gate done:
19030Sstevel@tonic-gate 	if (nc)
19040Sstevel@tonic-gate 		endnetpath(nc);
19050Sstevel@tonic-gate 
19060Sstevel@tonic-gate 	if (nb == NULL) {
190777Soa138391 		/*
190877Soa138391 		 * Check the saved errors. The RPC error has *
190977Soa138391 		 * precedence over the no host error.
191077Soa138391 		 */
191177Soa138391 		if (errsave_nohost.error_type != ERR_PROTO_NONE)
191277Soa138391 			SET_ERR_RET(error, errsave_nohost.error_type,
19135302Sth199096 			    errsave_nohost.error_value);
191477Soa138391 
191577Soa138391 		if (errsave_rpcerr.error_type != ERR_PROTO_NONE)
191677Soa138391 			SET_ERR_RET(error, errsave_rpcerr.error_type,
19175302Sth199096 			    errsave_rpcerr.error_value);
19180Sstevel@tonic-gate 	}
191977Soa138391 
19200Sstevel@tonic-gate 	return (nb);
19210Sstevel@tonic-gate }
19220Sstevel@tonic-gate 
19230Sstevel@tonic-gate /*
19240Sstevel@tonic-gate  * Get a file handle usinging multi-component lookup with the public
19250Sstevel@tonic-gate  * file handle.
19260Sstevel@tonic-gate  */
19270Sstevel@tonic-gate static int
19280Sstevel@tonic-gate get_fh_via_pub(struct nfs_args *args, char *fshost, char *fspath, bool_t url,
19290Sstevel@tonic-gate 	bool_t loud, int *versp, struct netconfig **nconfp, ushort_t port)
19300Sstevel@tonic-gate {
19310Sstevel@tonic-gate 	uint_t vers_min;
19320Sstevel@tonic-gate 	uint_t vers_max;
19330Sstevel@tonic-gate 	int r;
19340Sstevel@tonic-gate 	char *path;
19350Sstevel@tonic-gate 
19360Sstevel@tonic-gate 	if (nfsvers != 0) {
19370Sstevel@tonic-gate 		vers_max = vers_min = nfsvers;
19380Sstevel@tonic-gate 	} else {
19390Sstevel@tonic-gate 		vers_max = vers_max_default;
19400Sstevel@tonic-gate 		vers_min = vers_min_default;
19410Sstevel@tonic-gate 	}
19420Sstevel@tonic-gate 
19430Sstevel@tonic-gate 	if (url == FALSE) {
19440Sstevel@tonic-gate 		path = malloc(strlen(fspath) + 2);
19450Sstevel@tonic-gate 		if (path == NULL) {
19465302Sth199096 			if (loud == TRUE)
19470Sstevel@tonic-gate 				pr_err(gettext("no memory\n"));
19480Sstevel@tonic-gate 			return (RET_ERR);
19490Sstevel@tonic-gate 		}
19500Sstevel@tonic-gate 
19510Sstevel@tonic-gate 		path[0] = (char)WNL_NATIVEPATH;
19520Sstevel@tonic-gate 		(void) strcpy(&path[1], fspath);
19530Sstevel@tonic-gate 
19540Sstevel@tonic-gate 	} else  {
19550Sstevel@tonic-gate 		path = fspath;
19560Sstevel@tonic-gate 	}
19570Sstevel@tonic-gate 
19580Sstevel@tonic-gate 	for (nfsvers_to_use = vers_max; nfsvers_to_use >= vers_min;
19590Sstevel@tonic-gate 	    nfsvers_to_use--) {
19600Sstevel@tonic-gate 		/*
19610Sstevel@tonic-gate 		 * getaddr_nfs will also fill in the fh for us.
19620Sstevel@tonic-gate 		 */
19630Sstevel@tonic-gate 		r = getaddr_nfs(args, fshost, nconfp,
19645302Sth199096 		    TRUE, path, port, NULL, FALSE);
19650Sstevel@tonic-gate 
19660Sstevel@tonic-gate 		if (r == RET_OK) {
19670Sstevel@tonic-gate 			/*
19680Sstevel@tonic-gate 			 * Since we are using the public fh, and NLM is
19690Sstevel@tonic-gate 			 * not firewall friendly, use local locking.
19700Sstevel@tonic-gate 			 * Not the case for v4.
19710Sstevel@tonic-gate 			 */
19720Sstevel@tonic-gate 			*versp = nfsvers_to_use;
19730Sstevel@tonic-gate 			switch (nfsvers_to_use) {
19740Sstevel@tonic-gate 			case NFS_V4:
19750Sstevel@tonic-gate 				fstype = MNTTYPE_NFS4;
19760Sstevel@tonic-gate 				break;
19770Sstevel@tonic-gate 			case NFS_V3:
19780Sstevel@tonic-gate 				fstype = MNTTYPE_NFS3;
19790Sstevel@tonic-gate 				/* fall through to pick up llock option */
19800Sstevel@tonic-gate 			default:
19810Sstevel@tonic-gate 				args->flags |= NFSMNT_LLOCK;
19820Sstevel@tonic-gate 				break;
19830Sstevel@tonic-gate 			}
19840Sstevel@tonic-gate 			if (fspath != path)
19850Sstevel@tonic-gate 				free(path);
19860Sstevel@tonic-gate 
19870Sstevel@tonic-gate 			return (r);
19880Sstevel@tonic-gate 		}
19890Sstevel@tonic-gate 	}
19900Sstevel@tonic-gate 
19915302Sth199096 	if (fspath != path)
19920Sstevel@tonic-gate 		free(path);
19930Sstevel@tonic-gate 
19940Sstevel@tonic-gate 	if (loud == TRUE) {
19950Sstevel@tonic-gate 		pr_err(gettext("Could not use public filehandle in request to"
19965302Sth199096 		    " server %s\n"), fshost);
19970Sstevel@tonic-gate 	}
19980Sstevel@tonic-gate 
19990Sstevel@tonic-gate 	return (r);
20000Sstevel@tonic-gate }
20010Sstevel@tonic-gate 
20020Sstevel@tonic-gate /*
20030Sstevel@tonic-gate  * get fhandle of remote path from server's mountd
20040Sstevel@tonic-gate  */
20050Sstevel@tonic-gate static int
20060Sstevel@tonic-gate get_fh(struct nfs_args *args, char *fshost, char *fspath, int *versp,
20070Sstevel@tonic-gate 	bool_t loud_on_mnt_err, struct netconfig **nconfp, ushort_t port)
20080Sstevel@tonic-gate {
20090Sstevel@tonic-gate 	static struct fhstatus fhs;
20100Sstevel@tonic-gate 	static struct mountres3 mountres3;
20110Sstevel@tonic-gate 	static struct pathcnf p;
20120Sstevel@tonic-gate 	nfs_fh3 *fh3p;
20130Sstevel@tonic-gate 	struct timeval timeout = { 25, 0};
20140Sstevel@tonic-gate 	CLIENT *cl;
20150Sstevel@tonic-gate 	enum clnt_stat rpc_stat;
20160Sstevel@tonic-gate 	rpcvers_t outvers = 0;
20170Sstevel@tonic-gate 	rpcvers_t vers_to_try;
20180Sstevel@tonic-gate 	rpcvers_t vers_min;
20190Sstevel@tonic-gate 	static int printed = 0;
20200Sstevel@tonic-gate 	int count, i, *auths;
20210Sstevel@tonic-gate 	char *msg;
20220Sstevel@tonic-gate 
20230Sstevel@tonic-gate 	switch (nfsvers) {
20240Sstevel@tonic-gate 	case 2: /* version 2 specified try that only */
20250Sstevel@tonic-gate 		vers_to_try = MOUNTVERS_POSIX;
20260Sstevel@tonic-gate 		vers_min = MOUNTVERS;
20270Sstevel@tonic-gate 		break;
20280Sstevel@tonic-gate 	case 3: /* version 3 specified try that only */
20290Sstevel@tonic-gate 		vers_to_try = MOUNTVERS3;
20300Sstevel@tonic-gate 		vers_min = MOUNTVERS3;
20310Sstevel@tonic-gate 		break;
20320Sstevel@tonic-gate 	case 4: /* version 4 specified try that only */
20330Sstevel@tonic-gate 		/*
20340Sstevel@tonic-gate 		 * This assignment is in the wrong version sequence.
20350Sstevel@tonic-gate 		 * The above are MOUNT program and this is NFS
20360Sstevel@tonic-gate 		 * program.  However, it happens to work out since the
20370Sstevel@tonic-gate 		 * two don't collide for NFSv4.
20380Sstevel@tonic-gate 		 */
20390Sstevel@tonic-gate 		vers_to_try = NFS_V4;
20400Sstevel@tonic-gate 		vers_min = NFS_V4;
20410Sstevel@tonic-gate 		break;
20420Sstevel@tonic-gate 	default: /* no version specified, start with default */
2043489Soa138391 		/*
2044489Soa138391 		 * If the retry version is set, use that. This will
2045489Soa138391 		 * be set if the last mount attempt returned any other
2046489Soa138391 		 * besides an RPC error.
2047489Soa138391 		 */
2048489Soa138391 		if (nfsretry_vers)
2049489Soa138391 			vers_to_try = nfsretry_vers;
2050489Soa138391 		else {
2051489Soa138391 			vers_to_try = vers_max_default;
2052489Soa138391 			vers_min = vers_min_default;
2053489Soa138391 		}
2054489Soa138391 
20550Sstevel@tonic-gate 		break;
20560Sstevel@tonic-gate 	}
20570Sstevel@tonic-gate 
20580Sstevel@tonic-gate 	/*
20590Sstevel@tonic-gate 	 * In the case of version 4, just NULL proc the server since
20600Sstevel@tonic-gate 	 * there is no MOUNT program.  If this fails, then decrease
20610Sstevel@tonic-gate 	 * vers_to_try and continue on with regular MOUNT program
20620Sstevel@tonic-gate 	 * processing.
20630Sstevel@tonic-gate 	 */
20640Sstevel@tonic-gate 	if (vers_to_try == NFS_V4) {
20650Sstevel@tonic-gate 		int savevers = nfsvers_to_use;
20660Sstevel@tonic-gate 		err_ret_t error;
20670Sstevel@tonic-gate 		int retval;
20680Sstevel@tonic-gate 		SET_ERR_RET(&error, ERR_PROTO_NONE, 0);
20690Sstevel@tonic-gate 
20700Sstevel@tonic-gate 		/* Let's hope for the best */
20710Sstevel@tonic-gate 		nfsvers_to_use = NFS_V4;
20725302Sth199096 		retval = getaddr_nfs(args, fshost, nconfp, FALSE,
20735302Sth199096 		    fspath, port, &error, vers_min == NFS_V4);
20740Sstevel@tonic-gate 
20750Sstevel@tonic-gate 		if (retval == RET_OK) {
20760Sstevel@tonic-gate 			*versp = nfsvers_to_use = NFS_V4;
20770Sstevel@tonic-gate 			fstype = MNTTYPE_NFS4;
20780Sstevel@tonic-gate 			args->fh = strdup(fspath);
20790Sstevel@tonic-gate 			if (args->fh == NULL) {
20800Sstevel@tonic-gate 				pr_err(gettext("no memory\n"));
20810Sstevel@tonic-gate 				*versp = nfsvers_to_use = savevers;
20820Sstevel@tonic-gate 				return (RET_ERR);
20830Sstevel@tonic-gate 			}
20840Sstevel@tonic-gate 			return (RET_OK);
20850Sstevel@tonic-gate 		}
20860Sstevel@tonic-gate 		nfsvers_to_use = savevers;
20870Sstevel@tonic-gate 
20880Sstevel@tonic-gate 		vers_to_try--;
20890Sstevel@tonic-gate 		/* If no more versions to try, let the user know. */
20905302Sth199096 		if (vers_to_try < vers_min)
20910Sstevel@tonic-gate 			return (retval);
20920Sstevel@tonic-gate 
20930Sstevel@tonic-gate 		/*
20940Sstevel@tonic-gate 		 * If we are here, there are more versions to try but
20950Sstevel@tonic-gate 		 * there has been an error of some sort.  If it is not
20960Sstevel@tonic-gate 		 * an RPC error (e.g. host unknown), we just stop and
20970Sstevel@tonic-gate 		 * return the error since the other versions would see
20980Sstevel@tonic-gate 		 * the same error as well.
20990Sstevel@tonic-gate 		 */
21000Sstevel@tonic-gate 		if (retval == RET_ERR && error.error_type != ERR_RPCERROR)
21010Sstevel@tonic-gate 			return (retval);
21020Sstevel@tonic-gate 	}
21030Sstevel@tonic-gate 
21040Sstevel@tonic-gate 	while ((cl = clnt_create_vers(fshost, MOUNTPROG, &outvers,
21055302Sth199096 	    vers_min, vers_to_try, "datagram_v")) == NULL) {
21060Sstevel@tonic-gate 		if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST) {
21070Sstevel@tonic-gate 			pr_err(gettext("%s: %s\n"), fshost,
21080Sstevel@tonic-gate 			    clnt_spcreateerror(""));
21090Sstevel@tonic-gate 			return (RET_ERR);
21100Sstevel@tonic-gate 		}
21110Sstevel@tonic-gate 
21120Sstevel@tonic-gate 		/*
21130Sstevel@tonic-gate 		 * We don't want to downgrade version on lost packets
21140Sstevel@tonic-gate 		 */
21150Sstevel@tonic-gate 		if ((rpc_createerr.cf_stat == RPC_TIMEDOUT) ||
21165302Sth199096 		    (rpc_createerr.cf_stat == RPC_PMAPFAILURE)) {
21170Sstevel@tonic-gate 			pr_err(gettext("%s: %s\n"), fshost,
21180Sstevel@tonic-gate 			    clnt_spcreateerror(""));
21190Sstevel@tonic-gate 			return (RET_RETRY);
21200Sstevel@tonic-gate 		}
21210Sstevel@tonic-gate 
21220Sstevel@tonic-gate 		/*
21230Sstevel@tonic-gate 		 * back off and try the previous version - patch to the
21240Sstevel@tonic-gate 		 * problem of version numbers not being contigous and
21250Sstevel@tonic-gate 		 * clnt_create_vers failing (SunOS4.1 clients & SGI servers)
21260Sstevel@tonic-gate 		 * The problem happens with most non-Sun servers who
21270Sstevel@tonic-gate 		 * don't support mountd protocol #2. So, in case the
21280Sstevel@tonic-gate 		 * call fails, we re-try the call anyway.
21290Sstevel@tonic-gate 		 */
21300Sstevel@tonic-gate 		vers_to_try--;
21310Sstevel@tonic-gate 		if (vers_to_try < vers_min) {
21320Sstevel@tonic-gate 			if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH) {
21330Sstevel@tonic-gate 				if (nfsvers == 0) {
21340Sstevel@tonic-gate 					pr_err(gettext(
21350Sstevel@tonic-gate 			"%s:%s: no applicable versions of NFS supported\n"),
21360Sstevel@tonic-gate 					    fshost, fspath);
21370Sstevel@tonic-gate 				} else {
21380Sstevel@tonic-gate 					pr_err(gettext(
21390Sstevel@tonic-gate 			"%s:%s: NFS Version %d not supported\n"),
21400Sstevel@tonic-gate 					    fshost, fspath, nfsvers);
21410Sstevel@tonic-gate 				}
21420Sstevel@tonic-gate 				return (RET_ERR);
21430Sstevel@tonic-gate 			}
21440Sstevel@tonic-gate 			if (!printed) {
21450Sstevel@tonic-gate 				pr_err(gettext("%s: %s\n"), fshost,
21460Sstevel@tonic-gate 				    clnt_spcreateerror(""));
21470Sstevel@tonic-gate 				printed = 1;
21480Sstevel@tonic-gate 			}
21490Sstevel@tonic-gate 			return (RET_RETRY);
21500Sstevel@tonic-gate 		}
21510Sstevel@tonic-gate 	}
21520Sstevel@tonic-gate 	if (posix && outvers < MOUNTVERS_POSIX) {
21530Sstevel@tonic-gate 		pr_err(gettext("%s: %s: no pathconf info\n"),
21540Sstevel@tonic-gate 		    fshost, clnt_sperror(cl, ""));
21550Sstevel@tonic-gate 		clnt_destroy(cl);
21560Sstevel@tonic-gate 		return (RET_ERR);
21570Sstevel@tonic-gate 	}
21580Sstevel@tonic-gate 
21590Sstevel@tonic-gate 	if (__clnt_bindresvport(cl) < 0) {
21600Sstevel@tonic-gate 		pr_err(gettext("Couldn't bind to reserved port\n"));
21610Sstevel@tonic-gate 		clnt_destroy(cl);
21620Sstevel@tonic-gate 		return (RET_RETRY);
21630Sstevel@tonic-gate 	}
21640Sstevel@tonic-gate 
21650Sstevel@tonic-gate 	if ((cl->cl_auth = authsys_create_default()) == NULL) {
21660Sstevel@tonic-gate 		pr_err(
21670Sstevel@tonic-gate 		    gettext("Couldn't create default authentication handle\n"));
21680Sstevel@tonic-gate 		clnt_destroy(cl);
21690Sstevel@tonic-gate 		return (RET_RETRY);
21700Sstevel@tonic-gate 	}
21710Sstevel@tonic-gate 
21720Sstevel@tonic-gate 	switch (outvers) {
21730Sstevel@tonic-gate 	case MOUNTVERS:
21740Sstevel@tonic-gate 	case MOUNTVERS_POSIX:
21750Sstevel@tonic-gate 		*versp = nfsvers_to_use = NFS_VERSION;
21760Sstevel@tonic-gate 		rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath,
21775302Sth199096 		    (caddr_t)&fspath, xdr_fhstatus, (caddr_t)&fhs, timeout);
21780Sstevel@tonic-gate 		if (rpc_stat != RPC_SUCCESS) {
21790Sstevel@tonic-gate 			pr_err(gettext("%s:%s: server not responding %s\n"),
21800Sstevel@tonic-gate 			    fshost, fspath, clnt_sperror(cl, ""));
21810Sstevel@tonic-gate 			clnt_destroy(cl);
21820Sstevel@tonic-gate 			return (RET_RETRY);
21830Sstevel@tonic-gate 		}
21840Sstevel@tonic-gate 
21850Sstevel@tonic-gate 		if ((errno = fhs.fhs_status) != MNT_OK) {
21860Sstevel@tonic-gate 			if (loud_on_mnt_err) {
21875302Sth199096 				if (errno == EACCES) {
21885302Sth199096 					pr_err(gettext(
21895302Sth199096 					    "%s:%s: access denied\n"),
21905302Sth199096 					    fshost, fspath);
21915302Sth199096 				} else {
21925302Sth199096 					pr_err(gettext("%s:%s: %s\n"), fshost,
21935706Sgt29601 					    fspath, errno >= 0 ?
21945706Sgt29601 					    strerror(errno) : "invalid error "
21955706Sgt29601 					    "returned by server");
21965302Sth199096 				}
21970Sstevel@tonic-gate 			}
21980Sstevel@tonic-gate 			clnt_destroy(cl);
21990Sstevel@tonic-gate 			return (RET_MNTERR);
22000Sstevel@tonic-gate 		}
22010Sstevel@tonic-gate 		args->fh = malloc(sizeof (fhs.fhstatus_u.fhs_fhandle));
22020Sstevel@tonic-gate 		if (args->fh == NULL) {
22030Sstevel@tonic-gate 			pr_err(gettext("no memory\n"));
22040Sstevel@tonic-gate 			return (RET_ERR);
22050Sstevel@tonic-gate 		}
22060Sstevel@tonic-gate 		memcpy((caddr_t)args->fh, (caddr_t)&fhs.fhstatus_u.fhs_fhandle,
22075302Sth199096 		    sizeof (fhs.fhstatus_u.fhs_fhandle));
22080Sstevel@tonic-gate 		if (!errno && posix) {
22090Sstevel@tonic-gate 			rpc_stat = clnt_call(cl, MOUNTPROC_PATHCONF,
22105302Sth199096 			    xdr_dirpath, (caddr_t)&fspath, xdr_ppathcnf,
22115302Sth199096 			    (caddr_t)&p, timeout);
22120Sstevel@tonic-gate 			if (rpc_stat != RPC_SUCCESS) {
22130Sstevel@tonic-gate 				pr_err(gettext(
22140Sstevel@tonic-gate 				    "%s:%s: server not responding %s\n"),
22150Sstevel@tonic-gate 				    fshost, fspath, clnt_sperror(cl, ""));
22160Sstevel@tonic-gate 				free(args->fh);
22170Sstevel@tonic-gate 				clnt_destroy(cl);
22180Sstevel@tonic-gate 				return (RET_RETRY);
22190Sstevel@tonic-gate 			}
22200Sstevel@tonic-gate 			if (_PC_ISSET(_PC_ERROR, p.pc_mask)) {
22210Sstevel@tonic-gate 				pr_err(gettext(
22220Sstevel@tonic-gate 				    "%s:%s: no pathconf info\n"),
22230Sstevel@tonic-gate 				    fshost, fspath);
22240Sstevel@tonic-gate 				free(args->fh);
22250Sstevel@tonic-gate 				clnt_destroy(cl);
22260Sstevel@tonic-gate 				return (RET_ERR);
22270Sstevel@tonic-gate 			}
22280Sstevel@tonic-gate 			args->flags |= NFSMNT_POSIX;
22290Sstevel@tonic-gate 			args->pathconf = malloc(sizeof (p));
22300Sstevel@tonic-gate 			if (args->pathconf == NULL) {
22310Sstevel@tonic-gate 				pr_err(gettext("no memory\n"));
22320Sstevel@tonic-gate 				free(args->fh);
22330Sstevel@tonic-gate 				clnt_destroy(cl);
22340Sstevel@tonic-gate 				return (RET_ERR);
22350Sstevel@tonic-gate 			}
22360Sstevel@tonic-gate 			memcpy((caddr_t)args->pathconf, (caddr_t)&p,
22375302Sth199096 			    sizeof (p));
22380Sstevel@tonic-gate 		}
22390Sstevel@tonic-gate 		break;
22400Sstevel@tonic-gate 
22410Sstevel@tonic-gate 	case MOUNTVERS3:
22420Sstevel@tonic-gate 		*versp = nfsvers_to_use = NFS_V3;
22430Sstevel@tonic-gate 		rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath,
22445302Sth199096 		    (caddr_t)&fspath, xdr_mountres3, (caddr_t)&mountres3,
22455302Sth199096 		    timeout);
22460Sstevel@tonic-gate 		if (rpc_stat != RPC_SUCCESS) {
22470Sstevel@tonic-gate 			pr_err(gettext("%s:%s: server not responding %s\n"),
22480Sstevel@tonic-gate 			    fshost, fspath, clnt_sperror(cl, ""));
22490Sstevel@tonic-gate 			clnt_destroy(cl);
22500Sstevel@tonic-gate 			return (RET_RETRY);
22510Sstevel@tonic-gate 		}
22520Sstevel@tonic-gate 
22530Sstevel@tonic-gate 		/*
22540Sstevel@tonic-gate 		 * Assume here that most of the MNT3ERR_*
22550Sstevel@tonic-gate 		 * codes map into E* errors.
22560Sstevel@tonic-gate 		 */
22570Sstevel@tonic-gate 		if ((errno = mountres3.fhs_status) != MNT_OK) {
22585302Sth199096 			if (loud_on_mnt_err) {
22595302Sth199096 				switch (errno) {
22605302Sth199096 				case MNT3ERR_NAMETOOLONG:
22615302Sth199096 					msg = "path name is too long";
22625302Sth199096 					break;
22635302Sth199096 				case MNT3ERR_NOTSUPP:
22645302Sth199096 					msg = "operation not supported";
22655302Sth199096 					break;
22665302Sth199096 				case MNT3ERR_SERVERFAULT:
22675302Sth199096 					msg = "server fault";
22685302Sth199096 					break;
22695302Sth199096 				default:
22705706Sgt29601 					if (errno >= 0)
22715706Sgt29601 						msg = strerror(errno);
22725706Sgt29601 					else
22735706Sgt29601 						msg = "invalid error returned "
22745706Sgt29601 						    "by server";
22755302Sth199096 					break;
22765302Sth199096 				}
22775302Sth199096 				pr_err(gettext("%s:%s: %s\n"), fshost,
22785302Sth199096 				    fspath, msg);
22790Sstevel@tonic-gate 			}
22805302Sth199096 			clnt_destroy(cl);
22815302Sth199096 			return (RET_MNTERR);
22820Sstevel@tonic-gate 		}
22830Sstevel@tonic-gate 
22840Sstevel@tonic-gate 		fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
22850Sstevel@tonic-gate 		if (fh3p == NULL) {
22860Sstevel@tonic-gate 			pr_err(gettext("no memory\n"));
22870Sstevel@tonic-gate 			return (RET_ERR);
22880Sstevel@tonic-gate 		}
22890Sstevel@tonic-gate 		fh3p->fh3_length =
22905302Sth199096 		    mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len;
22910Sstevel@tonic-gate 		(void) memcpy(fh3p->fh3_u.data,
22925302Sth199096 		    mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val,
22935302Sth199096 		    fh3p->fh3_length);
22940Sstevel@tonic-gate 		args->fh = (caddr_t)fh3p;
22950Sstevel@tonic-gate 		fstype = MNTTYPE_NFS3;
22960Sstevel@tonic-gate 
22970Sstevel@tonic-gate 		/*
22980Sstevel@tonic-gate 		 * Check the security flavor to be used.
22990Sstevel@tonic-gate 		 *
23000Sstevel@tonic-gate 		 * If "secure" or "sec=flavor" is a mount
23010Sstevel@tonic-gate 		 * option, check if the server supports the "flavor".
23020Sstevel@tonic-gate 		 * If the server does not support the flavor, return
23030Sstevel@tonic-gate 		 * error.
23040Sstevel@tonic-gate 		 *
230510186SVallish.Vaidyeshwara@Sun.COM 		 * If no mount option is given then look for default auth
230610186SVallish.Vaidyeshwara@Sun.COM 		 * (default auth entry in /etc/nfssec.conf) in the auth list
230710186SVallish.Vaidyeshwara@Sun.COM 		 * returned from server. If default auth not found, then use
230810186SVallish.Vaidyeshwara@Sun.COM 		 * the first supported security flavor (by the client) in the
230910186SVallish.Vaidyeshwara@Sun.COM 		 * auth list returned from the server.
23100Sstevel@tonic-gate 		 *
23110Sstevel@tonic-gate 		 */
23120Sstevel@tonic-gate 		auths =
23135302Sth199096 		    mountres3.mountres3_u.mountinfo.auth_flavors
23145302Sth199096 		    .auth_flavors_val;
23150Sstevel@tonic-gate 		count =
23165302Sth199096 		    mountres3.mountres3_u.mountinfo.auth_flavors
23175302Sth199096 		    .auth_flavors_len;
23180Sstevel@tonic-gate 
231910186SVallish.Vaidyeshwara@Sun.COM 		if (count <= 0) {
232010186SVallish.Vaidyeshwara@Sun.COM 			pr_err(gettext(
232110186SVallish.Vaidyeshwara@Sun.COM 			    "server %s did not return any security mode\n"),
232210186SVallish.Vaidyeshwara@Sun.COM 			    fshost);
232310186SVallish.Vaidyeshwara@Sun.COM 			clnt_destroy(cl);
232410186SVallish.Vaidyeshwara@Sun.COM 			return (RET_ERR);
232510186SVallish.Vaidyeshwara@Sun.COM 		}
232610186SVallish.Vaidyeshwara@Sun.COM 
23270Sstevel@tonic-gate 		if (sec_opt) {
23280Sstevel@tonic-gate 			for (i = 0; i < count; i++) {
23290Sstevel@tonic-gate 				if (auths[i] == nfs_sec.sc_nfsnum)
23305302Sth199096 					break;
23310Sstevel@tonic-gate 			}
233210186SVallish.Vaidyeshwara@Sun.COM 			if (i == count)
23330Sstevel@tonic-gate 				goto autherr;
23340Sstevel@tonic-gate 		} else {
233510186SVallish.Vaidyeshwara@Sun.COM 			seconfig_t default_sec;
233610186SVallish.Vaidyeshwara@Sun.COM 
233710186SVallish.Vaidyeshwara@Sun.COM 			/*
233810186SVallish.Vaidyeshwara@Sun.COM 			 * Get client configured default auth.
233910186SVallish.Vaidyeshwara@Sun.COM 			 */
234010186SVallish.Vaidyeshwara@Sun.COM 			nfs_sec.sc_nfsnum = -1;
234110186SVallish.Vaidyeshwara@Sun.COM 			default_sec.sc_nfsnum = -1;
234210186SVallish.Vaidyeshwara@Sun.COM 			(void) nfs_getseconfig_default(&default_sec);
23435302Sth199096 
234410186SVallish.Vaidyeshwara@Sun.COM 			/*
234510186SVallish.Vaidyeshwara@Sun.COM 			 * Look for clients default auth in servers list.
234610186SVallish.Vaidyeshwara@Sun.COM 			 */
234710186SVallish.Vaidyeshwara@Sun.COM 			if (default_sec.sc_nfsnum != -1) {
234810186SVallish.Vaidyeshwara@Sun.COM 				for (i = 0; i < count; i++) {
234910186SVallish.Vaidyeshwara@Sun.COM 					if (auths[i] == default_sec.sc_nfsnum) {
235010186SVallish.Vaidyeshwara@Sun.COM 						sec_opt++;
235110186SVallish.Vaidyeshwara@Sun.COM 						nfs_sec = default_sec;
235210186SVallish.Vaidyeshwara@Sun.COM 						break;
235310186SVallish.Vaidyeshwara@Sun.COM 					}
23545302Sth199096 				}
23550Sstevel@tonic-gate 			}
23565302Sth199096 
235710186SVallish.Vaidyeshwara@Sun.COM 			/*
235810186SVallish.Vaidyeshwara@Sun.COM 			 * Could not find clients default auth in servers list.
235910186SVallish.Vaidyeshwara@Sun.COM 			 * Pick the first auth from servers list that is
236010186SVallish.Vaidyeshwara@Sun.COM 			 * also supported on the client.
236110186SVallish.Vaidyeshwara@Sun.COM 			 */
236210186SVallish.Vaidyeshwara@Sun.COM 			if (nfs_sec.sc_nfsnum == -1) {
236310186SVallish.Vaidyeshwara@Sun.COM 				for (i = 0; i < count; i++) {
236410186SVallish.Vaidyeshwara@Sun.COM 					if (!nfs_getseconfig_bynumber(auths[i],
236510186SVallish.Vaidyeshwara@Sun.COM 					    &nfs_sec)) {
236610186SVallish.Vaidyeshwara@Sun.COM 						sec_opt++;
236710186SVallish.Vaidyeshwara@Sun.COM 						break;
236810186SVallish.Vaidyeshwara@Sun.COM 
236910186SVallish.Vaidyeshwara@Sun.COM 					}
237010186SVallish.Vaidyeshwara@Sun.COM 				}
237110186SVallish.Vaidyeshwara@Sun.COM 			}
237210186SVallish.Vaidyeshwara@Sun.COM 
237310186SVallish.Vaidyeshwara@Sun.COM 			if (i == count)
23745302Sth199096 				goto autherr;
23750Sstevel@tonic-gate 		}
23760Sstevel@tonic-gate 		break;
23770Sstevel@tonic-gate 	default:
23780Sstevel@tonic-gate 		pr_err(gettext("%s:%s: Unknown MOUNT version %d\n"),
23790Sstevel@tonic-gate 		    fshost, fspath, outvers);
23800Sstevel@tonic-gate 		clnt_destroy(cl);
23810Sstevel@tonic-gate 		return (RET_ERR);
23820Sstevel@tonic-gate 	}
23830Sstevel@tonic-gate 
23840Sstevel@tonic-gate 	clnt_destroy(cl);
23850Sstevel@tonic-gate 	return (RET_OK);
23860Sstevel@tonic-gate 
23870Sstevel@tonic-gate autherr:
23880Sstevel@tonic-gate 	pr_err(gettext(
23895302Sth199096 	    "security mode does not match the server exporting %s:%s\n"),
23905302Sth199096 	    fshost, fspath);
23910Sstevel@tonic-gate 	clnt_destroy(cl);
23920Sstevel@tonic-gate 	return (RET_ERR);
23930Sstevel@tonic-gate }
23940Sstevel@tonic-gate 
23950Sstevel@tonic-gate /*
23960Sstevel@tonic-gate  * Fill in the address for the server's NFS service and
23970Sstevel@tonic-gate  * fill in a knetconfig structure for the transport that
23980Sstevel@tonic-gate  * the service is available on.
23990Sstevel@tonic-gate  */
24000Sstevel@tonic-gate static int
24010Sstevel@tonic-gate getaddr_nfs(struct nfs_args *args, char *fshost, struct netconfig **nconfp,
24020Sstevel@tonic-gate 	    bool_t get_pubfh, char *fspath, ushort_t port, err_ret_t *error,
24030Sstevel@tonic-gate 	    bool_t print_rpcerror)
24040Sstevel@tonic-gate {
24050Sstevel@tonic-gate 	struct stat sb;
24060Sstevel@tonic-gate 	struct netconfig *nconf;
24070Sstevel@tonic-gate 	struct knetconfig *knconfp;
24080Sstevel@tonic-gate 	static int printed = 0;
24090Sstevel@tonic-gate 	struct t_info tinfo;
24100Sstevel@tonic-gate 	err_ret_t addr_error;
24110Sstevel@tonic-gate 
24120Sstevel@tonic-gate 	SET_ERR_RET(error, ERR_PROTO_NONE, 0);
24130Sstevel@tonic-gate 	SET_ERR_RET(&addr_error, ERR_PROTO_NONE, 0);
24140Sstevel@tonic-gate 
24150Sstevel@tonic-gate 	if (nfs_proto) {
24160Sstevel@tonic-gate 		/*
24170Sstevel@tonic-gate 		 * If a proto is specified and its rdma try this. The kernel
24180Sstevel@tonic-gate 		 * will later do the reachablity test and fail form there
24190Sstevel@tonic-gate 		 * if rdma transport is not available to kernel rpc
24200Sstevel@tonic-gate 		 */
24210Sstevel@tonic-gate 		if (strcmp(nfs_proto, "rdma") == 0) {
24220Sstevel@tonic-gate 			args->addr = get_addr(fshost, NFS_PROGRAM,
24230Sstevel@tonic-gate 			    nfsvers_to_use, nconfp, NULL, port, &tinfo,
24240Sstevel@tonic-gate 			    &args->fh, get_pubfh, fspath, &addr_error);
24250Sstevel@tonic-gate 
24260Sstevel@tonic-gate 			args->flags |= NFSMNT_DORDMA;
24270Sstevel@tonic-gate 		} else {
24280Sstevel@tonic-gate 			args->addr = get_addr(fshost, NFS_PROGRAM,
24290Sstevel@tonic-gate 			    nfsvers_to_use, nconfp, nfs_proto, port, &tinfo,
24300Sstevel@tonic-gate 			    &args->fh, get_pubfh, fspath, &addr_error);
24310Sstevel@tonic-gate 		}
24320Sstevel@tonic-gate 	} else {
24330Sstevel@tonic-gate 		args->addr = get_addr(fshost, NFS_PROGRAM, nfsvers_to_use,
24340Sstevel@tonic-gate 		    nconfp, nfs_proto, port, &tinfo, &args->fh, get_pubfh,
24350Sstevel@tonic-gate 		    fspath, &addr_error);
24360Sstevel@tonic-gate 		/*
24370Sstevel@tonic-gate 		 * If no proto is specified set this flag.
24380Sstevel@tonic-gate 		 * Kernel mount code will try to use RDMA if its on the
24390Sstevel@tonic-gate 		 * system, otherwise it will keep on using the protocol
24400Sstevel@tonic-gate 		 * selected here, through the above get_addr call.
24410Sstevel@tonic-gate 		 */
24420Sstevel@tonic-gate 		if (nfs_proto == NULL)
24430Sstevel@tonic-gate 			args->flags |= NFSMNT_TRYRDMA;
24440Sstevel@tonic-gate 	}
24450Sstevel@tonic-gate 
24460Sstevel@tonic-gate 	if (args->addr == NULL) {
24470Sstevel@tonic-gate 		/*
24480Sstevel@tonic-gate 		 * We could have failed because the server had no public
24490Sstevel@tonic-gate 		 * file handle support. So don't print a message and don't
24500Sstevel@tonic-gate 		 * retry.
24510Sstevel@tonic-gate 		 */
24520Sstevel@tonic-gate 		if (get_pubfh == TRUE)
24530Sstevel@tonic-gate 			return (RET_ERR);
24540Sstevel@tonic-gate 
24550Sstevel@tonic-gate 		if (!printed) {
24560Sstevel@tonic-gate 			switch (addr_error.error_type) {
24570Sstevel@tonic-gate 			case 0:
2458489Soa138391 				printed = 1;
24590Sstevel@tonic-gate 				break;
24600Sstevel@tonic-gate 			case ERR_RPCERROR:
24610Sstevel@tonic-gate 				if (!print_rpcerror)
24620Sstevel@tonic-gate 					/* no error print at this time */
24630Sstevel@tonic-gate 					break;
24640Sstevel@tonic-gate 				pr_err(gettext("%s NFS service not"
24655302Sth199096 				    " available %s\n"), fshost,
24660Sstevel@tonic-gate 				    clnt_sperrno(addr_error.error_value));
2467489Soa138391 				printed = 1;
24680Sstevel@tonic-gate 				break;
24690Sstevel@tonic-gate 			case ERR_NETPATH:
24700Sstevel@tonic-gate 				pr_err(gettext("%s: Error in NETPATH.\n"),
24715302Sth199096 				    fshost);
2472489Soa138391 				printed = 1;
24730Sstevel@tonic-gate 				break;
24740Sstevel@tonic-gate 			case ERR_PROTO_INVALID:
24750Sstevel@tonic-gate 				pr_err(gettext("%s: NFS service does not"
24765302Sth199096 				    " recognize protocol: %s.\n"), fshost,
24775302Sth199096 				    nfs_proto);
2478489Soa138391 				printed = 1;
24790Sstevel@tonic-gate 				break;
24800Sstevel@tonic-gate 			case ERR_PROTO_UNSUPP:
2481489Soa138391 				if (nfsvers || nfsvers_to_use == NFS_VERSMIN) {
2482112Soa138391 					/*
2483489Soa138391 					 * Don't set "printed" here. Since we
2484489Soa138391 					 * have to keep checking here till we
2485489Soa138391 					 * exhaust transport errors on all vers.
2486489Soa138391 					 *
2487489Soa138391 					 * Print this message if:
2488489Soa138391 					 * 1. After we have tried all versions
2489489Soa138391 					 *    of NFS and none support the asked
2490489Soa138391 					 *    transport.
2491489Soa138391 					 *
2492489Soa138391 					 * 2. If a version is specified and it
2493489Soa138391 					 *    does'nt support the asked
2494489Soa138391 					 *    transport.
2495489Soa138391 					 *
2496489Soa138391 					 * Otherwise we decrement the version
2497112Soa138391 					 * and retry below.
2498112Soa138391 					 */
2499112Soa138391 					pr_err(gettext("%s: NFS service does"
25005302Sth199096 					    " not support protocol: %s.\n"),
25015302Sth199096 					    fshost, nfs_proto);
2502112Soa138391 				}
25030Sstevel@tonic-gate 				break;
25040Sstevel@tonic-gate 			case ERR_NOHOST:
250577Soa138391 				pr_err("%s: %s\n", fshost, "Unknown host");
2506489Soa138391 				printed = 1;
25070Sstevel@tonic-gate 				break;
25080Sstevel@tonic-gate 			default:
25090Sstevel@tonic-gate 				/* case ERR_PROTO_NONE falls through */
25100Sstevel@tonic-gate 				pr_err(gettext("%s: NFS service not responding"
25115302Sth199096 				    "\n"), fshost);
2512489Soa138391 				printed = 1;
25130Sstevel@tonic-gate 				break;
25140Sstevel@tonic-gate 			}
25150Sstevel@tonic-gate 		}
25160Sstevel@tonic-gate 		SET_ERR_RET(error,
25175302Sth199096 		    addr_error.error_type, addr_error.error_value);
25180Sstevel@tonic-gate 		if (addr_error.error_type == ERR_PROTO_NONE)
25190Sstevel@tonic-gate 			return (RET_RETRY);
25200Sstevel@tonic-gate 		else if (addr_error.error_type == ERR_RPCERROR &&
25215302Sth199096 		    !IS_UNRECOVERABLE_RPC(addr_error.error_value)) {
25220Sstevel@tonic-gate 			return (RET_RETRY);
2523112Soa138391 		} else if (nfsvers == 0 && addr_error.error_type ==
25245302Sth199096 		    ERR_PROTO_UNSUPP && nfsvers_to_use != NFS_VERSMIN) {
2525112Soa138391 			/*
2526112Soa138391 			 * If no version is specified, and the error is due
2527489Soa138391 			 * to an unsupported transport, then decrement the
2528112Soa138391 			 * version and retry.
2529112Soa138391 			 */
2530112Soa138391 			return (RET_RETRY);
2531112Soa138391 		} else
25320Sstevel@tonic-gate 			return (RET_ERR);
25330Sstevel@tonic-gate 	}
25340Sstevel@tonic-gate 	nconf = *nconfp;
25350Sstevel@tonic-gate 
25360Sstevel@tonic-gate 	if (stat(nconf->nc_device, &sb) < 0) {
25370Sstevel@tonic-gate 		pr_err(gettext("getaddr_nfs: couldn't stat: %s: %s\n"),
25380Sstevel@tonic-gate 		    nconf->nc_device, strerror(errno));
25390Sstevel@tonic-gate 		return (RET_ERR);
25400Sstevel@tonic-gate 	}
25410Sstevel@tonic-gate 
25420Sstevel@tonic-gate 	knconfp = (struct knetconfig *)malloc(sizeof (*knconfp));
25430Sstevel@tonic-gate 	if (!knconfp) {
25440Sstevel@tonic-gate 		pr_err(gettext("no memory\n"));
25450Sstevel@tonic-gate 		return (RET_ERR);
25460Sstevel@tonic-gate 	}
25470Sstevel@tonic-gate 	knconfp->knc_semantics = nconf->nc_semantics;
25480Sstevel@tonic-gate 	knconfp->knc_protofmly = nconf->nc_protofmly;
25490Sstevel@tonic-gate 	knconfp->knc_proto = nconf->nc_proto;
25500Sstevel@tonic-gate 	knconfp->knc_rdev = sb.st_rdev;
25510Sstevel@tonic-gate 
25520Sstevel@tonic-gate 	/* make sure we don't overload the transport */
25530Sstevel@tonic-gate 	if (tinfo.tsdu > 0 && tinfo.tsdu < NFS_MAXDATA + NFS_RPC_HDR) {
25540Sstevel@tonic-gate 		args->flags |= (NFSMNT_RSIZE | NFSMNT_WSIZE);
25550Sstevel@tonic-gate 		if (args->rsize == 0 || args->rsize > tinfo.tsdu - NFS_RPC_HDR)
25560Sstevel@tonic-gate 			args->rsize = tinfo.tsdu - NFS_RPC_HDR;
25570Sstevel@tonic-gate 		if (args->wsize == 0 || args->wsize > tinfo.tsdu - NFS_RPC_HDR)
25580Sstevel@tonic-gate 			args->wsize = tinfo.tsdu - NFS_RPC_HDR;
25590Sstevel@tonic-gate 	}
25600Sstevel@tonic-gate 
25610Sstevel@tonic-gate 	args->flags |= NFSMNT_KNCONF;
25620Sstevel@tonic-gate 	args->knconf = knconfp;
25630Sstevel@tonic-gate 	return (RET_OK);
25640Sstevel@tonic-gate }
25650Sstevel@tonic-gate 
25660Sstevel@tonic-gate static int
25670Sstevel@tonic-gate retry(struct mnttab *mntp, int ro)
25680Sstevel@tonic-gate {
25690Sstevel@tonic-gate 	int delay = 5;
25700Sstevel@tonic-gate 	int count = retries;
25710Sstevel@tonic-gate 	int r;
25720Sstevel@tonic-gate 
2573489Soa138391 	/*
2574489Soa138391 	 * Please see comments on nfsretry_vers in the beginning of this file
2575489Soa138391 	 * and in main() routine.
2576489Soa138391 	 */
2577489Soa138391 
25780Sstevel@tonic-gate 	if (bg) {
25790Sstevel@tonic-gate 		if (fork() > 0)
25800Sstevel@tonic-gate 			return (RET_OK);
25815706Sgt29601 		backgrounded = 1;
25820Sstevel@tonic-gate 		pr_err(gettext("backgrounding: %s\n"), mntp->mnt_mountp);
2583489Soa138391 	} else {
2584489Soa138391 		if (!nfsretry_vers)
2585489Soa138391 			pr_err(gettext("retrying: %s\n"), mntp->mnt_mountp);
2586489Soa138391 	}
25870Sstevel@tonic-gate 
25880Sstevel@tonic-gate 	while (count--) {
2589489Soa138391 		if ((r = mount_nfs(mntp, ro, NULL)) == RET_OK) {
25900Sstevel@tonic-gate 			pr_err(gettext("%s: mounted OK\n"), mntp->mnt_mountp);
25910Sstevel@tonic-gate 			return (RET_OK);
25920Sstevel@tonic-gate 		}
25930Sstevel@tonic-gate 		if (r != RET_RETRY)
25940Sstevel@tonic-gate 			break;
25950Sstevel@tonic-gate 
25960Sstevel@tonic-gate 		if (count > 0) {
25975302Sth199096 			(void) sleep(delay);
25985302Sth199096 			delay *= 2;
25995302Sth199096 			if (delay > 120)
26005302Sth199096 				delay = 120;
26010Sstevel@tonic-gate 		}
26020Sstevel@tonic-gate 	}
2603489Soa138391 
2604489Soa138391 	if (!nfsretry_vers)
2605489Soa138391 		pr_err(gettext("giving up on: %s\n"), mntp->mnt_mountp);
2606489Soa138391 
26070Sstevel@tonic-gate 	return (RET_ERR);
26080Sstevel@tonic-gate }
26090Sstevel@tonic-gate 
26100Sstevel@tonic-gate /*
26110Sstevel@tonic-gate  * Read the /etc/default/nfs configuration file to determine if the
26120Sstevel@tonic-gate  * client has been configured for a new min/max for the NFS version to
26130Sstevel@tonic-gate  * use.
26140Sstevel@tonic-gate  */
26150Sstevel@tonic-gate static void
26160Sstevel@tonic-gate read_default(void)
26170Sstevel@tonic-gate {
26180Sstevel@tonic-gate 	char *defval;
26190Sstevel@tonic-gate 	int errno;
26200Sstevel@tonic-gate 	int tmp;
26210Sstevel@tonic-gate 
26220Sstevel@tonic-gate 	/* Fail silently if error in opening the default nfs config file */
26230Sstevel@tonic-gate 	if ((defopen(NFSADMIN)) == 0) {
26240Sstevel@tonic-gate 		if ((defval = defread("NFS_CLIENT_VERSMIN=")) != NULL) {
26250Sstevel@tonic-gate 			errno = 0;
26260Sstevel@tonic-gate 			tmp = strtol(defval, (char **)NULL, 10);
26270Sstevel@tonic-gate 			if (errno == 0) {
26280Sstevel@tonic-gate 				vers_min_default = tmp;
26290Sstevel@tonic-gate 			}
26300Sstevel@tonic-gate 		}
26310Sstevel@tonic-gate 		if ((defval = defread("NFS_CLIENT_VERSMAX=")) != NULL) {
26320Sstevel@tonic-gate 			errno = 0;
26330Sstevel@tonic-gate 			tmp = strtol(defval, (char **)NULL, 10);
26340Sstevel@tonic-gate 			if (errno == 0) {
26350Sstevel@tonic-gate 				vers_max_default = tmp;
26360Sstevel@tonic-gate 			}
26370Sstevel@tonic-gate 		}
26380Sstevel@tonic-gate 		/* close defaults file */
26390Sstevel@tonic-gate 		defopen(NULL);
26400Sstevel@tonic-gate 	}
26410Sstevel@tonic-gate }
26420Sstevel@tonic-gate 
26430Sstevel@tonic-gate static void
26440Sstevel@tonic-gate sigusr1(int s)
26450Sstevel@tonic-gate {
26460Sstevel@tonic-gate }
2647