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