autod_nfs.c revision 249:6233bc9dd306
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <stdio.h>
30#include <unistd.h>
31#include <stdlib.h>
32#include <ctype.h>
33#include <syslog.h>
34#include <string.h>
35#include <deflt.h>
36#include <kstat.h>
37#include <sys/param.h>
38#include <sys/types.h>
39#include <sys/time.h>
40#include <sys/stat.h>
41#include <sys/wait.h>
42#include <sys/socket.h>
43#include <netinet/in.h>
44#include <signal.h>
45#include <sys/signal.h>
46#include <rpc/rpc.h>
47#include <rpc/pmap_clnt.h>
48#include <sys/mount.h>
49#include <sys/mntent.h>
50#include <sys/mnttab.h>
51#include <sys/fstyp.h>
52#include <sys/fsid.h>
53#include <arpa/inet.h>
54#include <netdb.h>
55#include <netconfig.h>
56#include <netdir.h>
57#include <errno.h>
58#define	NFSCLIENT
59#include <nfs/nfs.h>
60#include <nfs/mount.h>
61#include <rpcsvc/mount.h>
62#include <rpc/nettype.h>
63#include <locale.h>
64#include <setjmp.h>
65#include <sys/socket.h>
66#include <thread.h>
67#include <limits.h>
68#include <nss_dbdefs.h>			/* for NSS_BUFLEN_HOSTS */
69#include <nfs/nfs_sec.h>
70#include <sys/sockio.h>
71#include <net/if.h>
72#include <assert.h>
73#include <nfs/nfs_clnt.h>
74#include <rpcsvc/nfs4_prot.h>
75#define	NO_RDDIR_CACHE
76#include "automount.h"
77#include "replica.h"
78#include "nfs_subr.h"
79#include "webnfs.h"
80#include <sys/sockio.h>
81#include <net/if.h>
82#include <assert.h>
83#include <rpcsvc/daemon_utils.h>
84
85extern char *nfs_get_qop_name();
86extern AUTH *nfs_create_ah();
87extern enum snego_stat nfs_sec_nego();
88
89#define	MAXHOSTS	512
90
91/* number of transports to try */
92#define	MNT_PREF_LISTLEN	2
93#define	FIRST_TRY		1
94#define	SECOND_TRY		2
95
96#define	MNTTYPE_CACHEFS "cachefs"
97
98/*
99 * host cache states
100 */
101#define	NOHOST		0
102#define	GOODHOST	1
103#define	DEADHOST	2
104
105#define	NFS_ARGS_EXTB_secdata(args, secdata) \
106	{ (args).nfs_args_ext = NFS_ARGS_EXTB, \
107	(args).nfs_ext_u.nfs_extB.secdata = secdata; }
108
109struct cache_entry {
110	struct	cache_entry *cache_next;
111	char	*cache_host;
112	time_t	cache_time;
113	int	cache_state;
114	rpcvers_t cache_reqvers;
115	rpcvers_t cache_outvers;
116	char	*cache_proto;
117};
118
119struct mfs_snego_t {
120	int sec_opt;
121	bool_t snego_done;
122	char *nfs_flavor;
123	seconfig_t nfs_sec;
124};
125typedef struct mfs_snego_t mfs_snego_t;
126
127static struct cache_entry *cache_head = NULL;
128rwlock_t cache_lock;	/* protect the cache chain */
129
130static enum nfsstat nfsmount(struct mapfs *, char *, char *, int, int,
131	struct authunix_parms *);
132static int is_nfs_port(char *);
133
134static void netbuf_free(struct netbuf *);
135static struct knetconfig *get_knconf(struct netconfig *);
136static void free_knconf(struct knetconfig *);
137static int get_pathconf(CLIENT *, char *, char *, struct pathcnf **, int);
138static struct mapfs *enum_servers(struct mapent *, char *);
139static struct mapfs *get_mysubnet_servers(struct mapfs *);
140static int subnet_test(int af, struct sioc_addrreq *);
141static	struct	netbuf *get_addr(char *, rpcprog_t, rpcvers_t,
142	struct netconfig **, char *, ushort_t, struct t_info *);
143
144static	struct	netbuf *get_pubfh(char *, rpcvers_t, mfs_snego_t *,
145	struct netconfig **, char *, ushort_t, struct t_info *, caddr_t *,
146	bool_t, char *);
147
148enum type_of_stuff {
149	SERVER_ADDR = 0,
150	SERVER_PING = 1,
151	SERVER_FH = 2
152};
153
154static	void *get_server_stuff(enum type_of_stuff, char *, rpcprog_t,
155	rpcvers_t, mfs_snego_t *, struct netconfig **, char *, ushort_t,
156	struct t_info *, caddr_t *, bool_t, char *, enum clnt_stat *);
157
158static	void *get_the_stuff(enum type_of_stuff, char *, rpcprog_t,
159	rpcvers_t, mfs_snego_t *, struct netconfig *, ushort_t, struct t_info *,
160	caddr_t *, bool_t, char *, enum clnt_stat *);
161
162struct mapfs *add_mfs(struct mapfs *, int, struct mapfs **, struct mapfs **);
163void free_mfs(struct mapfs *);
164static void dump_mfs(struct mapfs *, char *, int);
165static char *dump_distance(struct mapfs *);
166static void cache_free(struct cache_entry *);
167static int cache_check(char *, rpcvers_t *, char *);
168static void cache_enter(char *, rpcvers_t, rpcvers_t, char *, int);
169static void destroy_auth_client_handle(CLIENT *cl);
170
171#ifdef CACHE_DEBUG
172static void trace_host_cache();
173static void trace_portmap_cache();
174#endif /* CACHE_DEBUG */
175
176static int rpc_timeout = 20;
177
178#ifdef CACHE_DEBUG
179/*
180 * host cache counters. These variables do not need to be protected
181 * by mutex's. They have been added to measure the utility of the
182 * goodhost/deadhost cache in the lazy hierarchical mounting scheme.
183 */
184static int host_cache_accesses = 0;
185static int host_cache_lookups = 0;
186static int deadhost_cache_hits = 0;
187static int goodhost_cache_hits = 0;
188
189/*
190 * portmap cache counters. These variables do not need to be protected
191 * by mutex's. They have been added to measure the utility of the portmap
192 * cache in the lazy hierarchical mounting scheme.
193 */
194static int portmap_cache_accesses = 0;
195static int portmap_cache_lookups = 0;
196static int portmap_cache_hits = 0;
197#endif /* CACHE_DEBUG */
198
199/*
200 * There are the defaults (range) for the client when determining
201 * which NFS version to use when probing the server (see above).
202 * These will only be used when the vers mount option is not used and
203 * these may be reset if /etc/default/nfs is configured to do so.
204 */
205static rpcvers_t vers_max_default = NFS_VERSMAX_DEFAULT;
206static rpcvers_t vers_min_default = NFS_VERSMIN_DEFAULT;
207
208/*
209 * list of support services needed
210 */
211static char	*service_list[] = { STATD, LOCKD, NULL };
212static char	*service_list_v4[] = { STATD, LOCKD, NFS4CBD, NFSMAPID, NULL };
213
214static void read_default_nfs(void);
215static int is_v4_mount(char *);
216static void start_nfs4cbd(void);
217
218int
219mount_nfs(me, mntpnt, prevhost, overlay, cred)
220	struct mapent *me;
221	char *mntpnt;
222	char *prevhost;
223	int overlay;
224	struct authunix_parms *cred;
225{
226	struct mapfs *mfs, *mp;
227	int err = -1;
228	int cached;
229
230	read_default_nfs();
231
232	mfs = enum_servers(me, prevhost);
233	if (mfs == NULL)
234		return (ENOENT);
235
236	/*
237	 * Try loopback if we have something on localhost; if nothing
238	 * works, we will fall back to NFS
239	 */
240	if (is_nfs_port(me->map_mntopts)) {
241		for (mp = mfs; mp; mp = mp->mfs_next) {
242			if (self_check(mp->mfs_host)) {
243				err = loopbackmount(mp->mfs_dir,
244					mntpnt, me->map_mntopts, overlay);
245				if (err) {
246					mp->mfs_ignore = 1;
247				} else {
248					break;
249				}
250			}
251		}
252	}
253	if (err) {
254		cached = strcmp(me->map_mounter, MNTTYPE_CACHEFS) == 0;
255		err = nfsmount(mfs, mntpnt, me->map_mntopts,
256				cached, overlay, cred);
257		if (err && trace > 1) {
258			trace_prt(1, "	Couldn't mount %s:%s, err=%d\n",
259				mfs->mfs_host, mfs->mfs_dir, err);
260		}
261	}
262	free_mfs(mfs);
263	return (err);
264}
265
266
267/*
268 * Using the new ioctl SIOCTONLINK to determine if a host is on the same
269 * subnet. Remove the old network, subnet check.
270 */
271
272static struct mapfs *
273get_mysubnet_servers(struct mapfs *mfs_in)
274{
275	int s;
276	struct mapfs *mfs, *p, *mfs_head = NULL, *mfs_tail = NULL;
277
278	struct netconfig *nconf;
279	NCONF_HANDLE *nc = NULL;
280	struct nd_hostserv hs;
281	struct nd_addrlist *retaddrs;
282	struct netbuf *nb;
283	struct sioc_addrreq areq;
284	int res;
285	int af;
286	int i;
287	int sa_size;
288
289	hs.h_serv = "rpcbind";
290
291	for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
292		nc = setnetconfig();
293
294		while (nconf = getnetconfig(nc)) {
295
296			/*
297			 * Care about INET family only. proto_done flag
298			 * indicates if we have already covered this
299			 * protocol family. If so skip it
300			 */
301			if (((strcmp(nconf->nc_protofmly, NC_INET6) == 0) ||
302				(strcmp(nconf->nc_protofmly, NC_INET) == 0)) &&
303				(nconf->nc_semantics == NC_TPI_CLTS)) {
304			} else
305				continue;
306
307			hs.h_host = mfs->mfs_host;
308
309			if (netdir_getbyname(nconf, &hs, &retaddrs) != ND_OK)
310				continue;
311
312			/*
313			 * For each host address see if it's on our
314			 * local subnet.
315			 */
316
317			if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
318				af = AF_INET6;
319			else
320				af = AF_INET;
321			nb = retaddrs->n_addrs;
322			for (i = 0; i < retaddrs->n_cnt; i++, nb++) {
323				memset(&areq.sa_addr, 0, sizeof (areq.sa_addr));
324				memcpy(&areq.sa_addr, nb->buf, MIN(nb->len,
325					sizeof (areq.sa_addr)));
326				if (res = subnet_test(af, &areq)) {
327					p = add_mfs(mfs, DIST_MYNET,
328						&mfs_head, &mfs_tail);
329					if (!p) {
330						netdir_free(retaddrs,
331							ND_ADDRLIST);
332						endnetconfig(nc);
333						return (NULL);
334					}
335					break;
336				}
337			}  /* end of every host */
338			if (trace > 2) {
339				trace_prt(1, "get_mysubnet_servers: host=%s "
340					"netid=%s res=%s\n", mfs->mfs_host,
341					nconf->nc_netid, res == 1?"SUC":"FAIL");
342			}
343
344			netdir_free(retaddrs, ND_ADDRLIST);
345		} /* end of while */
346
347		endnetconfig(nc);
348
349	} /* end of every map */
350
351	return (mfs_head);
352
353}
354
355int
356subnet_test(int af, struct sioc_addrreq *areq)
357{
358	int s;
359
360	if ((s = socket(af, SOCK_DGRAM, 0)) < 0) {
361		return (0);
362	}
363
364	areq->sa_res = -1;
365
366	if (ioctl(s, SIOCTONLINK, (caddr_t)areq) < 0) {
367		syslog(LOG_ERR, "subnet_test:SIOCTONLINK failed");
368		return (0);
369	}
370	close(s);
371	if (areq->sa_res == 1)
372		return (1);
373	else
374		return (0);
375
376
377}
378
379/*
380 * ping a bunch of hosts at once and sort by who responds first
381 */
382static struct mapfs *
383sort_servers(struct mapfs *mfs_in, int timeout)
384{
385	struct mapfs *m1 = NULL;
386	enum clnt_stat clnt_stat;
387
388	if (!mfs_in)
389		return (NULL);
390
391	clnt_stat = nfs_cast(mfs_in, &m1, timeout);
392
393	if (!m1) {
394		char buff[2048] = {'\0'};
395
396		for (m1 = mfs_in; m1; m1 = m1->mfs_next) {
397			(void) strcat(buff, m1->mfs_host);
398			if (m1->mfs_next)
399				(void) strcat(buff, ",");
400		}
401
402		syslog(LOG_ERR, "servers %s not responding: %s",
403			buff, clnt_sperrno(clnt_stat));
404	}
405
406	return (m1);
407}
408
409/*
410 * Add a mapfs entry to the list described by *mfs_head and *mfs_tail,
411 * provided it is not marked "ignored" and isn't a dupe of ones we've
412 * already seen.
413 */
414struct mapfs *
415add_mfs(struct mapfs *mfs, int distance, struct mapfs **mfs_head,
416	struct mapfs **mfs_tail)
417{
418	struct mapfs *tmp, *new;
419	void bcopy();
420
421	for (tmp = *mfs_head; tmp; tmp = tmp->mfs_next)
422		if ((strcmp(tmp->mfs_host, mfs->mfs_host) == 0 &&
423		    strcmp(tmp->mfs_dir, mfs->mfs_dir) == 0) ||
424			mfs->mfs_ignore)
425			return (*mfs_head);
426	new = (struct mapfs *)malloc(sizeof (struct mapfs));
427	if (!new) {
428		syslog(LOG_ERR, "Memory allocation failed: %m");
429		return (NULL);
430	}
431	bcopy(mfs, new, sizeof (struct mapfs));
432	new->mfs_next = NULL;
433	if (distance)
434		new->mfs_distance = distance;
435	if (!*mfs_head)
436		*mfs_tail = *mfs_head = new;
437	else {
438		(*mfs_tail)->mfs_next = new;
439		*mfs_tail = new;
440	}
441	return (*mfs_head);
442}
443
444static void
445dump_mfs(struct mapfs *mfs, char *message, int level)
446{
447	struct mapfs *m1;
448
449	if (trace <= level)
450		return;
451
452	trace_prt(1, "%s", message);
453	if (!mfs) {
454		trace_prt(0, "mfs is null\n");
455		return;
456	}
457	for (m1 = mfs; m1; m1 = m1->mfs_next)
458		trace_prt(0, "%s[%s] ", m1->mfs_host, dump_distance(m1));
459	trace_prt(0, "\n");
460}
461
462static char *
463dump_distance(struct mapfs *mfs)
464{
465	switch (mfs->mfs_distance) {
466	case 0:			return ("zero");
467	case DIST_SELF:		return ("self");
468	case DIST_MYSUB:	return ("mysub");
469	case DIST_MYNET:	return ("mynet");
470	case DIST_OTHER:	return ("other");
471	default:		return ("other");
472	}
473}
474
475/*
476 * Walk linked list "raw", building a new list consisting of members
477 * NOT found in list "filter", returning the result.
478 */
479static struct mapfs *
480filter_mfs(struct mapfs *raw, struct mapfs *filter)
481{
482	struct mapfs *mfs, *p, *mfs_head = NULL, *mfs_tail = NULL;
483	int skip;
484
485	if (!raw)
486		return (NULL);
487	for (mfs = raw; mfs; mfs = mfs->mfs_next) {
488		for (skip = 0, p = filter; p; p = p->mfs_next) {
489			if (strcmp(p->mfs_host, mfs->mfs_host) == 0 &&
490			    strcmp(p->mfs_dir, mfs->mfs_dir) == 0) {
491				skip = 1;
492				break;
493			}
494		}
495		if (skip)
496			continue;
497		p = add_mfs(mfs, 0, &mfs_head, &mfs_tail);
498		if (!p)
499			return (NULL);
500	}
501	return (mfs_head);
502}
503
504/*
505 * Walk a linked list of mapfs structs, freeing each member.
506 */
507void
508free_mfs(struct mapfs *mfs)
509{
510	struct mapfs *tmp;
511
512	while (mfs) {
513		tmp = mfs->mfs_next;
514		free(mfs);
515		mfs = tmp;
516	}
517}
518
519/*
520 * New code for NFS client failover: we need to carry and sort
521 * lists of server possibilities rather than return a single
522 * entry.  It preserves previous behaviour of sorting first by
523 * locality (loopback-or-preferred/subnet/net/other) and then
524 * by ping times.  We'll short-circuit this process when we
525 * have ENOUGH or more entries.
526 */
527static struct mapfs *
528enum_servers(struct mapent *me, char *preferred)
529{
530	struct mapfs *p, *m1, *m2, *mfs_head = NULL, *mfs_tail = NULL;
531
532	/*
533	 * Short-circuit for simple cases.
534	 */
535	if (!me->map_fs->mfs_next) {
536		p = add_mfs(me->map_fs, DIST_OTHER, &mfs_head, &mfs_tail);
537		if (!p)
538			return (NULL);
539		return (mfs_head);
540	}
541
542	dump_mfs(me->map_fs, "	enum_servers: mapent: ", 2);
543
544	/*
545	 * get addresses & see if any are myself
546	 * or were mounted from previously in a
547	 * hierarchical mount.
548	 */
549	if (trace > 2)
550		trace_prt(1, "	enum_servers: looking for pref/self\n");
551	for (m1 = me->map_fs; m1; m1 = m1->mfs_next) {
552		if (m1->mfs_ignore)
553			continue;
554		if (self_check(m1->mfs_host) ||
555		    strcmp(m1->mfs_host, preferred) == 0) {
556			p = add_mfs(m1, DIST_SELF, &mfs_head, &mfs_tail);
557			if (!p)
558				return (NULL);
559		}
560	}
561	if (trace > 2 && m1)
562		trace_prt(1, "	enum_servers: pref/self found, %s\n",
563			m1->mfs_host);
564
565	/*
566	 * look for entries on this subnet
567	 */
568	dump_mfs(m1, "	enum_servers: input of get_mysubnet_servers: ", 2);
569	m1 = get_mysubnet_servers(me->map_fs);
570	dump_mfs(m1, "	enum_servers: output of get_mysubnet_servers: ", 3);
571	if (m1 && m1->mfs_next) {
572		m2 = sort_servers(m1, rpc_timeout / 2);
573		dump_mfs(m2, "	enum_servers: output of sort_servers: ", 3);
574		free_mfs(m1);
575		m1 = m2;
576	}
577
578	for (m2 = m1; m2; m2 = m2->mfs_next) {
579		p = add_mfs(m2, 0, &mfs_head, &mfs_tail);
580		if (!p)
581			return (NULL);
582	}
583	if (m1)
584		free_mfs(m1);
585
586	/*
587	 * add the rest of the entries at the end
588	 */
589	m1 = filter_mfs(me->map_fs, mfs_head);
590	dump_mfs(m1, "	enum_servers: etc: output of filter_mfs: ", 3);
591	m2 = sort_servers(m1, rpc_timeout / 2);
592	dump_mfs(m2, "	enum_servers: etc: output of sort_servers: ", 3);
593	if (m1)
594		free_mfs(m1);
595	m1 = m2;
596	for (m2 = m1; m2; m2 = m2->mfs_next) {
597		p = add_mfs(m2, DIST_OTHER, &mfs_head, &mfs_tail);
598		if (!p)
599			return (NULL);
600	}
601	if (m1)
602		free_mfs(m1);
603
604done:
605	dump_mfs(mfs_head, "  enum_servers: output: ", 1);
606	return (mfs_head);
607}
608
609static enum nfsstat
610nfsmount(mfs_in, mntpnt, opts, cached, overlay, cred)
611	struct mapfs *mfs_in;
612	char *mntpnt, *opts;
613	int cached, overlay;
614	struct authunix_parms *cred;
615{
616	CLIENT *cl;
617	char remname[MAXPATHLEN], *mnttabtext = NULL;
618	char mopts[MAX_MNTOPT_STR];
619	char netname[MAXNETNAMELEN+1];
620	int mnttabcnt = 0;
621	int loglevel;
622	struct mnttab m;
623	struct nfs_args *argp = NULL, *head = NULL, *tail = NULL,
624		*prevhead, *prevtail;
625	int flags;
626	struct fhstatus fhs;
627	struct timeval timeout;
628	enum clnt_stat rpc_stat;
629	enum nfsstat status;
630	struct stat stbuf;
631	struct netconfig *nconf;
632	rpcvers_t vers, versmin; /* used to negotiate nfs version in pingnfs */
633				/* and mount version with mountd */
634	rpcvers_t outvers;	/* final version to be used during mount() */
635	rpcvers_t nfsvers;	/* version in map options, 0 if not there */
636	rpcvers_t mountversmax;	/* tracks the max mountvers during retries */
637
638	/* used to negotiate nfs version using webnfs */
639	rpcvers_t pubvers, pubversmin, pubversmax;
640	int posix;
641	struct nd_addrlist *retaddrs;
642	struct mountres3 res3;
643	nfs_fh3 fh3;
644	char *fstype;
645	int count, i;
646	char scerror_msg[MAXMSGLEN];
647	int *auths;
648	int delay;
649	int retries;
650	char *nfs_proto = NULL;
651	uint_t nfs_port = 0;
652	char *p, *host, *dir;
653	struct mapfs *mfs = NULL;
654	int error, last_error = 0;
655	int replicated;
656	int entries = 0;
657	int v2cnt = 0, v3cnt = 0, v4cnt = 0;
658	int v2near = 0, v3near = 0, v4near = 0;
659	int skipentry = 0;
660	char *nfs_flavor;
661	seconfig_t nfs_sec;
662	int sec_opt, scerror;
663	struct sec_data *secdata;
664	int secflags;
665	struct netbuf *syncaddr;
666	bool_t	use_pubfh;
667	ushort_t thisport;
668	int got_val;
669	mfs_snego_t mfssnego_init, mfssnego;
670
671	dump_mfs(mfs_in, "  nfsmount: input: ", 2);
672	replicated = (mfs_in->mfs_next != NULL);
673	m.mnt_mntopts = opts;
674	if (replicated && hasmntopt(&m, MNTOPT_SOFT)) {
675		if (verbose)
676			syslog(LOG_WARNING,
677		    "mount on %s is soft and will not be replicated.", mntpnt);
678		replicated = 0;
679	}
680	if (replicated && !hasmntopt(&m, MNTOPT_RO)) {
681		if (verbose)
682			syslog(LOG_WARNING,
683		    "mount on %s is not read-only and will not be replicated.",
684			    mntpnt);
685		replicated = 0;
686	}
687	if (replicated && cached) {
688		if (verbose)
689			syslog(LOG_WARNING,
690		    "mount on %s is cached and will not be replicated.",
691			mntpnt);
692		replicated = 0;
693	}
694	if (replicated)
695		loglevel = LOG_WARNING;
696	else
697		loglevel = LOG_ERR;
698
699	if (trace > 1) {
700		if (replicated)
701			trace_prt(1, "	nfsmount: replicated mount on %s %s:\n",
702				mntpnt, opts);
703		else
704			trace_prt(1, "	nfsmount: standard mount on %s %s:\n",
705				mntpnt, opts);
706		for (mfs = mfs_in; mfs; mfs = mfs->mfs_next)
707			trace_prt(1, "	  %s:%s\n",
708				mfs->mfs_host, mfs->mfs_dir);
709	}
710
711	/*
712	 * Make sure mountpoint is safe to mount on
713	 */
714	if (lstat(mntpnt, &stbuf) < 0) {
715		syslog(LOG_ERR, "Couldn't stat %s: %m", mntpnt);
716		return (NFSERR_NOENT);
717	}
718
719	/*
720	 * Get protocol specified in options list, if any.
721	 */
722	if ((str_opt(&m, "proto", &nfs_proto)) == -1) {
723		return (NFSERR_NOENT);
724	}
725
726	/*
727	 * Get port specified in options list, if any.
728	 */
729	got_val = nopt(&m, MNTOPT_PORT, (int *)&nfs_port);
730	if (!got_val)
731		nfs_port = 0;	/* "unspecified" */
732	if (nfs_port > USHRT_MAX) {
733		syslog(LOG_ERR, "%s: invalid port number %d", mntpnt, nfs_port);
734		return (NFSERR_NOENT);
735	}
736
737	/*
738	 * Set mount(2) flags here, outside of the loop.
739	 */
740	flags = MS_OPTIONSTR;
741	flags |= (hasmntopt(&m, MNTOPT_RO) == NULL) ? 0 : MS_RDONLY;
742	flags |= (hasmntopt(&m, MNTOPT_NOSUID) == NULL) ? 0 : MS_NOSUID;
743	flags |= overlay ? MS_OVERLAY : 0;
744	if (mntpnt[strlen(mntpnt) - 1] != ' ')
745		/* direct mount point without offsets */
746		flags |= MS_OVERLAY;
747
748	use_pubfh = (hasmntopt(&m, MNTOPT_PUBLIC) == NULL) ? FALSE : TRUE;
749
750	(void) memset(&mfssnego_init, 0, sizeof (mfs_snego_t));
751	if (hasmntopt(&m, MNTOPT_SECURE) != NULL) {
752		if (++mfssnego_init.sec_opt > 1) {
753			syslog(loglevel,
754			    "conflicting security options");
755			return (NFSERR_IO);
756		}
757		if (nfs_getseconfig_byname("dh", &mfssnego_init.nfs_sec)) {
758			syslog(loglevel,
759			    "error getting dh information from %s",
760			    NFSSEC_CONF);
761			return (NFSERR_IO);
762		}
763	}
764
765	/*
766	 * Have to workaround the fact that hasmntopt() returns true
767	 * when comparing "secure" (in &m) with "sec".
768	 */
769	if (hasmntopt(&m, "sec=") != NULL) {
770		if ((str_opt(&m, MNTOPT_SEC,
771			&mfssnego_init.nfs_flavor)) == -1) {
772			syslog(LOG_ERR, "nfsmount: no memory");
773			return (NFSERR_IO);
774		}
775	}
776
777	if (mfssnego_init.nfs_flavor) {
778		if (++mfssnego_init.sec_opt > 1) {
779			syslog(loglevel,
780			    "conflicting security options");
781			free(mfssnego_init.nfs_flavor);
782			return (NFSERR_IO);
783		}
784		if (nfs_getseconfig_byname(mfssnego_init.nfs_flavor,
785			&mfssnego_init.nfs_sec)) {
786			syslog(loglevel,
787			    "error getting %s information from %s",
788			    mfssnego_init.nfs_flavor, NFSSEC_CONF);
789			free(mfssnego_init.nfs_flavor);
790			return (NFSERR_IO);
791		}
792		free(mfssnego_init.nfs_flavor);
793	}
794
795nextentry:
796	skipentry = 0;
797
798	got_val = nopt(&m, MNTOPT_VERS, (int *)&nfsvers);
799	if (!got_val)
800		nfsvers = 0;	/* "unspecified" */
801	if (set_versrange(nfsvers, &vers, &versmin) != 0) {
802		syslog(LOG_ERR, "Incorrect NFS version specified for %s",
803			mntpnt);
804		last_error = NFSERR_NOENT;
805		goto ret;
806	}
807
808	if (nfsvers != 0) {
809		pubversmax = pubversmin = nfsvers;
810	} else {
811		pubversmax = vers;
812		pubversmin = versmin;
813	}
814
815	/*
816	 * Walk the whole list, pinging and collecting version
817	 * info so that we can make sure the mount will be
818	 * homogeneous with respect to version.
819	 *
820	 * If we have a version preference, this is easy; we'll
821	 * just reject anything that doesn't match.
822	 *
823	 * If not, we want to try to provide the best compromise
824	 * that considers proximity, preference for a higher version,
825	 * sorted order, and number of replicas.  We will count
826	 * the number of V2 and V3 replicas and also the number
827	 * which are "near", i.e. the localhost or on the same
828	 * subnet.
829	 */
830	for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
831
832
833		if (mfs->mfs_ignore)
834			continue;
835
836		host = mfs->mfs_host;
837		(void) memcpy(&mfssnego, &mfssnego_init, sizeof (mfs_snego_t));
838
839		if (use_pubfh == TRUE || mfs->mfs_flags & MFS_URL) {
840			char *path;
841
842			if (nfs_port != 0 && mfs->mfs_port != 0 &&
843			    nfs_port != mfs->mfs_port) {
844
845				syslog(LOG_ERR, "nfsmount: port (%u) in nfs URL"
846					" not the same as port (%d) in port "
847					"option\n", mfs->mfs_port, nfs_port);
848				last_error = NFSERR_IO;
849				goto out;
850
851			} else if (nfs_port != 0)
852				thisport = nfs_port;
853			else
854				thisport = mfs->mfs_port;
855
856			dir = mfs->mfs_dir;
857
858			if ((mfs->mfs_flags & MFS_URL) == 0) {
859				path = malloc(strlen(dir) + 2);
860				if (path == NULL) {
861					syslog(LOG_ERR, "nfsmount: no memory");
862					last_error = NFSERR_IO;
863					goto out;
864				}
865				path[0] = (char)WNL_NATIVEPATH;
866				(void) strcpy(&path[1], dir);
867			} else {
868				path = dir;
869			}
870
871			argp = (struct nfs_args *)
872				malloc(sizeof (struct nfs_args));
873
874			if (!argp) {
875				if (path != dir)
876					free(path);
877				syslog(LOG_ERR, "nfsmount: no memory");
878				last_error = NFSERR_IO;
879				goto out;
880			}
881			(void) memset(argp, 0, sizeof (*argp));
882
883			/*
884			 * RDMA support
885			 * By now Mount argument struct has been allocated,
886			 * either a pub_fh path will be taken or the regular
887			 * one. So here if a protocol was specified and it
888			 * was not rdma we let it be, else we set DO_RDMA.
889			 * If no proto was there we advise on trying RDMA.
890			 */
891			if (nfs_proto) {
892				if (strcmp(nfs_proto, "rdma") == 0) {
893					free(nfs_proto);
894					nfs_proto = NULL;
895					argp->flags |= NFSMNT_DORDMA;
896				}
897			} else
898				argp->flags |= NFSMNT_TRYRDMA;
899
900			for (pubvers = pubversmax; pubvers >= pubversmin;
901			    pubvers--) {
902
903				nconf = NULL;
904				argp->addr = get_pubfh(host, pubvers, &mfssnego,
905				    &nconf, nfs_proto, thisport, NULL,
906				    &argp->fh, TRUE, path);
907
908				if (argp->addr != NULL)
909					break;
910
911				if (nconf != NULL)
912					freenetconfigent(nconf);
913			}
914
915			if (path != dir)
916				free(path);
917
918			if (argp->addr != NULL) {
919
920				/*
921				 * The use of llock option for NFSv4
922				 * mounts is not required since file
923				 * locking is included within the protocol
924				 */
925				if (pubvers != NFS_V4)
926					argp->flags |= NFSMNT_LLOCK;
927
928				argp->flags |= NFSMNT_PUBLIC;
929
930				mfs->mfs_args = argp;
931				mfs->mfs_version = pubvers;
932				mfs->mfs_nconf = nconf;
933				mfs->mfs_flags |= MFS_FH_VIA_WEBNFS;
934
935			} else {
936				free(argp);
937
938				/*
939				 * If -public was specified, give up
940				 * on this entry now.
941				 */
942				if (use_pubfh == TRUE) {
943					syslog(loglevel,
944					    "%s: no public file handle support",
945					    host);
946					last_error = NFSERR_NOENT;
947					mfs->mfs_ignore = 1;
948					continue;
949				}
950
951				/*
952				 * Back off to a conventional mount.
953				 *
954				 * URL's can contain escape characters. Get
955				 * rid of them.
956				 */
957				path = malloc(strlen(dir) + 2);
958
959				if (path == NULL) {
960					syslog(LOG_ERR, "nfsmount: no memory");
961					last_error = NFSERR_IO;
962					goto out;
963				}
964
965				strcpy(path, dir);
966				URLparse(path);
967				mfs->mfs_dir = path;
968				mfs->mfs_flags |= MFS_ALLOC_DIR;
969				mfs->mfs_flags &= ~MFS_URL;
970			}
971		}
972
973		if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) ==  0) {
974			i = pingnfs(host, get_retry(opts) + 1, &vers, versmin,
975				0, FALSE, NULL, nfs_proto);
976			if (i != RPC_SUCCESS) {
977				if (i == RPC_PROGVERSMISMATCH) {
978					syslog(loglevel, "server %s: NFS "
979						"protocol version mismatch",
980						host);
981				} else {
982					syslog(loglevel, "server %s not "
983						"responding", host);
984				}
985				mfs->mfs_ignore = 1;
986				last_error = NFSERR_NOENT;
987				continue;
988			}
989			if (nfsvers != 0 && nfsvers != vers) {
990				if (nfs_proto == NULL)
991					syslog(loglevel,
992						"NFS version %d "
993						"not supported by %s",
994						nfsvers, host);
995				else
996					syslog(loglevel,
997						"NFS version %d "
998						"with proto %s "
999						"not supported by %s",
1000						nfsvers, nfs_proto, host);
1001				mfs->mfs_ignore = 1;
1002				last_error = NFSERR_NOENT;
1003				continue;
1004			}
1005		}
1006
1007		switch (vers) {
1008		case NFS_V4: v4cnt++; break;
1009		case NFS_V3: v3cnt++; break;
1010		case NFS_VERSION: v2cnt++; break;
1011		default: break;
1012		}
1013
1014		/*
1015		 * It's not clear how useful this stuff is if
1016		 * we are using webnfs across the internet, but it
1017		 * can't hurt.
1018		 */
1019		if (mfs->mfs_distance &&
1020		    mfs->mfs_distance <= DIST_MYSUB) {
1021			switch (vers) {
1022			case NFS_V4: v4near++; break;
1023			case NFS_V3: v3near++; break;
1024			case NFS_VERSION: v2near++; break;
1025			default: break;
1026			}
1027		}
1028
1029		/*
1030		 * If the mount is not replicated, we don't want to
1031		 * ping every entry, so we'll stop here.  This means
1032		 * that we may have to go back to "nextentry" above
1033		 * to consider another entry if there we can't get
1034		 * all the way to mount(2) with this one.
1035		 */
1036		if (!replicated)
1037			break;
1038	}
1039
1040	if (nfsvers == 0) {
1041		/*
1042		 * Choose the NFS version.
1043		 * We prefer higher versions, but will choose a one-
1044		 * version downgrade in service if we can use a local
1045		 * network interface and avoid a router.
1046		 */
1047		if (v4cnt && v4cnt >= v3cnt && (v4near || !v3near))
1048			nfsvers = NFS_V4;
1049		else if (v3cnt && v3cnt >= v2cnt && (v3near || !v2near))
1050			nfsvers = NFS_V3;
1051		else
1052			nfsvers = NFS_VERSION;
1053		if (trace > 2)
1054			trace_prt(1,
1055		    "  nfsmount: v4=%d[%d]v3=%d[%d],v2=%d[%d] => v%d.\n",
1056			    v4cnt, v4near, v3cnt, v3near,
1057			    v2cnt, v2near, nfsvers);
1058	}
1059
1060	/*
1061	 * Since we don't support different NFS versions in replicated
1062	 * mounts, set fstype now.
1063	 * Also take the opportunity to set
1064	 * the mount protocol version as appropriate.
1065	 */
1066	switch (nfsvers) {
1067	case NFS_V4:
1068		fstype = MNTTYPE_NFS4;
1069		break;
1070	case NFS_V3:
1071		fstype = MNTTYPE_NFS3;
1072		if (use_pubfh == FALSE) {
1073			mountversmax = MOUNTVERS3;
1074			versmin = MOUNTVERS3;
1075		}
1076		break;
1077	case NFS_VERSION:
1078		fstype = MNTTYPE_NFS;
1079		if (use_pubfh == FALSE) {
1080			mountversmax = MOUNTVERS_POSIX;
1081			versmin = MOUNTVERS;
1082		}
1083		break;
1084	}
1085
1086	/*
1087	 * Our goal here is to evaluate each of several possible
1088	 * replicas and try to come up with a list we can hand
1089	 * to mount(2).  If we don't have a valid "head" at the
1090	 * end of this process, it means we have rejected all
1091	 * potential server:/path tuples.  We will fail quietly
1092	 * in front of mount(2), and will have printed errors
1093	 * where we found them.
1094	 * XXX - do option work outside loop w careful design
1095	 * XXX - use macro for error condition free handling
1096	 */
1097	for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
1098
1099		/*
1100		 * Initialize retry and delay values on a per-server basis.
1101		 */
1102		retries = get_retry(opts);
1103		delay = INITDELAY;
1104retry:
1105		if (mfs->mfs_ignore)
1106			continue;
1107
1108		/*
1109		 * If we don't have a fh yet, and if this is not a replicated
1110		 * mount, we haven't done a pingnfs() on the next entry,
1111		 * so we don't know if the next entry is up or if it
1112		 * supports an NFS version we like.  So if we had a problem
1113		 * with an entry, we need to go back and run through some new
1114		 * code.
1115		 */
1116		if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
1117		    !replicated && skipentry)
1118			goto nextentry;
1119
1120		vers = mountversmax;
1121		host = mfs->mfs_host;
1122		dir = mfs->mfs_dir;
1123		(void) sprintf(remname, "%s:%s", host, dir);
1124		if (trace > 4 && replicated)
1125			trace_prt(1, "	nfsmount: examining %s\n", remname);
1126
1127		/*
1128		 * If it's cached we need to get cachefs to mount it.
1129		 */
1130		if (cached) {
1131			char *copts = opts;
1132
1133			/*
1134			 * If we started with a URL we need to turn on
1135			 * -o public if not on already
1136			 */
1137			if (use_pubfh == FALSE &&
1138			    (mfs->mfs_flags & MFS_FH_VIA_WEBNFS)) {
1139
1140				copts = malloc(strlen(opts) +
1141					strlen(",public")+1);
1142
1143				if (copts == NULL) {
1144					syslog(LOG_ERR, "nfsmount: no memory");
1145					last_error = NFSERR_IO;
1146					goto out;
1147				}
1148
1149				strcpy(copts, opts);
1150
1151				if (strlen(copts) != 0)
1152					strcat(copts, ",");
1153
1154				strcat(copts, "public");
1155			}
1156
1157			last_error = mount_generic(remname, MNTTYPE_CACHEFS,
1158				copts, mntpnt, overlay);
1159
1160			if (copts != opts)
1161				free(copts);
1162
1163			if (last_error) {
1164				skipentry = 1;
1165				mfs->mfs_ignore = 1;
1166				continue;
1167			}
1168			goto out;
1169		}
1170
1171		if (mfs->mfs_args == NULL) {
1172
1173			/*
1174			 * Allocate nfs_args structure
1175			 */
1176			argp = (struct nfs_args *)
1177				malloc(sizeof (struct nfs_args));
1178
1179			if (!argp) {
1180				syslog(LOG_ERR, "nfsmount: no memory");
1181				last_error = NFSERR_IO;
1182				goto out;
1183			}
1184
1185			(void) memset(argp, 0, sizeof (*argp));
1186
1187			/*
1188			 * RDMA support
1189			 * By now Mount argument struct has been allocated,
1190			 * either a pub_fh path will be taken or the regular
1191			 * one. So here if a protocol was specified and it
1192			 * was not rdma we let it be, else we set DO_RDMA.
1193			 * If no proto was there we advise on trying RDMA.
1194			 */
1195			if (nfs_proto) {
1196				if (strcmp(nfs_proto, "rdma") == 0) {
1197					free(nfs_proto);
1198					nfs_proto = NULL;
1199					argp->flags |= NFSMNT_DORDMA;
1200				}
1201			} else
1202				argp->flags |= NFSMNT_TRYRDMA;
1203		} else {
1204			argp = mfs->mfs_args;
1205			mfs->mfs_args = NULL;
1206
1207			/*
1208			 * Skip entry if we already have file handle but the
1209			 * NFS version is wrong.
1210			 */
1211			if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) &&
1212			    mfs->mfs_version != nfsvers) {
1213
1214				free(argp);
1215				skipentry = 1;
1216				mfs->mfs_ignore = 1;
1217				continue;
1218			}
1219		}
1220
1221		prevhead = head;
1222		prevtail = tail;
1223		if (!head)
1224			head = tail = argp;
1225		else
1226			tail = tail->nfs_ext_u.nfs_extB.next = argp;
1227
1228		/*
1229		 * WebNFS and NFSv4 behave similarly in that they
1230		 * don't use the mount protocol.  Therefore, avoid
1231		 * mount protocol like things when version 4 is being
1232		 * used.
1233		 */
1234		if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
1235			nfsvers != NFS_V4) {
1236		    timeout.tv_usec = 0;
1237		    timeout.tv_sec = rpc_timeout;
1238		    rpc_stat = RPC_TIMEDOUT;
1239
1240		    /* Create the client handle. */
1241
1242		    if (trace > 1) {
1243			trace_prt(1, "  nfsmount: Get mount version: request "
1244			    "vers=%d min=%d\n", vers, versmin);
1245		    }
1246
1247		    while ((cl = clnt_create_vers(host, MOUNTPROG, &outvers,
1248			versmin, vers, "udp")) == NULL) {
1249			if (trace > 4) {
1250			    trace_prt(1,
1251			    "  nfsmount: Can't get mount version: rpcerr=%d\n",
1252			    rpc_createerr.cf_stat);
1253			}
1254			if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST ||
1255			    rpc_createerr.cf_stat == RPC_TIMEDOUT)
1256				break;
1257
1258			/*
1259			 * backoff and return lower version to retry the ping.
1260			 * XXX we should be more careful and handle
1261			 * RPC_PROGVERSMISMATCH here, because that error
1262			 * is handled in clnt_create_vers(). It's not done to
1263			 * stay in sync with the nfs mount command.
1264			 */
1265			vers--;
1266			if (vers < versmin)
1267				break;
1268			if (trace > 4) {
1269			    trace_prt(1, "  nfsmount: Try version=%d\n", vers);
1270			}
1271		    }
1272
1273		    if (cl == NULL) {
1274			free(argp);
1275			head = prevhead;
1276			tail = prevtail;
1277			if (tail)
1278				tail->nfs_ext_u.nfs_extB.next = NULL;
1279			last_error = NFSERR_NOENT;
1280
1281			if (rpc_createerr.cf_stat != RPC_UNKNOWNHOST &&
1282			    rpc_createerr.cf_stat != RPC_PROGVERSMISMATCH &&
1283			    retries-- > 0) {
1284				DELAY(delay)
1285				goto retry;
1286			}
1287
1288			syslog(loglevel, "%s %s", host,
1289				clnt_spcreateerror("server not responding"));
1290			skipentry = 1;
1291			mfs->mfs_ignore = 1;
1292			continue;
1293		    }
1294		    if (trace > 1) {
1295			trace_prt(1, "	nfsmount: mount version=%d\n", outvers);
1296		    }
1297#ifdef MALLOC_DEBUG
1298		    add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__);
1299		    add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
1300			__FILE__, __LINE__);
1301#endif
1302
1303		    if (__clnt_bindresvport(cl) < 0) {
1304			free(argp);
1305			head = prevhead;
1306			tail = prevtail;
1307			if (tail)
1308				tail->nfs_ext_u.nfs_extB.next = NULL;
1309			last_error = NFSERR_NOENT;
1310
1311			if (retries-- > 0) {
1312				destroy_auth_client_handle(cl);
1313				DELAY(delay);
1314				goto retry;
1315			}
1316
1317			syslog(loglevel, "mount %s: %s", host,
1318				"Couldn't bind to reserved port");
1319			destroy_auth_client_handle(cl);
1320			skipentry = 1;
1321			mfs->mfs_ignore = 1;
1322			continue;
1323		    }
1324
1325#ifdef MALLOC_DEBUG
1326		    drop_alloc("AUTH_HANDLE", cl->cl_auth, __FILE__, __LINE__);
1327#endif
1328		    AUTH_DESTROY(cl->cl_auth);
1329		    if ((cl->cl_auth = authsys_create_default()) == NULL) {
1330			free(argp);
1331			head = prevhead;
1332			tail = prevtail;
1333			if (tail)
1334				tail->nfs_ext_u.nfs_extB.next = NULL;
1335			last_error = NFSERR_NOENT;
1336
1337			if (retries-- > 0) {
1338				destroy_auth_client_handle(cl);
1339				DELAY(delay);
1340				goto retry;
1341			}
1342
1343			syslog(loglevel, "mount %s: %s", host,
1344				"Failed creating default auth handle");
1345			destroy_auth_client_handle(cl);
1346			skipentry = 1;
1347			mfs->mfs_ignore = 1;
1348			continue;
1349		    }
1350#ifdef MALLOC_DEBUG
1351		    add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
1352			__FILE__, __LINE__);
1353#endif
1354		} else
1355		    cl = NULL;
1356
1357		/*
1358		 * set security options
1359		 */
1360		sec_opt = 0;
1361		(void) memset(&nfs_sec, 0, sizeof (nfs_sec));
1362		if (hasmntopt(&m, MNTOPT_SECURE) != NULL) {
1363			if (++sec_opt > 1) {
1364				syslog(loglevel,
1365				    "conflicting security options for %s",
1366				    remname);
1367				free(argp);
1368				head = prevhead;
1369				tail = prevtail;
1370				if (tail)
1371					tail->nfs_ext_u.nfs_extB.next = NULL;
1372				last_error = NFSERR_IO;
1373				destroy_auth_client_handle(cl);
1374				skipentry = 1;
1375				mfs->mfs_ignore = 1;
1376				continue;
1377			}
1378			if (nfs_getseconfig_byname("dh", &nfs_sec)) {
1379				syslog(loglevel,
1380				    "error getting dh information from %s",
1381				    NFSSEC_CONF);
1382				free(argp);
1383				head = prevhead;
1384				tail = prevtail;
1385				if (tail)
1386					tail->nfs_ext_u.nfs_extB.next = NULL;
1387				last_error = NFSERR_IO;
1388				destroy_auth_client_handle(cl);
1389				skipentry = 1;
1390				mfs->mfs_ignore = 1;
1391				continue;
1392			}
1393		}
1394
1395		nfs_flavor = NULL;
1396		/*
1397		 * Have to workaround the fact that hasmntopt() returns true
1398		 * when comparing "secure" (in &m) with "sec".
1399		 */
1400		if (hasmntopt(&m, "sec=") != NULL) {
1401			if ((str_opt(&m, MNTOPT_SEC, &nfs_flavor)) == -1) {
1402				syslog(LOG_ERR, "nfsmount: no memory");
1403				last_error = NFSERR_IO;
1404				destroy_auth_client_handle(cl);
1405				goto out;
1406			}
1407		}
1408
1409		if (nfs_flavor) {
1410			if (++sec_opt > 1) {
1411				syslog(loglevel,
1412				    "conflicting security options for %s",
1413				    remname);
1414				free(nfs_flavor);
1415				free(argp);
1416				head = prevhead;
1417				tail = prevtail;
1418				if (tail)
1419					tail->nfs_ext_u.nfs_extB.next = NULL;
1420				last_error = NFSERR_IO;
1421				destroy_auth_client_handle(cl);
1422				skipentry = 1;
1423				mfs->mfs_ignore = 1;
1424				continue;
1425			}
1426			if (nfs_getseconfig_byname(nfs_flavor, &nfs_sec)) {
1427				syslog(loglevel,
1428				    "error getting %s information from %s",
1429				    nfs_flavor, NFSSEC_CONF);
1430				free(nfs_flavor);
1431				free(argp);
1432				head = prevhead;
1433				tail = prevtail;
1434				if (tail)
1435					tail->nfs_ext_u.nfs_extB.next = NULL;
1436				last_error = NFSERR_IO;
1437				destroy_auth_client_handle(cl);
1438				skipentry = 1;
1439				mfs->mfs_ignore = 1;
1440				continue;
1441			}
1442			free(nfs_flavor);
1443		}
1444
1445		posix = (nfsvers != NFS_V4 &&
1446				hasmntopt(&m, MNTOPT_POSIX) != NULL) ? 1 : 0;
1447
1448		if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
1449			nfsvers != NFS_V4) {
1450		    bool_t give_up_on_mnt;
1451		    bool_t got_mnt_error;
1452		/*
1453		 * If we started with a URL, if first byte of path is not "/",
1454		 * then the mount will likely fail, so we should try again
1455		 * with a prepended "/".
1456		 */
1457		    if (mfs->mfs_flags & MFS_ALLOC_DIR && *dir != '/')
1458			give_up_on_mnt = FALSE;
1459		    else
1460			give_up_on_mnt = TRUE;
1461
1462		    got_mnt_error = FALSE;
1463
1464try_mnt_slash:
1465		    if (got_mnt_error == TRUE) {
1466			int i, l;
1467
1468			give_up_on_mnt = TRUE;
1469			l = strlen(dir);
1470
1471			/*
1472			 * Insert a "/" to front of mfs_dir.
1473			 */
1474			for (i = l; i > 0; i--)
1475				dir[i] = dir[i-1];
1476
1477			dir[0] = '/';
1478		    }
1479
1480		    /* Get fhandle of remote path from server's mountd */
1481
1482		    switch (outvers) {
1483		    case MOUNTVERS:
1484			if (posix) {
1485				free(argp);
1486				head = prevhead;
1487				tail = prevtail;
1488				if (tail)
1489					tail->nfs_ext_u.nfs_extB.next = NULL;
1490				last_error = NFSERR_NOENT;
1491				syslog(loglevel, "can't get posix info for %s",
1492					host);
1493				destroy_auth_client_handle(cl);
1494				skipentry = 1;
1495				mfs->mfs_ignore = 1;
1496				continue;
1497			}
1498		    /* FALLTHRU */
1499		    case MOUNTVERS_POSIX:
1500			if (nfsvers == NFS_V3) {
1501				free(argp);
1502				head = prevhead;
1503				tail = prevtail;
1504				if (tail)
1505					tail->nfs_ext_u.nfs_extB.next = NULL;
1506				last_error = NFSERR_NOENT;
1507				syslog(loglevel,
1508					"%s doesn't support NFS Version 3",
1509					host);
1510				destroy_auth_client_handle(cl);
1511				skipentry = 1;
1512				mfs->mfs_ignore = 1;
1513				continue;
1514			}
1515			rpc_stat = clnt_call(cl, MOUNTPROC_MNT,
1516				xdr_dirpath, (caddr_t)&dir,
1517				xdr_fhstatus, (caddr_t)&fhs, timeout);
1518			if (rpc_stat != RPC_SUCCESS) {
1519
1520				if (give_up_on_mnt == FALSE) {
1521					got_mnt_error = TRUE;
1522					goto try_mnt_slash;
1523				}
1524
1525				/*
1526				 * Given the way "clnt_sperror" works, the "%s"
1527				 * immediately following the "not responding"
1528				 * is correct.
1529				 */
1530				free(argp);
1531				head = prevhead;
1532				tail = prevtail;
1533				if (tail)
1534					tail->nfs_ext_u.nfs_extB.next = NULL;
1535				last_error = NFSERR_NOENT;
1536
1537				if (retries-- > 0) {
1538					destroy_auth_client_handle(cl);
1539					DELAY(delay);
1540					goto retry;
1541				}
1542
1543				if (trace > 3) {
1544				    trace_prt(1,
1545					"  nfsmount: mount RPC failed for %s\n",
1546					host);
1547				}
1548				syslog(loglevel, "%s server not responding%s",
1549				    host, clnt_sperror(cl, ""));
1550				destroy_auth_client_handle(cl);
1551				skipentry = 1;
1552				mfs->mfs_ignore = 1;
1553				continue;
1554			}
1555			if ((errno = fhs.fhs_status) != MNT_OK)  {
1556
1557				if (give_up_on_mnt == FALSE) {
1558					got_mnt_error = TRUE;
1559					goto try_mnt_slash;
1560				}
1561
1562				free(argp);
1563				head = prevhead;
1564				tail = prevtail;
1565				if (tail)
1566					tail->nfs_ext_u.nfs_extB.next = NULL;
1567				if (errno == EACCES) {
1568					status = NFSERR_ACCES;
1569				} else {
1570					syslog(loglevel, "%s: %m", host);
1571					status = NFSERR_IO;
1572				}
1573				if (trace > 3) {
1574				    trace_prt(1, "  nfsmount: mount RPC gave"
1575					" %d for %s:%s\n",
1576					errno, host, dir);
1577				}
1578				last_error = status;
1579				destroy_auth_client_handle(cl);
1580				skipentry = 1;
1581				mfs->mfs_ignore = 1;
1582				continue;
1583			}
1584			argp->fh = malloc((sizeof (fhandle)));
1585			if (!argp->fh) {
1586				syslog(LOG_ERR, "nfsmount: no memory");
1587				last_error = NFSERR_IO;
1588				destroy_auth_client_handle(cl);
1589				goto out;
1590			}
1591			(void) memcpy(argp->fh, &fhs.fhstatus_u.fhs_fhandle,
1592				sizeof (fhandle));
1593			break;
1594		    case MOUNTVERS3:
1595			posix = 0;
1596			(void) memset((char *)&res3, '\0', sizeof (res3));
1597			rpc_stat = clnt_call(cl, MOUNTPROC_MNT,
1598				xdr_dirpath, (caddr_t)&dir,
1599				xdr_mountres3, (caddr_t)&res3, timeout);
1600			if (rpc_stat != RPC_SUCCESS) {
1601
1602				if (give_up_on_mnt == FALSE) {
1603					got_mnt_error = TRUE;
1604					goto try_mnt_slash;
1605				}
1606
1607				/*
1608				 * Given the way "clnt_sperror" works, the "%s"
1609				 * immediately following the "not responding"
1610				 * is correct.
1611				 */
1612				free(argp);
1613				head = prevhead;
1614				tail = prevtail;
1615				if (tail)
1616					tail->nfs_ext_u.nfs_extB.next = NULL;
1617				last_error = NFSERR_NOENT;
1618
1619				if (retries-- > 0) {
1620					destroy_auth_client_handle(cl);
1621					DELAY(delay);
1622					goto retry;
1623				}
1624
1625				if (trace > 3) {
1626				    trace_prt(1,
1627					"  nfsmount: mount RPC failed for %s\n",
1628					host);
1629				}
1630				syslog(loglevel, "%s server not responding%s",
1631				    remname, clnt_sperror(cl, ""));
1632				destroy_auth_client_handle(cl);
1633				skipentry = 1;
1634				mfs->mfs_ignore = 1;
1635				continue;
1636			}
1637			if ((errno = res3.fhs_status) != MNT_OK)  {
1638
1639				if (give_up_on_mnt == FALSE) {
1640					got_mnt_error = TRUE;
1641					goto try_mnt_slash;
1642				}
1643
1644				free(argp);
1645				head = prevhead;
1646				tail = prevtail;
1647				if (tail)
1648					tail->nfs_ext_u.nfs_extB.next = NULL;
1649				if (errno == EACCES) {
1650					status = NFSERR_ACCES;
1651				} else {
1652					syslog(loglevel, "%s: %m", remname);
1653					status = NFSERR_IO;
1654				}
1655				if (trace > 3) {
1656				    trace_prt(1, "  nfsmount: mount RPC gave"
1657					" %d for %s:%s\n",
1658					errno, host, dir);
1659				}
1660				last_error = status;
1661				destroy_auth_client_handle(cl);
1662				skipentry = 1;
1663				mfs->mfs_ignore = 1;
1664				continue;
1665			}
1666
1667			/*
1668			 *  Negotiate the security flavor for nfs_mount
1669			 */
1670			auths =
1671		    res3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val;
1672			count =
1673		    res3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len;
1674
1675			if (sec_opt) {
1676				for (i = 0; i < count; i++)
1677					if (auths[i] == nfs_sec.sc_nfsnum) {
1678						break;
1679					}
1680				if (i >= count) {
1681					syslog(LOG_ERR,
1682				    "%s: does not support security \"%s\"\n",
1683					    remname, nfs_sec.sc_name);
1684					clnt_freeres(cl, xdr_mountres3,
1685					    (caddr_t)&res3);
1686					free(argp);
1687					head = prevhead;
1688					tail = prevtail;
1689					if (tail)
1690				tail->nfs_ext_u.nfs_extB.next = NULL;
1691					last_error = NFSERR_IO;
1692					destroy_auth_client_handle(cl);
1693					skipentry = 1;
1694					mfs->mfs_ignore = 1;
1695					continue;
1696				}
1697			} else {
1698				if (count > 0) {
1699					for (i = 0; i < count; i++) {
1700					    if (!(scerror =
1701				nfs_getseconfig_bynumber(auths[i], &nfs_sec))) {
1702						sec_opt++;
1703						break;
1704					    }
1705					}
1706					if (i >= count) {
1707						if (nfs_syslog_scerr(scerror,
1708								scerror_msg)
1709							!= -1) {
1710							syslog(LOG_ERR,
1711			"%s cannot be mounted because it is shared with "
1712			"security flavor %d which %s",
1713							remname,
1714							auths[i-1],
1715							scerror_msg);
1716						}
1717						clnt_freeres(cl, xdr_mountres3,
1718						    (caddr_t)&res3);
1719						free(argp);
1720						head = prevhead;
1721						tail = prevtail;
1722						if (tail)
1723					tail->nfs_ext_u.nfs_extB.next = NULL;
1724						last_error = NFSERR_IO;
1725						destroy_auth_client_handle(cl);
1726						skipentry = 1;
1727						mfs->mfs_ignore = 1;
1728						continue;
1729					}
1730				}
1731			}
1732
1733			fh3.fh3_length =
1734			    res3.mountres3_u.mountinfo.fhandle.fhandle3_len;
1735			(void) memcpy(fh3.fh3_u.data,
1736			    res3.mountres3_u.mountinfo.fhandle.fhandle3_val,
1737			    fh3.fh3_length);
1738			clnt_freeres(cl, xdr_mountres3,
1739			    (caddr_t)&res3);
1740			argp->fh = malloc(sizeof (nfs_fh3));
1741			if (!argp->fh) {
1742				syslog(LOG_ERR, "nfsmount: no memory");
1743				last_error = NFSERR_IO;
1744				destroy_auth_client_handle(cl);
1745				goto out;
1746			}
1747			(void) memcpy(argp->fh, &fh3, sizeof (nfs_fh3));
1748			break;
1749		    default:
1750			free(argp);
1751			head = prevhead;
1752			tail = prevtail;
1753			if (tail)
1754				tail->nfs_ext_u.nfs_extB.next = NULL;
1755			last_error = NFSERR_NOENT;
1756			syslog(loglevel, "unknown MOUNT version %ld on %s",
1757			    vers, remname);
1758			destroy_auth_client_handle(cl);
1759			skipentry = 1;
1760			mfs->mfs_ignore = 1;
1761			continue;
1762		    } /* switch */
1763		}
1764		if (nfsvers == NFS_V4) {
1765			argp->fh = strdup(dir);
1766			if (argp->fh == NULL) {
1767				syslog(LOG_ERR, "nfsmount: no memory");
1768				last_error = NFSERR_IO;
1769				goto out;
1770			}
1771		}
1772
1773		if (trace > 4)
1774			trace_prt(1, "	nfsmount: have %s filehandle for %s\n",
1775			    fstype, remname);
1776
1777		argp->flags |= NFSMNT_NEWARGS;
1778		argp->flags |= NFSMNT_INT;	/* default is "intr" */
1779		argp->hostname = host;
1780		argp->flags |= NFSMNT_HOSTNAME;
1781
1782		/*
1783		 * In this case, we want NFSv4 to behave like
1784		 * non-WebNFS so that we get the server address.
1785		 */
1786		if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0) {
1787			nconf = NULL;
1788
1789			if (nfs_port != 0)
1790				thisport = nfs_port;
1791			else
1792				thisport = mfs->mfs_port;
1793
1794			/*
1795			 * For NFSv4, we want to avoid rpcbind, so call
1796			 * get_server_stuff() directly to tell it that
1797			 * we want to go "direct_to_server".  Otherwise,
1798			 * do what has always been done.
1799			 */
1800			if (nfsvers == NFS_V4) {
1801				enum clnt_stat cstat;
1802				argp->addr = get_server_stuff(SERVER_ADDR,
1803					host, NFS_PROGRAM, nfsvers, NULL,
1804					&nconf, nfs_proto, thisport, NULL,
1805					NULL, TRUE, NULL, &cstat);
1806			} else {
1807				argp->addr = get_addr(host, NFS_PROGRAM,
1808					nfsvers, &nconf, nfs_proto,
1809					thisport, NULL);
1810			}
1811
1812			if (argp->addr == NULL) {
1813				free(argp->fh);
1814				free(argp);
1815				head = prevhead;
1816				tail = prevtail;
1817				if (tail)
1818					tail->nfs_ext_u.nfs_extB.next = NULL;
1819				last_error = NFSERR_NOENT;
1820
1821				if (retries-- > 0) {
1822					destroy_auth_client_handle(cl);
1823					DELAY(delay);
1824					goto retry;
1825				}
1826
1827				syslog(loglevel, "%s: no NFS service", host);
1828				destroy_auth_client_handle(cl);
1829				skipentry = 1;
1830				mfs->mfs_ignore = 1;
1831				continue;
1832			}
1833			if (trace > 4)
1834				trace_prt(1,
1835				    "\tnfsmount: have net address for %s\n",
1836				    remname);
1837
1838		} else {
1839			nconf = mfs->mfs_nconf;
1840			mfs->mfs_nconf = NULL;
1841		}
1842
1843		argp->flags |= NFSMNT_KNCONF;
1844		argp->knconf = get_knconf(nconf);
1845		if (argp->knconf == NULL) {
1846			netbuf_free(argp->addr);
1847			freenetconfigent(nconf);
1848			free(argp->fh);
1849			free(argp);
1850			head = prevhead;
1851			tail = prevtail;
1852			if (tail)
1853				tail->nfs_ext_u.nfs_extB.next = NULL;
1854			last_error = NFSERR_NOSPC;
1855			destroy_auth_client_handle(cl);
1856			skipentry = 1;
1857			mfs->mfs_ignore = 1;
1858			continue;
1859		}
1860		if (trace > 4)
1861			trace_prt(1,
1862			    "\tnfsmount: have net config for %s\n",
1863			    remname);
1864
1865		if (hasmntopt(&m, MNTOPT_SOFT) != NULL) {
1866			argp->flags |= NFSMNT_SOFT;
1867		}
1868		if (hasmntopt(&m, MNTOPT_NOINTR) != NULL) {
1869			argp->flags &= ~(NFSMNT_INT);
1870		}
1871		if (hasmntopt(&m, MNTOPT_NOAC) != NULL) {
1872			argp->flags |= NFSMNT_NOAC;
1873		}
1874		if (hasmntopt(&m, MNTOPT_NOCTO) != NULL) {
1875			argp->flags |= NFSMNT_NOCTO;
1876		}
1877		if (hasmntopt(&m, MNTOPT_FORCEDIRECTIO) != NULL) {
1878			argp->flags |= NFSMNT_DIRECTIO;
1879		}
1880		if (hasmntopt(&m, MNTOPT_NOFORCEDIRECTIO) != NULL) {
1881			argp->flags &= ~(NFSMNT_DIRECTIO);
1882		}
1883
1884		/*
1885		 * Set up security data for argp->nfs_ext_u.nfs_extB.secdata.
1886		 */
1887		if (mfssnego.snego_done) {
1888			memcpy(&nfs_sec, &mfssnego.nfs_sec,
1889				sizeof (seconfig_t));
1890		} else if (!sec_opt) {
1891			/*
1892			 * Get default security mode.
1893			 */
1894			if (nfs_getseconfig_default(&nfs_sec)) {
1895				syslog(loglevel,
1896				    "error getting default security entry\n");
1897				free_knconf(argp->knconf);
1898				netbuf_free(argp->addr);
1899				freenetconfigent(nconf);
1900				free(argp->fh);
1901				free(argp);
1902				head = prevhead;
1903				tail = prevtail;
1904				if (tail)
1905					tail->nfs_ext_u.nfs_extB.next = NULL;
1906				last_error = NFSERR_NOSPC;
1907				destroy_auth_client_handle(cl);
1908				skipentry = 1;
1909				mfs->mfs_ignore = 1;
1910				continue;
1911			}
1912			argp->flags |= NFSMNT_SECDEFAULT;
1913		}
1914
1915		/*
1916		 * For AUTH_DH
1917		 * get the network address for the time service on
1918		 * the server.	If an RPC based time service is
1919		 * not available then try the IP time service.
1920		 *
1921		 * Eventurally, we want to move this code to nfs_clnt_secdata()
1922		 * when autod_nfs.c and mount.c can share the same
1923		 * get_the_addr/get_the_stuff routine.
1924		 */
1925		secflags = 0;
1926		syncaddr = NULL;
1927		retaddrs = NULL;
1928
1929		if (nfs_sec.sc_rpcnum == AUTH_DH || nfsvers == NFS_V4) {
1930		/*
1931		 * If not using the public fh and not NFS_V4, we can try
1932		 * talking RPCBIND. Otherwise, assume that firewalls
1933		 * prevent us from doing that.
1934		 */
1935		    if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
1936				nfsvers != NFS_V4) {
1937			syncaddr = get_the_stuff(SERVER_ADDR, host, RPCBPROG,
1938				RPCBVERS, NULL, nconf, 0, NULL, NULL, FALSE,
1939				NULL, NULL);
1940		    }
1941
1942		    if (syncaddr != NULL) {
1943			/* for flags in sec_data */
1944			secflags |= AUTH_F_RPCTIMESYNC;
1945		    } else {
1946			struct nd_hostserv hs;
1947			int error;
1948
1949			hs.h_host = host;
1950			hs.h_serv = "timserver";
1951			error = netdir_getbyname(nconf, &hs, &retaddrs);
1952
1953			if (error != ND_OK && nfs_sec.sc_rpcnum == AUTH_DH) {
1954				syslog(loglevel,
1955				"%s: secure: no time service\n", host);
1956				free_knconf(argp->knconf);
1957				netbuf_free(argp->addr);
1958				freenetconfigent(nconf);
1959				free(argp->fh);
1960				free(argp);
1961				head = prevhead;
1962				tail = prevtail;
1963				if (tail)
1964					tail->nfs_ext_u.nfs_extB.next = NULL;
1965				last_error = NFSERR_IO;
1966				destroy_auth_client_handle(cl);
1967				skipentry = 1;
1968				mfs->mfs_ignore = 1;
1969				continue;
1970			}
1971
1972			if (error == ND_OK)
1973			    syncaddr = retaddrs->n_addrs;
1974
1975			/*
1976			 * For potential usage by NFS V4 when AUTH_DH
1977			 * is negotiated via SECINFO in the kernel.
1978			 */
1979			if (nfsvers == NFS_V4 && syncaddr &&
1980				    host2netname(netname, host, NULL)) {
1981			    argp->syncaddr = malloc(sizeof (struct netbuf));
1982			    argp->syncaddr->buf = malloc(syncaddr->len);
1983			    (void) memcpy(argp->syncaddr->buf,
1984					syncaddr->buf, syncaddr->len);
1985			    argp->syncaddr->len = syncaddr->len;
1986			    argp->syncaddr->maxlen = syncaddr->maxlen;
1987			    argp->netname = strdup(netname);
1988			    argp->flags |= NFSMNT_SECURE;
1989			}
1990		    } /* syncaddr */
1991		} /* AUTH_DH */
1992
1993		nfs_sec.sc_uid = cred->aup_uid;
1994		/*
1995		 * If AUTH_DH is a chosen flavor now, its data will be stored
1996		 * in the sec_data structure via nfs_clnt_secdata().
1997		 */
1998		if (!(secdata = nfs_clnt_secdata(&nfs_sec, host, argp->knconf,
1999					syncaddr, secflags))) {
2000			syslog(LOG_ERR,
2001				"errors constructing security related data\n");
2002			if (secflags & AUTH_F_RPCTIMESYNC)
2003				netbuf_free(syncaddr);
2004			else if (retaddrs)
2005				netdir_free(retaddrs, ND_ADDRLIST);
2006			if (argp->syncaddr)
2007				netbuf_free(argp->syncaddr);
2008			if (argp->netname)
2009				free(argp->netname);
2010			free_knconf(argp->knconf);
2011			netbuf_free(argp->addr);
2012			freenetconfigent(nconf);
2013			free(argp->fh);
2014			free(argp);
2015			head = prevhead;
2016			tail = prevtail;
2017			if (tail)
2018				tail->nfs_ext_u.nfs_extB.next = NULL;
2019			last_error = NFSERR_IO;
2020			destroy_auth_client_handle(cl);
2021			skipentry = 1;
2022			mfs->mfs_ignore = 1;
2023			continue;
2024		}
2025		NFS_ARGS_EXTB_secdata(*argp, secdata);
2026		/* end of security stuff */
2027
2028		if (trace > 4)
2029			trace_prt(1,
2030			    "  nfsmount: have secure info for %s\n", remname);
2031
2032		if (hasmntopt(&m, MNTOPT_GRPID) != NULL) {
2033			argp->flags |= NFSMNT_GRPID;
2034		}
2035		if (nopt(&m, MNTOPT_RSIZE, &argp->rsize)) {
2036			argp->flags |= NFSMNT_RSIZE;
2037		}
2038		if (nopt(&m, MNTOPT_WSIZE, &argp->wsize)) {
2039			argp->flags |= NFSMNT_WSIZE;
2040		}
2041		if (nopt(&m, MNTOPT_TIMEO, &argp->timeo)) {
2042			argp->flags |= NFSMNT_TIMEO;
2043		}
2044		if (nopt(&m, MNTOPT_RETRANS, &argp->retrans)) {
2045			argp->flags |= NFSMNT_RETRANS;
2046		}
2047		if (nopt(&m, MNTOPT_ACTIMEO, &argp->acregmax)) {
2048			argp->flags |= NFSMNT_ACREGMAX;
2049			argp->flags |= NFSMNT_ACDIRMAX;
2050			argp->flags |= NFSMNT_ACDIRMIN;
2051			argp->flags |= NFSMNT_ACREGMIN;
2052			argp->acdirmin = argp->acregmin = argp->acdirmax
2053				= argp->acregmax;
2054		} else {
2055			if (nopt(&m, MNTOPT_ACREGMIN, &argp->acregmin)) {
2056				argp->flags |= NFSMNT_ACREGMIN;
2057			}
2058			if (nopt(&m, MNTOPT_ACREGMAX, &argp->acregmax)) {
2059				argp->flags |= NFSMNT_ACREGMAX;
2060			}
2061			if (nopt(&m, MNTOPT_ACDIRMIN, &argp->acdirmin)) {
2062				argp->flags |= NFSMNT_ACDIRMIN;
2063			}
2064			if (nopt(&m, MNTOPT_ACDIRMAX, &argp->acdirmax)) {
2065				argp->flags |= NFSMNT_ACDIRMAX;
2066			}
2067		}
2068
2069		if (posix) {
2070			argp->pathconf = NULL;
2071			if (error = get_pathconf(cl, dir, remname,
2072			    &argp->pathconf, retries)) {
2073				if (secflags & AUTH_F_RPCTIMESYNC)
2074					netbuf_free(syncaddr);
2075				else if (retaddrs)
2076					netdir_free(retaddrs, ND_ADDRLIST);
2077				free_knconf(argp->knconf);
2078				netbuf_free(argp->addr);
2079				freenetconfigent(nconf);
2080				nfs_free_secdata(
2081					argp->nfs_ext_u.nfs_extB.secdata);
2082				if (argp->syncaddr)
2083					netbuf_free(argp->syncaddr);
2084				if (argp->netname)
2085					free(argp->netname);
2086				free(argp->fh);
2087				free(argp);
2088				head = prevhead;
2089				tail = prevtail;
2090				if (tail)
2091					tail->nfs_ext_u.nfs_extB.next = NULL;
2092				last_error = NFSERR_IO;
2093
2094				if (error == RET_RETRY && retries-- > 0) {
2095					destroy_auth_client_handle(cl);
2096					DELAY(delay);
2097					goto retry;
2098				}
2099
2100				destroy_auth_client_handle(cl);
2101				skipentry = 1;
2102				mfs->mfs_ignore = 1;
2103				continue;
2104			}
2105			argp->flags |= NFSMNT_POSIX;
2106			if (trace > 4)
2107				trace_prt(1,
2108				    "  nfsmount: have pathconf for %s\n",
2109				    remname);
2110		}
2111
2112		/*
2113		 * free loop-specific data structures
2114		 */
2115		destroy_auth_client_handle(cl);
2116		freenetconfigent(nconf);
2117		if (secflags & AUTH_F_RPCTIMESYNC)
2118			netbuf_free(syncaddr);
2119		else if (retaddrs)
2120			netdir_free(retaddrs, ND_ADDRLIST);
2121
2122		/*
2123		 * Decide whether to use remote host's lockd or local locking.
2124		 * If we are using the public fh, we've already turned
2125		 * LLOCK on.
2126		 */
2127		if (hasmntopt(&m, MNTOPT_LLOCK))
2128			argp->flags |= NFSMNT_LLOCK;
2129		if (!(argp->flags & NFSMNT_LLOCK) && nfsvers == NFS_VERSION &&
2130			remote_lock(host, argp->fh)) {
2131			syslog(loglevel, "No network locking on %s : "
2132			"contact admin to install server change", host);
2133			argp->flags |= NFSMNT_LLOCK;
2134		}
2135
2136		/*
2137		 * Build a string for /etc/mnttab.
2138		 * If possible, coalesce strings with same 'dir' info.
2139		 */
2140		if ((mfs->mfs_flags & MFS_URL) == 0) {
2141			char *tmp;
2142
2143			if (mnttabcnt) {
2144				p = strrchr(mnttabtext, (int)':');
2145				if (!p || strcmp(p+1, dir) != 0) {
2146					mnttabcnt += strlen(remname) + 2;
2147				} else {
2148					*p = '\0';
2149					mnttabcnt += strlen(host) + 2;
2150				}
2151				if ((tmp = realloc(mnttabtext,
2152				    mnttabcnt)) != NULL) {
2153					mnttabtext = tmp;
2154					strcat(mnttabtext, ",");
2155				} else {
2156					free(mnttabtext);
2157					mnttabtext = NULL;
2158				}
2159			} else {
2160				mnttabcnt = strlen(remname) + 1;
2161				if ((mnttabtext = malloc(mnttabcnt)) != NULL)
2162					mnttabtext[0] = '\0';
2163			}
2164
2165			if (mnttabtext != NULL)
2166				strcat(mnttabtext, remname);
2167
2168		} else {
2169			char *tmp;
2170			int more_cnt = 0;
2171			char sport[16];
2172
2173			more_cnt += strlen("nfs://");
2174			more_cnt += strlen(mfs->mfs_host);
2175
2176			if (mfs->mfs_port != 0) {
2177				(void) sprintf(sport, ":%u", mfs->mfs_port);
2178			} else
2179				sport[0] = '\0';
2180
2181			more_cnt += strlen(sport);
2182			more_cnt += 1; /* "/" */
2183			more_cnt += strlen(mfs->mfs_dir);
2184
2185			if (mnttabcnt) {
2186				more_cnt += 1; /* "," */
2187				mnttabcnt += more_cnt;
2188
2189				if ((tmp = realloc(mnttabtext,
2190				    mnttabcnt)) != NULL) {
2191					mnttabtext = tmp;
2192					strcat(mnttabtext, ",");
2193				} else {
2194					free(mnttabtext);
2195					mnttabtext = NULL;
2196				}
2197			} else {
2198				mnttabcnt = more_cnt + 1;
2199				if ((mnttabtext = malloc(mnttabcnt)) != NULL)
2200					mnttabtext[0] = '\0';
2201			}
2202
2203			if (mnttabtext != NULL) {
2204				strcat(mnttabtext, "nfs://");
2205				strcat(mnttabtext, mfs->mfs_host);
2206				strcat(mnttabtext, sport);
2207				strcat(mnttabtext, "/");
2208				strcat(mnttabtext, mfs->mfs_dir);
2209			}
2210		}
2211
2212		if (!mnttabtext) {
2213			syslog(LOG_ERR, "nfsmount: no memory");
2214			last_error = NFSERR_IO;
2215			goto out;
2216		}
2217
2218		/*
2219		 * At least one entry, can call mount(2).
2220		 */
2221		entries++;
2222
2223		/*
2224		 * If replication was defeated, don't do more work
2225		 */
2226		if (!replicated)
2227			break;
2228	}
2229
2230
2231	/*
2232	 * Did we get through all possibilities without success?
2233	 */
2234	if (!entries)
2235		goto out;
2236
2237	/* Make "xattr" the default if "noxattr" is not specified. */
2238	strcpy(mopts, opts);
2239	if (!hasmntopt(&m, MNTOPT_NOXATTR) && !hasmntopt(&m, MNTOPT_XATTR)) {
2240		if (strlen(mopts) > 0)
2241			strcat(mopts, ",");
2242		strcat(mopts, "xattr");
2243	}
2244
2245	/*
2246	 * enable services as needed.
2247	 */
2248	{
2249		char **sl;
2250
2251		if (strcmp(fstype, MNTTYPE_NFS4) == 0)
2252			sl = service_list_v4;
2253		else
2254			sl = service_list;
2255
2256		_check_services(sl);
2257	}
2258
2259	/*
2260	 * Whew; do the mount, at last.
2261	 */
2262	if (trace > 1) {
2263		trace_prt(1, "	mount %s %s (%s)\n", mnttabtext, mntpnt, mopts);
2264	}
2265
2266	if (mount(mnttabtext, mntpnt, flags | MS_DATA, fstype,
2267			head, sizeof (*head), mopts, MAX_MNTOPT_STR) < 0) {
2268		if (trace > 1)
2269			trace_prt(1, "	Mount of %s on %s: %d\n",
2270			    mnttabtext, mntpnt, errno);
2271		if (errno != EBUSY || verbose)
2272			syslog(LOG_ERR,
2273				"Mount of %s on %s: %m", mnttabtext, mntpnt);
2274		last_error = NFSERR_IO;
2275		goto out;
2276	}
2277
2278	last_error = NFS_OK;
2279	if (stat(mntpnt, &stbuf) == 0) {
2280		if (trace > 1) {
2281			trace_prt(1, "	mount %s dev=%x rdev=%x OK\n",
2282				mnttabtext, stbuf.st_dev, stbuf.st_rdev);
2283		}
2284	} else {
2285		if (trace > 1) {
2286			trace_prt(1, "	mount %s OK\n", mnttabtext);
2287			trace_prt(1, "	stat of %s failed\n", mntpnt);
2288		}
2289	}
2290
2291out:
2292	argp = head;
2293	while (argp) {
2294		if (argp->pathconf)
2295			free(argp->pathconf);
2296		free_knconf(argp->knconf);
2297		netbuf_free(argp->addr);
2298		if (argp->syncaddr)
2299			netbuf_free(argp->syncaddr);
2300		if (argp->netname) {
2301			free(argp->netname);
2302		}
2303		nfs_free_secdata(argp->nfs_ext_u.nfs_extB.secdata);
2304		free(argp->fh);
2305		head = argp;
2306		argp = argp->nfs_ext_u.nfs_extB.next;
2307		free(head);
2308	}
2309ret:
2310	if (nfs_proto)
2311		free(nfs_proto);
2312	if (mnttabtext)
2313		free(mnttabtext);
2314
2315	for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
2316
2317		if (mfs->mfs_flags & MFS_ALLOC_DIR) {
2318			free(mfs->mfs_dir);
2319			mfs->mfs_dir = NULL;
2320			mfs->mfs_flags &= ~MFS_ALLOC_DIR;
2321		}
2322
2323		if (mfs->mfs_args != NULL) {
2324			free(mfs->mfs_args);
2325			mfs->mfs_args = NULL;
2326		}
2327
2328		if (mfs->mfs_nconf != NULL) {
2329			freenetconfigent(mfs->mfs_nconf);
2330			mfs->mfs_nconf = NULL;
2331		}
2332	}
2333
2334	return (last_error);
2335}
2336
2337/*
2338 * get_pathconf(cl, path, fsname, pcnf, cretries)
2339 * ugliness that requires that ppathcnf and pathcnf stay consistent
2340 * cretries is a copy of retries used to determine when to syslog
2341 * on retry situations.
2342 */
2343static int
2344get_pathconf(CLIENT *cl, char *path, char *fsname, struct pathcnf **pcnf,
2345	int cretries)
2346{
2347	struct ppathcnf *p = NULL;
2348	enum clnt_stat rpc_stat;
2349	struct timeval timeout;
2350
2351	p = (struct ppathcnf *)malloc(sizeof (struct ppathcnf));
2352	if (p == NULL) {
2353		syslog(LOG_ERR, "get_pathconf: Out of memory");
2354		return (RET_ERR);
2355	}
2356	memset((caddr_t)p, 0, sizeof (struct ppathcnf));
2357
2358	timeout.tv_sec = 10;
2359	timeout.tv_usec = 0;
2360	rpc_stat = clnt_call(cl, MOUNTPROC_PATHCONF,
2361	    xdr_dirpath, (caddr_t)&path, xdr_ppathcnf, (caddr_t)p, timeout);
2362	if (rpc_stat != RPC_SUCCESS) {
2363		if (cretries-- <= 0) {
2364			syslog(LOG_ERR,
2365			    "get_pathconf: %s: server not responding: %s",
2366			    fsname, clnt_sperror(cl, ""));
2367		}
2368		free(p);
2369		return (RET_RETRY);
2370	}
2371	if (_PC_ISSET(_PC_ERROR, p->pc_mask)) {
2372		syslog(LOG_ERR, "get_pathconf: no info for %s", fsname);
2373		free(p);
2374		return (RET_ERR);
2375	}
2376	*pcnf = (struct pathcnf *)p;
2377	return (RET_OK);
2378}
2379
2380static struct knetconfig *
2381get_knconf(nconf)
2382	struct netconfig *nconf;
2383{
2384	struct stat stbuf;
2385	struct knetconfig *k;
2386
2387	if (stat(nconf->nc_device, &stbuf) < 0) {
2388		syslog(LOG_ERR, "get_knconf: stat %s: %m", nconf->nc_device);
2389		return (NULL);
2390	}
2391	k = (struct knetconfig *)malloc(sizeof (*k));
2392	if (k == NULL)
2393		goto nomem;
2394	k->knc_semantics = nconf->nc_semantics;
2395	k->knc_protofmly = strdup(nconf->nc_protofmly);
2396	if (k->knc_protofmly == NULL)
2397		goto nomem;
2398	k->knc_proto = strdup(nconf->nc_proto);
2399	if (k->knc_proto == NULL)
2400		goto nomem;
2401	k->knc_rdev = stbuf.st_rdev;
2402
2403	return (k);
2404
2405nomem:
2406	syslog(LOG_ERR, "get_knconf: no memory");
2407	free_knconf(k);
2408	return (NULL);
2409}
2410
2411static void
2412free_knconf(k)
2413	struct knetconfig *k;
2414{
2415	if (k == NULL)
2416		return;
2417	if (k->knc_protofmly)
2418		free(k->knc_protofmly);
2419	if (k->knc_proto)
2420		free(k->knc_proto);
2421	free(k);
2422}
2423
2424static void
2425netbuf_free(nb)
2426	struct netbuf *nb;
2427{
2428	if (nb == NULL)
2429		return;
2430	if (nb->buf)
2431		free(nb->buf);
2432	free(nb);
2433}
2434
2435#define	SMALL_HOSTNAME		20
2436#define	SMALL_PROTONAME		10
2437#define	SMALL_PROTOFMLYNAME		10
2438
2439struct portmap_cache {
2440	int cache_prog;
2441	int cache_vers;
2442	time_t cache_time;
2443	char cache_small_hosts[SMALL_HOSTNAME + 1];
2444	char *cache_hostname;
2445	char *cache_proto;
2446	char *cache_protofmly;
2447	char cache_small_protofmly[SMALL_PROTOFMLYNAME + 1];
2448	char cache_small_proto[SMALL_PROTONAME + 1];
2449	struct netbuf cache_srv_addr;
2450	struct portmap_cache *cache_prev, *cache_next;
2451};
2452
2453rwlock_t portmap_cache_lock;
2454static int portmap_cache_valid_time = 30;
2455struct portmap_cache *portmap_cache_head, *portmap_cache_tail;
2456
2457#ifdef MALLOC_DEBUG
2458void
2459portmap_cache_flush()
2460{
2461	struct  portmap_cache *next = NULL, *cp;
2462
2463	(void) rw_wrlock(&portmap_cache_lock);
2464	for (cp = portmap_cache_head; cp; cp = cp->cache_next) {
2465		if (cp->cache_hostname != NULL &&
2466		    cp->cache_hostname !=
2467		    cp->cache_small_hosts)
2468			free(cp->cache_hostname);
2469		if (cp->cache_proto != NULL &&
2470		    cp->cache_proto !=
2471		    cp->cache_small_proto)
2472			free(cp->cache_proto);
2473		if (cp->cache_srv_addr.buf != NULL)
2474			free(cp->cache_srv_addr.buf);
2475		next = cp->cache_next;
2476		free(cp);
2477	}
2478	portmap_cache_head = NULL;
2479	portmap_cache_tail = NULL;
2480	(void) rw_unlock(&portmap_cache_lock);
2481}
2482#endif
2483
2484/*
2485 * Returns 1 if the entry is found in the cache, 0 otherwise.
2486 */
2487static int
2488portmap_cache_lookup(hostname, prog, vers, nconf, addrp)
2489	char *hostname;
2490	rpcprog_t prog;
2491	rpcvers_t vers;
2492	struct netconfig *nconf;
2493	struct netbuf *addrp;
2494{
2495	struct	portmap_cache *cachep, *prev, *next = NULL, *cp;
2496	int	retval = 0;
2497
2498	timenow = time(NULL);
2499
2500	(void) rw_rdlock(&portmap_cache_lock);
2501
2502	/*
2503	 * Increment the portmap cache counters for # accesses and lookups
2504	 * Use a smaller factor (100 vs 1000 for the host cache) since
2505	 * initial analysis shows this cache is looked up 10% that of the
2506	 * host cache.
2507	 */
2508#ifdef CACHE_DEBUG
2509	portmap_cache_accesses++;
2510	portmap_cache_lookups++;
2511	if ((portmap_cache_lookups%100) == 0)
2512		trace_portmap_cache();
2513#endif /* CACHE_DEBUG */
2514
2515	for (cachep = portmap_cache_head; cachep;
2516		cachep = cachep->cache_next) {
2517		if (timenow > cachep->cache_time) {
2518			/*
2519			 * We stumbled across an entry in the cache which
2520			 * has timed out. Free up all the entries that
2521			 * were added before it, which will positionally
2522			 * be after this entry. And adjust neighboring
2523			 * pointers.
2524			 * When we drop the lock and re-acquire it, we
2525			 * need to start from the beginning.
2526			 */
2527			(void) rw_unlock(&portmap_cache_lock);
2528			(void) rw_wrlock(&portmap_cache_lock);
2529			for (cp = portmap_cache_head;
2530				cp && (cp->cache_time >= timenow);
2531				cp = cp->cache_next)
2532				;
2533			if (cp == NULL)
2534				goto done;
2535			/*
2536			 * Adjust the link of the predecessor.
2537			 * Make the tail point to the new last entry.
2538			 */
2539			prev = cp->cache_prev;
2540			if (prev == NULL) {
2541				portmap_cache_head = NULL;
2542				portmap_cache_tail = NULL;
2543			} else {
2544				prev->cache_next = NULL;
2545				portmap_cache_tail = prev;
2546			}
2547			for (; cp; cp = next) {
2548				if (cp->cache_hostname != NULL &&
2549				    cp->cache_hostname !=
2550				    cp->cache_small_hosts)
2551					free(cp->cache_hostname);
2552				if (cp->cache_proto != NULL &&
2553				    cp->cache_proto !=
2554				    cp->cache_small_proto)
2555					free(cp->cache_proto);
2556				if (cp->cache_srv_addr.buf != NULL)
2557					free(cp->cache_srv_addr.buf);
2558				next = cp->cache_next;
2559				free(cp);
2560			}
2561			goto done;
2562		}
2563		if (cachep->cache_hostname == NULL ||
2564		    prog != cachep->cache_prog || vers != cachep->cache_vers ||
2565		    strcmp(nconf->nc_proto, cachep->cache_proto) != 0 ||
2566		    strcmp(nconf->nc_protofmly, cachep->cache_protofmly) != 0 ||
2567		    strcmp(hostname, cachep->cache_hostname) != 0)
2568			continue;
2569		/*
2570		 * Cache Hit.
2571		 */
2572#ifdef CACHE_DEBUG
2573		portmap_cache_hits++;	/* up portmap cache hit counter */
2574#endif /* CACHE_DEBUG */
2575		addrp->len = cachep->cache_srv_addr.len;
2576		memcpy(addrp->buf, cachep->cache_srv_addr.buf, addrp->len);
2577		retval = 1;
2578		break;
2579	}
2580done:
2581	(void) rw_unlock(&portmap_cache_lock);
2582	return (retval);
2583}
2584
2585static void
2586portmap_cache_enter(hostname, prog, vers, nconf, addrp)
2587	char *hostname;
2588	rpcprog_t prog;
2589	rpcvers_t vers;
2590	struct netconfig *nconf;
2591	struct netbuf *addrp;
2592{
2593	struct portmap_cache *cachep;
2594	int protofmlylen;
2595	int protolen, hostnamelen;
2596
2597	timenow = time(NULL);
2598
2599	cachep = malloc(sizeof (struct portmap_cache));
2600	if (cachep == NULL)
2601		return;
2602	memset((char *)cachep, 0, sizeof (*cachep));
2603
2604	hostnamelen = strlen(hostname);
2605	if (hostnamelen <= SMALL_HOSTNAME)
2606		cachep->cache_hostname = cachep->cache_small_hosts;
2607	else {
2608		cachep->cache_hostname = malloc(hostnamelen + 1);
2609		if (cachep->cache_hostname == NULL)
2610			goto nomem;
2611	}
2612	strcpy(cachep->cache_hostname, hostname);
2613	protolen = strlen(nconf->nc_proto);
2614	if (protolen <= SMALL_PROTONAME)
2615		cachep->cache_proto = cachep->cache_small_proto;
2616	else {
2617		cachep->cache_proto = malloc(protolen + 1);
2618		if (cachep->cache_proto == NULL)
2619			goto nomem;
2620	}
2621	protofmlylen = strlen(nconf->nc_protofmly);
2622	if (protofmlylen <= SMALL_PROTOFMLYNAME)
2623		cachep->cache_protofmly = cachep->cache_small_protofmly;
2624	else {
2625		cachep->cache_protofmly = malloc(protofmlylen + 1);
2626		if (cachep->cache_protofmly == NULL)
2627			goto nomem;
2628	}
2629
2630	strcpy(cachep->cache_proto, nconf->nc_proto);
2631	cachep->cache_prog = prog;
2632	cachep->cache_vers = vers;
2633	cachep->cache_time = timenow + portmap_cache_valid_time;
2634	cachep->cache_srv_addr.len = addrp->len;
2635	cachep->cache_srv_addr.buf = malloc(addrp->len);
2636	if (cachep->cache_srv_addr.buf == NULL)
2637		goto nomem;
2638	memcpy(cachep->cache_srv_addr.buf, addrp->buf, addrp->maxlen);
2639	cachep->cache_prev = NULL;
2640	(void) rw_wrlock(&portmap_cache_lock);
2641	/*
2642	 * There's a window in which we could have multiple threads making
2643	 * the same cache entry. This can be avoided by walking the cache
2644	 * once again here to check and see if there are duplicate entries
2645	 * (after grabbing the write lock). This isn't fatal and I'm not
2646	 * going to bother with this.
2647	 */
2648#ifdef CACHE_DEBUG
2649	portmap_cache_accesses++;	/* up portmap cache access counter */
2650#endif /* CACHE_DEBUG */
2651	cachep->cache_next = portmap_cache_head;
2652	if (portmap_cache_head != NULL)
2653		portmap_cache_head->cache_prev = cachep;
2654	portmap_cache_head = cachep;
2655	(void) rw_unlock(&portmap_cache_lock);
2656	return;
2657
2658nomem:
2659	syslog(LOG_ERR, "portmap_cache_enter: Memory allocation failed");
2660	if (cachep->cache_srv_addr.buf)
2661		free(cachep->cache_srv_addr.buf);
2662	if (cachep->cache_proto && protolen > SMALL_PROTONAME)
2663		free(cachep->cache_proto);
2664	if (cachep->cache_hostname && hostnamelen > SMALL_HOSTNAME)
2665		free(cachep->cache_hostname);
2666	if (cachep->cache_protofmly && protofmlylen > SMALL_PROTOFMLYNAME)
2667		free(cachep->cache_protofmly);
2668	if (cachep)
2669		free(cachep);
2670	cachep = NULL;
2671}
2672
2673static int
2674get_cached_srv_addr(char *hostname, rpcprog_t prog, rpcvers_t vers,
2675	struct netconfig *nconf, struct netbuf *addrp)
2676{
2677	if (portmap_cache_lookup(hostname, prog, vers, nconf, addrp))
2678		return (1);
2679	if (rpcb_getaddr(prog, vers, nconf, addrp, hostname) == 0)
2680		return (0);
2681	portmap_cache_enter(hostname, prog, vers, nconf, addrp);
2682	return (1);
2683}
2684
2685/*
2686 * Get the network address on "hostname" for program "prog"
2687 * with version "vers" by using the nconf configuration data
2688 * passed in.
2689 *
2690 * If the address of a netconfig pointer is null then
2691 * information is not sufficient and no netbuf will be returned.
2692 *
2693 * tinfo argument is for matching the get_the_addr() defined in
2694 * ../nfs/mount/mount.c
2695 */
2696static void *
2697get_the_stuff(
2698	enum type_of_stuff type_of_stuff,
2699	char *hostname,
2700	rpcprog_t prog,
2701	rpcprog_t vers,
2702	mfs_snego_t *mfssnego,
2703	struct netconfig *nconf,
2704	ushort_t port,
2705	struct t_info *tinfo,
2706	caddr_t *fhp,
2707	bool_t direct_to_server,
2708	char *fspath,
2709	enum clnt_stat *cstat)
2710
2711{
2712	struct netbuf *nb = NULL;
2713	struct t_bind *tbind = NULL;
2714	int fd = -1;
2715	enum clnt_stat cs = RPC_TIMEDOUT;
2716	CLIENT *cl = NULL;
2717	struct timeval tv;
2718	AUTH *ah = NULL;
2719	AUTH *new_ah = NULL;
2720	struct snego_t snego;
2721
2722	if (nconf == NULL) {
2723		goto done;
2724	}
2725
2726	if (prog == NFS_PROGRAM && vers == NFS_V4)
2727		if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0)
2728			goto done;
2729
2730	if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0) {
2731		goto done;
2732	}
2733
2734	/* LINTED pointer alignment */
2735	if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR))
2736		    == NULL) {
2737			goto done;
2738	}
2739
2740	if (direct_to_server == TRUE) {
2741		struct nd_hostserv hs;
2742		struct nd_addrlist *retaddrs;
2743		hs.h_host = hostname;
2744
2745		if (trace > 1)
2746			trace_prt(1, "	get_the_stuff: %s call "
2747				"direct to server %s\n",
2748				type_of_stuff == SERVER_FH ? "pub fh" :
2749				type_of_stuff == SERVER_ADDR ? "get address" :
2750				type_of_stuff == SERVER_PING ? "ping" :
2751				"unknown", hostname);
2752		if (port == 0)
2753			hs.h_serv = "nfs";
2754		else
2755			hs.h_serv = NULL;
2756
2757		if (netdir_getbyname(nconf, &hs, &retaddrs) != ND_OK) {
2758			goto done;
2759		}
2760		memcpy(tbind->addr.buf, retaddrs->n_addrs->buf,
2761			retaddrs->n_addrs->len);
2762		tbind->addr.len = retaddrs->n_addrs->len;
2763		netdir_free((void *)retaddrs, ND_ADDRLIST);
2764		if (port) {
2765			/* LINTED pointer alignment */
2766
2767			if (strcmp(nconf->nc_protofmly, NC_INET) == NULL)
2768				((struct sockaddr_in *)
2769				tbind->addr.buf)->sin_port =
2770					htons((ushort_t)port);
2771			else if (strcmp(nconf->nc_protofmly, NC_INET6) == NULL)
2772				((struct sockaddr_in6 *)
2773				tbind->addr.buf)->sin6_port =
2774					htons((ushort_t)port);
2775		}
2776
2777		if (type_of_stuff == SERVER_FH) {
2778			if (netdir_options(nconf, ND_SET_RESERVEDPORT, fd,
2779				NULL) == -1)
2780				if (trace > 1)
2781					trace_prt(1, "\tget_the_stuff: "
2782						"ND_SET_RESERVEDPORT(%s) "
2783						"failed\n", hostname);
2784		}
2785
2786		cl = clnt_tli_create(fd, nconf, &tbind->addr, prog,
2787			vers, 0, 0);
2788
2789		if (trace > 1)
2790			trace_prt(1, "	get_the_stuff: clnt_tli_create(%s) "
2791				"returned %p\n", hostname, cl);
2792		if (cl == NULL)
2793			goto done;
2794#ifdef MALLOC_DEBUG
2795		add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__);
2796		add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
2797			__FILE__, __LINE__);
2798#endif
2799
2800		switch (type_of_stuff) {
2801		case SERVER_FH:
2802		    {
2803		    enum snego_stat sec;
2804
2805		    ah = authsys_create_default();
2806		    if (ah != NULL) {
2807#ifdef MALLOC_DEBUG
2808			drop_alloc("AUTH_HANDLE", cl->cl_auth,
2809				__FILE__, __LINE__);
2810#endif
2811			AUTH_DESTROY(cl->cl_auth);
2812			cl->cl_auth = ah;
2813#ifdef MALLOC_DEBUG
2814			add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
2815				__FILE__, __LINE__);
2816#endif
2817		    }
2818
2819		    if (!mfssnego->snego_done && vers != NFS_V4) {
2820			/*
2821			 * negotiate sec flavor.
2822			 */
2823			snego.cnt = 0;
2824			if ((sec = nfs_sec_nego(vers, cl, fspath, &snego)) ==
2825				SNEGO_SUCCESS) {
2826			    int jj;
2827
2828			/*
2829			 * check if server supports the one
2830			 * specified in the sec= option.
2831			 */
2832			    if (mfssnego->sec_opt) {
2833				for (jj = 0; jj < snego.cnt; jj++) {
2834				    if (snego.array[jj] ==
2835					mfssnego->nfs_sec.sc_nfsnum) {
2836					mfssnego->snego_done = TRUE;
2837					break;
2838				    }
2839				}
2840			    }
2841
2842			/*
2843			 * find a common sec flavor
2844			 */
2845			    if (!mfssnego->snego_done) {
2846				for (jj = 0; jj < snego.cnt; jj++) {
2847				    if (!nfs_getseconfig_bynumber(
2848					snego.array[jj], &mfssnego->nfs_sec)) {
2849					mfssnego->snego_done = TRUE;
2850					break;
2851				    }
2852				}
2853			    }
2854			    if (!mfssnego->snego_done)
2855				return (NULL);
2856
2857			/*
2858			 * Now that the flavor has been
2859			 * negotiated, get the fh.
2860			 *
2861			 * First, create an auth handle using the negotiated
2862			 * sec flavor in the next lookup to
2863			 * fetch the filehandle.
2864			 */
2865			    new_ah = nfs_create_ah(cl, hostname,
2866					&mfssnego->nfs_sec);
2867			    if (new_ah == NULL)
2868				goto done;
2869#ifdef MALLOC_DEBUG
2870			    drop_alloc("AUTH_HANDLE", cl->cl_auth,
2871				__FILE__, __LINE__);
2872#endif
2873			    AUTH_DESTROY(cl->cl_auth);
2874			    cl->cl_auth = new_ah;
2875#ifdef MALLOC_DEBUG
2876			    add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
2877				__FILE__, __LINE__);
2878#endif
2879			} else if (sec == SNEGO_ARRAY_TOO_SMALL ||
2880			    sec == SNEGO_FAILURE) {
2881			    goto done;
2882			}
2883			/*
2884			 * Note that if sec == SNEGO_DEF_VALID
2885			 * the default sec flavor is acceptable.
2886			 * Use it to get the filehandle.
2887			 */
2888		    }
2889		    }
2890
2891		    switch (vers) {
2892		    case NFS_VERSION:
2893			    {
2894			    wnl_diropargs arg;
2895			    wnl_diropres *res;
2896
2897			    memset((char *)&arg.dir, 0, sizeof (wnl_fh));
2898			    arg.name = fspath;
2899			    res = wnlproc_lookup_2(&arg, cl);
2900
2901			    if (res == NULL || res->status != NFS_OK)
2902				    goto done;
2903			    *fhp = malloc(sizeof (wnl_fh));
2904
2905			    if (*fhp == NULL) {
2906				    syslog(LOG_ERR, "no memory\n");
2907				    goto done;
2908			    }
2909
2910			    memcpy((char *)*fhp,
2911			    (char *)&res->wnl_diropres_u.wnl_diropres.file,
2912				sizeof (wnl_fh));
2913			    cs = RPC_SUCCESS;
2914			    }
2915			    break;
2916		    case NFS_V3:
2917			    {
2918			    WNL_LOOKUP3args arg;
2919			    WNL_LOOKUP3res *res;
2920			    nfs_fh3 *fh3p;
2921
2922			    memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3));
2923			    arg.what.name = fspath;
2924			    res = wnlproc3_lookup_3(&arg, cl);
2925
2926			    if (res == NULL || res->status != NFS3_OK)
2927				    goto done;
2928
2929			    fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
2930
2931			    if (fh3p == NULL) {
2932				    syslog(LOG_ERR, "no memory\n");
2933				    CLNT_FREERES(cl, xdr_WNL_LOOKUP3res,
2934					(char *)res);
2935				    goto done;
2936			    }
2937
2938			    fh3p->fh3_length = res->
2939				WNL_LOOKUP3res_u.res_ok.object.data.data_len;
2940			    memcpy(fh3p->fh3_u.data, res->
2941				WNL_LOOKUP3res_u.res_ok.object.data.data_val,
2942				fh3p->fh3_length);
2943
2944			    *fhp = (caddr_t)fh3p;
2945
2946			    CLNT_FREERES(cl, xdr_WNL_LOOKUP3res, (char *)res);
2947			    cs = RPC_SUCCESS;
2948			    }
2949			    break;
2950		    case NFS_V4:
2951			    *fhp = strdup(fspath);
2952			    cs = RPC_SUCCESS;
2953			    break;
2954		    }
2955		    break;
2956		case SERVER_ADDR:
2957		case SERVER_PING:
2958			tv.tv_sec = 10;
2959			tv.tv_usec = 0;
2960			cs = clnt_call(cl, NULLPROC, xdr_void, 0,
2961				xdr_void, 0, tv);
2962			if (trace > 1)
2963				trace_prt(1,
2964					"get_the_stuff: clnt_call(%s) "
2965					"returned %s\n",
2966				hostname,
2967					cs == RPC_SUCCESS ? "success" :
2968					"failure");
2969
2970			if (cs != RPC_SUCCESS)
2971				goto done;
2972			break;
2973		}
2974
2975	} else if (type_of_stuff != SERVER_FH) {
2976
2977		if (type_of_stuff == SERVER_ADDR) {
2978			if (get_cached_srv_addr(hostname, prog, vers, nconf,
2979			    &tbind->addr) == 0)
2980				goto done;
2981		}
2982
2983		if (port) {
2984			/* LINTED pointer alignment */
2985			if (strcmp(nconf->nc_protofmly, NC_INET) == NULL)
2986				((struct sockaddr_in *)
2987				tbind->addr.buf)->sin_port =
2988					htons((ushort_t)port);
2989			else if (strcmp(nconf->nc_protofmly, NC_INET6) == NULL)
2990				((struct sockaddr_in6 *)
2991				tbind->addr.buf)->sin6_port =
2992					htons((ushort_t)port);
2993			cl = clnt_tli_create(fd, nconf, &tbind->addr,
2994				prog, vers, 0, 0);
2995			if (cl == NULL)
2996				goto done;
2997#ifdef MALLOC_DEBUG
2998			add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__);
2999			add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
3000				__FILE__, __LINE__);
3001#endif
3002			tv.tv_sec = 10;
3003			tv.tv_usec = 0;
3004			cs = clnt_call(cl, NULLPROC, xdr_void, 0, xdr_void,
3005				0, tv);
3006			if (cs != RPC_SUCCESS)
3007				goto done;
3008		}
3009
3010	} else {
3011		/* can't happen */
3012		goto done;
3013	}
3014
3015	if (type_of_stuff != SERVER_PING) {
3016
3017		cs = RPC_SYSTEMERROR;
3018
3019		/*
3020		 * Make a copy of the netbuf to return
3021		 */
3022		nb = (struct netbuf *)malloc(sizeof (struct netbuf));
3023		if (nb == NULL) {
3024			syslog(LOG_ERR, "no memory\n");
3025			goto done;
3026		}
3027		*nb = tbind->addr;
3028		nb->buf = (char *)malloc(nb->maxlen);
3029		if (nb->buf == NULL) {
3030			syslog(LOG_ERR, "no memory\n");
3031			free(nb);
3032			nb = NULL;
3033			goto done;
3034		}
3035		(void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
3036
3037		cs = RPC_SUCCESS;
3038	}
3039
3040done:
3041	if (cl != NULL) {
3042		if (ah != NULL) {
3043#ifdef MALLOC_DEBUG
3044			drop_alloc("AUTH_HANDLE", cl->cl_auth,
3045				__FILE__, __LINE__);
3046#endif
3047			AUTH_DESTROY(cl->cl_auth);
3048			cl->cl_auth = NULL;
3049		}
3050#ifdef MALLOC_DEBUG
3051		drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__);
3052#endif
3053		clnt_destroy(cl);
3054	}
3055
3056	if (tbind) {
3057		t_free((char *)tbind, T_BIND);
3058		tbind = NULL;
3059	}
3060
3061	if (fd >= 0)
3062		(void) t_close(fd);
3063
3064	if (cstat != NULL)
3065		*cstat = cs;
3066
3067	return (nb);
3068}
3069
3070/*
3071 * Get a network address on "hostname" for program "prog"
3072 * with version "vers".  If the port number is specified (non zero)
3073 * then try for a TCP/UDP transport and set the port number of the
3074 * resulting IP address.
3075 *
3076 * If the address of a netconfig pointer was passed and
3077 * if it's not null, use it as the netconfig otherwise
3078 * assign the address of the netconfig that was used to
3079 * establish contact with the service.
3080 *
3081 * tinfo argument is for matching the get_addr() defined in
3082 * ../nfs/mount/mount.c
3083 */
3084
3085static struct netbuf *
3086get_addr(char *hostname, rpcprog_t prog, rpcvers_t vers,
3087	struct netconfig **nconfp, char *proto, ushort_t port,
3088	struct t_info *tinfo)
3089
3090{
3091	enum clnt_stat cstat;
3092
3093	return (get_server_stuff(SERVER_ADDR, hostname, prog, vers, NULL,
3094		nconfp, proto, port, tinfo, NULL, FALSE, NULL, &cstat));
3095}
3096
3097static struct netbuf *
3098get_pubfh(char *hostname, rpcvers_t vers, mfs_snego_t *mfssnego,
3099	struct netconfig **nconfp, char *proto, ushort_t port,
3100	struct t_info *tinfo, caddr_t *fhp, bool_t get_pubfh, char *fspath)
3101{
3102	enum clnt_stat cstat;
3103
3104	return (get_server_stuff(SERVER_FH, hostname, NFS_PROGRAM, vers,
3105		mfssnego, nconfp, proto, port, tinfo, fhp, get_pubfh, fspath,
3106		&cstat));
3107}
3108
3109static enum clnt_stat
3110get_ping(char *hostname, rpcprog_t prog, rpcvers_t vers,
3111	struct netconfig **nconfp, ushort_t port, bool_t direct_to_server)
3112{
3113	enum clnt_stat cstat;
3114
3115	(void) get_server_stuff(SERVER_PING, hostname, prog, vers, NULL, nconfp,
3116		NULL, port, NULL, NULL, direct_to_server, NULL, &cstat);
3117
3118	return (cstat);
3119}
3120
3121static void *
3122get_server_stuff(
3123	enum type_of_stuff type_of_stuff,
3124	char *hostname,
3125	rpcprog_t prog,
3126	rpcvers_t vers,
3127	mfs_snego_t *mfssnego,
3128	struct netconfig **nconfp,
3129	char *proto,
3130	ushort_t port,			/* may be zero */
3131	struct t_info *tinfo,
3132	caddr_t *fhp,
3133	bool_t direct_to_server,
3134	char *fspath,
3135	enum clnt_stat *cstatp)
3136{
3137	struct netbuf *nb = NULL;
3138	struct netconfig *nconf = NULL;
3139	NCONF_HANDLE *nc = NULL;
3140	int nthtry = FIRST_TRY;
3141
3142	if (nconfp && *nconfp)
3143		return (get_the_stuff(type_of_stuff, hostname, prog, vers,
3144			mfssnego, *nconfp, port, tinfo, fhp, direct_to_server,
3145			fspath, cstatp));
3146
3147
3148	/*
3149	 * No nconf passed in.
3150	 *
3151	 * Try to get a nconf from /etc/netconfig.
3152	 * First choice is COTS, second is CLTS unless proto
3153	 * is specified.  When we retry, we reset the
3154	 * netconfig list, so that we search the whole list
3155	 * for the next choice.
3156	 */
3157	if ((nc = setnetpath()) == NULL)
3158		goto done;
3159
3160	/*
3161	 * If proto is specified, then only search for the match,
3162	 * otherwise try COTS first, if failed, then try CLTS.
3163	 */
3164	if (proto) {
3165
3166		while (nconf = getnetpath(nc)) {
3167			if (strcmp(nconf->nc_proto, proto))
3168				continue;
3169			/*
3170			 * If the port number is specified then TCP/UDP
3171			 * is needed. Otherwise any cots/clts will do.
3172			 */
3173			if (port)  {
3174				if ((strcmp(nconf->nc_protofmly, NC_INET) &&
3175				    strcmp(nconf->nc_protofmly, NC_INET6)) ||
3176				    (strcmp(nconf->nc_proto, NC_TCP) &&
3177				    strcmp(nconf->nc_proto, NC_UDP)))
3178					continue;
3179			}
3180
3181			nb = get_the_stuff(type_of_stuff, hostname, prog, vers,
3182				mfssnego, nconf, port, tinfo, fhp,
3183				direct_to_server, fspath, cstatp);
3184
3185			if (*cstatp == RPC_SUCCESS)
3186				break;
3187
3188			assert(nb == NULL);
3189
3190		} /* end of while */
3191
3192		if (nconf == NULL)
3193			goto done;
3194
3195	} else {
3196retry:
3197		while (nconf = getnetpath(nc)) {
3198			if (nconf->nc_flag & NC_VISIBLE) {
3199			    if (nthtry == FIRST_TRY) {
3200				if ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
3201					(nconf->nc_semantics == NC_TPI_COTS)) {
3202				    if (port == 0)
3203					break;
3204				    if ((strcmp(nconf->nc_protofmly,
3205					NC_INET) == 0 ||
3206					strcmp(nconf->nc_protofmly,
3207					NC_INET6) == 0) &&
3208					(strcmp(nconf->nc_proto, NC_TCP) == 0))
3209					break;
3210				}
3211			    }
3212			    if (nthtry == SECOND_TRY) {
3213				if (nconf->nc_semantics == NC_TPI_CLTS) {
3214				    if (port == 0)
3215					break;
3216				    if ((strcmp(nconf->nc_protofmly,
3217					NC_INET) == 0 ||
3218					strcmp(nconf->nc_protofmly,
3219					NC_INET6) == 0) &&
3220					(strcmp(nconf->nc_proto, NC_UDP) == 0))
3221					break;
3222				}
3223			    }
3224			}
3225		    } /* while */
3226		    if (nconf == NULL) {
3227			if (++nthtry <= MNT_PREF_LISTLEN) {
3228				endnetpath(nc);
3229				if ((nc = setnetpath()) == NULL)
3230					goto done;
3231				goto retry;
3232			} else
3233				goto done;
3234		    } else {
3235			nb = get_the_stuff(type_of_stuff, hostname, prog, vers,
3236			    mfssnego, nconf, port, tinfo, fhp, direct_to_server,
3237			    fspath, cstatp);
3238			if (*cstatp != RPC_SUCCESS)
3239				/*
3240				 * Continue the same search path in the
3241				 * netconfig db until no more matched nconf
3242				 * (nconf == NULL).
3243				 */
3244				goto retry;
3245		    }
3246	} /* if !proto */
3247
3248	/*
3249	 * Got nconf and nb.  Now dup the netconfig structure (nconf)
3250	 * and return it thru nconfp.
3251	 */
3252	*nconfp = getnetconfigent(nconf->nc_netid);
3253	if (*nconfp == NULL) {
3254		syslog(LOG_ERR, "no memory\n");
3255		free(nb);
3256		nb = NULL;
3257	}
3258done:
3259	if (nc)
3260		endnetpath(nc);
3261	return (nb);
3262}
3263
3264
3265/*
3266 * Sends a null call to the remote host's (NFS program, versp). versp
3267 * may be "NULL" in which case the default maximum version is used.
3268 * Upon return, versp contains the maximum version supported iff versp!= NULL.
3269 */
3270enum clnt_stat
3271pingnfs(
3272	char *hostpart,
3273	int attempts,
3274	rpcvers_t *versp,
3275	rpcvers_t versmin,
3276	ushort_t port,			/* may be zeor */
3277	bool_t usepub,
3278	char *path,
3279	char *proto)
3280{
3281	CLIENT *cl = NULL;
3282	struct timeval rpc_to_new = {15, 0};
3283	static struct timeval rpc_rtrans_new = {-1, -1};
3284	enum clnt_stat clnt_stat;
3285	int i, j;
3286	rpcvers_t versmax;	/* maximum version to try against server */
3287	rpcvers_t outvers;	/* version supported by host on last call */
3288	rpcvers_t vers_to_try;	/* to try different versions against host */
3289	char *hostname = hostpart;
3290	struct netconfig *nconf;
3291
3292	if (path != NULL && strcmp(hostname, "nfs") == 0 &&
3293	    strncmp(path, "//", 2) == 0) {
3294		char *sport;
3295
3296		hostname = strdup(path+2);
3297
3298		if (hostname == NULL)
3299			return (RPC_SYSTEMERROR);
3300
3301		path = strchr(hostname, '/');
3302
3303		/*
3304		 * This cannot happen. If it does, give up
3305		 * on the ping as this is obviously a corrupt
3306		 * entry.
3307		 */
3308		if (path == NULL) {
3309			free(hostname);
3310			return (RPC_SUCCESS);
3311		}
3312
3313		/*
3314		 * Probable end point of host string.
3315		 */
3316		*path = '\0';
3317
3318		sport = strchr(hostname, ':');
3319
3320		if (sport != NULL && sport < path) {
3321
3322			/*
3323			 * Actual end point of host string.
3324			 */
3325			*sport = '\0';
3326			port = htons((ushort_t)atoi(sport+1));
3327		}
3328
3329		usepub = TRUE;
3330	}
3331
3332	/* Pick up the default versions and then set them appropriately */
3333	if (versp) {
3334		versmax = *versp;
3335		/* use versmin passed in */
3336	} else {
3337		read_default_nfs();
3338		set_versrange(0, &versmax, &versmin);
3339	}
3340
3341	if (proto &&
3342	    strncasecmp(proto, NC_UDP, strlen(NC_UDP)) == 0 &&
3343	    versmax == NFS_V4) {
3344		if (versmin == NFS_V4) {
3345			if (versp) {
3346				*versp = versmax - 1;
3347				return (RPC_SUCCESS);
3348			}
3349			return (RPC_PROGUNAVAIL);
3350		} else {
3351			versmax--;
3352		}
3353	}
3354
3355	if (versp)
3356		*versp = versmax;
3357
3358	switch (cache_check(hostname, versp, proto)) {
3359	case GOODHOST:
3360		if (hostname != hostpart)
3361			free(hostname);
3362		return (RPC_SUCCESS);
3363	case DEADHOST:
3364		if (hostname != hostpart)
3365			free(hostname);
3366		return (RPC_TIMEDOUT);
3367	case NOHOST:
3368	default:
3369		break;
3370	}
3371
3372	/*
3373	 * XXX The retransmission time rpcbrmttime is a global defined
3374	 * in the rpc library (rpcb_clnt.c). We use (and like) the default
3375	 * value of 15 sec in the rpc library. The code below is to protect
3376	 * us in case it changes. This need not be done under a lock since
3377	 * any # of threads entering this function will get the same
3378	 * retransmission value.
3379	 */
3380	if (rpc_rtrans_new.tv_sec == -1 && rpc_rtrans_new.tv_usec == -1) {
3381		__rpc_control(CLCR_GET_RPCB_RMTTIME, (char *)&rpc_rtrans_new);
3382		if (rpc_rtrans_new.tv_sec != 15 && rpc_rtrans_new.tv_sec != 0)
3383			if (trace > 1)
3384				trace_prt(1, "RPC library rttimer changed\n");
3385	}
3386
3387	/*
3388	 * XXX Manipulate the total timeout to get the number of
3389	 * desired retransmissions. This code is heavily dependant on
3390	 * the RPC backoff mechanism in clnt_dg_call (clnt_dg.c).
3391	 */
3392	for (i = 0, j = rpc_rtrans_new.tv_sec; i < attempts-1; i++) {
3393		if (j < RPC_MAX_BACKOFF)
3394			j *= 2;
3395		else
3396			j = RPC_MAX_BACKOFF;
3397		rpc_to_new.tv_sec += j;
3398	}
3399
3400	vers_to_try = versmax;
3401
3402	/*
3403	 * check the host's version within the timeout
3404	 */
3405	if (trace > 1)
3406		trace_prt(1, "	ping: %s timeout=%ld request vers=%d min=%d\n",
3407				hostname, rpc_to_new.tv_sec, versmax, versmin);
3408
3409	if (usepub == FALSE) {
3410	    do {
3411		/*
3412		 * If NFSv4, then we do the same thing as is used
3413		 * for public filehandles so that we avoid rpcbind
3414		 */
3415		if (vers_to_try == NFS_V4) {
3416			if (trace > 4) {
3417				trace_prt(1, "  pingnfs: Trying ping via "
3418					"\"circuit_v\"\n");
3419				}
3420
3421			if ((cl = clnt_create_service_timed(hostname, "nfs",
3422							    NFS_PROGRAM,
3423							    vers_to_try,
3424							    port, "circuit_v",
3425							    &rpc_to_new))
3426			    != NULL) {
3427				outvers = vers_to_try;
3428				break;
3429			}
3430			if (trace > 4) {
3431				trace_prt(1, "  pingnfs: Can't ping via "
3432					"\"circuit_v\" %s: RPC error=%d\n",
3433					hostname, rpc_createerr.cf_stat);
3434			}
3435
3436		} else {
3437			if ((cl = clnt_create_vers_timed(hostname, NFS_PROGRAM,
3438				&outvers, versmin, vers_to_try,
3439				"datagram_v", &rpc_to_new))
3440				!= NULL)
3441				break;
3442			if (trace > 4) {
3443				trace_prt(1, "  pingnfs: Can't ping via "
3444					"\"datagram_v\"%s: RPC error=%d\n",
3445					hostname, rpc_createerr.cf_stat);
3446			}
3447			if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST ||
3448				rpc_createerr.cf_stat == RPC_TIMEDOUT)
3449				break;
3450			if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED) {
3451				if (trace > 4) {
3452					trace_prt(1, "  pingnfs: Trying ping "
3453						"via \"circuit_v\"\n");
3454				}
3455				if ((cl = clnt_create_vers_timed(hostname,
3456					NFS_PROGRAM, &outvers,
3457					versmin, vers_to_try,
3458					"circuit_v", &rpc_to_new)) != NULL)
3459					break;
3460				if (trace > 4) {
3461					trace_prt(1, "  pingnfs: Can't ping "
3462						"via \"circuit_v\" %s: "
3463						"RPC error=%d\n",
3464						hostname,
3465						rpc_createerr.cf_stat);
3466				}
3467			}
3468		}
3469
3470		/*
3471		 * backoff and return lower version to retry the ping.
3472		 * XXX we should be more careful and handle
3473		 * RPC_PROGVERSMISMATCH here, because that error is handled
3474		 * in clnt_create_vers(). It's not done to stay in sync
3475		 * with the nfs mount command.
3476		 */
3477		    vers_to_try--;
3478		    if (vers_to_try < versmin)
3479			    break;
3480		    if (versp != NULL) {	/* recheck the cache */
3481			    *versp = vers_to_try;
3482			    if (trace > 4) {
3483				trace_prt(1,
3484				    "  pingnfs: check cache: vers=%d\n",
3485				    *versp);
3486			    }
3487			    switch (cache_check(hostname, versp, proto)) {
3488			    case GOODHOST:
3489				    if (hostname != hostpart)
3490					    free(hostname);
3491				    return (RPC_SUCCESS);
3492			    case DEADHOST:
3493				    if (hostname != hostpart)
3494					    free(hostname);
3495				    return (RPC_TIMEDOUT);
3496			    case NOHOST:
3497			    default:
3498				    break;
3499			    }
3500		    }
3501		    if (trace > 4) {
3502			trace_prt(1, "  pingnfs: Try version=%d\n",
3503				vers_to_try);
3504		    }
3505	    } while (cl == NULL);
3506
3507
3508	    if (cl == NULL) {
3509		    if (verbose)
3510			    syslog(LOG_ERR, "pingnfs: %s%s",
3511				    hostname, clnt_spcreateerror(""));
3512		    clnt_stat = rpc_createerr.cf_stat;
3513	    } else {
3514		    clnt_destroy(cl);
3515		    clnt_stat = RPC_SUCCESS;
3516	    }
3517
3518	} else {
3519		for (vers_to_try = versmax; vers_to_try >= versmin;
3520		    vers_to_try--) {
3521
3522			nconf = NULL;
3523
3524			if (trace > 4) {
3525				trace_prt(1, "  pingnfs: Try version=%d "
3526					"using get_ping()\n", vers_to_try);
3527			}
3528
3529			clnt_stat = get_ping(hostname, NFS_PROGRAM,
3530				vers_to_try, &nconf, port, TRUE);
3531
3532			if (nconf != NULL)
3533				freenetconfigent(nconf);
3534
3535			if (clnt_stat == RPC_SUCCESS) {
3536				outvers = vers_to_try;
3537				break;
3538			}
3539		}
3540	}
3541
3542	if (trace > 1)
3543		clnt_stat == RPC_SUCCESS ?
3544			trace_prt(1, "	pingnfs OK: nfs version=%d\n", outvers):
3545			trace_prt(1, "	pingnfs FAIL: can't get nfs version\n");
3546
3547	if (clnt_stat == RPC_SUCCESS) {
3548		cache_enter(hostname, versmax, outvers, proto, GOODHOST);
3549		if (versp != NULL)
3550			*versp = outvers;
3551	} else
3552		cache_enter(hostname, versmax, versmax, proto, DEADHOST);
3553
3554	if (hostpart != hostname)
3555		free(hostname);
3556
3557	return (clnt_stat);
3558}
3559
3560#define	MNTTYPE_LOFS	"lofs"
3561
3562int
3563loopbackmount(fsname, dir, mntopts, overlay)
3564	char *fsname;		/* Directory being mounted */
3565	char *dir;		/* Directory being mounted on */
3566	char *mntopts;
3567	int overlay;
3568{
3569	struct mnttab mnt;
3570	int flags = 0;
3571	char fstype[] = MNTTYPE_LOFS;
3572	int dirlen;
3573	struct stat st;
3574	char optbuf[MAX_MNTOPT_STR];
3575
3576	dirlen = strlen(dir);
3577	if (dir[dirlen-1] == ' ')
3578		dirlen--;
3579
3580	if (dirlen == strlen(fsname) &&
3581		strncmp(fsname, dir, dirlen) == 0) {
3582		syslog(LOG_ERR,
3583			"Mount of %s on %s would result in deadlock, aborted\n",
3584			fsname, dir);
3585		return (RET_ERR);
3586	}
3587	mnt.mnt_mntopts = mntopts;
3588	if (hasmntopt(&mnt, MNTOPT_RO) != NULL)
3589		flags |= MS_RDONLY;
3590
3591	(void) strlcpy(optbuf, mntopts, sizeof (optbuf));
3592
3593	if (overlay)
3594		flags |= MS_OVERLAY;
3595
3596	if (trace > 1)
3597		trace_prt(1,
3598			"  loopbackmount: fsname=%s, dir=%s, flags=%d\n",
3599			fsname, dir, flags);
3600
3601	if (mount(fsname, dir, flags | MS_DATA | MS_OPTIONSTR, fstype,
3602	    NULL, 0, optbuf, sizeof (optbuf)) < 0) {
3603		syslog(LOG_ERR, "Mount of %s on %s: %m", fsname, dir);
3604		return (RET_ERR);
3605	}
3606
3607	if (stat(dir, &st) == 0) {
3608		if (trace > 1) {
3609			trace_prt(1,
3610			    "  loopbackmount of %s on %s dev=%x rdev=%x OK\n",
3611			    fsname, dir, st.st_dev, st.st_rdev);
3612		}
3613	} else {
3614		if (trace > 1) {
3615			trace_prt(1,
3616			    "  loopbackmount of %s on %s OK\n", fsname, dir);
3617			trace_prt(1, "	stat of %s failed\n", dir);
3618		}
3619	}
3620
3621	return (0);
3622}
3623
3624/*
3625 * Look for the value of a numeric option of the form foo=x.  If found, set
3626 * *valp to the value and return non-zero.  If not found or the option is
3627 * malformed, return zero.
3628 */
3629
3630int
3631nopt(mnt, opt, valp)
3632	struct mnttab *mnt;
3633	char *opt;
3634	int *valp;			/* OUT */
3635{
3636	char *equal;
3637	char *str;
3638
3639	/*
3640	 * We should never get a null pointer, but if we do, it's better to
3641	 * ignore the option than to dump core.
3642	 */
3643
3644	if (valp == NULL) {
3645		syslog(LOG_DEBUG, "null pointer for %s option", opt);
3646		return (0);
3647	}
3648
3649	if (str = hasmntopt(mnt, opt)) {
3650		if (equal = strchr(str, '=')) {
3651			*valp = atoi(&equal[1]);
3652			return (1);
3653		} else {
3654			syslog(LOG_ERR, "Bad numeric option '%s'", str);
3655		}
3656	}
3657	return (0);
3658}
3659
3660int
3661nfsunmount(mnt)
3662	struct mnttab *mnt;
3663{
3664	struct timeval timeout;
3665	CLIENT *cl;
3666	enum clnt_stat rpc_stat;
3667	char *host, *path;
3668	struct replica *list;
3669	int i, count = 0;
3670	int isv4mount = is_v4_mount(mnt->mnt_mountp);
3671
3672	if (trace > 1)
3673		trace_prt(1, "	nfsunmount: umount %s\n", mnt->mnt_mountp);
3674
3675	if (umount(mnt->mnt_mountp) < 0) {
3676		if (trace > 1)
3677			trace_prt(1, "	nfsunmount: umount %s FAILED\n",
3678				mnt->mnt_mountp);
3679		if (errno)
3680			return (errno);
3681	}
3682
3683	/*
3684	 * If this is a NFSv4 mount, the mount protocol was not used
3685	 * so we just return.
3686	 */
3687	if (isv4mount) {
3688		if (trace > 1)
3689			trace_prt(1, "	nfsunmount: umount %s OK\n",
3690				mnt->mnt_mountp);
3691		return (0);
3692	}
3693
3694	/*
3695	 * If mounted with -o public, then no need to contact server
3696	 * because mount protocol was not used.
3697	 */
3698	if (hasmntopt(mnt, MNTOPT_PUBLIC) != NULL) {
3699		return (0);
3700	}
3701
3702	/*
3703	 * The rest of this code is advisory to the server.
3704	 * If it fails return success anyway.
3705	 */
3706
3707	list = parse_replica(mnt->mnt_special, &count);
3708	if (!list) {
3709		if (count >= 0)
3710			syslog(LOG_ERR,
3711			    "Memory allocation failed: %m");
3712		return (ENOMEM);
3713	}
3714
3715	for (i = 0; i < count; i++) {
3716
3717		host = list[i].host;
3718		path = list[i].path;
3719
3720		/*
3721		 * Skip file systems mounted using WebNFS, because mount
3722		 * protocol was not used.
3723		 */
3724		if (strcmp(host, "nfs") == 0 && strncmp(path, "//", 2) == 0)
3725			continue;
3726
3727		cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "datagram_v");
3728		if (cl == NULL)
3729			break;
3730#ifdef MALLOC_DEBUG
3731		add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__);
3732		add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
3733			__FILE__, __LINE__);
3734#endif
3735		if (__clnt_bindresvport(cl) < 0) {
3736			if (verbose)
3737				syslog(LOG_ERR, "umount %s:%s: %s",
3738					host, path,
3739					"Couldn't bind to reserved port");
3740			destroy_auth_client_handle(cl);
3741			continue;
3742		}
3743#ifdef MALLOC_DEBUG
3744		drop_alloc("AUTH_HANDLE", cl->cl_auth, __FILE__, __LINE__);
3745#endif
3746		AUTH_DESTROY(cl->cl_auth);
3747		if ((cl->cl_auth = authsys_create_default()) == NULL) {
3748			if (verbose)
3749				syslog(LOG_ERR, "umount %s:%s: %s",
3750					host, path,
3751					"Failed creating default auth handle");
3752			destroy_auth_client_handle(cl);
3753			continue;
3754		}
3755#ifdef MALLOC_DEBUG
3756		add_alloc("AUTH_HANDLE", cl->cl_auth, 0, __FILE__, __LINE__);
3757#endif
3758		timeout.tv_usec = 0;
3759		timeout.tv_sec = 5;
3760		rpc_stat = clnt_call(cl, MOUNTPROC_UMNT, xdr_dirpath,
3761			    (caddr_t)&path, xdr_void, (char *)NULL, timeout);
3762		if (verbose && rpc_stat != RPC_SUCCESS)
3763			syslog(LOG_ERR, "%s: %s",
3764				host, clnt_sperror(cl, "unmount"));
3765		destroy_auth_client_handle(cl);
3766	}
3767
3768	free_replica(list, count);
3769
3770	if (trace > 1)
3771		trace_prt(1, "	nfsunmount: umount %s OK\n", mnt->mnt_mountp);
3772
3773done:
3774	return (0);
3775}
3776
3777/*
3778 * Put a new entry in the cache chain by prepending it to the front.
3779 * If there isn't enough memory then just give up.
3780 */
3781static void
3782cache_enter(host, reqvers, outvers, proto, state)
3783	char *host;
3784	rpcvers_t reqvers;
3785	rpcvers_t outvers;
3786	char *proto;
3787	int state;
3788{
3789	struct cache_entry *entry;
3790	int cache_time = 30;	/* sec */
3791
3792	timenow = time(NULL);
3793
3794	entry = (struct cache_entry *)malloc(sizeof (struct cache_entry));
3795	if (entry == NULL)
3796		return;
3797	(void) memset((caddr_t)entry, 0, sizeof (struct cache_entry));
3798	entry->cache_host = strdup(host);
3799	if (entry->cache_host == NULL) {
3800		cache_free(entry);
3801		return;
3802	}
3803	entry->cache_reqvers = reqvers;
3804	entry->cache_outvers = outvers;
3805	entry->cache_proto = (proto == NULL ? NULL : strdup(proto));
3806	entry->cache_state = state;
3807	entry->cache_time = timenow + cache_time;
3808	(void) rw_wrlock(&cache_lock);
3809#ifdef CACHE_DEBUG
3810	host_cache_accesses++;		/* up host cache access counter */
3811#endif /* CACHE DEBUG */
3812	entry->cache_next = cache_head;
3813	cache_head = entry;
3814	(void) rw_unlock(&cache_lock);
3815}
3816
3817static int
3818cache_check(host, versp, proto)
3819	char *host;
3820	rpcvers_t *versp;
3821	char *proto;
3822{
3823	int state = NOHOST;
3824	struct cache_entry *ce, *prev;
3825
3826	timenow = time(NULL);
3827
3828	(void) rw_rdlock(&cache_lock);
3829
3830#ifdef CACHE_DEBUG
3831	/* Increment the lookup and access counters for the host cache */
3832	host_cache_accesses++;
3833	host_cache_lookups++;
3834	if ((host_cache_lookups%1000) == 0)
3835		trace_host_cache();
3836#endif /* CACHE DEBUG */
3837
3838	for (ce = cache_head; ce; ce = ce->cache_next) {
3839		if (timenow > ce->cache_time) {
3840			(void) rw_unlock(&cache_lock);
3841			(void) rw_wrlock(&cache_lock);
3842			for (prev = NULL, ce = cache_head; ce;
3843				prev = ce, ce = ce->cache_next) {
3844				if (timenow > ce->cache_time) {
3845					cache_free(ce);
3846					if (prev)
3847						prev->cache_next = NULL;
3848					else
3849						cache_head = NULL;
3850					break;
3851				}
3852			}
3853			(void) rw_unlock(&cache_lock);
3854			return (state);
3855		}
3856		if (strcmp(host, ce->cache_host) != 0)
3857			continue;
3858		if ((proto == NULL && ce->cache_proto != NULL) ||
3859		    (proto != NULL && ce->cache_proto == NULL))
3860			continue;
3861		if (proto != NULL &&
3862		    strcmp(proto, ce->cache_proto) != 0)
3863			continue;
3864
3865		if (versp == NULL ||
3866			(versp != NULL && *versp == ce->cache_reqvers) ||
3867			(versp != NULL && *versp == ce->cache_outvers)) {
3868				if (versp != NULL)
3869					*versp = ce->cache_outvers;
3870				state = ce->cache_state;
3871
3872				/* increment the host cache hit counters */
3873#ifdef CACHE_DEBUG
3874				if (state == GOODHOST)
3875					goodhost_cache_hits++;
3876				if (state == DEADHOST)
3877					deadhost_cache_hits++;
3878#endif /* CACHE_DEBUG */
3879				(void) rw_unlock(&cache_lock);
3880				return (state);
3881		}
3882	}
3883	(void) rw_unlock(&cache_lock);
3884	return (state);
3885}
3886
3887/*
3888 * Free a cache entry and all entries
3889 * further down the chain since they
3890 * will also be expired.
3891 */
3892static void
3893cache_free(entry)
3894	struct cache_entry *entry;
3895{
3896	struct cache_entry *ce, *next = NULL;
3897
3898	for (ce = entry; ce; ce = next) {
3899		if (ce->cache_host)
3900			free(ce->cache_host);
3901		if (ce->cache_proto)
3902			free(ce->cache_proto);
3903		next = ce->cache_next;
3904		free(ce);
3905	}
3906}
3907
3908#ifdef MALLOC_DEBUG
3909void
3910cache_flush()
3911{
3912	(void) rw_wrlock(&cache_lock);
3913	cache_free(cache_head);
3914	cache_head = NULL;
3915	(void) rw_unlock(&cache_lock);
3916}
3917
3918void
3919flush_caches()
3920{
3921	mutex_lock(&cleanup_lock);
3922	cond_signal(&cleanup_start_cv);
3923	(void) cond_wait(&cleanup_done_cv, &cleanup_lock);
3924	mutex_unlock(&cleanup_lock);
3925	cache_flush();
3926	portmap_cache_flush();
3927}
3928#endif
3929
3930/*
3931 * Returns 1, if port option is NFS_PORT or
3932 *	nfsd is running on the port given
3933 * Returns 0, if both port is not NFS_PORT and nfsd is not
3934 *	running on the port.
3935 */
3936
3937static int
3938is_nfs_port(char *opts)
3939{
3940	struct mnttab m;
3941	uint_t nfs_port = 0;
3942	struct servent sv;
3943	char buf[256];
3944	int got_port;
3945
3946	m.mnt_mntopts = opts;
3947
3948	/*
3949	 * Get port specified in options list, if any.
3950	 */
3951	got_port = nopt(&m, MNTOPT_PORT, (int *)&nfs_port);
3952
3953	/*
3954	 * if no port specified or it is same as NFS_PORT return nfs
3955	 * To use any other daemon the port number should be different
3956	 */
3957	if (!got_port || nfs_port == NFS_PORT)
3958		return (1);
3959	/*
3960	 * If daemon is nfsd, return nfs
3961	 */
3962	if (getservbyport_r(nfs_port, NULL, &sv, buf, 256) == &sv &&
3963		strcmp(sv.s_name, "nfsd") == 0)
3964		return (1);
3965
3966	/*
3967	 * daemon is not nfs
3968	 */
3969	return (0);
3970}
3971
3972
3973/*
3974 * destroy_auth_client_handle(cl)
3975 * destroys the created client handle
3976 */
3977static void
3978destroy_auth_client_handle(CLIENT *cl)
3979{
3980	if (cl) {
3981		if (cl->cl_auth) {
3982#ifdef MALLOC_DEBUG
3983			drop_alloc("AUTH_HANDLE", cl->cl_auth,
3984				__FILE__, __LINE__);
3985#endif
3986			AUTH_DESTROY(cl->cl_auth);
3987			cl->cl_auth = NULL;
3988		}
3989#ifdef MALLOC_DEBUG
3990		drop_alloc("CLNT_HANDLE", cl,
3991			__FILE__, __LINE__);
3992#endif
3993		clnt_destroy(cl);
3994	}
3995}
3996
3997
3998/*
3999 * Attempt to figure out which version of NFS to use in pingnfs().  If
4000 * the version number was specified (i.e., non-zero), then use it.
4001 * Otherwise, default to the compiled-in default or the default as set
4002 * by the /etc/default/nfs configuration (as read by read_default().
4003 */
4004int
4005set_versrange(rpcvers_t nfsvers, rpcvers_t *vers, rpcvers_t *versmin)
4006{
4007	switch (nfsvers) {
4008	case 0:
4009		*vers = vers_max_default;
4010		*versmin = vers_min_default;
4011		break;
4012	case NFS_V4:
4013		*vers = NFS_V4;
4014		*versmin = NFS_V4;
4015		break;
4016	case NFS_V3:
4017		*vers = NFS_V3;
4018		*versmin = NFS_V3;
4019		break;
4020	case NFS_VERSION:
4021		*vers = NFS_VERSION;		/* version 2 */
4022		*versmin = NFS_VERSMIN;		/* version 2 */
4023		break;
4024	default:
4025		return (-1);
4026	}
4027	return (0);
4028}
4029
4030#ifdef CACHE_DEBUG
4031/*
4032 * trace_portmap_cache()
4033 * traces the portmap cache values at desired points
4034 */
4035static void
4036trace_portmap_cache()
4037{
4038	syslog(LOG_ERR, "portmap_cache: accesses=%d lookups=%d hits=%d\n",
4039		portmap_cache_accesses, portmap_cache_lookups,
4040		portmap_cache_hits);
4041}
4042
4043/*
4044 * trace_host_cache()
4045 * traces the host cache values at desired points
4046 */
4047static void
4048trace_host_cache()
4049{
4050	syslog(LOG_ERR,
4051		"host_cache: accesses=%d lookups=%d deadhits=%d goodhits=%d\n",
4052		host_cache_accesses, host_cache_lookups, deadhost_cache_hits,
4053		goodhost_cache_hits);
4054}
4055#endif /* CACHE_DEBUG */
4056
4057/*
4058 * Read the /etc/default/nfs configuration file to determine if the
4059 * client has been configured for a new min/max for the NFS version to
4060 * use.
4061 */
4062
4063#define	NFS_DEFAULT_CHECK 60  /* Seconds to check for nfs default changes */
4064
4065static void
4066read_default_nfs(void)
4067{
4068	static time_t lastread = 0;
4069	struct stat buf;
4070	char *defval;
4071	int errno;
4072	int tmp;
4073
4074	/*
4075	 * Fail silently if we can't stat the default nfs config file
4076	 */
4077	if (stat(NFSADMIN, &buf))
4078		return;
4079
4080	if (buf.st_mtime == lastread)
4081		return;
4082
4083	/*
4084	 * Fail silently if error in opening the default nfs config file
4085	 * We'll check back in NFS_DEFAULT_CHECK seconds
4086	 */
4087	if ((defopen(NFSADMIN)) == 0) {
4088		if ((defval = defread("NFS_CLIENT_VERSMIN=")) != NULL) {
4089			errno = 0;
4090			tmp = strtol(defval, (char **)NULL, 10);
4091			if (errno == 0) {
4092				vers_min_default = tmp;
4093			}
4094		}
4095		if ((defval = defread("NFS_CLIENT_VERSMAX=")) != NULL) {
4096			errno = 0;
4097			tmp = strtol(defval, (char **)NULL, 10);
4098			if (errno == 0) {
4099				vers_max_default = tmp;
4100			}
4101		}
4102		/* close defaults file */
4103		defopen(NULL);
4104
4105		lastread = buf.st_mtime;
4106
4107		/*
4108		 * Quick sanity check on the values picked up from the
4109		 * defaults file.  Make sure that a mistake wasn't
4110		 * made that will confuse things later on.
4111		 * If so, reset to compiled-in defaults
4112		 */
4113		if (vers_min_default > vers_max_default ||
4114			vers_min_default < NFS_VERSMIN ||
4115			vers_max_default > NFS_VERSMAX) {
4116			if (trace > 1) {
4117				trace_prt(1,
4118	"  read_default: version minimum/maximum incorrectly configured\n");
4119				trace_prt(1,
4120"  read_default: config is min=%d, max%d. Resetting to min=%d, max%d\n",
4121					vers_min_default, vers_max_default,
4122					NFS_VERSMIN_DEFAULT,
4123					NFS_VERSMAX_DEFAULT);
4124			}
4125			vers_min_default = NFS_VERSMIN_DEFAULT;
4126			vers_max_default = NFS_VERSMAX_DEFAULT;
4127		}
4128	}
4129}
4130
4131/*
4132 *  Find the mnttab entry that corresponds to "name".
4133 *  We're not sure what the name represents: either
4134 *  a mountpoint name, or a special name (server:/path).
4135 *  Return the last entry in the file that matches.
4136 */
4137static struct extmnttab *
4138mnttab_find(dirname)
4139	char *dirname;
4140{
4141	FILE *fp;
4142	struct extmnttab mnt;
4143	struct extmnttab *res = NULL;
4144
4145	fp = fopen(MNTTAB, "r");
4146	if (fp == NULL) {
4147		if (trace > 1)
4148			trace_prt(1, "	mnttab_find: unable to open mnttab\n");
4149		return (NULL);
4150	}
4151	while (getextmntent(fp, &mnt, sizeof (struct extmnttab)) == 0) {
4152		if (strcmp(mnt.mnt_mountp, dirname) == 0 ||
4153		    strcmp(mnt.mnt_special, dirname) == 0) {
4154			if (res)
4155				fsfreemnttab(res);
4156			res = fsdupmnttab(&mnt);
4157		}
4158	}
4159
4160	resetmnttab(fp);
4161	fclose(fp);
4162	if (res == NULL) {
4163		if (trace > 1)
4164			trace_prt(1, "	mnttab_find: unable to find %s\n",
4165				dirname);
4166	}
4167	return (res);
4168}
4169
4170/*
4171 * This function's behavior is taken from nfsstat.
4172 * Trying to determine what NFS version was used for the mount.
4173 */
4174static int
4175is_v4_mount(char *mntpath)
4176{
4177	kstat_ctl_t *kc = NULL;		/* libkstat cookie */
4178	kstat_t *ksp;
4179	ulong_t fsid;
4180	struct mntinfo_kstat mik;
4181	struct extmnttab *mntp;
4182	uint_t mnt_minor;
4183
4184	if ((mntp = mnttab_find(mntpath)) == NULL)
4185		return (FALSE);
4186
4187	/* save the minor number and free the struct so we don't forget */
4188	mnt_minor = mntp->mnt_minor;
4189	fsfreemnttab(mntp);
4190
4191	if ((kc = kstat_open()) == NULL)
4192		return (FALSE);
4193
4194	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
4195		if (ksp->ks_type != KSTAT_TYPE_RAW)
4196			continue;
4197		if (strcmp(ksp->ks_module, "nfs") != 0)
4198			continue;
4199		if (strcmp(ksp->ks_name, "mntinfo") != 0)
4200			continue;
4201		if (mnt_minor != ksp->ks_instance)
4202			continue;
4203
4204		if (kstat_read(kc, ksp, &mik) == -1)
4205			continue;
4206
4207		(void) kstat_close(kc);
4208		if (mik.mik_vers == 4)
4209			return (TRUE);
4210		else
4211			return (FALSE);
4212	}
4213	(void) kstat_close(kc);
4214
4215	return (FALSE);
4216}
4217