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