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