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