xref: /illumos-gate/usr/src/cmd/fs.d/autofs/autod_nfs.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
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 
92 extern void set_nfsv4_ephemeral_mount_to(void);
93 
94 extern char *nfs_get_qop_name();
95 extern AUTH *nfs_create_ah();
96 extern 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 
111 struct 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 
121 struct mfs_snego_t {
122 	int sec_opt;
123 	bool_t snego_done;
124 	char *nfs_flavor;
125 	seconfig_t nfs_sec;
126 };
127 typedef struct mfs_snego_t mfs_snego_t;
128 
129 static struct cache_entry *cache_head = NULL;
130 rwlock_t cache_lock;	/* protect the cache chain */
131 
132 static enum nfsstat nfsmount(struct mapfs *, char *, char *, int, uid_t,
133 	action_list *);
134 static int is_nfs_port(char *);
135 
136 static void netbuf_free(struct netbuf *);
137 static int get_pathconf(CLIENT *, char *, char *, struct pathcnf **, int);
138 static struct mapfs *enum_servers(struct mapent *, char *);
139 static struct mapfs *get_mysubnet_servers(struct mapfs *);
140 static int subnet_test(int af, struct sioc_addrreq *);
141 static	struct	netbuf *get_addr(char *, rpcprog_t, rpcvers_t,
142 	struct netconfig **, char *, ushort_t, struct t_info *);
143 
144 static	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 
148 static int create_homedir(const char *, const char *);
149 
150 enum type_of_stuff {
151 	SERVER_ADDR = 0,
152 	SERVER_PING = 1,
153 	SERVER_FH = 2
154 };
155 
156 static 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 *);
159 static 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 *);
163 static 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);
166 static 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 
170 struct mapfs *add_mfs(struct mapfs *, int, struct mapfs **, struct mapfs **);
171 void free_mfs(struct mapfs *);
172 static void dump_mfs(struct mapfs *, char *, int);
173 static char *dump_distance(struct mapfs *);
174 static void cache_free(struct cache_entry *);
175 static int cache_check(char *, rpcvers_t *, char *);
176 static void cache_enter(char *, rpcvers_t, rpcvers_t, char *, int);
177 void destroy_auth_client_handle(CLIENT *cl);
178 
179 #ifdef CACHE_DEBUG
180 static void trace_host_cache(void);
181 static void trace_portmap_cache(void);
182 #endif /* CACHE_DEBUG */
183 
184 static 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  */
192 static int host_cache_accesses = 0;
193 static int host_cache_lookups = 0;
194 static int deadhost_cache_hits = 0;
195 static 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  */
202 static int portmap_cache_accesses = 0;
203 static int portmap_cache_lookups = 0;
204 static 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  */
213 static rpcvers_t vers_max_default = NFS_VERSMAX_DEFAULT;
214 static rpcvers_t vers_min_default = NFS_VERSMIN_DEFAULT;
215 
216 /*
217  * list of support services needed
218  */
219 static char	*service_list[] = { STATD, LOCKD, NULL };
220 static char	*service_list_v4[] = { STATD, LOCKD, NFS4CBD, NFSMAPID, NULL };
221 
222 static void read_default_nfs(void);
223 static int is_v4_mount(char *);
224 static void start_nfs4cbd(void);
225 
226 int
227 mount_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 
293 static struct mapfs *
294 get_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 
376 int
377 subnet_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  */
403 static struct mapfs *
404 sort_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  */
435 struct mapfs *
436 add_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 
464 static void
465 dump_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 
482 static char *
483 dump_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  */
499 static struct mapfs *
500 filter_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  */
527 void
528 free_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  */
547 static struct mapfs *
548 enum_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 
624 done:
625 	dump_mfs(mfs_head, "  enum_servers: output: ", 1);
626 	return (mfs_head);
627 }
628 
629 static enum nfsstat
630 nfsmount(
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 
806 nextentry:
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;
1130 retry:
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 
1464 try_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 
2390 out:
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 	}
2410 ret:
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  */
2444 static int
2445 get_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 
2481 void
2482 netbuf_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 
2494 struct 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 
2508 rwlock_t portmap_cache_lock;
2509 static int portmap_cache_valid_time = 30;
2510 struct portmap_cache *portmap_cache_head, *portmap_cache_tail;
2511 
2512 #ifdef MALLOC_DEBUG
2513 void
2514 portmap_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  */
2542 static int
2543 portmap_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 	}
2631 done:
2632 	(void) rw_unlock(&portmap_cache_lock);
2633 	return (retval);
2634 }
2635 
2636 static void
2637 portmap_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 
2705 nomem:
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 
2720 static int
2721 get_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 
2747 static struct netbuf *
2748 get_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 
2758 static struct netbuf *
2759 get_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 
2770 static enum clnt_stat
2771 get_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 
2783 void *
2784 get_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 {
2856 retry:
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 	}
2927 done:
2928 	if (nc)
2929 		endnetpath(nc);
2930 	return (nb);
2931 }
2932 
2933 void *
2934 get_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;
3141 done:
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  */
3154 enum clnt_stat
3155 pingnfs(
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 
3456 int
3457 loopbackmount(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 
3527 int
3528 nopt(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 
3554 int
3555 nfsunmount(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 
3667 done:
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  */
3675 static void
3676 cache_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 
3707 static int
3708 cache_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  */
3779 static void
3780 cache_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
3795 void
3796 cache_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 
3804 void
3805 flush_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 
3823 static int
3824 is_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  */
3863 void
3864 destroy_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  */
3890 int
3891 set_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  */
3921 static void
3922 trace_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  */
3933 static void
3934 trace_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 
3951 static void
3952 read_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  */
4010 static struct extmnttab *
4011 mnttab_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  */
4047 static int
4048 is_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 
4091 static int
4092 create_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 
4168 void
4169 free_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 
4240 void *
4241 get_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  */
4279 void *
4280 get_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 	}
4352 done:
4353 	destroy_auth_client_handle(cl);
4354 	cleanup_tli_parms(tbind, fd);
4355 	*cstat = cs;
4356 	return (nb);
4357 }
4358