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