xref: /onnv-gate/usr/src/cmd/fs.d/nfs/mount/mount.c (revision 112:620299cea3ac)
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
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
230Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
280Sstevel@tonic-gate /*	  All Rights Reserved  	*/
290Sstevel@tonic-gate 
300Sstevel@tonic-gate /*
310Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
320Sstevel@tonic-gate  * The Regents of the University of California
330Sstevel@tonic-gate  * All Rights Reserved
340Sstevel@tonic-gate  *
350Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
360Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
370Sstevel@tonic-gate  * contributors.
380Sstevel@tonic-gate  */
390Sstevel@tonic-gate 
400Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
410Sstevel@tonic-gate 
420Sstevel@tonic-gate /*
430Sstevel@tonic-gate  * nfs mount
440Sstevel@tonic-gate  */
450Sstevel@tonic-gate 
460Sstevel@tonic-gate #define	NFSCLIENT
470Sstevel@tonic-gate #include <locale.h>
480Sstevel@tonic-gate #include <stdio.h>
490Sstevel@tonic-gate #include <string.h>
500Sstevel@tonic-gate #include <memory.h>
510Sstevel@tonic-gate #include <stdarg.h>
520Sstevel@tonic-gate #include <unistd.h>
530Sstevel@tonic-gate #include <ctype.h>
540Sstevel@tonic-gate #include <stdlib.h>
550Sstevel@tonic-gate #include <signal.h>
560Sstevel@tonic-gate #include <sys/param.h>
570Sstevel@tonic-gate #include <rpc/rpc.h>
580Sstevel@tonic-gate #include <errno.h>
590Sstevel@tonic-gate #include <sys/stat.h>
600Sstevel@tonic-gate #include <netdb.h>
610Sstevel@tonic-gate #include <sys/mount.h>
620Sstevel@tonic-gate #include <sys/mntent.h>
630Sstevel@tonic-gate #include <sys/mnttab.h>
640Sstevel@tonic-gate #include <nfs/nfs.h>
650Sstevel@tonic-gate #include <nfs/mount.h>
660Sstevel@tonic-gate #include <rpcsvc/mount.h>
670Sstevel@tonic-gate #include <sys/pathconf.h>
680Sstevel@tonic-gate #include <netdir.h>
690Sstevel@tonic-gate #include <netconfig.h>
700Sstevel@tonic-gate #include <sys/sockio.h>
710Sstevel@tonic-gate #include <net/if.h>
720Sstevel@tonic-gate #include <syslog.h>
730Sstevel@tonic-gate #include <fslib.h>
740Sstevel@tonic-gate #include <deflt.h>
750Sstevel@tonic-gate #include <sys/wait.h>
760Sstevel@tonic-gate #include "replica.h"
770Sstevel@tonic-gate #include <netinet/in.h>
780Sstevel@tonic-gate #include <nfs/nfs_sec.h>
790Sstevel@tonic-gate #include <rpcsvc/daemon_utils.h>
800Sstevel@tonic-gate #include <priv.h>
810Sstevel@tonic-gate #include "nfs_subr.h"
820Sstevel@tonic-gate #include "webnfs.h"
830Sstevel@tonic-gate #include <rpcsvc/nfs4_prot.h>
840Sstevel@tonic-gate 
850Sstevel@tonic-gate #ifndef	NFS_VERSMAX
860Sstevel@tonic-gate #define	NFS_VERSMAX	4
870Sstevel@tonic-gate #endif
880Sstevel@tonic-gate #ifndef	NFS_VERSMIN
890Sstevel@tonic-gate #define	NFS_VERSMIN	2
900Sstevel@tonic-gate #endif
910Sstevel@tonic-gate 
920Sstevel@tonic-gate #define	RET_OK		0
930Sstevel@tonic-gate #define	RET_RETRY	32
940Sstevel@tonic-gate #define	RET_ERR		33
950Sstevel@tonic-gate #define	RET_MNTERR	1000
960Sstevel@tonic-gate #define	ERR_PROTO_NONE		0
970Sstevel@tonic-gate #define	ERR_PROTO_INVALID	901
980Sstevel@tonic-gate #define	ERR_PROTO_UNSUPP	902
990Sstevel@tonic-gate #define	ERR_NETPATH		903
1000Sstevel@tonic-gate #define	ERR_NOHOST		904
1010Sstevel@tonic-gate #define	ERR_RPCERROR		905
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate typedef struct err_ret {
1040Sstevel@tonic-gate 	int error_type;
1050Sstevel@tonic-gate 	int error_value;
1060Sstevel@tonic-gate } err_ret_t;
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate #define	SET_ERR_RET(errst, etype, eval) \
1090Sstevel@tonic-gate 	if (errst) { \
1100Sstevel@tonic-gate 		(errst)->error_type = etype; \
1110Sstevel@tonic-gate 		(errst)->error_value = eval; \
1120Sstevel@tonic-gate 	}
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate /* number of transports to try */
1150Sstevel@tonic-gate #define	MNT_PREF_LISTLEN	2
1160Sstevel@tonic-gate #define	FIRST_TRY		1
1170Sstevel@tonic-gate #define	SECOND_TRY		2
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate #define	BIGRETRY	10000
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate /* maximum length of RPC header for NFS messages */
1220Sstevel@tonic-gate #define	NFS_RPC_HDR	432
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate #define	NFS_ARGS_EXTB_secdata(args, secdata) \
1250Sstevel@tonic-gate 	{ (args)->nfs_args_ext = NFS_ARGS_EXTB, \
1260Sstevel@tonic-gate 	(args)->nfs_ext_u.nfs_extB.secdata = secdata; }
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate extern int __clnt_bindresvport();
1290Sstevel@tonic-gate extern char *nfs_get_qop_name();
1300Sstevel@tonic-gate extern AUTH * nfs_create_ah();
1310Sstevel@tonic-gate extern enum snego_stat nfs_sec_nego();
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate static void usage(void);
1340Sstevel@tonic-gate static int retry(struct mnttab *, int);
1350Sstevel@tonic-gate static int set_args(int *, struct nfs_args *, char *, struct mnttab *);
1360Sstevel@tonic-gate static int get_fh_via_pub(struct nfs_args *, char *, char *, bool_t, bool_t,
1370Sstevel@tonic-gate 	int *, struct netconfig **, ushort_t);
1380Sstevel@tonic-gate static int get_fh(struct nfs_args *, char *, char *, int *, bool_t,
1390Sstevel@tonic-gate 	struct netconfig **, ushort_t);
1400Sstevel@tonic-gate static int make_secure(struct nfs_args *, char *, struct netconfig *,
1410Sstevel@tonic-gate 	bool_t, rpcvers_t);
1420Sstevel@tonic-gate static int mount_nfs(struct mnttab *, int);
1430Sstevel@tonic-gate static int getaddr_nfs(struct nfs_args *, char *, struct netconfig **,
1440Sstevel@tonic-gate 		    bool_t, char *, ushort_t, err_ret_t *, bool_t);
1450Sstevel@tonic-gate static void pr_err(const char *fmt, ...);
1460Sstevel@tonic-gate static void usage(void);
1470Sstevel@tonic-gate static struct netbuf *get_addr(char *, rpcprog_t, rpcvers_t,
1480Sstevel@tonic-gate 	struct netconfig **, char *, ushort_t, struct t_info *,
1490Sstevel@tonic-gate 	caddr_t *, bool_t, char *, err_ret_t *);
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate static struct netbuf *get_the_addr(char *, rpcprog_t, rpcvers_t,
1520Sstevel@tonic-gate 	struct netconfig *, ushort_t, struct t_info *, caddr_t *,
1530Sstevel@tonic-gate 	bool_t, char *, err_ret_t *);
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate extern int self_check(char *);
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate static void read_default(void);
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate static char typename[64];
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate static int bg = 0;
1620Sstevel@tonic-gate static int backgrounded = 0;
1630Sstevel@tonic-gate static int posix = 0;
1640Sstevel@tonic-gate static int retries = BIGRETRY;
1650Sstevel@tonic-gate static ushort_t nfs_port = 0;
1660Sstevel@tonic-gate static char *nfs_proto = NULL;
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate static int mflg = 0;
1690Sstevel@tonic-gate static int Oflg = 0;	/* Overlay mounts */
1700Sstevel@tonic-gate static int qflg = 0;	/* quiet - don't print warnings on bad options */
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate static char *fstype = MNTTYPE_NFS;
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate static seconfig_t nfs_sec;
1750Sstevel@tonic-gate static int sec_opt = 0;	/* any security option ? */
1760Sstevel@tonic-gate static bool_t snego_done;
1770Sstevel@tonic-gate static void sigusr1(int);
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate /*
1800Sstevel@tonic-gate  * list of support services needed
1810Sstevel@tonic-gate  */
1820Sstevel@tonic-gate static char	*service_list[] = { STATD, LOCKD, NULL };
1830Sstevel@tonic-gate static char	*service_list_v4[] = { STATD, LOCKD, NFS4CBD, NFSMAPID, NULL };
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate /*
1860Sstevel@tonic-gate  * These two variables control the NFS version number to be used.
1870Sstevel@tonic-gate  *
1880Sstevel@tonic-gate  * nfsvers defaults to 0 which means to use the highest number that
1890Sstevel@tonic-gate  * both the client and the server support.  It can also be set to
1900Sstevel@tonic-gate  * a particular value, either 2, 3, or 4 to indicate the version
1910Sstevel@tonic-gate  * number of choice.  If the server (or the client) do not support
1920Sstevel@tonic-gate  * the version indicated, then the mount attempt will be failed.
1930Sstevel@tonic-gate  *
1940Sstevel@tonic-gate  * nfsvers_to_use is the actual version number found to use.  It
1950Sstevel@tonic-gate  * is determined in get_fh by pinging the various versions of the
1960Sstevel@tonic-gate  * NFS service on the server to see which responds positively.
1970Sstevel@tonic-gate  */
1980Sstevel@tonic-gate static rpcvers_t nfsvers = 0;
1990Sstevel@tonic-gate static rpcvers_t nfsvers_to_use = 0;
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate /*
2020Sstevel@tonic-gate  * There are the defaults (range) for the client when determining
2030Sstevel@tonic-gate  * which NFS version to use when probing the server (see above).
2040Sstevel@tonic-gate  * These will only be used when the vers mount option is not used and
2050Sstevel@tonic-gate  * these may be reset if /etc/default/nfs is configured to do so.
2060Sstevel@tonic-gate  */
2070Sstevel@tonic-gate static rpcvers_t vers_max_default = NFS_VERSMAX_DEFAULT;
2080Sstevel@tonic-gate static rpcvers_t vers_min_default = NFS_VERSMIN_DEFAULT;
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate /*
2110Sstevel@tonic-gate  * This variable controls whether to try the public file handle.
2120Sstevel@tonic-gate  */
2130Sstevel@tonic-gate static bool_t public_opt;
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate main(int argc, char **argv)
2160Sstevel@tonic-gate {
2170Sstevel@tonic-gate 	struct mnttab mnt;
2180Sstevel@tonic-gate 	extern char *optarg;
2190Sstevel@tonic-gate 	extern int optind;
2200Sstevel@tonic-gate 	char optbuf[MAX_MNTOPT_STR];
2210Sstevel@tonic-gate 	int ro = 0;
2220Sstevel@tonic-gate 	int r;
2230Sstevel@tonic-gate 	int c;
2240Sstevel@tonic-gate 	char *myname;
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
2270Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
2280Sstevel@tonic-gate #define	TEXT_DOMAIN	"SYS_TEST"
2290Sstevel@tonic-gate #endif
2300Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate 	myname = strrchr(argv[0], '/');
2330Sstevel@tonic-gate 	myname = myname ? myname + 1 : argv[0];
2340Sstevel@tonic-gate 	(void) snprintf(typename, sizeof (typename), "%s %s",
2350Sstevel@tonic-gate 	    MNTTYPE_NFS, myname);
2360Sstevel@tonic-gate 	argv[0] = typename;
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 	mnt.mnt_mntopts = optbuf;
2390Sstevel@tonic-gate 	(void) strcpy(optbuf, "rw");
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate 	/*
2420Sstevel@tonic-gate 	 * Set options
2430Sstevel@tonic-gate 	 */
2440Sstevel@tonic-gate 	while ((c = getopt(argc, argv, "ro:mOq")) != EOF) {
2450Sstevel@tonic-gate 		switch (c) {
2460Sstevel@tonic-gate 		case 'r':
2470Sstevel@tonic-gate 			ro++;
2480Sstevel@tonic-gate 			break;
2490Sstevel@tonic-gate 		case 'o':
2500Sstevel@tonic-gate 			if (strlen(optarg) >= MAX_MNTOPT_STR) {
2510Sstevel@tonic-gate 				pr_err(gettext("option string too long"));
2520Sstevel@tonic-gate 				return (RET_ERR);
2530Sstevel@tonic-gate 			}
2540Sstevel@tonic-gate 			(void) strcpy(mnt.mnt_mntopts, optarg);
2550Sstevel@tonic-gate #ifdef LATER					/* XXX */
2560Sstevel@tonic-gate 			if (strstr(optarg, MNTOPT_REMOUNT)) {
2570Sstevel@tonic-gate 				/*
2580Sstevel@tonic-gate 				 * If remount is specified, only rw is allowed.
2590Sstevel@tonic-gate 				 */
2600Sstevel@tonic-gate 				if ((strcmp(optarg, MNTOPT_REMOUNT) != 0) &&
2610Sstevel@tonic-gate 				    (strcmp(optarg, "remount,rw") != 0) &&
2620Sstevel@tonic-gate 				    (strcmp(optarg, "rw,remount") != 0)) {
2630Sstevel@tonic-gate 					pr_err(gettext("Invalid options\n"));
2640Sstevel@tonic-gate 					exit(RET_ERR);
2650Sstevel@tonic-gate 				}
2660Sstevel@tonic-gate 			}
2670Sstevel@tonic-gate #endif /* LATER */				/* XXX */
2680Sstevel@tonic-gate 			break;
2690Sstevel@tonic-gate 		case 'm':
2700Sstevel@tonic-gate 			mflg++;
2710Sstevel@tonic-gate 			break;
2720Sstevel@tonic-gate 		case 'O':
2730Sstevel@tonic-gate 			Oflg++;
2740Sstevel@tonic-gate 			break;
2750Sstevel@tonic-gate 		case 'q':
2760Sstevel@tonic-gate 			qflg++;
2770Sstevel@tonic-gate 			break;
2780Sstevel@tonic-gate 		default:
2790Sstevel@tonic-gate 			usage();
2800Sstevel@tonic-gate 			exit(RET_ERR);
2810Sstevel@tonic-gate 		}
2820Sstevel@tonic-gate 	}
2830Sstevel@tonic-gate 	if (argc - optind != 2) {
2840Sstevel@tonic-gate 		usage();
2850Sstevel@tonic-gate 		exit(RET_ERR);
2860Sstevel@tonic-gate 	}
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	mnt.mnt_special = argv[optind];
2890Sstevel@tonic-gate 	mnt.mnt_mountp = argv[optind+1];
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 	if (!priv_ineffect(PRIV_SYS_MOUNT) ||
2920Sstevel@tonic-gate 	    !priv_ineffect(PRIV_NET_PRIVADDR)) {
2930Sstevel@tonic-gate 		pr_err(gettext("insufficient privileges\n"));
2940Sstevel@tonic-gate 		exit(RET_ERR);
2950Sstevel@tonic-gate 	}
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 	/*
2980Sstevel@tonic-gate 	 * Read the defaults file to see if the min/max versions have
2990Sstevel@tonic-gate 	 * been set and therefore would override the encoded defaults.
3000Sstevel@tonic-gate 	 * Then check to make sure that if they were set that the
3010Sstevel@tonic-gate 	 * values are reasonable.
3020Sstevel@tonic-gate 	 */
3030Sstevel@tonic-gate 	read_default();
3040Sstevel@tonic-gate 	if (vers_min_default > vers_max_default ||
3050Sstevel@tonic-gate 		vers_min_default < NFS_VERSMIN ||
3060Sstevel@tonic-gate 		vers_max_default > NFS_VERSMAX) {
3070Sstevel@tonic-gate 		pr_err("%s %s\n%s %s\n",
3080Sstevel@tonic-gate 			gettext("Incorrect configuration of client\'s"),
3090Sstevel@tonic-gate 			NFSADMIN,
3100Sstevel@tonic-gate 			gettext("NFS_CLIENT_VERSMIN or NFS_CLIENT_VERSMAX"),
3110Sstevel@tonic-gate 			gettext("is either out of range or overlaps."));
3120Sstevel@tonic-gate 	}
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 	r = mount_nfs(&mnt, ro);
3150Sstevel@tonic-gate 	if (r == RET_RETRY && retries)
3160Sstevel@tonic-gate 		r = retry(&mnt, ro);
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	/*
3190Sstevel@tonic-gate 	 * exit(r);
3200Sstevel@tonic-gate 	 */
3210Sstevel@tonic-gate 	return (r);
3220Sstevel@tonic-gate }
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate static void
3250Sstevel@tonic-gate pr_err(const char *fmt, ...)
3260Sstevel@tonic-gate {
3270Sstevel@tonic-gate 	va_list ap;
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 	va_start(ap, fmt);
3300Sstevel@tonic-gate 	if (backgrounded != 0) {
3310Sstevel@tonic-gate 		(void) vsyslog(LOG_ERR, fmt, ap);
3320Sstevel@tonic-gate 	} else {
3330Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: ", typename);
3340Sstevel@tonic-gate 		(void) vfprintf(stderr, fmt, ap);
3350Sstevel@tonic-gate 		(void) fflush(stderr);
3360Sstevel@tonic-gate 	}
3370Sstevel@tonic-gate 	va_end(ap);
3380Sstevel@tonic-gate }
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate static void
3410Sstevel@tonic-gate usage()
3420Sstevel@tonic-gate {
3430Sstevel@tonic-gate 	(void) fprintf(stderr,
3440Sstevel@tonic-gate 	    gettext("Usage: nfs mount [-r] [-o opts] [server:]path dir\n"));
3450Sstevel@tonic-gate 	exit(RET_ERR);
3460Sstevel@tonic-gate }
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate static int
3490Sstevel@tonic-gate mount_nfs(struct mnttab *mntp, int ro)
3500Sstevel@tonic-gate {
3510Sstevel@tonic-gate 	struct nfs_args *args = NULL, *argp = NULL, *prev_argp = NULL;
3520Sstevel@tonic-gate 	struct netconfig *nconf = NULL;
3530Sstevel@tonic-gate 	struct replica *list = NULL;
3540Sstevel@tonic-gate 	int mntflags = 0;
3550Sstevel@tonic-gate 	int i, r, n;
3560Sstevel@tonic-gate 	int oldvers = 0, vers = 0;
3570Sstevel@tonic-gate 	int last_error = RET_OK;
3580Sstevel@tonic-gate 	int replicated = 0;
3590Sstevel@tonic-gate 	char *p;
3600Sstevel@tonic-gate 	bool_t url;
3610Sstevel@tonic-gate 	bool_t use_pubfh;
3620Sstevel@tonic-gate 	char *special = NULL;
3630Sstevel@tonic-gate 	char *oldpath = NULL;
3640Sstevel@tonic-gate 	char *newpath = NULL;
3650Sstevel@tonic-gate 	char *service;
3660Sstevel@tonic-gate 	pid_t pi;
3670Sstevel@tonic-gate 	struct flock f;
3680Sstevel@tonic-gate 	char *saveopts = NULL;
3690Sstevel@tonic-gate 	char **sl = NULL;
3700Sstevel@tonic-gate 
3710Sstevel@tonic-gate 	mntp->mnt_fstype = MNTTYPE_NFS;
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	if (ro) {
3740Sstevel@tonic-gate 		mntflags |= MS_RDONLY;
3750Sstevel@tonic-gate 		/* convert "rw"->"ro" */
3760Sstevel@tonic-gate 		if (p = strstr(mntp->mnt_mntopts, "rw")) {
3770Sstevel@tonic-gate 			if (*(p+2) == ',' || *(p+2) == '\0')
3780Sstevel@tonic-gate 				*(p+1) = 'o';
3790Sstevel@tonic-gate 		}
3800Sstevel@tonic-gate 	}
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	if (Oflg)
3830Sstevel@tonic-gate 		mntflags |= MS_OVERLAY;
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate 	list = parse_replica(mntp->mnt_special, &n);
3860Sstevel@tonic-gate 	if (list == NULL) {
3870Sstevel@tonic-gate 		if (n < 0)
3880Sstevel@tonic-gate 			pr_err(gettext("nfs file system; use [host:]path\n"));
3890Sstevel@tonic-gate 		else
3900Sstevel@tonic-gate 			pr_err(gettext("no memory\n"));
3910Sstevel@tonic-gate 		return (RET_ERR);
3920Sstevel@tonic-gate 	}
3930Sstevel@tonic-gate 
3940Sstevel@tonic-gate 	replicated = (n > 1);
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 	/*
3970Sstevel@tonic-gate 	 * There are some free() calls at the bottom of this loop, so be
3980Sstevel@tonic-gate 	 * careful about adding continue statements.
3990Sstevel@tonic-gate 	 */
4000Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
4010Sstevel@tonic-gate 		char *path;
4020Sstevel@tonic-gate 		char *host;
4030Sstevel@tonic-gate 		ushort_t port;
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate 		argp = (struct nfs_args *)malloc(sizeof (*argp));
4060Sstevel@tonic-gate 		if (argp == NULL) {
4070Sstevel@tonic-gate 			pr_err(gettext("no memory\n"));
4080Sstevel@tonic-gate 			last_error = RET_ERR;
4090Sstevel@tonic-gate 			goto out;
4100Sstevel@tonic-gate 		}
4110Sstevel@tonic-gate 		memset(argp, 0, sizeof (*argp));
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 		memset(&nfs_sec, 0, sizeof (nfs_sec));
4140Sstevel@tonic-gate 		sec_opt = 0;
4150Sstevel@tonic-gate 		use_pubfh = FALSE;
4160Sstevel@tonic-gate 		url = FALSE;
4170Sstevel@tonic-gate 		port = 0;
4180Sstevel@tonic-gate 		snego_done = FALSE;
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate 		/*
4210Sstevel@tonic-gate 		 * Looking for resources of the form
4220Sstevel@tonic-gate 		 *	nfs://server_host[:port_number]/path_name
4230Sstevel@tonic-gate 		 */
4240Sstevel@tonic-gate 		if (strcmp(list[i].host, "nfs") == 0 && strncmp(list[i].path,
4250Sstevel@tonic-gate 		    "//", 2) == 0) {
4260Sstevel@tonic-gate 			char *sport, *cb;
4270Sstevel@tonic-gate 			url = TRUE;
4280Sstevel@tonic-gate 			oldpath = strdup(list[i].path);
4290Sstevel@tonic-gate 			if (oldpath == NULL) {
4300Sstevel@tonic-gate 				pr_err(gettext("memory allocation failure\n"));
4310Sstevel@tonic-gate 				last_error = RET_ERR;
4320Sstevel@tonic-gate 				goto out;
4330Sstevel@tonic-gate 			}
4340Sstevel@tonic-gate 			host = list[i].path+2;
4350Sstevel@tonic-gate 			path = strchr(host, '/');
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate 			if (path == NULL) {
4380Sstevel@tonic-gate 				pr_err(gettext(
4390Sstevel@tonic-gate 				    "illegal nfs url syntax\n"));
4400Sstevel@tonic-gate 				last_error = RET_ERR;
4410Sstevel@tonic-gate 				goto out;
4420Sstevel@tonic-gate 			}
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate 			*path = '\0';
4450Sstevel@tonic-gate 			if (*host == '[') {
4460Sstevel@tonic-gate 				cb = strchr(host, ']');
4470Sstevel@tonic-gate 				if (cb == NULL) {
4480Sstevel@tonic-gate 					pr_err(gettext(
4490Sstevel@tonic-gate 						"illegal nfs url syntax\n"));
4500Sstevel@tonic-gate 					last_error = RET_ERR;
4510Sstevel@tonic-gate 					goto out;
4520Sstevel@tonic-gate 				} else {
4530Sstevel@tonic-gate 					*cb = '\0';
4540Sstevel@tonic-gate 					host++;
4550Sstevel@tonic-gate 					cb++;
4560Sstevel@tonic-gate 					if (*cb == ':')
4570Sstevel@tonic-gate 						port = htons((ushort_t)
4580Sstevel@tonic-gate 							atoi(cb+1));
4590Sstevel@tonic-gate 				}
4600Sstevel@tonic-gate 			} else {
4610Sstevel@tonic-gate 				sport = strchr(host, ':');
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 				if (sport != NULL && sport < path) {
4640Sstevel@tonic-gate 					*sport = '\0';
4650Sstevel@tonic-gate 					port = htons((ushort_t)atoi(sport+1));
4660Sstevel@tonic-gate 				}
4670Sstevel@tonic-gate 			}
4680Sstevel@tonic-gate 
4690Sstevel@tonic-gate 			path++;
4700Sstevel@tonic-gate 			if (*path == '\0')
4710Sstevel@tonic-gate 				path = ".";
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 		} else {
4740Sstevel@tonic-gate 			host = list[i].host;
4750Sstevel@tonic-gate 			path = list[i].path;
4760Sstevel@tonic-gate 		}
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate 		if (r = set_args(&mntflags, argp, host, mntp)) {
4790Sstevel@tonic-gate 			last_error = r;
4800Sstevel@tonic-gate 			goto out;
4810Sstevel@tonic-gate 		}
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 		if (public_opt == TRUE)
4840Sstevel@tonic-gate 			use_pubfh = TRUE;
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 		if (port == 0) {
4870Sstevel@tonic-gate 			port = nfs_port;
4880Sstevel@tonic-gate 		} else if (nfs_port != 0 && nfs_port != port) {
4890Sstevel@tonic-gate 			pr_err(gettext(
4900Sstevel@tonic-gate 			    "port (%u) in nfs URL not the same"
4910Sstevel@tonic-gate 			    " as port (%u) in port option\n"),
4920Sstevel@tonic-gate 			    (unsigned int)ntohs(port),
4930Sstevel@tonic-gate 			    (unsigned int)ntohs(nfs_port));
4940Sstevel@tonic-gate 			last_error = RET_ERR;
4950Sstevel@tonic-gate 			goto out;
4960Sstevel@tonic-gate 		}
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate 		if (replicated && !(mntflags & MS_RDONLY)) {
5000Sstevel@tonic-gate 			pr_err(gettext(
5010Sstevel@tonic-gate 				"replicated mounts must be read-only\n"));
5020Sstevel@tonic-gate 			last_error = RET_ERR;
5030Sstevel@tonic-gate 			goto out;
5040Sstevel@tonic-gate 		}
5050Sstevel@tonic-gate 
5060Sstevel@tonic-gate 		if (replicated && (argp->flags & NFSMNT_SOFT)) {
5070Sstevel@tonic-gate 			pr_err(gettext(
5080Sstevel@tonic-gate 				"replicated mounts must not be soft\n"));
5090Sstevel@tonic-gate 			last_error = RET_ERR;
5100Sstevel@tonic-gate 			goto out;
5110Sstevel@tonic-gate 		}
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 		oldvers = vers;
5140Sstevel@tonic-gate 		nconf = NULL;
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 		r = RET_ERR;
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 		/*
5190Sstevel@tonic-gate 		 * If -o public was specified, and/or a URL was specified,
5200Sstevel@tonic-gate 		 * then try the public file handle method.
5210Sstevel@tonic-gate 		 */
5220Sstevel@tonic-gate 		if ((use_pubfh == TRUE) || (url == TRUE)) {
5230Sstevel@tonic-gate 			r = get_fh_via_pub(argp, host, path, url, use_pubfh,
5240Sstevel@tonic-gate 				&vers, &nconf, port);
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 			if (r != RET_OK) {
5270Sstevel@tonic-gate 				/*
5280Sstevel@tonic-gate 				 * If -o public was specified, then return the
5290Sstevel@tonic-gate 				 * error now.
5300Sstevel@tonic-gate 				 */
5310Sstevel@tonic-gate 				if (use_pubfh == TRUE) {
5320Sstevel@tonic-gate 					last_error = r;
5330Sstevel@tonic-gate 					goto out;
5340Sstevel@tonic-gate 				}
5350Sstevel@tonic-gate 			} else
5360Sstevel@tonic-gate 				use_pubfh = TRUE;
5370Sstevel@tonic-gate 			argp->flags |= NFSMNT_PUBLIC;
5380Sstevel@tonic-gate 		}
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 		if ((r != RET_OK) || (vers == NFS_V4)) {
5410Sstevel@tonic-gate 			bool_t loud_on_mnt_err;
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 			/*
5440Sstevel@tonic-gate 			 * This can happen if -o public is not specified,
5450Sstevel@tonic-gate 			 * special is a URL, and server doesn't support
5460Sstevel@tonic-gate 			 * public file handle.
5470Sstevel@tonic-gate 			 */
5480Sstevel@tonic-gate 			if (url) {
5490Sstevel@tonic-gate 				URLparse(path);
5500Sstevel@tonic-gate 			}
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 			/*
5530Sstevel@tonic-gate 			 * If the path portion of the URL didn't have
5540Sstevel@tonic-gate 			 * a leading / then there is good possibility
5550Sstevel@tonic-gate 			 * that a mount without a leading slash will
5560Sstevel@tonic-gate 			 * fail.
5570Sstevel@tonic-gate 			 */
5580Sstevel@tonic-gate 			if (url == TRUE && *path != '/')
5590Sstevel@tonic-gate 				loud_on_mnt_err = FALSE;
5600Sstevel@tonic-gate 			else
5610Sstevel@tonic-gate 				loud_on_mnt_err = TRUE;
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 			r = get_fh(argp, host, path, &vers,
5640Sstevel@tonic-gate 				loud_on_mnt_err, &nconf, port);
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 			if (r != RET_OK) {
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate 				/*
5690Sstevel@tonic-gate 				 * If there was no leading / and the path was
5700Sstevel@tonic-gate 				 * derived from a URL, then try again
5710Sstevel@tonic-gate 				 * with a leading /.
5720Sstevel@tonic-gate 				 */
5730Sstevel@tonic-gate 				if ((r == RET_MNTERR) &&
5740Sstevel@tonic-gate 				    (loud_on_mnt_err == FALSE)) {
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate 					newpath = malloc(strlen(path)+2);
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 					if (newpath == NULL) {
5790Sstevel@tonic-gate 						pr_err(gettext("memory "
5800Sstevel@tonic-gate 						    "allocation failure\n"));
5810Sstevel@tonic-gate 						last_error = RET_ERR;
5820Sstevel@tonic-gate 						goto out;
5830Sstevel@tonic-gate 					}
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate 					strcpy(newpath, "/");
5860Sstevel@tonic-gate 					strcat(newpath, path);
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate 					r = get_fh(argp, host, newpath, &vers,
5890Sstevel@tonic-gate 						TRUE, &nconf, port);
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 					if (r == RET_OK)
5920Sstevel@tonic-gate 						path = newpath;
5930Sstevel@tonic-gate 				}
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 				/*
5960Sstevel@tonic-gate 				 * map exit code back to RET_ERR.
5970Sstevel@tonic-gate 				 */
5980Sstevel@tonic-gate 				if (r == RET_MNTERR)
5990Sstevel@tonic-gate 					r = RET_ERR;
6000Sstevel@tonic-gate 
6010Sstevel@tonic-gate 				if (r != RET_OK) {
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate 					if (replicated) {
6040Sstevel@tonic-gate 						if (argp->fh)
6050Sstevel@tonic-gate 							free(argp->fh);
6060Sstevel@tonic-gate 						if (argp->pathconf)
6070Sstevel@tonic-gate 							free(argp->pathconf);
6080Sstevel@tonic-gate 						free(argp);
6090Sstevel@tonic-gate 						goto cont;
6100Sstevel@tonic-gate 					}
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 					last_error = r;
6130Sstevel@tonic-gate 					goto out;
6140Sstevel@tonic-gate 				}
6150Sstevel@tonic-gate 			}
6160Sstevel@tonic-gate 		}
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate 		if (oldvers && vers != oldvers) {
6190Sstevel@tonic-gate 			pr_err(
6200Sstevel@tonic-gate 			    gettext("replicas must have the same version\n"));
6210Sstevel@tonic-gate 			last_error = RET_ERR;
6220Sstevel@tonic-gate 			goto out;
6230Sstevel@tonic-gate 		}
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 		/*
6260Sstevel@tonic-gate 		 * decide whether to use remote host's
6270Sstevel@tonic-gate 		 * lockd or do local locking
6280Sstevel@tonic-gate 		 */
6290Sstevel@tonic-gate 		if (!(argp->flags & NFSMNT_LLOCK) && vers == NFS_VERSION &&
6300Sstevel@tonic-gate 		    remote_lock(host, argp->fh)) {
6310Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
6320Sstevel@tonic-gate 			    "WARNING: No network locking on %s:%s:"),
6330Sstevel@tonic-gate 			    host, path);
6340Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
6350Sstevel@tonic-gate 			    " contact admin to install server change\n"));
6360Sstevel@tonic-gate 				argp->flags |= NFSMNT_LLOCK;
6370Sstevel@tonic-gate 		}
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate 		if (self_check(host))
6400Sstevel@tonic-gate 			argp->flags |= NFSMNT_LOOPBACK;
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate 		if (use_pubfh == FALSE) {
6430Sstevel@tonic-gate 			/*
6440Sstevel@tonic-gate 			 * Call to get_fh() above may have obtained the
6450Sstevel@tonic-gate 			 * netconfig info and NULL proc'd the server.
6460Sstevel@tonic-gate 			 * This would be the case with v4
6470Sstevel@tonic-gate 			 */
6480Sstevel@tonic-gate 			if (!(argp->flags & NFSMNT_KNCONF)) {
6490Sstevel@tonic-gate 				nconf = NULL;
6500Sstevel@tonic-gate 				if (r = getaddr_nfs(argp, host, &nconf,
6510Sstevel@tonic-gate 					    FALSE, path, port, NULL, TRUE)) {
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 (make_secure(argp, host, nconf, use_pubfh, vers) < 0) {
6590Sstevel@tonic-gate 			last_error = RET_ERR;
6600Sstevel@tonic-gate 			goto out;
6610Sstevel@tonic-gate 		}
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 		if ((url == TRUE) && (use_pubfh == FALSE)) {
6640Sstevel@tonic-gate 			/*
6650Sstevel@tonic-gate 			 * Convert the special from
6660Sstevel@tonic-gate 			 *	nfs://host/path
6670Sstevel@tonic-gate 			 * to
6680Sstevel@tonic-gate 			 *	host:path
6690Sstevel@tonic-gate 			 */
6700Sstevel@tonic-gate 			if (convert_special(&special, host, oldpath, path,
6710Sstevel@tonic-gate 			    mntp->mnt_special) == -1) {
6720Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(
6730Sstevel@tonic-gate 				    "could not convert URL nfs:%s to %s:%s\n"),
6740Sstevel@tonic-gate 				    oldpath, host, path);
6750Sstevel@tonic-gate 				last_error = RET_ERR;
6760Sstevel@tonic-gate 				goto out;
6770Sstevel@tonic-gate 			} else {
6780Sstevel@tonic-gate 				mntp->mnt_special = special;
6790Sstevel@tonic-gate 			}
6800Sstevel@tonic-gate 		}
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 		if (prev_argp == NULL)
6830Sstevel@tonic-gate 			args = argp;
6840Sstevel@tonic-gate 		else
6850Sstevel@tonic-gate 			prev_argp->nfs_ext_u.nfs_extB.next = argp;
6860Sstevel@tonic-gate 		prev_argp = argp;
6870Sstevel@tonic-gate 
6880Sstevel@tonic-gate cont:
6890Sstevel@tonic-gate 		if (oldpath != NULL) {
6900Sstevel@tonic-gate 			free(oldpath);
6910Sstevel@tonic-gate 			oldpath = NULL;
6920Sstevel@tonic-gate 		}
6930Sstevel@tonic-gate 
6940Sstevel@tonic-gate 		if (newpath != NULL) {
6950Sstevel@tonic-gate 			free(newpath);
6960Sstevel@tonic-gate 			newpath = NULL;
6970Sstevel@tonic-gate 		}
6980Sstevel@tonic-gate 	}
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate 	argp = NULL;
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate 	if (args == NULL) {
7030Sstevel@tonic-gate 		last_error = RET_RETRY;
7040Sstevel@tonic-gate 		goto out;
7050Sstevel@tonic-gate 	}
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 	/* Determine which services are appropriate for the NFS version */
7080Sstevel@tonic-gate 	if (strcmp(fstype, MNTTYPE_NFS4) == 0)
7090Sstevel@tonic-gate 		sl = service_list_v4;
7100Sstevel@tonic-gate 	else
7110Sstevel@tonic-gate 		sl = service_list;
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate 	/*
7140Sstevel@tonic-gate 	 * enable services as needed.
7150Sstevel@tonic-gate 	 */
7160Sstevel@tonic-gate 	_check_services(sl);
7170Sstevel@tonic-gate 
7180Sstevel@tonic-gate 	mntflags |= MS_DATA | MS_OPTIONSTR;
7190Sstevel@tonic-gate 
7200Sstevel@tonic-gate 	if (mflg)
7210Sstevel@tonic-gate 		mntflags |= MS_NOMNTTAB;
7220Sstevel@tonic-gate 
7230Sstevel@tonic-gate 	if (!qflg)
7240Sstevel@tonic-gate 		saveopts = strdup(mntp->mnt_mntopts);
7250Sstevel@tonic-gate 
7260Sstevel@tonic-gate 	if (mount(mntp->mnt_special, mntp->mnt_mountp, mntflags, fstype, args,
7270Sstevel@tonic-gate 		sizeof (*args), mntp->mnt_mntopts, MAX_MNTOPT_STR) < 0) {
7280Sstevel@tonic-gate 		if (errno != ENOENT) {
7290Sstevel@tonic-gate 			pr_err(gettext("mount: %s: %s\n"),
7300Sstevel@tonic-gate 				mntp->mnt_mountp, strerror(errno));
7310Sstevel@tonic-gate 		} else {
7320Sstevel@tonic-gate 			struct stat sb;
7330Sstevel@tonic-gate 			if (stat(mntp->mnt_mountp, &sb) < 0 && errno == ENOENT)
7340Sstevel@tonic-gate 				pr_err(gettext("mount: %s: %s\n"),
7350Sstevel@tonic-gate 					mntp->mnt_mountp, strerror(ENOENT));
7360Sstevel@tonic-gate 			else
7370Sstevel@tonic-gate 				pr_err("%s: %s\n", mntp->mnt_special,
7380Sstevel@tonic-gate 					strerror(ENOENT));
7390Sstevel@tonic-gate 		}
7400Sstevel@tonic-gate 
7410Sstevel@tonic-gate 		last_error = RET_ERR;
7420Sstevel@tonic-gate 		goto out;
7430Sstevel@tonic-gate 	}
7440Sstevel@tonic-gate 
7450Sstevel@tonic-gate 	if (!qflg && saveopts != NULL) {
7460Sstevel@tonic-gate 		cmp_requested_to_actual_options(saveopts, mntp->mnt_mntopts,
7470Sstevel@tonic-gate 		    mntp->mnt_special, mntp->mnt_mountp);
7480Sstevel@tonic-gate 	}
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate out:
7510Sstevel@tonic-gate 	if (saveopts != NULL)
7520Sstevel@tonic-gate 		free(saveopts);
7530Sstevel@tonic-gate 	if (special != NULL)
7540Sstevel@tonic-gate 		free(special);
7550Sstevel@tonic-gate 	if (oldpath != NULL)
7560Sstevel@tonic-gate 		free(oldpath);
7570Sstevel@tonic-gate 	if (newpath != NULL)
7580Sstevel@tonic-gate 		free(newpath);
7590Sstevel@tonic-gate 
7600Sstevel@tonic-gate 	free_replica(list, n);
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate 	if (argp != NULL) {
7630Sstevel@tonic-gate 		/*
7640Sstevel@tonic-gate 		 * If we had a new entry which was not added to the
7650Sstevel@tonic-gate 		 * list yet, then add it now that it can be freed.
7660Sstevel@tonic-gate 		 */
7670Sstevel@tonic-gate 		if (prev_argp == NULL)
7680Sstevel@tonic-gate 			args = argp;
7690Sstevel@tonic-gate 		else
7700Sstevel@tonic-gate 			prev_argp->nfs_ext_u.nfs_extB.next = argp;
7710Sstevel@tonic-gate 	}
7720Sstevel@tonic-gate 	argp = args;
7730Sstevel@tonic-gate 	while (argp != NULL) {
7740Sstevel@tonic-gate 		if (argp->fh)
7750Sstevel@tonic-gate 			free(argp->fh);
7760Sstevel@tonic-gate 		if (argp->pathconf)
7770Sstevel@tonic-gate 			free(argp->pathconf);
7780Sstevel@tonic-gate 		if (argp->knconf)
7790Sstevel@tonic-gate 			free(argp->knconf);
7800Sstevel@tonic-gate 		if (argp->addr) {
7810Sstevel@tonic-gate 			free(argp->addr->buf);
7820Sstevel@tonic-gate 			free(argp->addr);
7830Sstevel@tonic-gate 		}
7840Sstevel@tonic-gate 		nfs_free_secdata(argp->nfs_ext_u.nfs_extB.secdata);
7850Sstevel@tonic-gate 		if (argp->syncaddr) {
7860Sstevel@tonic-gate 			free(argp->syncaddr->buf);
7870Sstevel@tonic-gate 			free(argp->syncaddr);
7880Sstevel@tonic-gate 		}
7890Sstevel@tonic-gate 		if (argp->netname)
7900Sstevel@tonic-gate 			free(argp->netname);
7910Sstevel@tonic-gate 		prev_argp = argp;
7920Sstevel@tonic-gate 		argp = argp->nfs_ext_u.nfs_extB.next;
7930Sstevel@tonic-gate 		free(prev_argp);
7940Sstevel@tonic-gate 	}
7950Sstevel@tonic-gate 
7960Sstevel@tonic-gate 	return (last_error);
7970Sstevel@tonic-gate }
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate /*
8000Sstevel@tonic-gate  * These options are duplicated in uts/common/fs/nfs/nfs_dlinet.c
8010Sstevel@tonic-gate  * Changes must be made to both lists.
8020Sstevel@tonic-gate  */
8030Sstevel@tonic-gate static char *optlist[] = {
8040Sstevel@tonic-gate #define	OPT_RO		0
8050Sstevel@tonic-gate 	MNTOPT_RO,
8060Sstevel@tonic-gate #define	OPT_RW		1
8070Sstevel@tonic-gate 	MNTOPT_RW,
8080Sstevel@tonic-gate #define	OPT_QUOTA	2
8090Sstevel@tonic-gate 	MNTOPT_QUOTA,
8100Sstevel@tonic-gate #define	OPT_NOQUOTA	3
8110Sstevel@tonic-gate 	MNTOPT_NOQUOTA,
8120Sstevel@tonic-gate #define	OPT_SOFT	4
8130Sstevel@tonic-gate 	MNTOPT_SOFT,
8140Sstevel@tonic-gate #define	OPT_HARD	5
8150Sstevel@tonic-gate 	MNTOPT_HARD,
8160Sstevel@tonic-gate #define	OPT_SUID	6
8170Sstevel@tonic-gate 	MNTOPT_SUID,
8180Sstevel@tonic-gate #define	OPT_NOSUID	7
8190Sstevel@tonic-gate 	MNTOPT_NOSUID,
8200Sstevel@tonic-gate #define	OPT_GRPID	8
8210Sstevel@tonic-gate 	MNTOPT_GRPID,
8220Sstevel@tonic-gate #define	OPT_REMOUNT	9
8230Sstevel@tonic-gate 	MNTOPT_REMOUNT,
8240Sstevel@tonic-gate #define	OPT_NOSUB	10
8250Sstevel@tonic-gate 	MNTOPT_NOSUB,
8260Sstevel@tonic-gate #define	OPT_INTR	11
8270Sstevel@tonic-gate 	MNTOPT_INTR,
8280Sstevel@tonic-gate #define	OPT_NOINTR	12
8290Sstevel@tonic-gate 	MNTOPT_NOINTR,
8300Sstevel@tonic-gate #define	OPT_PORT	13
8310Sstevel@tonic-gate 	MNTOPT_PORT,
8320Sstevel@tonic-gate #define	OPT_SECURE	14
8330Sstevel@tonic-gate 	MNTOPT_SECURE,
8340Sstevel@tonic-gate #define	OPT_RSIZE	15
8350Sstevel@tonic-gate 	MNTOPT_RSIZE,
8360Sstevel@tonic-gate #define	OPT_WSIZE	16
8370Sstevel@tonic-gate 	MNTOPT_WSIZE,
8380Sstevel@tonic-gate #define	OPT_TIMEO	17
8390Sstevel@tonic-gate 	MNTOPT_TIMEO,
8400Sstevel@tonic-gate #define	OPT_RETRANS	18
8410Sstevel@tonic-gate 	MNTOPT_RETRANS,
8420Sstevel@tonic-gate #define	OPT_ACTIMEO	19
8430Sstevel@tonic-gate 	MNTOPT_ACTIMEO,
8440Sstevel@tonic-gate #define	OPT_ACREGMIN	20
8450Sstevel@tonic-gate 	MNTOPT_ACREGMIN,
8460Sstevel@tonic-gate #define	OPT_ACREGMAX	21
8470Sstevel@tonic-gate 	MNTOPT_ACREGMAX,
8480Sstevel@tonic-gate #define	OPT_ACDIRMIN	22
8490Sstevel@tonic-gate 	MNTOPT_ACDIRMIN,
8500Sstevel@tonic-gate #define	OPT_ACDIRMAX	23
8510Sstevel@tonic-gate 	MNTOPT_ACDIRMAX,
8520Sstevel@tonic-gate #define	OPT_BG		24
8530Sstevel@tonic-gate 	MNTOPT_BG,
8540Sstevel@tonic-gate #define	OPT_FG		25
8550Sstevel@tonic-gate 	MNTOPT_FG,
8560Sstevel@tonic-gate #define	OPT_RETRY	26
8570Sstevel@tonic-gate 	MNTOPT_RETRY,
8580Sstevel@tonic-gate #define	OPT_NOAC	27
8590Sstevel@tonic-gate 	MNTOPT_NOAC,
8600Sstevel@tonic-gate #define	OPT_NOCTO	28
8610Sstevel@tonic-gate 	MNTOPT_NOCTO,
8620Sstevel@tonic-gate #define	OPT_LLOCK	29
8630Sstevel@tonic-gate 	MNTOPT_LLOCK,
8640Sstevel@tonic-gate #define	OPT_POSIX	30
8650Sstevel@tonic-gate 	MNTOPT_POSIX,
8660Sstevel@tonic-gate #define	OPT_VERS	31
8670Sstevel@tonic-gate 	MNTOPT_VERS,
8680Sstevel@tonic-gate #define	OPT_PROTO	32
8690Sstevel@tonic-gate 	MNTOPT_PROTO,
8700Sstevel@tonic-gate #define	OPT_SEMISOFT	33
8710Sstevel@tonic-gate 	MNTOPT_SEMISOFT,
8720Sstevel@tonic-gate #define	OPT_NOPRINT	34
8730Sstevel@tonic-gate 	MNTOPT_NOPRINT,
8740Sstevel@tonic-gate #define	OPT_SEC		35
8750Sstevel@tonic-gate 	MNTOPT_SEC,
8760Sstevel@tonic-gate #define	OPT_LARGEFILES	36
8770Sstevel@tonic-gate 	MNTOPT_LARGEFILES,
8780Sstevel@tonic-gate #define	OPT_NOLARGEFILES 37
8790Sstevel@tonic-gate 	MNTOPT_NOLARGEFILES,
8800Sstevel@tonic-gate #define	OPT_PUBLIC	38
8810Sstevel@tonic-gate 	MNTOPT_PUBLIC,
8820Sstevel@tonic-gate #define	OPT_DIRECTIO	39
8830Sstevel@tonic-gate 	MNTOPT_FORCEDIRECTIO,
8840Sstevel@tonic-gate #define	OPT_NODIRECTIO	40
8850Sstevel@tonic-gate 	MNTOPT_NOFORCEDIRECTIO,
8860Sstevel@tonic-gate #define	OPT_XATTR	41
8870Sstevel@tonic-gate 	MNTOPT_XATTR,
8880Sstevel@tonic-gate #define	OPT_NOXATTR	42
8890Sstevel@tonic-gate 	MNTOPT_NOXATTR,
8900Sstevel@tonic-gate #define	OPT_DEVICES	43
8910Sstevel@tonic-gate 	MNTOPT_DEVICES,
8920Sstevel@tonic-gate #define	OPT_NODEVICES	44
8930Sstevel@tonic-gate 	MNTOPT_NODEVICES,
8940Sstevel@tonic-gate #define	OPT_SETUID	45
8950Sstevel@tonic-gate 	MNTOPT_SETUID,
8960Sstevel@tonic-gate #define	OPT_NOSETUID	46
8970Sstevel@tonic-gate 	MNTOPT_NOSETUID,
8980Sstevel@tonic-gate #define	OPT_EXEC	47
8990Sstevel@tonic-gate 	MNTOPT_EXEC,
9000Sstevel@tonic-gate #define	OPT_NOEXEC	48
9010Sstevel@tonic-gate 	MNTOPT_NOEXEC,
9020Sstevel@tonic-gate 	NULL
9030Sstevel@tonic-gate };
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate #define	bad(val) (val == NULL || !isdigit(*val))
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate static int
9080Sstevel@tonic-gate set_args(int *mntflags, struct nfs_args *args, char *fshost, struct mnttab *mnt)
9090Sstevel@tonic-gate {
9100Sstevel@tonic-gate 	char *saveopt, *optstr, *opts, *newopts, *val;
9110Sstevel@tonic-gate 	int largefiles = 0;
9120Sstevel@tonic-gate 	int invalid = 0;
9130Sstevel@tonic-gate 	int attrpref = 0;
9140Sstevel@tonic-gate 	int optlen;
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 	args->flags = NFSMNT_INT;	/* default is "intr" */
9170Sstevel@tonic-gate 	args->flags |= NFSMNT_HOSTNAME;
9180Sstevel@tonic-gate 	args->flags |= NFSMNT_NEWARGS;	/* using extented nfs_args structure */
9190Sstevel@tonic-gate 	args->hostname = fshost;
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 	optstr = opts = strdup(mnt->mnt_mntopts);
9220Sstevel@tonic-gate 	/* sizeof (MNTOPT_XXX) includes one extra byte we may need for "," */
9230Sstevel@tonic-gate 	optlen = strlen(mnt->mnt_mntopts) + sizeof (MNTOPT_XATTR) + 1;
9240Sstevel@tonic-gate 	if (optlen > MAX_MNTOPT_STR) {
9250Sstevel@tonic-gate 		pr_err(gettext("option string too long"));
9260Sstevel@tonic-gate 		return (RET_ERR);
9270Sstevel@tonic-gate 	}
9280Sstevel@tonic-gate 	newopts = malloc(optlen);
9290Sstevel@tonic-gate 	if (opts == NULL || newopts == NULL) {
9300Sstevel@tonic-gate 		pr_err(gettext("no memory"));
9310Sstevel@tonic-gate 		if (opts)
9320Sstevel@tonic-gate 			free(opts);
9330Sstevel@tonic-gate 		if (newopts)
9340Sstevel@tonic-gate 			free(newopts);
9350Sstevel@tonic-gate 		return (RET_ERR);
9360Sstevel@tonic-gate 	}
9370Sstevel@tonic-gate 	newopts[0] = '\0';
9380Sstevel@tonic-gate 
9390Sstevel@tonic-gate 	while (*opts) {
9400Sstevel@tonic-gate 		invalid = 0;
9410Sstevel@tonic-gate 		saveopt = opts;
9420Sstevel@tonic-gate 		switch (getsubopt(&opts, optlist, &val)) {
9430Sstevel@tonic-gate 		case OPT_RO:
9440Sstevel@tonic-gate 			*mntflags |= MS_RDONLY;
9450Sstevel@tonic-gate 			break;
9460Sstevel@tonic-gate 		case OPT_RW:
9470Sstevel@tonic-gate 			*mntflags &= ~(MS_RDONLY);
9480Sstevel@tonic-gate 			break;
9490Sstevel@tonic-gate 		case OPT_QUOTA:
9500Sstevel@tonic-gate 		case OPT_NOQUOTA:
9510Sstevel@tonic-gate 			break;
9520Sstevel@tonic-gate 		case OPT_SOFT:
9530Sstevel@tonic-gate 			args->flags |= NFSMNT_SOFT;
9540Sstevel@tonic-gate 			args->flags &= ~(NFSMNT_SEMISOFT);
9550Sstevel@tonic-gate 			break;
9560Sstevel@tonic-gate 		case OPT_SEMISOFT:
9570Sstevel@tonic-gate 			args->flags |= NFSMNT_SOFT;
9580Sstevel@tonic-gate 			args->flags |= NFSMNT_SEMISOFT;
9590Sstevel@tonic-gate 			break;
9600Sstevel@tonic-gate 		case OPT_HARD:
9610Sstevel@tonic-gate 			args->flags &= ~(NFSMNT_SOFT);
9620Sstevel@tonic-gate 			args->flags &= ~(NFSMNT_SEMISOFT);
9630Sstevel@tonic-gate 			break;
9640Sstevel@tonic-gate 		case OPT_SUID:
9650Sstevel@tonic-gate 			*mntflags &= ~(MS_NOSUID);
9660Sstevel@tonic-gate 			break;
9670Sstevel@tonic-gate 		case OPT_NOSUID:
9680Sstevel@tonic-gate 			*mntflags |= MS_NOSUID;
9690Sstevel@tonic-gate 			break;
9700Sstevel@tonic-gate 		case OPT_GRPID:
9710Sstevel@tonic-gate 			args->flags |= NFSMNT_GRPID;
9720Sstevel@tonic-gate 			break;
9730Sstevel@tonic-gate 		case OPT_REMOUNT:
9740Sstevel@tonic-gate 			*mntflags |= MS_REMOUNT;
9750Sstevel@tonic-gate 			break;
9760Sstevel@tonic-gate 		case OPT_INTR:
9770Sstevel@tonic-gate 			args->flags |= NFSMNT_INT;
9780Sstevel@tonic-gate 			break;
9790Sstevel@tonic-gate 		case OPT_NOINTR:
9800Sstevel@tonic-gate 			args->flags &= ~(NFSMNT_INT);
9810Sstevel@tonic-gate 			break;
9820Sstevel@tonic-gate 		case OPT_NOAC:
9830Sstevel@tonic-gate 			args->flags |= NFSMNT_NOAC;
9840Sstevel@tonic-gate 			break;
9850Sstevel@tonic-gate 		case OPT_PORT:
9860Sstevel@tonic-gate 			if (bad(val))
9870Sstevel@tonic-gate 				goto badopt;
9880Sstevel@tonic-gate 			nfs_port = htons((ushort_t)atoi(val));
9890Sstevel@tonic-gate 			break;
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 		case OPT_SECURE:
9920Sstevel@tonic-gate 			if (nfs_getseconfig_byname("dh", &nfs_sec)) {
9930Sstevel@tonic-gate 			    pr_err(gettext("can not get \"dh\" from %s\n"),
9940Sstevel@tonic-gate 						NFSSEC_CONF);
9950Sstevel@tonic-gate 			    goto badopt;
9960Sstevel@tonic-gate 			}
9970Sstevel@tonic-gate 			sec_opt++;
9980Sstevel@tonic-gate 			break;
9990Sstevel@tonic-gate 
10000Sstevel@tonic-gate 		case OPT_NOCTO:
10010Sstevel@tonic-gate 			args->flags |= NFSMNT_NOCTO;
10020Sstevel@tonic-gate 			break;
10030Sstevel@tonic-gate 
10040Sstevel@tonic-gate 		case OPT_RSIZE:
10050Sstevel@tonic-gate 			args->flags |= NFSMNT_RSIZE;
10060Sstevel@tonic-gate 			if (bad(val))
10070Sstevel@tonic-gate 				goto badopt;
10080Sstevel@tonic-gate 			args->rsize = atoi(val);
10090Sstevel@tonic-gate 			break;
10100Sstevel@tonic-gate 		case OPT_WSIZE:
10110Sstevel@tonic-gate 			args->flags |= NFSMNT_WSIZE;
10120Sstevel@tonic-gate 			if (bad(val))
10130Sstevel@tonic-gate 				goto badopt;
10140Sstevel@tonic-gate 			args->wsize = atoi(val);
10150Sstevel@tonic-gate 			break;
10160Sstevel@tonic-gate 		case OPT_TIMEO:
10170Sstevel@tonic-gate 			args->flags |= NFSMNT_TIMEO;
10180Sstevel@tonic-gate 			if (bad(val))
10190Sstevel@tonic-gate 				goto badopt;
10200Sstevel@tonic-gate 			args->timeo = atoi(val);
10210Sstevel@tonic-gate 			break;
10220Sstevel@tonic-gate 		case OPT_RETRANS:
10230Sstevel@tonic-gate 			args->flags |= NFSMNT_RETRANS;
10240Sstevel@tonic-gate 			if (bad(val))
10250Sstevel@tonic-gate 				goto badopt;
10260Sstevel@tonic-gate 			args->retrans = atoi(val);
10270Sstevel@tonic-gate 			break;
10280Sstevel@tonic-gate 		case OPT_ACTIMEO:
10290Sstevel@tonic-gate 			args->flags |= NFSMNT_ACDIRMAX;
10300Sstevel@tonic-gate 			args->flags |= NFSMNT_ACREGMAX;
10310Sstevel@tonic-gate 			args->flags |= NFSMNT_ACDIRMIN;
10320Sstevel@tonic-gate 			args->flags |= NFSMNT_ACREGMIN;
10330Sstevel@tonic-gate 			if (bad(val))
10340Sstevel@tonic-gate 				goto badopt;
10350Sstevel@tonic-gate 			args->acdirmin = args->acregmin = args->acdirmax
10360Sstevel@tonic-gate 				= args->acregmax = atoi(val);
10370Sstevel@tonic-gate 			break;
10380Sstevel@tonic-gate 		case OPT_ACREGMIN:
10390Sstevel@tonic-gate 			args->flags |= NFSMNT_ACREGMIN;
10400Sstevel@tonic-gate 			if (bad(val))
10410Sstevel@tonic-gate 				goto badopt;
10420Sstevel@tonic-gate 			args->acregmin = atoi(val);
10430Sstevel@tonic-gate 			break;
10440Sstevel@tonic-gate 		case OPT_ACREGMAX:
10450Sstevel@tonic-gate 			args->flags |= NFSMNT_ACREGMAX;
10460Sstevel@tonic-gate 			if (bad(val))
10470Sstevel@tonic-gate 				goto badopt;
10480Sstevel@tonic-gate 			args->acregmax = atoi(val);
10490Sstevel@tonic-gate 			break;
10500Sstevel@tonic-gate 		case OPT_ACDIRMIN:
10510Sstevel@tonic-gate 			args->flags |= NFSMNT_ACDIRMIN;
10520Sstevel@tonic-gate 			if (bad(val))
10530Sstevel@tonic-gate 				goto badopt;
10540Sstevel@tonic-gate 			args->acdirmin = atoi(val);
10550Sstevel@tonic-gate 			break;
10560Sstevel@tonic-gate 		case OPT_ACDIRMAX:
10570Sstevel@tonic-gate 			args->flags |= NFSMNT_ACDIRMAX;
10580Sstevel@tonic-gate 			if (bad(val))
10590Sstevel@tonic-gate 				goto badopt;
10600Sstevel@tonic-gate 			args->acdirmax = atoi(val);
10610Sstevel@tonic-gate 			break;
10620Sstevel@tonic-gate 		case OPT_BG:
10630Sstevel@tonic-gate 			bg++;
10640Sstevel@tonic-gate 			break;
10650Sstevel@tonic-gate 		case OPT_FG:
10660Sstevel@tonic-gate 			bg = 0;
10670Sstevel@tonic-gate 			break;
10680Sstevel@tonic-gate 		case OPT_RETRY:
10690Sstevel@tonic-gate 			if (bad(val))
10700Sstevel@tonic-gate 				goto badopt;
10710Sstevel@tonic-gate 			retries = atoi(val);
10720Sstevel@tonic-gate 			break;
10730Sstevel@tonic-gate 		case OPT_LLOCK:
10740Sstevel@tonic-gate 			args->flags |= NFSMNT_LLOCK;
10750Sstevel@tonic-gate 			break;
10760Sstevel@tonic-gate 		case OPT_POSIX:
10770Sstevel@tonic-gate 			posix = 1;
10780Sstevel@tonic-gate 			break;
10790Sstevel@tonic-gate 		case OPT_VERS:
10800Sstevel@tonic-gate 			if (bad(val))
10810Sstevel@tonic-gate 				goto badopt;
10820Sstevel@tonic-gate 			nfsvers = (rpcvers_t)atoi(val);
10830Sstevel@tonic-gate 			break;
10840Sstevel@tonic-gate 		case OPT_PROTO:
108577Soa138391 			if (val == NULL)
108677Soa138391 				goto badopt;
108777Soa138391 
10880Sstevel@tonic-gate 			nfs_proto = (char *)malloc(strlen(val)+1);
108977Soa138391 			if (!nfs_proto) {
109077Soa138391 				pr_err(gettext("no memory"));
109177Soa138391 				return (RET_ERR);
109277Soa138391 			}
109377Soa138391 
109477Soa138391 			(void) strncpy(nfs_proto, val, strlen(val)+1);
10950Sstevel@tonic-gate 			break;
10960Sstevel@tonic-gate 		case OPT_NOPRINT:
10970Sstevel@tonic-gate 			args->flags |= NFSMNT_NOPRINT;
10980Sstevel@tonic-gate 			break;
10990Sstevel@tonic-gate 		case OPT_LARGEFILES:
11000Sstevel@tonic-gate 			largefiles = 1;
11010Sstevel@tonic-gate 			break;
11020Sstevel@tonic-gate 		case OPT_NOLARGEFILES:
11030Sstevel@tonic-gate 			pr_err(gettext("NFS can't support \"nolargefiles\"\n"));
11040Sstevel@tonic-gate 			free(optstr);
11050Sstevel@tonic-gate 			return (RET_ERR);
11060Sstevel@tonic-gate 
11070Sstevel@tonic-gate 		case OPT_SEC:
11080Sstevel@tonic-gate 			if (nfs_getseconfig_byname(val, &nfs_sec)) {
11090Sstevel@tonic-gate 			    pr_err(gettext("can not get \"%s\" from %s\n"),
11100Sstevel@tonic-gate 						val, NFSSEC_CONF);
11110Sstevel@tonic-gate 			    return (RET_ERR);
11120Sstevel@tonic-gate 			}
11130Sstevel@tonic-gate 			sec_opt++;
11140Sstevel@tonic-gate 			break;
11150Sstevel@tonic-gate 
11160Sstevel@tonic-gate 		case OPT_PUBLIC:
11170Sstevel@tonic-gate 			public_opt = TRUE;
11180Sstevel@tonic-gate 			break;
11190Sstevel@tonic-gate 
11200Sstevel@tonic-gate 		case OPT_DIRECTIO:
11210Sstevel@tonic-gate 			args->flags |= NFSMNT_DIRECTIO;
11220Sstevel@tonic-gate 			break;
11230Sstevel@tonic-gate 
11240Sstevel@tonic-gate 		case OPT_NODIRECTIO:
11250Sstevel@tonic-gate 			args->flags &= ~(NFSMNT_DIRECTIO);
11260Sstevel@tonic-gate 			break;
11270Sstevel@tonic-gate 
11280Sstevel@tonic-gate 		case OPT_XATTR:
11290Sstevel@tonic-gate 		case OPT_NOXATTR:
11300Sstevel@tonic-gate 			/*
11310Sstevel@tonic-gate 			 * VFS options; just need to get them into the
11320Sstevel@tonic-gate 			 * new mount option string and note we've seen them
11330Sstevel@tonic-gate 			 */
11340Sstevel@tonic-gate 			attrpref = 1;
11350Sstevel@tonic-gate 			break;
11360Sstevel@tonic-gate 		default:
11370Sstevel@tonic-gate 			/*
11380Sstevel@tonic-gate 			 * Note that this could be a valid OPT_* option so
11390Sstevel@tonic-gate 			 * we can't use "val" but need to use "saveopt".
11400Sstevel@tonic-gate 			 */
11410Sstevel@tonic-gate 			if (fsisstdopt(saveopt))
11420Sstevel@tonic-gate 				break;
11430Sstevel@tonic-gate 			invalid = 1;
11440Sstevel@tonic-gate 			if (!qflg)
11450Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(
11460Sstevel@tonic-gate 				    "mount: %s on %s - WARNING unknown option"
11470Sstevel@tonic-gate 				    " \"%s\"\n"), mnt->mnt_special,
11480Sstevel@tonic-gate 				    mnt->mnt_mountp, saveopt);
11490Sstevel@tonic-gate 			break;
11500Sstevel@tonic-gate 		}
11510Sstevel@tonic-gate 		if (!invalid) {
11520Sstevel@tonic-gate 			if (newopts[0])
11530Sstevel@tonic-gate 				strcat(newopts, ",");
11540Sstevel@tonic-gate 			strcat(newopts, saveopt);
11550Sstevel@tonic-gate 		}
11560Sstevel@tonic-gate 	}
11570Sstevel@tonic-gate 	/* Default is to turn extended attrs on */
11580Sstevel@tonic-gate 	if (!attrpref) {
11590Sstevel@tonic-gate 		if (newopts[0])
11600Sstevel@tonic-gate 			strcat(newopts, ",");
11610Sstevel@tonic-gate 		strcat(newopts, MNTOPT_XATTR);
11620Sstevel@tonic-gate 	}
11630Sstevel@tonic-gate 	strcpy(mnt->mnt_mntopts, newopts);
11640Sstevel@tonic-gate 	free(newopts);
11650Sstevel@tonic-gate 	free(optstr);
11660Sstevel@tonic-gate 
11670Sstevel@tonic-gate 	/* ensure that only one secure mode is requested */
11680Sstevel@tonic-gate 	if (sec_opt > 1) {
11690Sstevel@tonic-gate 		pr_err(gettext("Security options conflict\n"));
11700Sstevel@tonic-gate 		return (RET_ERR);
11710Sstevel@tonic-gate 	}
11720Sstevel@tonic-gate 
11730Sstevel@tonic-gate 	/* ensure that the user isn't trying to get large files over V2 */
11740Sstevel@tonic-gate 	if (nfsvers == NFS_VERSION && largefiles) {
11750Sstevel@tonic-gate 		pr_err(gettext("NFS V2 can't support \"largefiles\"\n"));
11760Sstevel@tonic-gate 		return (RET_ERR);
11770Sstevel@tonic-gate 	}
11780Sstevel@tonic-gate 
11790Sstevel@tonic-gate 	if (nfsvers == NFS_V4 &&
11800Sstevel@tonic-gate 	    nfs_proto != NULL &&
11810Sstevel@tonic-gate 	    strncasecmp(nfs_proto, NC_UDP, strlen(NC_UDP)) == 0) {
11820Sstevel@tonic-gate 		pr_err(gettext("NFS V4 does not support %s\n"), nfs_proto);
11830Sstevel@tonic-gate 		return (RET_ERR);
11840Sstevel@tonic-gate 	}
11850Sstevel@tonic-gate 
11860Sstevel@tonic-gate 	return (RET_OK);
11870Sstevel@tonic-gate 
11880Sstevel@tonic-gate badopt:
11890Sstevel@tonic-gate 	pr_err(gettext("invalid option: \"%s\"\n"), saveopt);
11900Sstevel@tonic-gate 	free(optstr);
11910Sstevel@tonic-gate 	return (RET_ERR);
11920Sstevel@tonic-gate }
11930Sstevel@tonic-gate 
11940Sstevel@tonic-gate static int
11950Sstevel@tonic-gate make_secure(struct nfs_args *args, char *hostname, struct netconfig *nconf,
11960Sstevel@tonic-gate 	bool_t use_pubfh, rpcvers_t vers)
11970Sstevel@tonic-gate {
11980Sstevel@tonic-gate 	sec_data_t *secdata;
11990Sstevel@tonic-gate 	int flags;
12000Sstevel@tonic-gate 	struct netbuf *syncaddr = NULL;
12010Sstevel@tonic-gate 	struct nd_addrlist *retaddrs = NULL;
12020Sstevel@tonic-gate 	char netname[MAXNETNAMELEN+1];
12030Sstevel@tonic-gate 
12040Sstevel@tonic-gate 	/*
12050Sstevel@tonic-gate 	 * check to see if any secure mode is requested.
12060Sstevel@tonic-gate 	 * if not, use default security mode.
12070Sstevel@tonic-gate 	 */
12080Sstevel@tonic-gate 	if (!snego_done && !sec_opt) {
12090Sstevel@tonic-gate 		/*
12100Sstevel@tonic-gate 		 *  Get default security mode.
12110Sstevel@tonic-gate 		 *  AUTH_UNIX has been the default choice for a long time.
12120Sstevel@tonic-gate 		 *  The better NFS security service becomes, the better chance
12130Sstevel@tonic-gate 		 *  we will set stronger security service as the default NFS
12140Sstevel@tonic-gate 		 *  security mode.
12150Sstevel@tonic-gate 		 *
12160Sstevel@tonic-gate 		 */
12170Sstevel@tonic-gate 	    if (nfs_getseconfig_default(&nfs_sec)) {
12180Sstevel@tonic-gate 		pr_err(gettext("error getting default security entry\n"));
12190Sstevel@tonic-gate 		return (-1);
12200Sstevel@tonic-gate 	    }
12210Sstevel@tonic-gate 	    args->flags |= NFSMNT_SECDEFAULT;
12220Sstevel@tonic-gate 	}
12230Sstevel@tonic-gate 
12240Sstevel@tonic-gate 	/*
12250Sstevel@tonic-gate 	 * Get the network address for the time service on the server.
12260Sstevel@tonic-gate 	 * If an RPC based time service is not available then try the
12270Sstevel@tonic-gate 	 * IP time service.
12280Sstevel@tonic-gate 	 *
12290Sstevel@tonic-gate 	 * This is for AUTH_DH processing. We will also pass down syncaddr
12300Sstevel@tonic-gate 	 * and netname for NFS V4 even if AUTH_DH is not requested right now.
12310Sstevel@tonic-gate 	 * NFS V4 does security negotiation in the kernel via SECINFO.
12320Sstevel@tonic-gate 	 * These information might be needed later in the kernel.
12330Sstevel@tonic-gate 	 *
12340Sstevel@tonic-gate 	 * Eventurally, we want to move this code to nfs_clnt_secdata()
12350Sstevel@tonic-gate 	 * when autod_nfs.c and mount.c can share the same get_the_addr()
12360Sstevel@tonic-gate 	 * routine.
12370Sstevel@tonic-gate 	 */
12380Sstevel@tonic-gate 	flags = 0;
12390Sstevel@tonic-gate 	syncaddr = NULL;
12400Sstevel@tonic-gate 
12410Sstevel@tonic-gate 	if (nfs_sec.sc_rpcnum == AUTH_DH || vers == NFS_V4) {
12420Sstevel@tonic-gate 		/*
12430Sstevel@tonic-gate 		 * If using the public fh or nfsv4, we will not contact the
12440Sstevel@tonic-gate 		 * remote RPCBINDer, since it is possibly behind a firewall.
12450Sstevel@tonic-gate 		 */
12460Sstevel@tonic-gate 		if (use_pubfh == FALSE && vers != NFS_V4) {
12470Sstevel@tonic-gate 			syncaddr = get_the_addr(hostname, RPCBPROG, RPCBVERS,
12480Sstevel@tonic-gate 				nconf, 0, NULL, NULL, FALSE, NULL, NULL);
12490Sstevel@tonic-gate 		}
12500Sstevel@tonic-gate 
12510Sstevel@tonic-gate 		if (syncaddr != NULL) {
12520Sstevel@tonic-gate 			/* for flags in sec_data */
12530Sstevel@tonic-gate 			flags |= AUTH_F_RPCTIMESYNC;
12540Sstevel@tonic-gate 		} else {
12550Sstevel@tonic-gate 			struct nd_hostserv hs;
12560Sstevel@tonic-gate 			int error;
12570Sstevel@tonic-gate 
12580Sstevel@tonic-gate 			hs.h_host = hostname;
12590Sstevel@tonic-gate 			hs.h_serv = "timserver";
12600Sstevel@tonic-gate 
12610Sstevel@tonic-gate 			error = netdir_getbyname(nconf, &hs, &retaddrs);
12620Sstevel@tonic-gate 
12630Sstevel@tonic-gate 			if (error != ND_OK && (nfs_sec.sc_rpcnum == AUTH_DH)) {
12640Sstevel@tonic-gate 			    pr_err(gettext("%s: secure: no time service\n"),
12650Sstevel@tonic-gate 						hostname);
12660Sstevel@tonic-gate 			    return (-1);
12670Sstevel@tonic-gate 			}
12680Sstevel@tonic-gate 
12690Sstevel@tonic-gate 			if (error == ND_OK)
12700Sstevel@tonic-gate 				syncaddr = retaddrs->n_addrs;
12710Sstevel@tonic-gate 
12720Sstevel@tonic-gate 			/*
12730Sstevel@tonic-gate 			 * For NFS_V4 if AUTH_DH is negotiated later in the
12740Sstevel@tonic-gate 			 * kernel thru SECINFO, it will need syncaddr
12750Sstevel@tonic-gate 			 * and netname data.
12760Sstevel@tonic-gate 			 */
12770Sstevel@tonic-gate 			if (vers == NFS_V4 && syncaddr &&
12780Sstevel@tonic-gate 				    host2netname(netname, hostname, NULL)) {
12790Sstevel@tonic-gate 			    args->syncaddr = malloc(sizeof (struct netbuf));
12800Sstevel@tonic-gate 			    args->syncaddr->buf = malloc(syncaddr->len);
12810Sstevel@tonic-gate 			    (void) memcpy(args->syncaddr->buf, syncaddr->buf,
12820Sstevel@tonic-gate 							syncaddr->len);
12830Sstevel@tonic-gate 			    args->syncaddr->len = syncaddr->len;
12840Sstevel@tonic-gate 			    args->syncaddr->maxlen = syncaddr->maxlen;
12850Sstevel@tonic-gate 			    args->netname = strdup(netname);
12860Sstevel@tonic-gate 			    args->flags |= NFSMNT_SECURE;
12870Sstevel@tonic-gate 			}
12880Sstevel@tonic-gate 		}
12890Sstevel@tonic-gate 	}
12900Sstevel@tonic-gate 
12910Sstevel@tonic-gate 	/*
12920Sstevel@tonic-gate 	 * For the initial chosen flavor (any flavor defined in nfssec.conf),
12930Sstevel@tonic-gate 	 * the data will be stored in the sec_data structure via
12940Sstevel@tonic-gate 	 * nfs_clnt_secdata() and be passed to the kernel via nfs_args_*
12950Sstevel@tonic-gate 	 * extended data structure.
12960Sstevel@tonic-gate 	 */
12970Sstevel@tonic-gate 	if (!(secdata = nfs_clnt_secdata(&nfs_sec, hostname, args->knconf,
12980Sstevel@tonic-gate 					syncaddr, flags))) {
12990Sstevel@tonic-gate 		pr_err(gettext("errors constructing security related data\n"));
13000Sstevel@tonic-gate 		if (flags & AUTH_F_RPCTIMESYNC) {
13010Sstevel@tonic-gate 			free(syncaddr->buf);
13020Sstevel@tonic-gate 			free(syncaddr);
13030Sstevel@tonic-gate 		} else if (retaddrs)
13040Sstevel@tonic-gate 			netdir_free((void *)retaddrs, ND_ADDRLIST);
13050Sstevel@tonic-gate 		return (-1);
13060Sstevel@tonic-gate 	}
13070Sstevel@tonic-gate 
13080Sstevel@tonic-gate 	NFS_ARGS_EXTB_secdata(args, secdata);
13090Sstevel@tonic-gate 	if (flags & AUTH_F_RPCTIMESYNC) {
13100Sstevel@tonic-gate 		free(syncaddr->buf);
13110Sstevel@tonic-gate 		free(syncaddr);
13120Sstevel@tonic-gate 	} else if (retaddrs)
13130Sstevel@tonic-gate 		netdir_free((void *)retaddrs, ND_ADDRLIST);
13140Sstevel@tonic-gate 	return (0);
13150Sstevel@tonic-gate }
13160Sstevel@tonic-gate 
13170Sstevel@tonic-gate /*
13180Sstevel@tonic-gate  * Get the network address on "hostname" for program "prog"
13190Sstevel@tonic-gate  * with version "vers" by using the nconf configuration data
13200Sstevel@tonic-gate  * passed in.
13210Sstevel@tonic-gate  *
13220Sstevel@tonic-gate  * If the address of a netconfig pointer is null then
13230Sstevel@tonic-gate  * information is not sufficient and no netbuf will be returned.
13240Sstevel@tonic-gate  *
13250Sstevel@tonic-gate  * Finally, ping the null procedure of that service.
13260Sstevel@tonic-gate  *
13270Sstevel@tonic-gate  * A similar routine is also defined in ../../autofs/autod_nfs.c.
13280Sstevel@tonic-gate  * This is a potential routine to move to ../lib for common usage.
13290Sstevel@tonic-gate  */
13300Sstevel@tonic-gate static struct netbuf *
13310Sstevel@tonic-gate get_the_addr(char *hostname, ulong_t prog, ulong_t vers,
13320Sstevel@tonic-gate 	struct netconfig *nconf, ushort_t port, struct t_info *tinfo,
13330Sstevel@tonic-gate 	caddr_t *fhp, bool_t get_pubfh, char *fspath, err_ret_t *error)
13340Sstevel@tonic-gate {
13350Sstevel@tonic-gate 	struct netbuf *nb = NULL;
13360Sstevel@tonic-gate 	struct t_bind *tbind = NULL;
13370Sstevel@tonic-gate 	CLIENT *cl = NULL;
13380Sstevel@tonic-gate 	struct timeval tv;
13390Sstevel@tonic-gate 	int fd = -1;
13400Sstevel@tonic-gate 	AUTH *ah = NULL;
13410Sstevel@tonic-gate 	AUTH *new_ah = NULL;
13420Sstevel@tonic-gate 	struct snego_t snego;
13430Sstevel@tonic-gate 
13440Sstevel@tonic-gate 	if (nconf == NULL)
13450Sstevel@tonic-gate 		return (NULL);
13460Sstevel@tonic-gate 
13470Sstevel@tonic-gate 	if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) == -1)
13480Sstevel@tonic-gate 		    goto done;
13490Sstevel@tonic-gate 
13500Sstevel@tonic-gate 	/* LINTED pointer alignment */
13510Sstevel@tonic-gate 	if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR))
13520Sstevel@tonic-gate 		== NULL)
13530Sstevel@tonic-gate 		goto done;
13540Sstevel@tonic-gate 
13550Sstevel@tonic-gate 	/*
13560Sstevel@tonic-gate 	 * In the case of public filehandle usage or NFSv4 we want to
13570Sstevel@tonic-gate 	 * avoid use of the rpcbind/portmap protocol
13580Sstevel@tonic-gate 	 */
13590Sstevel@tonic-gate 	if ((get_pubfh == TRUE) || (vers == NFS_V4)) {
13600Sstevel@tonic-gate 		struct nd_hostserv hs;
13610Sstevel@tonic-gate 		struct nd_addrlist *retaddrs;
13620Sstevel@tonic-gate 		int retval;
13630Sstevel@tonic-gate 		hs.h_host = hostname;
13640Sstevel@tonic-gate 
13650Sstevel@tonic-gate 		/* NFS where vers==4 does not support UDP */
13660Sstevel@tonic-gate 		if (vers == NFS_V4 &&
13670Sstevel@tonic-gate 		    strncasecmp(nconf->nc_proto, NC_UDP,
13680Sstevel@tonic-gate 				strlen(NC_UDP)) == 0) {
136977Soa138391 			SET_ERR_RET(error, ERR_PROTO_UNSUPP, 0);
13700Sstevel@tonic-gate 			goto done;
13710Sstevel@tonic-gate 		}
13720Sstevel@tonic-gate 
13730Sstevel@tonic-gate 		if (port == 0)
13740Sstevel@tonic-gate 			hs.h_serv = "nfs";
13750Sstevel@tonic-gate 		else
13760Sstevel@tonic-gate 			hs.h_serv = NULL;
13770Sstevel@tonic-gate 
13780Sstevel@tonic-gate 		if ((retval = netdir_getbyname(nconf, &hs, &retaddrs))
13790Sstevel@tonic-gate 		    != ND_OK) {
13800Sstevel@tonic-gate 			/*
13810Sstevel@tonic-gate 			 * Carefully set the error value here. Want to signify
13820Sstevel@tonic-gate 			 * that the error was an unknown host.
13830Sstevel@tonic-gate 			 */
13840Sstevel@tonic-gate 			if (retval == ND_NOHOST) {
13850Sstevel@tonic-gate 				SET_ERR_RET(error, ERR_NOHOST, retval);
13860Sstevel@tonic-gate 			}
13870Sstevel@tonic-gate 
13880Sstevel@tonic-gate 			goto done;
13890Sstevel@tonic-gate 		}
13900Sstevel@tonic-gate 		memcpy(tbind->addr.buf, retaddrs->n_addrs->buf,
13910Sstevel@tonic-gate 			retaddrs->n_addrs->len);
13920Sstevel@tonic-gate 		tbind->addr.len = retaddrs->n_addrs->len;
13930Sstevel@tonic-gate 		netdir_free((void *)retaddrs, ND_ADDRLIST);
13940Sstevel@tonic-gate 		(void) netdir_options(nconf, ND_SET_RESERVEDPORT, fd, NULL);
13950Sstevel@tonic-gate 
13960Sstevel@tonic-gate 	} else {
13970Sstevel@tonic-gate 		if (rpcb_getaddr(prog, vers, nconf, &tbind->addr,
13980Sstevel@tonic-gate 		    hostname) == FALSE) {
13990Sstevel@tonic-gate 			goto done;
14000Sstevel@tonic-gate 		}
14010Sstevel@tonic-gate 	}
14020Sstevel@tonic-gate 
14030Sstevel@tonic-gate 	if (port) {
14040Sstevel@tonic-gate 		/* LINTED pointer alignment */
14050Sstevel@tonic-gate 		if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
14060Sstevel@tonic-gate 			((struct sockaddr_in *)tbind->addr.buf)->sin_port
14070Sstevel@tonic-gate 				= port;
14080Sstevel@tonic-gate 		else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
14090Sstevel@tonic-gate 			((struct sockaddr_in6 *)tbind->addr.buf)->sin6_port
14100Sstevel@tonic-gate 				= port;
14110Sstevel@tonic-gate 
14120Sstevel@tonic-gate 	}
14130Sstevel@tonic-gate 
14140Sstevel@tonic-gate 	cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0);
14150Sstevel@tonic-gate 	if (cl == NULL) {
141677Soa138391 		/*
141777Soa138391 		 * clnt_tli_create() returns either RPC_SYSTEMERROR,
141877Soa138391 		 * RPC_UNKNOWNPROTO or RPC_TLIERROR. The RPC_TLIERROR translates
141977Soa138391 		 * to "Misc. TLI error". This is not too helpful. Most likely
142077Soa138391 		 * the connection to the remote server timed out, so this
142177Soa138391 		 * error is at least less perplexing.
142277Soa138391 		 * See: usr/src/cmd/rpcinfo/rpcinfo.c
142377Soa138391 		 */
142477Soa138391 		if (rpc_createerr.cf_stat == RPC_TLIERROR) {
142577Soa138391 			SET_ERR_RET(error, ERR_RPCERROR, RPC_PMAPFAILURE);
142677Soa138391 		} else {
142777Soa138391 			SET_ERR_RET(error, ERR_RPCERROR, rpc_createerr.cf_stat);
142877Soa138391 		}
14290Sstevel@tonic-gate 		goto done;
14300Sstevel@tonic-gate 	}
14310Sstevel@tonic-gate 
14320Sstevel@tonic-gate 	ah = authsys_create_default();
14330Sstevel@tonic-gate 	if (ah != NULL)
14340Sstevel@tonic-gate 		cl->cl_auth = ah;
14350Sstevel@tonic-gate 
14360Sstevel@tonic-gate 	tv.tv_sec = 5;
14370Sstevel@tonic-gate 	tv.tv_usec = 0;
14380Sstevel@tonic-gate 
14390Sstevel@tonic-gate 	(void) clnt_control(cl, CLSET_TIMEOUT, (char *)&tv);
14400Sstevel@tonic-gate 
14410Sstevel@tonic-gate 	if ((get_pubfh == TRUE) && (vers != NFS_V4)) {
14420Sstevel@tonic-gate 	    enum snego_stat sec;
14430Sstevel@tonic-gate 
14440Sstevel@tonic-gate 	    if (!snego_done) {
14450Sstevel@tonic-gate 		/*
14460Sstevel@tonic-gate 		 * negotiate sec flavor.
14470Sstevel@tonic-gate 		 */
14480Sstevel@tonic-gate 		snego.cnt = 0;
14490Sstevel@tonic-gate 		if ((sec = nfs_sec_nego(vers, cl, fspath, &snego)) ==
14500Sstevel@tonic-gate 			SNEGO_SUCCESS) {
14510Sstevel@tonic-gate 		    int jj;
14520Sstevel@tonic-gate 
14530Sstevel@tonic-gate 		/*
14540Sstevel@tonic-gate 		 * check if server supports the one
14550Sstevel@tonic-gate 		 * specified in the sec= option.
14560Sstevel@tonic-gate 		 */
14570Sstevel@tonic-gate 		    if (sec_opt) {
14580Sstevel@tonic-gate 			for (jj = 0; jj < snego.cnt; jj++) {
14590Sstevel@tonic-gate 			    if (snego.array[jj] == nfs_sec.sc_nfsnum) {
14600Sstevel@tonic-gate 				snego_done = TRUE;
14610Sstevel@tonic-gate 				break;
14620Sstevel@tonic-gate 			    }
14630Sstevel@tonic-gate 			}
14640Sstevel@tonic-gate 		    }
14650Sstevel@tonic-gate 
14660Sstevel@tonic-gate 		/*
14670Sstevel@tonic-gate 		 * find a common sec flavor
14680Sstevel@tonic-gate 		 */
14690Sstevel@tonic-gate 		    if (!snego_done) {
14700Sstevel@tonic-gate 			if (sec_opt) {
14710Sstevel@tonic-gate 			    pr_err(gettext(
14720Sstevel@tonic-gate 				"Server does not support the security"
14730Sstevel@tonic-gate 				" flavor specified.\n"));
14740Sstevel@tonic-gate 			}
14750Sstevel@tonic-gate 			for (jj = 0; jj < snego.cnt; jj++) {
14760Sstevel@tonic-gate 			    if (!nfs_getseconfig_bynumber(snego.array[jj],
14770Sstevel@tonic-gate 				&nfs_sec)) {
14780Sstevel@tonic-gate 				snego_done = TRUE;
14790Sstevel@tonic-gate 				if (sec_opt) {
14800Sstevel@tonic-gate 				    pr_err(gettext(
14810Sstevel@tonic-gate 					"Security flavor %d was negotiated and"
14820Sstevel@tonic-gate 					" will be used.\n"),
14830Sstevel@tonic-gate 					nfs_sec.sc_nfsnum);
14840Sstevel@tonic-gate 				}
14850Sstevel@tonic-gate 				break;
14860Sstevel@tonic-gate 			    }
14870Sstevel@tonic-gate 			}
14880Sstevel@tonic-gate 		    }
14890Sstevel@tonic-gate 		    if (!snego_done)
14900Sstevel@tonic-gate 			return (NULL);
14910Sstevel@tonic-gate 
14920Sstevel@tonic-gate 		/*
14930Sstevel@tonic-gate 		 * Now that the flavor has been
14940Sstevel@tonic-gate 		 * negotiated, get the fh.
14950Sstevel@tonic-gate 		 *
14960Sstevel@tonic-gate 		 * First, create an auth handle using the negotiated
14970Sstevel@tonic-gate 		 * sec flavor in the next lookup to
14980Sstevel@tonic-gate 		 * fetch the filehandle.
14990Sstevel@tonic-gate 		 */
15000Sstevel@tonic-gate 		    new_ah = nfs_create_ah(cl, hostname, &nfs_sec);
15010Sstevel@tonic-gate 		    if (new_ah == NULL)
15020Sstevel@tonic-gate 			goto done;
15030Sstevel@tonic-gate 		    cl->cl_auth = new_ah;
15040Sstevel@tonic-gate 		} else if (sec == SNEGO_ARRAY_TOO_SMALL || sec ==
15050Sstevel@tonic-gate 		    SNEGO_FAILURE) {
15060Sstevel@tonic-gate 			goto done;
15070Sstevel@tonic-gate 		}
15080Sstevel@tonic-gate 		/*
15090Sstevel@tonic-gate 		 * Note that if sec == SNEGO_DEF_VALID
15100Sstevel@tonic-gate 		 * default sec flavor is acceptable.
15110Sstevel@tonic-gate 		 * Use it to get the filehandle.
15120Sstevel@tonic-gate 		 */
15130Sstevel@tonic-gate 	    }
15140Sstevel@tonic-gate 
15150Sstevel@tonic-gate 	    if (vers == NFS_VERSION) {
15160Sstevel@tonic-gate 		wnl_diropargs arg;
15170Sstevel@tonic-gate 		wnl_diropres *res;
15180Sstevel@tonic-gate 
15190Sstevel@tonic-gate 		memset((char *)&arg.dir, 0, sizeof (wnl_fh));
15200Sstevel@tonic-gate 		arg.name = fspath;
15210Sstevel@tonic-gate 		res = wnlproc_lookup_2(&arg, cl);
15220Sstevel@tonic-gate 
15230Sstevel@tonic-gate 		if (res == NULL || res->status != NFS_OK)
15240Sstevel@tonic-gate 		    goto done;
15250Sstevel@tonic-gate 		*fhp = malloc(sizeof (wnl_fh));
15260Sstevel@tonic-gate 
15270Sstevel@tonic-gate 		if (*fhp == NULL) {
15280Sstevel@tonic-gate 		    pr_err(gettext("no memory\n"));
15290Sstevel@tonic-gate 		    goto done;
15300Sstevel@tonic-gate 		}
15310Sstevel@tonic-gate 
15320Sstevel@tonic-gate 		memcpy((char *)*fhp,
15330Sstevel@tonic-gate 		    (char *)&res->wnl_diropres_u.wnl_diropres.file,
15340Sstevel@tonic-gate 		    sizeof (wnl_fh));
15350Sstevel@tonic-gate 	    } else {
15360Sstevel@tonic-gate 		WNL_LOOKUP3args arg;
15370Sstevel@tonic-gate 		WNL_LOOKUP3res *res;
15380Sstevel@tonic-gate 		nfs_fh3 *fh3p;
15390Sstevel@tonic-gate 
15400Sstevel@tonic-gate 		memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3));
15410Sstevel@tonic-gate 		arg.what.name = fspath;
15420Sstevel@tonic-gate 		res = wnlproc3_lookup_3(&arg, cl);
15430Sstevel@tonic-gate 
15440Sstevel@tonic-gate 		if (res == NULL || res->status != NFS3_OK)
15450Sstevel@tonic-gate 		    goto done;
15460Sstevel@tonic-gate 
15470Sstevel@tonic-gate 		fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
15480Sstevel@tonic-gate 
15490Sstevel@tonic-gate 		if (fh3p == NULL) {
15500Sstevel@tonic-gate 		    pr_err(gettext("no memory\n"));
15510Sstevel@tonic-gate 		    CLNT_FREERES(cl, xdr_WNL_LOOKUP3res, (char *)res);
15520Sstevel@tonic-gate 		    goto done;
15530Sstevel@tonic-gate 		}
15540Sstevel@tonic-gate 
15550Sstevel@tonic-gate 		fh3p->fh3_length =
15560Sstevel@tonic-gate 		    res->WNL_LOOKUP3res_u.res_ok.object.data.data_len;
15570Sstevel@tonic-gate 		memcpy(fh3p->fh3_u.data,
15580Sstevel@tonic-gate 		    res->WNL_LOOKUP3res_u.res_ok.object.data.data_val,
15590Sstevel@tonic-gate 		    fh3p->fh3_length);
15600Sstevel@tonic-gate 
15610Sstevel@tonic-gate 		*fhp = (caddr_t)fh3p;
15620Sstevel@tonic-gate 
15630Sstevel@tonic-gate 		CLNT_FREERES(cl, xdr_WNL_LOOKUP3res, (char *)res);
15640Sstevel@tonic-gate 	    }
15650Sstevel@tonic-gate 	} else {
15660Sstevel@tonic-gate 		void *res;
15670Sstevel@tonic-gate 		struct rpc_err r_err;
15680Sstevel@tonic-gate 
15690Sstevel@tonic-gate 		if (vers == NFS_VERSION)
15700Sstevel@tonic-gate 		    res = wnlproc_null_2(NULL, cl);
15710Sstevel@tonic-gate 		else if (vers == NFS_V3)
15720Sstevel@tonic-gate 		    res = wnlproc3_null_3(NULL, cl);
15730Sstevel@tonic-gate 		else
15740Sstevel@tonic-gate 		    res = wnlproc4_null_4(NULL, cl);
15750Sstevel@tonic-gate 
15760Sstevel@tonic-gate 		if (res == NULL) {
15770Sstevel@tonic-gate 			clnt_geterr(cl, &r_err);
15780Sstevel@tonic-gate 			if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
15790Sstevel@tonic-gate 				switch (r_err.re_status) {
15800Sstevel@tonic-gate 				case RPC_TLIERROR:
15810Sstevel@tonic-gate 				case RPC_CANTRECV:
15820Sstevel@tonic-gate 				case RPC_CANTSEND:
15830Sstevel@tonic-gate 					r_err.re_status = RPC_PROGVERSMISMATCH;
15840Sstevel@tonic-gate 				}
15850Sstevel@tonic-gate 			}
15860Sstevel@tonic-gate 			SET_ERR_RET(error, ERR_RPCERROR, r_err.re_status);
15870Sstevel@tonic-gate 			goto done;
15880Sstevel@tonic-gate 		}
15890Sstevel@tonic-gate 	}
15900Sstevel@tonic-gate 
15910Sstevel@tonic-gate 	/*
15920Sstevel@tonic-gate 	 * Make a copy of the netbuf to return
15930Sstevel@tonic-gate 	 */
15940Sstevel@tonic-gate 	nb = (struct netbuf *)malloc(sizeof (*nb));
15950Sstevel@tonic-gate 	if (nb == NULL) {
15960Sstevel@tonic-gate 		pr_err(gettext("no memory\n"));
15970Sstevel@tonic-gate 		goto done;
15980Sstevel@tonic-gate 	}
15990Sstevel@tonic-gate 	*nb = tbind->addr;
16000Sstevel@tonic-gate 	nb->buf = (char *)malloc(nb->maxlen);
16010Sstevel@tonic-gate 	if (nb->buf == NULL) {
16020Sstevel@tonic-gate 		pr_err(gettext("no memory\n"));
16030Sstevel@tonic-gate 		free(nb);
16040Sstevel@tonic-gate 		nb = NULL;
16050Sstevel@tonic-gate 		goto done;
16060Sstevel@tonic-gate 	}
16070Sstevel@tonic-gate 	(void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
16080Sstevel@tonic-gate 
16090Sstevel@tonic-gate done:
16100Sstevel@tonic-gate 	if (cl) {
16110Sstevel@tonic-gate 	    if (ah != NULL) {
16120Sstevel@tonic-gate 		if (new_ah != NULL)
16130Sstevel@tonic-gate 		    AUTH_DESTROY(ah);
16140Sstevel@tonic-gate 		AUTH_DESTROY(cl->cl_auth);
16150Sstevel@tonic-gate 		cl->cl_auth = NULL;
16160Sstevel@tonic-gate 	    }
16170Sstevel@tonic-gate 	    clnt_destroy(cl);
16180Sstevel@tonic-gate 	    cl = NULL;
16190Sstevel@tonic-gate 	}
16200Sstevel@tonic-gate 	if (tbind) {
16210Sstevel@tonic-gate 		t_free((char *)tbind, T_BIND);
16220Sstevel@tonic-gate 		tbind = NULL;
16230Sstevel@tonic-gate 	}
16240Sstevel@tonic-gate 	if (fd >= 0)
16250Sstevel@tonic-gate 		(void) t_close(fd);
16260Sstevel@tonic-gate 	return (nb);
16270Sstevel@tonic-gate }
16280Sstevel@tonic-gate 
16290Sstevel@tonic-gate /*
16300Sstevel@tonic-gate  * Get a network address on "hostname" for program "prog"
16310Sstevel@tonic-gate  * with version "vers".  If the port number is specified (non zero)
16320Sstevel@tonic-gate  * then try for a TCP/UDP transport and set the port number of the
16330Sstevel@tonic-gate  * resulting IP address.
16340Sstevel@tonic-gate  *
16350Sstevel@tonic-gate  * If the address of a netconfig pointer was passed and
16360Sstevel@tonic-gate  * if it's not null, use it as the netconfig otherwise
16370Sstevel@tonic-gate  * assign the address of the netconfig that was used to
16380Sstevel@tonic-gate  * establish contact with the service.
16390Sstevel@tonic-gate  *
16400Sstevel@tonic-gate  * A similar routine is also defined in ../../autofs/autod_nfs.c.
16410Sstevel@tonic-gate  * This is a potential routine to move to ../lib for common usage.
16420Sstevel@tonic-gate  *
16430Sstevel@tonic-gate  * "error" refers to a more descriptive term when get_addr fails
16440Sstevel@tonic-gate  * and returns NULL: ERR_PROTO_NONE if no error introduced by
16450Sstevel@tonic-gate  * -o proto option, ERR_NETPATH if error found in NETPATH
16460Sstevel@tonic-gate  * environment variable, ERR_PROTO_INVALID if an unrecognized
16470Sstevel@tonic-gate  * protocol is specified by user, and ERR_PROTO_UNSUPP for a
16480Sstevel@tonic-gate  * recognized but invalid protocol (eg. ticlts, ticots, etc.).
16490Sstevel@tonic-gate  * "error" is ignored if get_addr returns non-NULL result.
16500Sstevel@tonic-gate  *
16510Sstevel@tonic-gate  */
16520Sstevel@tonic-gate static struct netbuf *
16530Sstevel@tonic-gate get_addr(char *hostname, ulong_t prog, ulong_t vers, struct netconfig **nconfp,
16540Sstevel@tonic-gate 	char *proto, ushort_t port, struct t_info *tinfo, caddr_t *fhp,
16550Sstevel@tonic-gate 	bool_t get_pubfh, char *fspath, err_ret_t *error)
16560Sstevel@tonic-gate {
16570Sstevel@tonic-gate 	struct netbuf *nb = NULL;
16580Sstevel@tonic-gate 	struct netconfig *nconf = NULL;
16590Sstevel@tonic-gate 	NCONF_HANDLE *nc = NULL;
16600Sstevel@tonic-gate 	int nthtry = FIRST_TRY;
16610Sstevel@tonic-gate 	err_ret_t errsave_nohost, errsave_rpcerr;
16620Sstevel@tonic-gate 
16630Sstevel@tonic-gate 	SET_ERR_RET(&errsave_nohost, ERR_PROTO_NONE, 0);
16640Sstevel@tonic-gate 	SET_ERR_RET(&errsave_rpcerr, ERR_PROTO_NONE, 0);
16650Sstevel@tonic-gate 
16660Sstevel@tonic-gate 	SET_ERR_RET(error, ERR_PROTO_NONE, 0);
16670Sstevel@tonic-gate 
16680Sstevel@tonic-gate 	if (nconfp && *nconfp)
16690Sstevel@tonic-gate 		return (get_the_addr(hostname, prog, vers, *nconfp, port,
16700Sstevel@tonic-gate 			tinfo, fhp, get_pubfh, fspath, error));
16710Sstevel@tonic-gate 	/*
16720Sstevel@tonic-gate 	 * No nconf passed in.
16730Sstevel@tonic-gate 	 *
16740Sstevel@tonic-gate 	 * Try to get a nconf from /etc/netconfig filtered by
16750Sstevel@tonic-gate 	 * the NETPATH environment variable.
16760Sstevel@tonic-gate 	 * First search for COTS, second for CLTS unless proto
16770Sstevel@tonic-gate 	 * is specified.  When we retry, we reset the
16780Sstevel@tonic-gate 	 * netconfig list so that we would search the whole list
16790Sstevel@tonic-gate 	 * all over again.
16800Sstevel@tonic-gate 	 */
16810Sstevel@tonic-gate 
16820Sstevel@tonic-gate 	if ((nc = setnetpath()) == NULL) {
16830Sstevel@tonic-gate 		/* should only return an error if problems with NETPATH */
16840Sstevel@tonic-gate 		/* In which case you are hosed */
16850Sstevel@tonic-gate 		SET_ERR_RET(error, ERR_NETPATH, 0);
16860Sstevel@tonic-gate 		goto done;
16870Sstevel@tonic-gate 	}
16880Sstevel@tonic-gate 
16890Sstevel@tonic-gate 	/*
16900Sstevel@tonic-gate 	 * If proto is specified, then only search for the match,
16910Sstevel@tonic-gate 	 * otherwise try COTS first, if failed, try CLTS.
16920Sstevel@tonic-gate 	 */
16930Sstevel@tonic-gate 	if (proto) {
16940Sstevel@tonic-gate 		/* no matching proto name */
16950Sstevel@tonic-gate 		SET_ERR_RET(error, ERR_PROTO_INVALID, 0);
16960Sstevel@tonic-gate 
16970Sstevel@tonic-gate 		while (nconf = getnetpath(nc)) {
16980Sstevel@tonic-gate 			if (strcmp(nconf->nc_netid, proto))
16990Sstevel@tonic-gate 				continue;
17000Sstevel@tonic-gate 
17010Sstevel@tonic-gate 			/* may be unsupported */
17020Sstevel@tonic-gate 			SET_ERR_RET(error, ERR_PROTO_UNSUPP, 0);
17030Sstevel@tonic-gate 
17040Sstevel@tonic-gate 			if ((port != 0) &&
17050Sstevel@tonic-gate 				((strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
17060Sstevel@tonic-gate 				strcmp(nconf->nc_protofmly, NC_INET6) == 0) &&
17070Sstevel@tonic-gate 				(strcmp(nconf->nc_proto, NC_TCP) != 0 &&
17080Sstevel@tonic-gate 				strcmp(nconf->nc_proto, NC_UDP) != 0)))
17090Sstevel@tonic-gate 
17100Sstevel@tonic-gate 				continue;
17110Sstevel@tonic-gate 
17120Sstevel@tonic-gate 			else {
17130Sstevel@tonic-gate 				nb = get_the_addr(hostname, prog,
17140Sstevel@tonic-gate 					vers, nconf, port, tinfo,
17150Sstevel@tonic-gate 						fhp, get_pubfh, fspath, error);
17160Sstevel@tonic-gate 
17170Sstevel@tonic-gate 				if (nb != NULL)
17180Sstevel@tonic-gate 					break;
17190Sstevel@tonic-gate 
17200Sstevel@tonic-gate 				/* nb is NULL - deal with errors */
17210Sstevel@tonic-gate 				if (error) {
17220Sstevel@tonic-gate 					if (error->error_type == ERR_NOHOST)
17230Sstevel@tonic-gate 						SET_ERR_RET(&errsave_nohost,
17240Sstevel@tonic-gate 							error->error_type,
17250Sstevel@tonic-gate 							error->error_value);
17260Sstevel@tonic-gate 					if (error->error_type == ERR_RPCERROR)
17270Sstevel@tonic-gate 						SET_ERR_RET(&errsave_rpcerr,
17280Sstevel@tonic-gate 							error->error_type,
17290Sstevel@tonic-gate 							error->error_value);
17300Sstevel@tonic-gate 				}
17310Sstevel@tonic-gate 				/*
17320Sstevel@tonic-gate 				 * continue with same protocol
17330Sstevel@tonic-gate 				 * selection
17340Sstevel@tonic-gate 				 */
17350Sstevel@tonic-gate 				continue;
17360Sstevel@tonic-gate 			}
17370Sstevel@tonic-gate 		} /* end of while */
17380Sstevel@tonic-gate 
17390Sstevel@tonic-gate 		if (nconf == NULL)
17400Sstevel@tonic-gate 			goto done;
17410Sstevel@tonic-gate 
17420Sstevel@tonic-gate 		if ((nb = get_the_addr(hostname, prog, vers, nconf, port,
17430Sstevel@tonic-gate 				tinfo, fhp, get_pubfh, fspath, error)) == NULL)
17440Sstevel@tonic-gate 			goto done;
17450Sstevel@tonic-gate 
17460Sstevel@tonic-gate 
17470Sstevel@tonic-gate 	} else {
17480Sstevel@tonic-gate retry:
17490Sstevel@tonic-gate 		SET_ERR_RET(error, ERR_NETPATH, 0);
17500Sstevel@tonic-gate 		while (nconf = getnetpath(nc)) {
17510Sstevel@tonic-gate 			SET_ERR_RET(error, ERR_PROTO_NONE, 0);
17520Sstevel@tonic-gate 			if (nconf->nc_flag & NC_VISIBLE) {
17530Sstevel@tonic-gate 				if (nthtry == FIRST_TRY) {
17540Sstevel@tonic-gate 					if ((nconf->nc_semantics ==
17550Sstevel@tonic-gate 						NC_TPI_COTS_ORD) ||
17560Sstevel@tonic-gate 					    (nconf->nc_semantics ==
17570Sstevel@tonic-gate 						NC_TPI_COTS)) {
17580Sstevel@tonic-gate 
17590Sstevel@tonic-gate 						if (port == 0)
17600Sstevel@tonic-gate 							break;
17610Sstevel@tonic-gate 
17620Sstevel@tonic-gate 						if ((strcmp(nconf->nc_protofmly,
17630Sstevel@tonic-gate 							NC_INET) == 0 ||
17640Sstevel@tonic-gate 							strcmp(nconf->
17650Sstevel@tonic-gate 							nc_protofmly,
17660Sstevel@tonic-gate 							NC_INET6) == 0) &&
17670Sstevel@tonic-gate 						    (strcmp(nconf->nc_proto,
17680Sstevel@tonic-gate 							NC_TCP) == 0))
17690Sstevel@tonic-gate 
17700Sstevel@tonic-gate 							break;
17710Sstevel@tonic-gate 					}
17720Sstevel@tonic-gate 				}
17730Sstevel@tonic-gate 				if (nthtry == SECOND_TRY) {
17740Sstevel@tonic-gate 					if (nconf->nc_semantics ==
17750Sstevel@tonic-gate 						NC_TPI_CLTS) {
17760Sstevel@tonic-gate 						if (port == 0)
17770Sstevel@tonic-gate 							break;
17780Sstevel@tonic-gate 						if ((strcmp(nconf->nc_protofmly,
17790Sstevel@tonic-gate 							NC_INET) == 0 ||
17800Sstevel@tonic-gate 							strcmp(nconf->
17810Sstevel@tonic-gate 							nc_protofmly, NC_INET6)
17820Sstevel@tonic-gate 							== 0) &&
17830Sstevel@tonic-gate 							(strcmp(
17840Sstevel@tonic-gate 							nconf->nc_proto,
17850Sstevel@tonic-gate 							NC_UDP) == 0))
17860Sstevel@tonic-gate 							break;
17870Sstevel@tonic-gate 					}
17880Sstevel@tonic-gate 				}
17890Sstevel@tonic-gate 			}
17900Sstevel@tonic-gate 		} /* while */
17910Sstevel@tonic-gate 		if (nconf == NULL) {
17920Sstevel@tonic-gate 			if (++nthtry <= MNT_PREF_LISTLEN) {
17930Sstevel@tonic-gate 				endnetpath(nc);
17940Sstevel@tonic-gate 				if ((nc = setnetpath()) == NULL)
17950Sstevel@tonic-gate 					goto done;
17960Sstevel@tonic-gate 				goto retry;
17970Sstevel@tonic-gate 			} else
17980Sstevel@tonic-gate 				goto done;
17990Sstevel@tonic-gate 		} else {
18000Sstevel@tonic-gate 			if ((nb = get_the_addr(hostname, prog, vers, nconf,
18010Sstevel@tonic-gate 				port, tinfo, fhp, get_pubfh, fspath, error))
18020Sstevel@tonic-gate 				== NULL) {
18030Sstevel@tonic-gate 				/* nb is NULL - deal with errors */
18040Sstevel@tonic-gate 				if (error) {
18050Sstevel@tonic-gate 					if (error->error_type == ERR_NOHOST)
18060Sstevel@tonic-gate 						SET_ERR_RET(&errsave_nohost,
18070Sstevel@tonic-gate 							error->error_type,
18080Sstevel@tonic-gate 							error->error_value);
18090Sstevel@tonic-gate 					if (error->error_type == ERR_RPCERROR)
18100Sstevel@tonic-gate 						SET_ERR_RET(&errsave_rpcerr,
18110Sstevel@tonic-gate 							error->error_type,
18120Sstevel@tonic-gate 							error->error_value);
18130Sstevel@tonic-gate 				}
18140Sstevel@tonic-gate 				/*
18150Sstevel@tonic-gate 				 * Continue the same search path in the
18160Sstevel@tonic-gate 				 * netconfig db until no more matched
18170Sstevel@tonic-gate 				 * nconf (nconf == NULL).
18180Sstevel@tonic-gate 				 */
18190Sstevel@tonic-gate 				goto retry;
18200Sstevel@tonic-gate 			}
18210Sstevel@tonic-gate 		}
18220Sstevel@tonic-gate 	}
18230Sstevel@tonic-gate 	SET_ERR_RET(error, ERR_PROTO_NONE, 0);
18240Sstevel@tonic-gate 
18250Sstevel@tonic-gate 	/*
18260Sstevel@tonic-gate 	 * Got nconf and nb.  Now dup the netconfig structure (nconf)
18270Sstevel@tonic-gate 	 * and return it thru nconfp.
18280Sstevel@tonic-gate 	 */
18290Sstevel@tonic-gate 	*nconfp = getnetconfigent(nconf->nc_netid);
18300Sstevel@tonic-gate 	if (*nconfp == NULL) {
18310Sstevel@tonic-gate 		syslog(LOG_ERR, "no memory\n");
18320Sstevel@tonic-gate 		free(nb);
18330Sstevel@tonic-gate 		nb = NULL;
18340Sstevel@tonic-gate 	}
18350Sstevel@tonic-gate done:
18360Sstevel@tonic-gate 	if (nc)
18370Sstevel@tonic-gate 		endnetpath(nc);
18380Sstevel@tonic-gate 
18390Sstevel@tonic-gate 	if (nb == NULL) {
184077Soa138391 		/*
184177Soa138391 		 * Check the saved errors. The RPC error has *
184277Soa138391 		 * precedence over the no host error.
184377Soa138391 		 */
184477Soa138391 		if (errsave_nohost.error_type != ERR_PROTO_NONE)
184577Soa138391 			SET_ERR_RET(error, errsave_nohost.error_type,
18460Sstevel@tonic-gate 					errsave_nohost.error_value);
184777Soa138391 
184877Soa138391 		if (errsave_rpcerr.error_type != ERR_PROTO_NONE)
184977Soa138391 			SET_ERR_RET(error, errsave_rpcerr.error_type,
18500Sstevel@tonic-gate 					errsave_rpcerr.error_value);
18510Sstevel@tonic-gate 	}
185277Soa138391 
18530Sstevel@tonic-gate 	return (nb);
18540Sstevel@tonic-gate }
18550Sstevel@tonic-gate 
18560Sstevel@tonic-gate /*
18570Sstevel@tonic-gate  * Get a file handle usinging multi-component lookup with the public
18580Sstevel@tonic-gate  * file handle.
18590Sstevel@tonic-gate  */
18600Sstevel@tonic-gate static int
18610Sstevel@tonic-gate get_fh_via_pub(struct nfs_args *args, char *fshost, char *fspath, bool_t url,
18620Sstevel@tonic-gate 	bool_t loud, int *versp, struct netconfig **nconfp, ushort_t port)
18630Sstevel@tonic-gate {
18640Sstevel@tonic-gate 	uint_t vers_min;
18650Sstevel@tonic-gate 	uint_t vers_max;
18660Sstevel@tonic-gate 	int r;
18670Sstevel@tonic-gate 	char *path;
18680Sstevel@tonic-gate 
18690Sstevel@tonic-gate 	if (nfsvers != 0) {
18700Sstevel@tonic-gate 		vers_max = vers_min = nfsvers;
18710Sstevel@tonic-gate 	} else {
18720Sstevel@tonic-gate 		vers_max = vers_max_default;
18730Sstevel@tonic-gate 		vers_min = vers_min_default;
18740Sstevel@tonic-gate 	}
18750Sstevel@tonic-gate 
18760Sstevel@tonic-gate 	if (url == FALSE) {
18770Sstevel@tonic-gate 		path = malloc(strlen(fspath) + 2);
18780Sstevel@tonic-gate 		if (path == NULL) {
18790Sstevel@tonic-gate 			if (loud == TRUE)  {
18800Sstevel@tonic-gate 				pr_err(gettext("no memory\n"));
18810Sstevel@tonic-gate 			}
18820Sstevel@tonic-gate 			return (RET_ERR);
18830Sstevel@tonic-gate 		}
18840Sstevel@tonic-gate 
18850Sstevel@tonic-gate 		path[0] = (char)WNL_NATIVEPATH;
18860Sstevel@tonic-gate 		(void) strcpy(&path[1], fspath);
18870Sstevel@tonic-gate 
18880Sstevel@tonic-gate 	} else  {
18890Sstevel@tonic-gate 		path = fspath;
18900Sstevel@tonic-gate 	}
18910Sstevel@tonic-gate 
18920Sstevel@tonic-gate 	for (nfsvers_to_use = vers_max; nfsvers_to_use >= vers_min;
18930Sstevel@tonic-gate 	    nfsvers_to_use--) {
18940Sstevel@tonic-gate 		/*
18950Sstevel@tonic-gate 		 * getaddr_nfs will also fill in the fh for us.
18960Sstevel@tonic-gate 		 */
18970Sstevel@tonic-gate 		r = getaddr_nfs(args, fshost, nconfp,
18980Sstevel@tonic-gate 				TRUE, path, port, NULL, FALSE);
18990Sstevel@tonic-gate 
19000Sstevel@tonic-gate 		if (r == RET_OK) {
19010Sstevel@tonic-gate 			/*
19020Sstevel@tonic-gate 			 * Since we are using the public fh, and NLM is
19030Sstevel@tonic-gate 			 * not firewall friendly, use local locking.
19040Sstevel@tonic-gate 			 * Not the case for v4.
19050Sstevel@tonic-gate 			 */
19060Sstevel@tonic-gate 			*versp = nfsvers_to_use;
19070Sstevel@tonic-gate 			switch (nfsvers_to_use) {
19080Sstevel@tonic-gate 			case NFS_V4:
19090Sstevel@tonic-gate 				fstype = MNTTYPE_NFS4;
19100Sstevel@tonic-gate 				break;
19110Sstevel@tonic-gate 			case NFS_V3:
19120Sstevel@tonic-gate 				fstype = MNTTYPE_NFS3;
19130Sstevel@tonic-gate 				/* fall through to pick up llock option */
19140Sstevel@tonic-gate 			default:
19150Sstevel@tonic-gate 				args->flags |= NFSMNT_LLOCK;
19160Sstevel@tonic-gate 				break;
19170Sstevel@tonic-gate 			}
19180Sstevel@tonic-gate 			if (fspath != path)
19190Sstevel@tonic-gate 				free(path);
19200Sstevel@tonic-gate 
19210Sstevel@tonic-gate 			return (r);
19220Sstevel@tonic-gate 		}
19230Sstevel@tonic-gate 	}
19240Sstevel@tonic-gate 
19250Sstevel@tonic-gate 	if (fspath != path) {
19260Sstevel@tonic-gate 		free(path);
19270Sstevel@tonic-gate 	}
19280Sstevel@tonic-gate 
19290Sstevel@tonic-gate 	if (loud == TRUE) {
19300Sstevel@tonic-gate 		pr_err(gettext("Could not use public filehandle in request to"
19310Sstevel@tonic-gate 			" server %s\n"), fshost);
19320Sstevel@tonic-gate 	}
19330Sstevel@tonic-gate 
19340Sstevel@tonic-gate 	return (r);
19350Sstevel@tonic-gate }
19360Sstevel@tonic-gate 
19370Sstevel@tonic-gate /*
19380Sstevel@tonic-gate  * get fhandle of remote path from server's mountd
19390Sstevel@tonic-gate  */
19400Sstevel@tonic-gate static int
19410Sstevel@tonic-gate get_fh(struct nfs_args *args, char *fshost, char *fspath, int *versp,
19420Sstevel@tonic-gate 	bool_t loud_on_mnt_err, struct netconfig **nconfp, ushort_t port)
19430Sstevel@tonic-gate {
19440Sstevel@tonic-gate 	static struct fhstatus fhs;
19450Sstevel@tonic-gate 	static struct mountres3 mountres3;
19460Sstevel@tonic-gate 	static struct pathcnf p;
19470Sstevel@tonic-gate 	nfs_fh3 *fh3p;
19480Sstevel@tonic-gate 	struct timeval timeout = { 25, 0};
19490Sstevel@tonic-gate 	CLIENT *cl;
19500Sstevel@tonic-gate 	enum clnt_stat rpc_stat;
19510Sstevel@tonic-gate 	rpcvers_t outvers = 0;
19520Sstevel@tonic-gate 	rpcvers_t vers_to_try;
19530Sstevel@tonic-gate 	rpcvers_t vers_min;
19540Sstevel@tonic-gate 	static int printed = 0;
19550Sstevel@tonic-gate 	int count, i, *auths;
19560Sstevel@tonic-gate 	char *msg;
19570Sstevel@tonic-gate 
19580Sstevel@tonic-gate 	switch (nfsvers) {
19590Sstevel@tonic-gate 	case 2: /* version 2 specified try that only */
19600Sstevel@tonic-gate 		vers_to_try = MOUNTVERS_POSIX;
19610Sstevel@tonic-gate 		vers_min = MOUNTVERS;
19620Sstevel@tonic-gate 		break;
19630Sstevel@tonic-gate 	case 3: /* version 3 specified try that only */
19640Sstevel@tonic-gate 		vers_to_try = MOUNTVERS3;
19650Sstevel@tonic-gate 		vers_min = MOUNTVERS3;
19660Sstevel@tonic-gate 		break;
19670Sstevel@tonic-gate 	case 4: /* version 4 specified try that only */
19680Sstevel@tonic-gate 		/*
19690Sstevel@tonic-gate 		 * This assignment is in the wrong version sequence.
19700Sstevel@tonic-gate 		 * The above are MOUNT program and this is NFS
19710Sstevel@tonic-gate 		 * program.  However, it happens to work out since the
19720Sstevel@tonic-gate 		 * two don't collide for NFSv4.
19730Sstevel@tonic-gate 		 */
19740Sstevel@tonic-gate 		vers_to_try = NFS_V4;
19750Sstevel@tonic-gate 		vers_min = NFS_V4;
19760Sstevel@tonic-gate 		break;
19770Sstevel@tonic-gate 	default: /* no version specified, start with default */
19780Sstevel@tonic-gate 		vers_to_try = vers_max_default;
19790Sstevel@tonic-gate 		vers_min = vers_min_default;
19800Sstevel@tonic-gate 		break;
19810Sstevel@tonic-gate 	}
19820Sstevel@tonic-gate 
19830Sstevel@tonic-gate 	/*
19840Sstevel@tonic-gate 	 * In the case of version 4, just NULL proc the server since
19850Sstevel@tonic-gate 	 * there is no MOUNT program.  If this fails, then decrease
19860Sstevel@tonic-gate 	 * vers_to_try and continue on with regular MOUNT program
19870Sstevel@tonic-gate 	 * processing.
19880Sstevel@tonic-gate 	 */
19890Sstevel@tonic-gate 	if (vers_to_try == NFS_V4) {
19900Sstevel@tonic-gate 		int savevers = nfsvers_to_use;
19910Sstevel@tonic-gate 		err_ret_t error;
19920Sstevel@tonic-gate 		int retval;
19930Sstevel@tonic-gate 		SET_ERR_RET(&error, ERR_PROTO_NONE, 0);
19940Sstevel@tonic-gate 
19950Sstevel@tonic-gate 		/* Let's hope for the best */
19960Sstevel@tonic-gate 		nfsvers_to_use = NFS_V4;
19970Sstevel@tonic-gate 		retval =
19980Sstevel@tonic-gate 			getaddr_nfs(args, fshost, nconfp, FALSE,
19990Sstevel@tonic-gate 				    fspath, port, &error, vers_min == NFS_V4);
20000Sstevel@tonic-gate 
20010Sstevel@tonic-gate 		if (retval == RET_OK) {
20020Sstevel@tonic-gate 			*versp = nfsvers_to_use = NFS_V4;
20030Sstevel@tonic-gate 			fstype = MNTTYPE_NFS4;
20040Sstevel@tonic-gate 			args->fh = strdup(fspath);
20050Sstevel@tonic-gate 			if (args->fh == NULL) {
20060Sstevel@tonic-gate 				pr_err(gettext("no memory\n"));
20070Sstevel@tonic-gate 				*versp = nfsvers_to_use = savevers;
20080Sstevel@tonic-gate 				return (RET_ERR);
20090Sstevel@tonic-gate 			}
20100Sstevel@tonic-gate 			return (RET_OK);
20110Sstevel@tonic-gate 		}
20120Sstevel@tonic-gate 		nfsvers_to_use = savevers;
20130Sstevel@tonic-gate 
20140Sstevel@tonic-gate 		vers_to_try--;
20150Sstevel@tonic-gate 		/* If no more versions to try, let the user know. */
20160Sstevel@tonic-gate 		if (vers_to_try < vers_min) {
20170Sstevel@tonic-gate 			return (retval);
20180Sstevel@tonic-gate 		}
20190Sstevel@tonic-gate 
20200Sstevel@tonic-gate 		/*
20210Sstevel@tonic-gate 		 * If we are here, there are more versions to try but
20220Sstevel@tonic-gate 		 * there has been an error of some sort.  If it is not
20230Sstevel@tonic-gate 		 * an RPC error (e.g. host unknown), we just stop and
20240Sstevel@tonic-gate 		 * return the error since the other versions would see
20250Sstevel@tonic-gate 		 * the same error as well.
20260Sstevel@tonic-gate 		 */
20270Sstevel@tonic-gate 		if (retval == RET_ERR && error.error_type != ERR_RPCERROR)
20280Sstevel@tonic-gate 			return (retval);
20290Sstevel@tonic-gate 	}
20300Sstevel@tonic-gate 
20310Sstevel@tonic-gate 	while ((cl = clnt_create_vers(fshost, MOUNTPROG, &outvers,
20320Sstevel@tonic-gate 			vers_min, vers_to_try, "datagram_v")) == NULL) {
20330Sstevel@tonic-gate 		if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST) {
20340Sstevel@tonic-gate 			pr_err(gettext("%s: %s\n"), fshost,
20350Sstevel@tonic-gate 			    clnt_spcreateerror(""));
20360Sstevel@tonic-gate 			return (RET_ERR);
20370Sstevel@tonic-gate 		}
20380Sstevel@tonic-gate 
20390Sstevel@tonic-gate 		/*
20400Sstevel@tonic-gate 		 * We don't want to downgrade version on lost packets
20410Sstevel@tonic-gate 		 */
20420Sstevel@tonic-gate 		if ((rpc_createerr.cf_stat == RPC_TIMEDOUT) ||
20430Sstevel@tonic-gate 			(rpc_createerr.cf_stat == RPC_PMAPFAILURE)) {
20440Sstevel@tonic-gate 			pr_err(gettext("%s: %s\n"), fshost,
20450Sstevel@tonic-gate 			    clnt_spcreateerror(""));
20460Sstevel@tonic-gate 			return (RET_RETRY);
20470Sstevel@tonic-gate 		}
20480Sstevel@tonic-gate 
20490Sstevel@tonic-gate 		/*
20500Sstevel@tonic-gate 		 * back off and try the previous version - patch to the
20510Sstevel@tonic-gate 		 * problem of version numbers not being contigous and
20520Sstevel@tonic-gate 		 * clnt_create_vers failing (SunOS4.1 clients & SGI servers)
20530Sstevel@tonic-gate 		 * The problem happens with most non-Sun servers who
20540Sstevel@tonic-gate 		 * don't support mountd protocol #2. So, in case the
20550Sstevel@tonic-gate 		 * call fails, we re-try the call anyway.
20560Sstevel@tonic-gate 		 */
20570Sstevel@tonic-gate 		vers_to_try--;
20580Sstevel@tonic-gate 		if (vers_to_try < vers_min) {
20590Sstevel@tonic-gate 			if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH) {
20600Sstevel@tonic-gate 				if (nfsvers == 0) {
20610Sstevel@tonic-gate 					pr_err(gettext(
20620Sstevel@tonic-gate 			"%s:%s: no applicable versions of NFS supported\n"),
20630Sstevel@tonic-gate 					    fshost, fspath);
20640Sstevel@tonic-gate 				} else {
20650Sstevel@tonic-gate 					pr_err(gettext(
20660Sstevel@tonic-gate 			"%s:%s: NFS Version %d not supported\n"),
20670Sstevel@tonic-gate 					    fshost, fspath, nfsvers);
20680Sstevel@tonic-gate 				}
20690Sstevel@tonic-gate 				return (RET_ERR);
20700Sstevel@tonic-gate 			}
20710Sstevel@tonic-gate 			if (!printed) {
20720Sstevel@tonic-gate 				pr_err(gettext("%s: %s\n"), fshost,
20730Sstevel@tonic-gate 				    clnt_spcreateerror(""));
20740Sstevel@tonic-gate 				printed = 1;
20750Sstevel@tonic-gate 			}
20760Sstevel@tonic-gate 			return (RET_RETRY);
20770Sstevel@tonic-gate 		}
20780Sstevel@tonic-gate 	}
20790Sstevel@tonic-gate 	if (posix && outvers < MOUNTVERS_POSIX) {
20800Sstevel@tonic-gate 		pr_err(gettext("%s: %s: no pathconf info\n"),
20810Sstevel@tonic-gate 		    fshost, clnt_sperror(cl, ""));
20820Sstevel@tonic-gate 		clnt_destroy(cl);
20830Sstevel@tonic-gate 		return (RET_ERR);
20840Sstevel@tonic-gate 	}
20850Sstevel@tonic-gate 
20860Sstevel@tonic-gate 	if (__clnt_bindresvport(cl) < 0) {
20870Sstevel@tonic-gate 		pr_err(gettext("Couldn't bind to reserved port\n"));
20880Sstevel@tonic-gate 		clnt_destroy(cl);
20890Sstevel@tonic-gate 		return (RET_RETRY);
20900Sstevel@tonic-gate 	}
20910Sstevel@tonic-gate 
20920Sstevel@tonic-gate 	if ((cl->cl_auth = authsys_create_default()) == NULL) {
20930Sstevel@tonic-gate 		pr_err(
20940Sstevel@tonic-gate 		    gettext("Couldn't create default authentication handle\n"));
20950Sstevel@tonic-gate 		clnt_destroy(cl);
20960Sstevel@tonic-gate 		return (RET_RETRY);
20970Sstevel@tonic-gate 	}
20980Sstevel@tonic-gate 
20990Sstevel@tonic-gate 	switch (outvers) {
21000Sstevel@tonic-gate 	case MOUNTVERS:
21010Sstevel@tonic-gate 	case MOUNTVERS_POSIX:
21020Sstevel@tonic-gate 		*versp = nfsvers_to_use = NFS_VERSION;
21030Sstevel@tonic-gate 		rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath,
21040Sstevel@tonic-gate 			(caddr_t)&fspath, xdr_fhstatus, (caddr_t)&fhs, timeout);
21050Sstevel@tonic-gate 		if (rpc_stat != RPC_SUCCESS) {
21060Sstevel@tonic-gate 			pr_err(gettext("%s:%s: server not responding %s\n"),
21070Sstevel@tonic-gate 			    fshost, fspath, clnt_sperror(cl, ""));
21080Sstevel@tonic-gate 			clnt_destroy(cl);
21090Sstevel@tonic-gate 			return (RET_RETRY);
21100Sstevel@tonic-gate 		}
21110Sstevel@tonic-gate 
21120Sstevel@tonic-gate 		if ((errno = fhs.fhs_status) != MNT_OK) {
21130Sstevel@tonic-gate 			if (loud_on_mnt_err) {
21140Sstevel@tonic-gate 			    if (errno == EACCES) {
21150Sstevel@tonic-gate 				pr_err(gettext("%s:%s: access denied\n"),
21160Sstevel@tonic-gate 				    fshost, fspath);
21170Sstevel@tonic-gate 			    } else {
21180Sstevel@tonic-gate 				pr_err(gettext("%s:%s: %s\n"), fshost, fspath,
21190Sstevel@tonic-gate 				    strerror(errno));
21200Sstevel@tonic-gate 			    }
21210Sstevel@tonic-gate 			}
21220Sstevel@tonic-gate 			clnt_destroy(cl);
21230Sstevel@tonic-gate 			return (RET_MNTERR);
21240Sstevel@tonic-gate 		}
21250Sstevel@tonic-gate 		args->fh = malloc(sizeof (fhs.fhstatus_u.fhs_fhandle));
21260Sstevel@tonic-gate 		if (args->fh == NULL) {
21270Sstevel@tonic-gate 			pr_err(gettext("no memory\n"));
21280Sstevel@tonic-gate 			return (RET_ERR);
21290Sstevel@tonic-gate 		}
21300Sstevel@tonic-gate 		memcpy((caddr_t)args->fh, (caddr_t)&fhs.fhstatus_u.fhs_fhandle,
21310Sstevel@tonic-gate 			sizeof (fhs.fhstatus_u.fhs_fhandle));
21320Sstevel@tonic-gate 		if (!errno && posix) {
21330Sstevel@tonic-gate 			rpc_stat = clnt_call(cl, MOUNTPROC_PATHCONF,
21340Sstevel@tonic-gate 				xdr_dirpath, (caddr_t)&fspath, xdr_ppathcnf,
21350Sstevel@tonic-gate 				(caddr_t)&p, timeout);
21360Sstevel@tonic-gate 			if (rpc_stat != RPC_SUCCESS) {
21370Sstevel@tonic-gate 				pr_err(gettext(
21380Sstevel@tonic-gate 				    "%s:%s: server not responding %s\n"),
21390Sstevel@tonic-gate 				    fshost, fspath, clnt_sperror(cl, ""));
21400Sstevel@tonic-gate 				free(args->fh);
21410Sstevel@tonic-gate 				clnt_destroy(cl);
21420Sstevel@tonic-gate 				return (RET_RETRY);
21430Sstevel@tonic-gate 			}
21440Sstevel@tonic-gate 			if (_PC_ISSET(_PC_ERROR, p.pc_mask)) {
21450Sstevel@tonic-gate 				pr_err(gettext(
21460Sstevel@tonic-gate 				    "%s:%s: no pathconf info\n"),
21470Sstevel@tonic-gate 				    fshost, fspath);
21480Sstevel@tonic-gate 				free(args->fh);
21490Sstevel@tonic-gate 				clnt_destroy(cl);
21500Sstevel@tonic-gate 				return (RET_ERR);
21510Sstevel@tonic-gate 			}
21520Sstevel@tonic-gate 			args->flags |= NFSMNT_POSIX;
21530Sstevel@tonic-gate 			args->pathconf = malloc(sizeof (p));
21540Sstevel@tonic-gate 			if (args->pathconf == NULL) {
21550Sstevel@tonic-gate 				pr_err(gettext("no memory\n"));
21560Sstevel@tonic-gate 				free(args->fh);
21570Sstevel@tonic-gate 				clnt_destroy(cl);
21580Sstevel@tonic-gate 				return (RET_ERR);
21590Sstevel@tonic-gate 			}
21600Sstevel@tonic-gate 			memcpy((caddr_t)args->pathconf, (caddr_t)&p,
21610Sstevel@tonic-gate 				sizeof (p));
21620Sstevel@tonic-gate 		}
21630Sstevel@tonic-gate 		break;
21640Sstevel@tonic-gate 
21650Sstevel@tonic-gate 	case MOUNTVERS3:
21660Sstevel@tonic-gate 		*versp = nfsvers_to_use = NFS_V3;
21670Sstevel@tonic-gate 		rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath,
21680Sstevel@tonic-gate 				(caddr_t)&fspath,
21690Sstevel@tonic-gate 				xdr_mountres3, (caddr_t)&mountres3, timeout);
21700Sstevel@tonic-gate 		if (rpc_stat != RPC_SUCCESS) {
21710Sstevel@tonic-gate 			pr_err(gettext("%s:%s: server not responding %s\n"),
21720Sstevel@tonic-gate 			    fshost, fspath, clnt_sperror(cl, ""));
21730Sstevel@tonic-gate 			clnt_destroy(cl);
21740Sstevel@tonic-gate 			return (RET_RETRY);
21750Sstevel@tonic-gate 		}
21760Sstevel@tonic-gate 
21770Sstevel@tonic-gate 		/*
21780Sstevel@tonic-gate 		 * Assume here that most of the MNT3ERR_*
21790Sstevel@tonic-gate 		 * codes map into E* errors.
21800Sstevel@tonic-gate 		 */
21810Sstevel@tonic-gate 		if ((errno = mountres3.fhs_status) != MNT_OK) {
21820Sstevel@tonic-gate 		    if (loud_on_mnt_err) {
21830Sstevel@tonic-gate 			switch (errno) {
21840Sstevel@tonic-gate 			case MNT3ERR_NAMETOOLONG:
21850Sstevel@tonic-gate 				msg = "path name is too long";
21860Sstevel@tonic-gate 				break;
21870Sstevel@tonic-gate 			case MNT3ERR_NOTSUPP:
21880Sstevel@tonic-gate 				msg = "operation not supported";
21890Sstevel@tonic-gate 				break;
21900Sstevel@tonic-gate 			case MNT3ERR_SERVERFAULT:
21910Sstevel@tonic-gate 				msg = "server fault";
21920Sstevel@tonic-gate 				break;
21930Sstevel@tonic-gate 			default:
21940Sstevel@tonic-gate 				msg = strerror(errno);
21950Sstevel@tonic-gate 				break;
21960Sstevel@tonic-gate 			}
21970Sstevel@tonic-gate 			pr_err(gettext("%s:%s: %s\n"), fshost, fspath, msg);
21980Sstevel@tonic-gate 		    }
21990Sstevel@tonic-gate 		    clnt_destroy(cl);
22000Sstevel@tonic-gate 		    return (RET_MNTERR);
22010Sstevel@tonic-gate 		}
22020Sstevel@tonic-gate 
22030Sstevel@tonic-gate 		fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
22040Sstevel@tonic-gate 		if (fh3p == NULL) {
22050Sstevel@tonic-gate 			pr_err(gettext("no memory\n"));
22060Sstevel@tonic-gate 			return (RET_ERR);
22070Sstevel@tonic-gate 		}
22080Sstevel@tonic-gate 		fh3p->fh3_length =
22090Sstevel@tonic-gate 			mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len;
22100Sstevel@tonic-gate 		(void) memcpy(fh3p->fh3_u.data,
22110Sstevel@tonic-gate 			mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val,
22120Sstevel@tonic-gate 			fh3p->fh3_length);
22130Sstevel@tonic-gate 		args->fh = (caddr_t)fh3p;
22140Sstevel@tonic-gate 		fstype = MNTTYPE_NFS3;
22150Sstevel@tonic-gate 
22160Sstevel@tonic-gate 		/*
22170Sstevel@tonic-gate 		 * Check the security flavor to be used.
22180Sstevel@tonic-gate 		 *
22190Sstevel@tonic-gate 		 * If "secure" or "sec=flavor" is a mount
22200Sstevel@tonic-gate 		 * option, check if the server supports the "flavor".
22210Sstevel@tonic-gate 		 * If the server does not support the flavor, return
22220Sstevel@tonic-gate 		 * error.
22230Sstevel@tonic-gate 		 *
22240Sstevel@tonic-gate 		 * If no mount option is given then use the first supported
22250Sstevel@tonic-gate 		 * security flavor (by the client) in the auth list returned
22260Sstevel@tonic-gate 		 * from the server.
22270Sstevel@tonic-gate 		 *
22280Sstevel@tonic-gate 		 */
22290Sstevel@tonic-gate 		auths =
22300Sstevel@tonic-gate 		mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val;
22310Sstevel@tonic-gate 		count =
22320Sstevel@tonic-gate 		mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len;
22330Sstevel@tonic-gate 
22340Sstevel@tonic-gate 		if (sec_opt) {
22350Sstevel@tonic-gate 			for (i = 0; i < count; i++) {
22360Sstevel@tonic-gate 				if (auths[i] == nfs_sec.sc_nfsnum)
22370Sstevel@tonic-gate 				    break;
22380Sstevel@tonic-gate 			}
22390Sstevel@tonic-gate 			if (i >= count) {
22400Sstevel@tonic-gate 				goto autherr;
22410Sstevel@tonic-gate 			}
22420Sstevel@tonic-gate 		} else {
22430Sstevel@tonic-gate 		    if (count > 0) {
22440Sstevel@tonic-gate 			for (i = 0; i < count; i++) {
22450Sstevel@tonic-gate 			    if (!nfs_getseconfig_bynumber(auths[i], &nfs_sec)) {
22460Sstevel@tonic-gate 				sec_opt++;
22470Sstevel@tonic-gate 				break;
22480Sstevel@tonic-gate 			    }
22490Sstevel@tonic-gate 			}
22500Sstevel@tonic-gate 			if (i >= count) {
22510Sstevel@tonic-gate 			    goto autherr;
22520Sstevel@tonic-gate 			}
22530Sstevel@tonic-gate 		    }
22540Sstevel@tonic-gate 		}
22550Sstevel@tonic-gate 		break;
22560Sstevel@tonic-gate 	default:
22570Sstevel@tonic-gate 		pr_err(gettext("%s:%s: Unknown MOUNT version %d\n"),
22580Sstevel@tonic-gate 		    fshost, fspath, outvers);
22590Sstevel@tonic-gate 		clnt_destroy(cl);
22600Sstevel@tonic-gate 		return (RET_ERR);
22610Sstevel@tonic-gate 	}
22620Sstevel@tonic-gate 
22630Sstevel@tonic-gate 	clnt_destroy(cl);
22640Sstevel@tonic-gate 	return (RET_OK);
22650Sstevel@tonic-gate 
22660Sstevel@tonic-gate autherr:
22670Sstevel@tonic-gate 	pr_err(gettext(
22680Sstevel@tonic-gate 		"security mode does not match the server exporting %s:%s\n"),
22690Sstevel@tonic-gate 		fshost, fspath);
22700Sstevel@tonic-gate 	clnt_destroy(cl);
22710Sstevel@tonic-gate 	return (RET_ERR);
22720Sstevel@tonic-gate }
22730Sstevel@tonic-gate 
22740Sstevel@tonic-gate /*
22750Sstevel@tonic-gate  * Fill in the address for the server's NFS service and
22760Sstevel@tonic-gate  * fill in a knetconfig structure for the transport that
22770Sstevel@tonic-gate  * the service is available on.
22780Sstevel@tonic-gate  */
22790Sstevel@tonic-gate static int
22800Sstevel@tonic-gate getaddr_nfs(struct nfs_args *args, char *fshost, struct netconfig **nconfp,
22810Sstevel@tonic-gate 	    bool_t get_pubfh, char *fspath, ushort_t port, err_ret_t *error,
22820Sstevel@tonic-gate 	    bool_t print_rpcerror)
22830Sstevel@tonic-gate {
22840Sstevel@tonic-gate 	struct stat sb;
22850Sstevel@tonic-gate 	struct netconfig *nconf;
22860Sstevel@tonic-gate 	struct knetconfig *knconfp;
22870Sstevel@tonic-gate 	static int printed = 0;
22880Sstevel@tonic-gate 	struct t_info tinfo;
22890Sstevel@tonic-gate 	err_ret_t addr_error;
22900Sstevel@tonic-gate 
22910Sstevel@tonic-gate 	SET_ERR_RET(error, ERR_PROTO_NONE, 0);
22920Sstevel@tonic-gate 	SET_ERR_RET(&addr_error, ERR_PROTO_NONE, 0);
22930Sstevel@tonic-gate 
22940Sstevel@tonic-gate 	if (nfs_proto) {
22950Sstevel@tonic-gate 		/*
22960Sstevel@tonic-gate 		 * If a proto is specified and its rdma try this. The kernel
22970Sstevel@tonic-gate 		 * will later do the reachablity test and fail form there
22980Sstevel@tonic-gate 		 * if rdma transport is not available to kernel rpc
22990Sstevel@tonic-gate 		 */
23000Sstevel@tonic-gate 		if (strcmp(nfs_proto, "rdma") == 0) {
23010Sstevel@tonic-gate 			args->addr = get_addr(fshost, NFS_PROGRAM,
23020Sstevel@tonic-gate 			    nfsvers_to_use, nconfp, NULL, port, &tinfo,
23030Sstevel@tonic-gate 			    &args->fh, get_pubfh, fspath, &addr_error);
23040Sstevel@tonic-gate 
23050Sstevel@tonic-gate 			args->flags |= NFSMNT_DORDMA;
23060Sstevel@tonic-gate 		} else {
23070Sstevel@tonic-gate 			args->addr = get_addr(fshost, NFS_PROGRAM,
23080Sstevel@tonic-gate 			    nfsvers_to_use, nconfp, nfs_proto, port, &tinfo,
23090Sstevel@tonic-gate 			    &args->fh, get_pubfh, fspath, &addr_error);
23100Sstevel@tonic-gate 		}
23110Sstevel@tonic-gate 	} else {
23120Sstevel@tonic-gate 		args->addr = get_addr(fshost, NFS_PROGRAM, nfsvers_to_use,
23130Sstevel@tonic-gate 		    nconfp, nfs_proto, port, &tinfo, &args->fh, get_pubfh,
23140Sstevel@tonic-gate 		    fspath, &addr_error);
23150Sstevel@tonic-gate 		/*
23160Sstevel@tonic-gate 		 * If no proto is specified set this flag.
23170Sstevel@tonic-gate 		 * Kernel mount code will try to use RDMA if its on the
23180Sstevel@tonic-gate 		 * system, otherwise it will keep on using the protocol
23190Sstevel@tonic-gate 		 * selected here, through the above get_addr call.
23200Sstevel@tonic-gate 		 */
23210Sstevel@tonic-gate 		if (nfs_proto == NULL)
23220Sstevel@tonic-gate 			args->flags |= NFSMNT_TRYRDMA;
23230Sstevel@tonic-gate 	}
23240Sstevel@tonic-gate 
23250Sstevel@tonic-gate 	if (args->addr == NULL) {
23260Sstevel@tonic-gate 		/*
23270Sstevel@tonic-gate 		 * We could have failed because the server had no public
23280Sstevel@tonic-gate 		 * file handle support. So don't print a message and don't
23290Sstevel@tonic-gate 		 * retry.
23300Sstevel@tonic-gate 		 */
23310Sstevel@tonic-gate 		if (get_pubfh == TRUE)
23320Sstevel@tonic-gate 			return (RET_ERR);
23330Sstevel@tonic-gate 
23340Sstevel@tonic-gate 		if (!printed) {
23350Sstevel@tonic-gate 			switch (addr_error.error_type) {
23360Sstevel@tonic-gate 			case 0:
23370Sstevel@tonic-gate 				break;
23380Sstevel@tonic-gate 			case ERR_RPCERROR:
23390Sstevel@tonic-gate 				if (!print_rpcerror)
23400Sstevel@tonic-gate 					/* no error print at this time */
23410Sstevel@tonic-gate 					break;
23420Sstevel@tonic-gate 				pr_err(gettext("%s NFS service not"
23430Sstevel@tonic-gate 					    " available %s\n"), fshost,
23440Sstevel@tonic-gate 				    clnt_sperrno(addr_error.error_value));
23450Sstevel@tonic-gate 				break;
23460Sstevel@tonic-gate 			case ERR_NETPATH:
23470Sstevel@tonic-gate 				pr_err(gettext("%s: Error in NETPATH.\n"),
23480Sstevel@tonic-gate 					fshost);
23490Sstevel@tonic-gate 				break;
23500Sstevel@tonic-gate 			case ERR_PROTO_INVALID:
23510Sstevel@tonic-gate 				pr_err(gettext("%s: NFS service does not"
23520Sstevel@tonic-gate 					" recognize protocol: %s.\n"), fshost,
23530Sstevel@tonic-gate 					nfs_proto);
23540Sstevel@tonic-gate 				break;
23550Sstevel@tonic-gate 			case ERR_PROTO_UNSUPP:
2356*112Soa138391 				if (nfsvers_to_use == NFS_VERSMIN) {
2357*112Soa138391 					/*
2358*112Soa138391 					 * Print this message after we have
2359*112Soa138391 					 * tried all versions of NFS and none
2360*112Soa138391 					 * support the asked transport.
2361*112Soa138391 					 * Otherwise we depricate the version
2362*112Soa138391 					 * and retry below.
2363*112Soa138391 					 */
2364*112Soa138391 					pr_err(gettext("%s: NFS service does"
2365*112Soa138391 						" not support protocol: %s.\n"),
2366*112Soa138391 						fshost, nfs_proto);
2367*112Soa138391 				}
23680Sstevel@tonic-gate 				break;
23690Sstevel@tonic-gate 			case ERR_NOHOST:
237077Soa138391 				pr_err("%s: %s\n", fshost, "Unknown host");
23710Sstevel@tonic-gate 				break;
23720Sstevel@tonic-gate 			default:
23730Sstevel@tonic-gate 				/* case ERR_PROTO_NONE falls through */
23740Sstevel@tonic-gate 				pr_err(gettext("%s: NFS service not responding"
23750Sstevel@tonic-gate 					"\n"), fshost);
23760Sstevel@tonic-gate 				break;
23770Sstevel@tonic-gate 			}
23780Sstevel@tonic-gate 			printed = 1;
23790Sstevel@tonic-gate 		}
23800Sstevel@tonic-gate 		SET_ERR_RET(error,
23810Sstevel@tonic-gate 			addr_error.error_type, addr_error.error_value);
23820Sstevel@tonic-gate 		if (addr_error.error_type == ERR_PROTO_NONE)
23830Sstevel@tonic-gate 			return (RET_RETRY);
23840Sstevel@tonic-gate 		else if (addr_error.error_type == ERR_RPCERROR &&
23850Sstevel@tonic-gate 			! IS_UNRECOVERABLE_RPC(addr_error.error_value)) {
23860Sstevel@tonic-gate 			return (RET_RETRY);
2387*112Soa138391 		} else if (nfsvers == 0 && addr_error.error_type ==
2388*112Soa138391 			ERR_PROTO_UNSUPP && nfsvers_to_use != NFS_VERSMIN) {
2389*112Soa138391 			/*
2390*112Soa138391 			 * If no version is specified, and the error is due
2391*112Soa138391 			 * to an unsupported transport, then depricate the
2392*112Soa138391 			 * version and retry.
2393*112Soa138391 			 */
2394*112Soa138391 			return (RET_RETRY);
2395*112Soa138391 		} else
23960Sstevel@tonic-gate 			return (RET_ERR);
23970Sstevel@tonic-gate 	}
23980Sstevel@tonic-gate 	nconf = *nconfp;
23990Sstevel@tonic-gate 
24000Sstevel@tonic-gate 	if (stat(nconf->nc_device, &sb) < 0) {
24010Sstevel@tonic-gate 		pr_err(gettext("getaddr_nfs: couldn't stat: %s: %s\n"),
24020Sstevel@tonic-gate 		    nconf->nc_device, strerror(errno));
24030Sstevel@tonic-gate 		return (RET_ERR);
24040Sstevel@tonic-gate 	}
24050Sstevel@tonic-gate 
24060Sstevel@tonic-gate 	knconfp = (struct knetconfig *)malloc(sizeof (*knconfp));
24070Sstevel@tonic-gate 	if (!knconfp) {
24080Sstevel@tonic-gate 		pr_err(gettext("no memory\n"));
24090Sstevel@tonic-gate 		return (RET_ERR);
24100Sstevel@tonic-gate 	}
24110Sstevel@tonic-gate 	knconfp->knc_semantics = nconf->nc_semantics;
24120Sstevel@tonic-gate 	knconfp->knc_protofmly = nconf->nc_protofmly;
24130Sstevel@tonic-gate 	knconfp->knc_proto = nconf->nc_proto;
24140Sstevel@tonic-gate 	knconfp->knc_rdev = sb.st_rdev;
24150Sstevel@tonic-gate 
24160Sstevel@tonic-gate 	/* make sure we don't overload the transport */
24170Sstevel@tonic-gate 	if (tinfo.tsdu > 0 && tinfo.tsdu < NFS_MAXDATA + NFS_RPC_HDR) {
24180Sstevel@tonic-gate 		args->flags |= (NFSMNT_RSIZE | NFSMNT_WSIZE);
24190Sstevel@tonic-gate 		if (args->rsize == 0 || args->rsize > tinfo.tsdu - NFS_RPC_HDR)
24200Sstevel@tonic-gate 			args->rsize = tinfo.tsdu - NFS_RPC_HDR;
24210Sstevel@tonic-gate 		if (args->wsize == 0 || args->wsize > tinfo.tsdu - NFS_RPC_HDR)
24220Sstevel@tonic-gate 			args->wsize = tinfo.tsdu - NFS_RPC_HDR;
24230Sstevel@tonic-gate 	}
24240Sstevel@tonic-gate 
24250Sstevel@tonic-gate 	args->flags |= NFSMNT_KNCONF;
24260Sstevel@tonic-gate 	args->knconf = knconfp;
24270Sstevel@tonic-gate 	return (RET_OK);
24280Sstevel@tonic-gate }
24290Sstevel@tonic-gate 
24300Sstevel@tonic-gate static int
24310Sstevel@tonic-gate retry(struct mnttab *mntp, int ro)
24320Sstevel@tonic-gate {
24330Sstevel@tonic-gate 	int delay = 5;
24340Sstevel@tonic-gate 	int count = retries;
24350Sstevel@tonic-gate 	int r;
24360Sstevel@tonic-gate 
24370Sstevel@tonic-gate 	if (bg) {
24380Sstevel@tonic-gate 		if (fork() > 0)
24390Sstevel@tonic-gate 			return (RET_OK);
24400Sstevel@tonic-gate 		pr_err(gettext("backgrounding: %s\n"), mntp->mnt_mountp);
24410Sstevel@tonic-gate 		backgrounded = 1;
24420Sstevel@tonic-gate 	} else
24430Sstevel@tonic-gate 		pr_err(gettext("retrying: %s\n"), mntp->mnt_mountp);
24440Sstevel@tonic-gate 
24450Sstevel@tonic-gate 	while (count--) {
24460Sstevel@tonic-gate 		if ((r = mount_nfs(mntp, ro)) == RET_OK) {
24470Sstevel@tonic-gate 			pr_err(gettext("%s: mounted OK\n"), mntp->mnt_mountp);
24480Sstevel@tonic-gate 			return (RET_OK);
24490Sstevel@tonic-gate 		}
24500Sstevel@tonic-gate 		if (r != RET_RETRY)
24510Sstevel@tonic-gate 			break;
24520Sstevel@tonic-gate 
24530Sstevel@tonic-gate 		if (count > 0) {
24540Sstevel@tonic-gate 		    (void) sleep(delay);
24550Sstevel@tonic-gate 		    delay *= 2;
24560Sstevel@tonic-gate 		    if (delay > 120)
24570Sstevel@tonic-gate 			    delay = 120;
24580Sstevel@tonic-gate 		}
24590Sstevel@tonic-gate 	}
24600Sstevel@tonic-gate 	pr_err(gettext("giving up on: %s\n"), mntp->mnt_mountp);
24610Sstevel@tonic-gate 	return (RET_ERR);
24620Sstevel@tonic-gate }
24630Sstevel@tonic-gate 
24640Sstevel@tonic-gate /*
24650Sstevel@tonic-gate  * Read the /etc/default/nfs configuration file to determine if the
24660Sstevel@tonic-gate  * client has been configured for a new min/max for the NFS version to
24670Sstevel@tonic-gate  * use.
24680Sstevel@tonic-gate  */
24690Sstevel@tonic-gate static void
24700Sstevel@tonic-gate read_default(void)
24710Sstevel@tonic-gate {
24720Sstevel@tonic-gate 	char *defval;
24730Sstevel@tonic-gate 	int errno;
24740Sstevel@tonic-gate 	int tmp;
24750Sstevel@tonic-gate 
24760Sstevel@tonic-gate 	/* Fail silently if error in opening the default nfs config file */
24770Sstevel@tonic-gate 	if ((defopen(NFSADMIN)) == 0) {
24780Sstevel@tonic-gate 		if ((defval = defread("NFS_CLIENT_VERSMIN=")) != NULL) {
24790Sstevel@tonic-gate 			errno = 0;
24800Sstevel@tonic-gate 			tmp = strtol(defval, (char **)NULL, 10);
24810Sstevel@tonic-gate 			if (errno == 0) {
24820Sstevel@tonic-gate 				vers_min_default = tmp;
24830Sstevel@tonic-gate 			}
24840Sstevel@tonic-gate 		}
24850Sstevel@tonic-gate 		if ((defval = defread("NFS_CLIENT_VERSMAX=")) != NULL) {
24860Sstevel@tonic-gate 			errno = 0;
24870Sstevel@tonic-gate 			tmp = strtol(defval, (char **)NULL, 10);
24880Sstevel@tonic-gate 			if (errno == 0) {
24890Sstevel@tonic-gate 				vers_max_default = tmp;
24900Sstevel@tonic-gate 			}
24910Sstevel@tonic-gate 		}
24920Sstevel@tonic-gate 		/* close defaults file */
24930Sstevel@tonic-gate 		defopen(NULL);
24940Sstevel@tonic-gate 	}
24950Sstevel@tonic-gate }
24960Sstevel@tonic-gate 
24970Sstevel@tonic-gate static void
24980Sstevel@tonic-gate sigusr1(int s)
24990Sstevel@tonic-gate {
25000Sstevel@tonic-gate }
2501