xref: /illumos-gate/usr/src/cmd/fs.d/autofs/autod_parse.c (revision 814a60b13c0ad90e5d2edfd29a7a84bbf416cc1a)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  *	autod_parse.c
24  *
25  *	Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
26  *	Use is subject to license terms.
27  */
28 
29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
30 
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <string.h>
34 #include <syslog.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/param.h>
38 #include <errno.h>
39 #include <pwd.h>
40 #include <netinet/in.h>
41 #include <netdb.h>
42 #include <sys/tiuser.h>
43 #include <locale.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <thread.h>
47 #include <rpc/rpc.h>
48 #include <rpcsvc/mount.h>
49 #include <fcntl.h>
50 #include <limits.h>
51 #include "automount.h"
52 
53 /*
54  * This structure is used to determine the hierarchical
55  * relationship between directories
56  */
57 typedef struct _hiernode {
58 	char dirname[MAXFILENAMELEN+1];
59 	struct _hiernode *subdir;
60 	struct _hiernode *leveldir;
61 	struct mapent *mapent;
62 } hiernode;
63 
64 void free_mapent(struct mapent *);
65 
66 static int mapline_to_mapent(struct mapent **, struct mapline *, char *, char *,
67 				char *, char *, uint_t);
68 static int hierarchical_sort(struct mapent *, hiernode **, char *, char *);
69 static int push_options(hiernode *, char *, char *, int);
70 static int set_mapent_opts(struct mapent *, char *, char *, char *);
71 static void get_opts(char *, char *, char *, bool_t *);
72 static int fstype_opts(struct mapent *, char *, char *, char *);
73 static int modify_mapents(struct mapent **, char *, char *, char *, hiernode *,
74 			char *, uint_t, bool_t);
75 static int set_and_fake_mapent_mntlevel(hiernode *, char *, char *, char *,
76 				struct mapent **, uint_t, char *, bool_t);
77 static int mark_level1_root(hiernode *, char *);
78 static int mark_and_fake_level1_noroot(hiernode *, char *, char *, char *,
79 				    struct mapent **, uint_t i, char *);
80 static int convert_mapent_to_automount(struct mapent *, char *, char *);
81 static int automount_opts(char **, char *);
82 static int parse_fsinfo(char *, struct mapent *);
83 static int parse_nfs(char *, struct mapent *, char *, char *, char **, char **,
84 				int);
85 static int parse_special(struct mapent *, char *, char *, char **, char **,
86 				int);
87 static int get_dir_from_path(char *, char **, int);
88 static int alloc_hiernode(hiernode **, char *);
89 static void free_hiernode(hiernode *);
90 static void trace_mapents(char *, struct mapent *);
91 static void trace_hierarchy(hiernode *, int);
92 static struct mapent *do_mapent_hosts(char *, char *, uint_t);
93 static void freeex_ent(struct exportnode *);
94 static void freeex(struct exportnode *);
95 static void dump_mapent_err(struct mapent *, char *, char *);
96 
97 #define	PARSE_OK	0
98 #define	PARSE_ERROR	-1
99 #define	MAX_FSLEN	32
100 
101 /*
102  * mapentry error type defininitions
103  */
104 #define	MAPENT_NOERR	0
105 #define	MAPENT_UATFS	1
106 
107 /*
108  * parse_entry(char *key, char *mapname, char *mapopts, struct mapline *ml,
109  *			char *subdir, uint_t isdirect, bool_t mount_access)
110  * Parses the data in ml to build a mapentry list containing the information
111  * for the mounts/lookups to be performed. Builds an intermediate mapentry list
112  * by processing ml, hierarchically sorts (builds a tree of) the list according
113  * to mountpoint. Then pushes options down the hierarchy, and fills in the mount
114  * file system. Finally, modifies the intermediate list depending on how far
115  * in the hierarchy the current request is (uses subdir). Deals with special
116  * case of /net map parsing.
117  * Returns a pointer to the head of the mapentry list.
118  */
119 struct mapent *
120 parse_entry(char *key, char *mapname, char *mapopts, struct mapline *ml,
121 			char *subdir, uint_t isdirect, bool_t mount_access)
122 {
123 	char *p;
124 	char defaultopts[AUTOFS_MAXOPTSLEN];
125 
126 	struct mapent *mapents = NULL;
127 	hiernode *rootnode = NULL;
128 	char *lp = ml->linebuf;
129 
130 	if (trace > 1)
131 		trace_prt(1, "  mapline: %s\n", ml->linebuf);
132 
133 	/*
134 	 * Assure the key is only one token long.
135 	 * This prevents options from sneaking in through the
136 	 * command line or corruption of /etc/mnttab.
137 	 */
138 	for (p = key; *p != '\0'; p++) {
139 		if (isspace(*p)) {
140 			syslog(LOG_ERR,
141 			"parse_entry: bad key in map %s: %s", mapname, key);
142 			return ((struct mapent *)NULL);
143 		}
144 	}
145 
146 	/*
147 	 * select the appropriate parser, and build the mapentry list
148 	 */
149 	if (strcmp(lp, "-hosts") == 0) {
150 		/*
151 		 * the /net parser - uses do_mapent_hosts to build mapents.
152 		 * The mapopts are considered default for every entry, so we
153 		 * don't push options down hierarchies.
154 		 */
155 		mapents = do_mapent_hosts(mapopts, key, isdirect);
156 		if (mapents == NULL)		/* nothing to free */
157 			return (mapents);
158 
159 		if (trace > 3)
160 			trace_mapents("do_mapent_hosts:(return)", mapents);
161 
162 		if (hierarchical_sort(mapents, &rootnode, key, mapname)
163 				!= PARSE_OK)
164 			goto parse_error;
165 	} else {
166 		/*
167 		 * all other parsing
168 		 */
169 		if (mapline_to_mapent(&mapents, ml, key, mapname,
170 		    mapopts, defaultopts, isdirect) != PARSE_OK)
171 			goto parse_error;
172 
173 		if (mapents == NULL)
174 			return (mapents);
175 
176 		if (hierarchical_sort(mapents, &rootnode, key, mapname)
177 		    != PARSE_OK)
178 			goto parse_error;
179 
180 		if (push_options(rootnode, defaultopts, mapopts,
181 		    MAPENT_NOERR) != PARSE_OK)
182 			goto parse_error;
183 
184 		if (trace > 3) {
185 			trace_prt(1, "\n\tpush_options (return)\n");
186 			trace_prt(0, "\tdefault options=%s\n", defaultopts);
187 			trace_hierarchy(rootnode, 0);
188 		};
189 
190 		if (parse_fsinfo(mapname, mapents) != PARSE_OK)
191 			goto parse_error;
192 	}
193 
194 	/*
195 	 * Modify the mapentry list. We *must* do this only after
196 	 * the mapentry list is completely built (since we need to
197 	 * have parse_fsinfo called first).
198 	 */
199 	if (modify_mapents(&mapents, mapname, mapopts, subdir,
200 	    rootnode, key, isdirect, mount_access) != PARSE_OK)
201 		goto parse_error;
202 
203 	/*
204 	 * XXX: its dangerous to use rootnode after modify mapents as
205 	 * it may be pointing to mapents that have been freed
206 	 */
207 	if (rootnode != NULL)
208 		free_hiernode(rootnode);
209 
210 	return (mapents);
211 
212 parse_error:
213 	syslog(LOG_ERR, "parse_entry: mapentry parse error: map=%s key=%s",
214 	    mapname, key);
215 	free_mapent(mapents);
216 	if (rootnode != NULL)
217 		free_hiernode(rootnode);
218 	return ((struct mapent *)NULL);
219 }
220 
221 
222 /*
223  * mapline_to_mapent(struct mapent **mapents, struct mapline *ml,
224  *		char *key, char *mapname, char *mapopts, char *defaultopts,
225  *              uint_t isdirect)
226  * Parses the mapline information in ml word by word to build an intermediate
227  * mapentry list, which is passed back to the caller. The mapentries may have
228  * holes (example no options), as they are completed only later. The logic is
229  * awkward, but needed to provide the supported flexibility in the map entries.
230  * (especially the first line). Note that the key is the full pathname of the
231  * directory to be mounted in a direct map, and ml is the mapentry beyond key.
232  * Returns PARSE_OK or an appropriate error value.
233  */
234 static int
235 mapline_to_mapent(struct mapent **mapents, struct mapline *ml, char *key,
236 		char *mapname, char *mapopts, char *defaultopts,
237 		uint_t isdirect)
238 {
239 	struct mapent *me = NULL;
240 	struct mapent *mp;
241 	char w[MAXPATHLEN];
242 	char wq[MAXPATHLEN];
243 	char w1[MAXPATHLEN];
244 	int implied;
245 
246 	char *lp = ml->linebuf;
247 	char *lq = ml->lineqbuf;
248 
249 	/* do any macro expansions that are required to complete ml */
250 	if (macro_expand(key, lp, lq, LINESZ)) {
251 		syslog(LOG_ERR,
252 		"mapline_to_mapent: map %s: line too long (max %d chars)",
253 			mapname, LINESZ - 1);
254 		return (PARSE_ERROR);
255 	}
256 	if (trace > 3 && (strcmp(ml->linebuf, lp) != 0))
257 		trace_prt(1,
258 			"  mapline_to_mapent: (expanded) mapline (%s,%s)\n",
259 			ml->linebuf, ml->lineqbuf);
260 
261 	/* init the head of mapentry list to null */
262 	*mapents = NULL;
263 
264 	/*
265 	 * Get the first word - its either a '-' if default options provided,
266 	 * a '/', if the mountroot is implicitly provided, or a mount filesystem
267 	 * if the mountroot is implicit. Note that if the first word begins with
268 	 * a '-' then the second must be read and it must be a mountpoint or a
269 	 * mount filesystem. Use mapopts if no default opts are provided.
270 	 */
271 	if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1)
272 		return (PARSE_ERROR);
273 	if (*w == '-') {
274 		strcpy(defaultopts, w);
275 		if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1)
276 			return (PARSE_ERROR);
277 	} else
278 		strcpy(defaultopts, mapopts);
279 
280 	implied = *w != '/'; /* implied is 1 only if '/' is implicit */
281 	while (*w == '/' || implied) {
282 		mp = me;
283 		if ((me = (struct mapent *)malloc(sizeof (*me))) == NULL)
284 			goto alloc_failed;
285 		(void) memset((char *)me, 0, sizeof (*me));
286 		if (*mapents == NULL)	/* special case of head */
287 			*mapents = me;
288 		else
289 			mp->map_next = me;
290 
291 		/*
292 		 * direct maps get an empty string as root - to be filled
293 		 * by the entire path later. Indirect maps get /key as the
294 		 * map root. Note that xfn maps don't care about the root
295 		 * - they override it in getmapent_fn().
296 		 */
297 		if (isdirect) {
298 			*w1 = '\0';
299 		} else {
300 			strcpy(w1, "/");
301 			strcat(w1, key);
302 		}
303 		if ((me->map_root = strdup(w1)) == NULL)
304 			goto alloc_failed;
305 
306 		/* mntpnt is empty for the mount root */
307 		if (strcmp(w, "/") == 0 || implied)
308 			me->map_mntpnt = strdup("");
309 		else
310 			me->map_mntpnt = strdup(w);
311 		if (me->map_mntpnt == NULL)
312 			goto alloc_failed;
313 
314 		/*
315 		 * If implied, the word must be a mount filesystem,
316 		 * and its already read in; also turn off implied - its
317 		 * not applicable except for the mount root. Else,
318 		 * read another (or two) words depending on if there's
319 		 * an option.
320 		 */
321 		if (implied)   /* must be a mount filesystem */
322 			implied = 0;
323 		else {
324 			if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1)
325 				return (PARSE_ERROR);
326 			if (w[0] == '-') {
327 				/* mount options */
328 				if ((me->map_mntopts = strdup(w)) == NULL)
329 					goto alloc_failed;
330 				if (getword(w, wq, &lp, &lq, ' ',
331 				    sizeof (w)) == -1)
332 					return (PARSE_ERROR);
333 			}
334 		}
335 
336 		/*
337 		 * must be a mount filesystem or a set of filesystems at
338 		 * this point.
339 		 */
340 		if (w[0] == '\0' || w[0] == '-') {
341 			syslog(LOG_ERR,
342 			"mapline_to_mapent: bad location=%s map=%s key=%s",
343 				w, mapname, key);
344 			return (PARSE_ERROR);
345 		}
346 
347 		/*
348 		 * map_fsw and map_fswq hold information which will be
349 		 * used to determine filesystem information at a later
350 		 * point. This is required since we can only find out
351 		 * about the mount file system after the directories
352 		 * are hierarchically sorted and options have been pushed
353 		 * down the hierarchies.
354 		 */
355 		if (((me->map_fsw = strdup(w)) == NULL) ||
356 		    ((me->map_fswq = strdup(wq)) == NULL))
357 			goto alloc_failed;
358 
359 		/*
360 		 * the next word, if any, is either another mount point or a
361 		 * mount filesystem if more than one server is listed.
362 		 */
363 		if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1)
364 			return (PARSE_ERROR);
365 		while (*w && *w != '/') {	/* more than 1 server listed */
366 			int len;
367 			char *fsw, *fswq;
368 			len = strlen(me->map_fsw) + strlen(w) + 4;
369 			if ((fsw = (char *)malloc(len)) == NULL)
370 				goto alloc_failed;
371 			sprintf(fsw, "%s   %s", me->map_fsw, w);
372 			free(me->map_fsw);
373 			me->map_fsw = fsw;
374 			len = strlen(me->map_fswq) + strlen(wq) + 4;
375 			if ((fswq = (char *)malloc(len)) == NULL)
376 				goto alloc_failed;
377 			sprintf(fswq, "%s   %s", me->map_fswq, wq);
378 			free(me->map_fswq);
379 			me->map_fswq = fswq;
380 			if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1)
381 				return (PARSE_ERROR);
382 		}
383 
384 		/* initialize flags */
385 		me->map_mntlevel = -1;
386 		me->map_modified = FALSE;
387 		me->map_faked = FALSE;
388 		me->map_err = MAPENT_NOERR;
389 
390 		me->map_next = NULL;
391 	}
392 
393 	if (*mapents == NULL || w[0] != '\0') {	/* sanity check */
394 		if (verbose) {
395 			if (*mapents == NULL)
396 				syslog(LOG_ERR,
397 				"mapline_to_mapent: parsed with null mapents");
398 			else
399 				syslog(LOG_ERR,
400 				"mapline_to_mapent: parsed nononempty w=%s", w);
401 		}
402 		return (PARSE_ERROR);
403 	}
404 
405 	if (trace > 3)
406 		trace_mapents("mapline_to_mapent:", *mapents);
407 
408 	return (PARSE_OK);
409 
410 alloc_failed:
411 	syslog(LOG_ERR, "mapline_to_mapent: Memory allocation failed");
412 	return (ENOMEM);
413 }
414 
415 /*
416  * hierarchical_sort(struct mapent *mapents, hiernode **rootnode, char *key
417  *                   char *mapname)
418  * sorts the mntpnts in each mapent to build a hierarchy of nodes, with
419  * with the rootnode being the mount root. The hierarchy is setup as
420  * levels, and subdirs below each level. Provides a link from node to
421  * the relevant mapentry.
422  * Returns PARSE_OK or appropriate error value
423  */
424 static int
425 hierarchical_sort(struct mapent *mapents, hiernode **rootnode, char *key,
426 	char *mapname)
427 {
428 	hiernode *prevnode, *currnode, *newnode;
429 	char *path;
430 	char dirname[MAXFILENAMELEN];
431 
432 	int rc = PARSE_OK;
433 	struct mapent *me = mapents;
434 
435 	/* allocate the rootnode with a default path of "" */
436 	*rootnode = NULL;
437 	if ((rc = alloc_hiernode(rootnode, "")) != PARSE_OK)
438 		return (rc);
439 
440 	/*
441 	 * walk through mapents - for each mapent, locate the position
442 	 * within the hierarchy by walking across leveldirs, and
443 	 * subdirs of matched leveldirs. Starts one level below
444 	 * the root (assumes an implicit match with rootnode).
445 	 * XXX - this could probably be done more cleanly using recursion.
446 	 */
447 	while (me != NULL) {
448 
449 		path = me->map_mntpnt;
450 
451 		if ((rc = get_dir_from_path(dirname, &path,
452 		    sizeof (dirname))) != PARSE_OK)
453 			return (rc);
454 
455 		prevnode = *rootnode;
456 		currnode = (*rootnode)->subdir;
457 
458 		while (dirname[0] != '\0') {
459 			if (currnode != NULL) {
460 				if (strcmp(currnode->dirname, dirname) == 0) {
461 					/*
462 					 * match found - mntpnt is a child of
463 					 * this node
464 					 */
465 					prevnode = currnode;
466 					currnode = currnode->subdir;
467 				} else {
468 					prevnode = currnode;
469 					currnode = currnode->leveldir;
470 
471 					if (currnode == NULL) {
472 						/*
473 						 * No more leveldirs to match.
474 						 * Add a new one
475 						 */
476 						if ((rc = alloc_hiernode
477 							(&newnode, dirname))
478 							!= PARSE_OK)
479 							return (rc);
480 						prevnode->leveldir = newnode;
481 						prevnode = newnode;
482 						currnode = newnode->subdir;
483 					} else {
484 						/* try this leveldir */
485 						continue;
486 					}
487 				}
488 			} else {
489 				/* no more subdirs to match. Add a new one */
490 				if ((rc = alloc_hiernode(&newnode,
491 				    dirname)) != PARSE_OK)
492 					return (rc);
493 				prevnode->subdir = newnode;
494 				prevnode = newnode;
495 				currnode = newnode->subdir;
496 			}
497 			if ((rc = get_dir_from_path(dirname, &path,
498 			    sizeof (dirname))) != PARSE_OK)
499 				return (rc);
500 		}
501 
502 		if (prevnode->mapent != NULL) {
503 			/* duplicate mntpoint found */
504 			syslog(LOG_ERR,
505 			"hierarchical_sort: duplicate mntpnt map=%s key=%s",
506 				mapname, key);
507 			return (PARSE_ERROR);
508 		}
509 
510 		/* provide a pointer from node to mapent */
511 		prevnode->mapent = me;
512 		me = me->map_next;
513 	}
514 
515 	if (trace > 3) {
516 		trace_prt(1, "\n\thierarchical_sort:\n");
517 		trace_hierarchy(*rootnode, 0);	/* 0 is rootnode's level */
518 	}
519 
520 	return (rc);
521 }
522 
523 /*
524  * push_options(hiernode *node, char *opts, char *mapopts, int err)
525  * Pushes the options down a hierarchical structure. Works recursively from the
526  * root, which is passed in on the first call. Uses a replacement policy.
527  * If a node points to a mapentry, and it has an option, then thats the option
528  * for that mapentry. Else, the node's mapent inherits the option from the
529  * default (which may be the global option for the entry or mapopts).
530  * err is useful in flagging entries with errors in pushing options.
531  * returns PARSE_OK or appropriate error value.
532  */
533 static int
534 push_options(hiernode *node, char *defaultopts, char *mapopts, int err)
535 {
536 	int rc = PARSE_OK;
537 	struct mapent *me = NULL;
538 
539 	/* ensure that all the dirs at a level are passed the default options */
540 	while (node != NULL) {
541 		me = node->mapent;
542 		if (me != NULL) {	/* not all nodes point to a mapentry */
543 			me->map_err = err;
544 			if ((rc = set_mapent_opts(me, me->map_mntopts,
545 				defaultopts, mapopts)) != PARSE_OK)
546 				return (rc);
547 		}
548 
549 		/* push the options to subdirs */
550 		if (node->subdir != NULL) {
551 			if (node->mapent && strcmp(node->mapent->map_fstype,
552 				MNTTYPE_AUTOFS) == 0)
553 				err = MAPENT_UATFS;
554 			if ((rc = push_options(node->subdir, defaultopts,
555 				mapopts, err)) != PARSE_OK)
556 				return (rc);
557 		}
558 		node = node->leveldir;
559 	}
560 	return (rc);
561 }
562 
563 #define	BACKFSTYPE "backfstype" /* used in cachefs options */
564 #define	BACKFSTYPE_EQ "backfstype="
565 #define	FSTYPE "fstype"
566 #define	FSTYPE_EQ "fstype="
567 #define	NO_OPTS ""
568 
569 /*
570  * set_mapent_opts(struct mapent *me, char *opts, char *defaultopts,
571  *			char *mapopts)
572  * sets the mapentry's options, fstype and mounter fields by separating
573  * out the fstype part from the opts. Use default options if opts is NULL.
574  * Note taht defaultopts may be the same as mapopts.
575  * Returns PARSE_OK or appropriate error value.
576  */
577 static int
578 set_mapent_opts(struct mapent *me, char *opts, char *defaultopts,
579 		char *mapopts)
580 {
581 	char entryopts[AUTOFS_MAXOPTSLEN];
582 	char fstype[MAX_FSLEN], mounter[MAX_FSLEN];
583 	int rc = PARSE_OK;
584 	bool_t fstype_opt = FALSE;
585 
586 	strcpy(fstype, MNTTYPE_NFS);		/* default */
587 
588 	/* set options to default options, if none exist for this entry */
589 	if (opts == NULL) {
590 		opts = defaultopts;
591 		if (defaultopts == NULL) { /* NULL opts for entry */
592 			strcpy(mounter, fstype);
593 			goto done;
594 		}
595 	}
596 	if (*opts == '-')
597 		opts++;
598 
599 	/* separate opts into fstype and (other) entrypopts */
600 	get_opts(opts,	entryopts, fstype, &fstype_opt);
601 
602 	/* replace any existing opts */
603 	if (me->map_mntopts != NULL)
604 		free(me->map_mntopts);
605 	if ((me->map_mntopts = strdup(entryopts)) == NULL)
606 		return (ENOMEM);
607 	strcpy(mounter,	fstype);
608 
609 	/*
610 	 * The following ugly chunk of code crept in as a result of
611 	 * cachefs.  If it's a cachefs mount of an nfs filesystem, then
612 	 * it's important to parse the nfs special field.  Otherwise,
613 	 * just hand the special field to the fs-specific mount
614 	 */
615 	if (strcmp(fstype, MNTTYPE_CACHEFS) ==  0) {
616 		struct mnttab m;
617 		char *p;
618 
619 		m.mnt_mntopts = entryopts;
620 		if ((p = hasmntopt(&m, BACKFSTYPE)) != NULL) {
621 			int len = strlen(MNTTYPE_NFS);
622 
623 			p += strlen(BACKFSTYPE_EQ);
624 
625 			if (strncmp(p, MNTTYPE_NFS, len) ==  0 &&
626 				(p[len] == '\0' || p[len] == ',')) {
627 				/*
628 				 * Cached nfs mount
629 				 */
630 				(void) strcpy(fstype, MNTTYPE_NFS);
631 				(void) strcpy(mounter, MNTTYPE_CACHEFS);
632 			}
633 		}
634 	}
635 
636 	/*
637 	 * child options are exactly fstype = somefs, we need to do some
638 	 * more option pushing work.
639 	 */
640 	if (fstype_opt == TRUE &&
641 		(strcmp(me->map_mntopts, NO_OPTS) == 0)) {
642 		free(me->map_mntopts);
643 		if ((rc = fstype_opts(me, opts, defaultopts,
644 		    mapopts)) != PARSE_OK)
645 			return (rc);
646 	}
647 
648 done:
649 	if (((me->map_fstype = strdup(fstype)) == NULL) ||
650 		((me->map_mounter = strdup(mounter)) == NULL)) {
651 		if (me->map_fstype != NULL)
652 			free(me->map_fstype);
653 		syslog(LOG_ERR, "set_mapent_opts: No memory");
654 		return (ENOMEM);
655 	}
656 
657 	return (rc);
658 }
659 
660 /*
661  * Check the option string for an "fstype"
662  * option.  If found, return the fstype
663  * and the option string with the fstype
664  * option removed, e.g.
665  *
666  *  input:  "fstype=cachefs,ro,nosuid"
667  *  opts:   "ro,nosuid"
668  *  fstype: "cachefs"
669  *
670  * Also indicates if the fstype option was present
671  * by setting a flag, if the pointer to the flag
672  * is not NULL.
673  */
674 static void
675 get_opts(input, opts, fstype, fstype_opt)
676 	char *input;
677 	char *opts; 	/* output */
678 	char *fstype;   /* output */
679 	bool_t *fstype_opt;
680 {
681 	char *p, *pb;
682 	char buf[MAXOPTSLEN];
683 	char *placeholder;
684 
685 	*opts = '\0';
686 	(void) strcpy(buf, input);
687 	pb = buf;
688 	while (p = (char *)strtok_r(pb, ",", &placeholder)) {
689 		pb = NULL;
690 		if (strncmp(p, FSTYPE_EQ, 7) == 0) {
691 			if (fstype_opt != NULL)
692 				*fstype_opt = TRUE;
693 			(void) strcpy(fstype, p + 7);
694 		} else {
695 			if (*opts)
696 				(void) strcat(opts, ",");
697 			(void) strcat(opts, p);
698 		}
699 	}
700 }
701 
702 /*
703  * fstype_opts(struct mapent *me, char *opts, char *defaultopts,
704  *				char *mapopts)
705  * We need to push global options to the child entry if it is exactly
706  * fstype=somefs.
707  */
708 static int
709 fstype_opts(struct mapent *me, char *opts, char *defaultopts,
710 				char *mapopts)
711 {
712 	char pushopts[AUTOFS_MAXOPTSLEN];
713 	char pushentryopts[AUTOFS_MAXOPTSLEN];
714 	char pushfstype[MAX_FSLEN];
715 
716 	if (defaultopts && *defaultopts == '-')
717 		defaultopts++;
718 
719 	/*
720 	 * the options to push are the global defaults for the entry,
721 	 * if they exist, or mapopts, if the global defaults for the
722 	 * entry does not exist.
723 	 */
724 	if (strcmp(defaultopts, opts) == 0) {
725 		if (*mapopts == '-')
726 			mapopts++;
727 		get_opts(mapopts, pushentryopts, pushfstype, NULL);
728 		strcpy(pushopts, mapopts);
729 	} else {
730 		get_opts(defaultopts, pushentryopts, pushfstype, NULL);
731 		strcpy(pushopts, defaultopts);
732 	}
733 
734 	if (strcmp(pushfstype, MNTTYPE_CACHEFS) == 0)
735 		me->map_mntopts = strdup(pushopts);
736 	else
737 		me->map_mntopts = strdup(pushentryopts);
738 
739 	if (!me->map_mntopts) {
740 		syslog(LOG_ERR, "fstype_opts: No memory");
741 		return (ENOMEM);
742 	}
743 
744 	return (PARSE_OK);
745 }
746 
747 /*
748  * modify_mapents(struct mapent **mapents, char *mapname,
749  *			char *mapopts, char *subdir, hiernode *rootnode,
750  * 			char *key, uint_t isdirect, bool_t mount_access)
751  * modifies the intermediate mapentry list into the final one, and passes
752  * back a pointer to it. The final list may contain faked mapentries for
753  * hiernodes that do not point to a mapentry, or converted mapentries, if
754  * hiernodes that point to a mapentry need to be converted from nfs to autofs.
755  * mounts. Entries that are not directly 1 level below the subdir are removed.
756  * Returns PARSE_OK or PARSE_ERROR
757  */
758 static int
759 modify_mapents(struct mapent **mapents, char *mapname,
760 			char *mapopts, char *subdir, hiernode *rootnode,
761 			char *key, uint_t isdirect, bool_t mount_access)
762 {
763 	struct mapent *mp = NULL;
764 	char w[MAXPATHLEN];
765 
766 	struct mapent *me;
767 	int rc = PARSE_OK;
768 	struct mapent *faked_mapents = NULL;
769 
770 	/*
771 	 * correct the mapentry mntlevel from default -1 to level depending on
772 	 * position in hierarchy, and build any faked mapentries, if required
773 	 * at one level below the rootnode given by subdir.
774 	 */
775 	if ((rc = set_and_fake_mapent_mntlevel(rootnode, subdir, key, mapname,
776 		&faked_mapents, isdirect, mapopts, mount_access)) != PARSE_OK)
777 		return (rc);
778 
779 	/*
780 	 * attaches faked mapents to real mapents list. Assumes mapents
781 	 * is not NULL.
782 	 */
783 	me = *mapents;
784 	while (me->map_next != NULL)
785 		me = me->map_next;
786 	me->map_next = faked_mapents;
787 
788 	/*
789 	 * get rid of nodes marked at level -1
790 	 */
791 	me = *mapents;
792 	while (me != NULL) {
793 		if ((me->map_mntlevel ==  -1) || (me->map_err) ||
794 			(mount_access == FALSE && me->map_mntlevel == 0)) {
795 			/*
796 			 * syslog any errors and free entry
797 			 */
798 			if (me->map_err)
799 				dump_mapent_err(me, key, mapname);
800 
801 			if (me ==  (*mapents)) {
802 				/* special case when head has to be freed */
803 				*mapents = me->map_next;
804 				if ((*mapents) ==  NULL) {
805 					/* something wierd happened */
806 					if (verbose)
807 						syslog(LOG_ERR,
808 						"modify_mapents: level error");
809 					return (PARSE_ERROR);
810 				}
811 
812 				/* separate out the node */
813 				me->map_next = NULL;
814 				free_mapent(me);
815 				me = *mapents;
816 			} else {
817 				mp->map_next = me->map_next;
818 				me->map_next = NULL;
819 				free_mapent(me);
820 				me = mp->map_next;
821 			}
822 			continue;
823 		}
824 
825 		/*
826 		 * convert level 1 mapents that are not already autonodes
827 		 * to autonodes
828 		 */
829 		if (me->map_mntlevel == 1 &&
830 			(strcmp(me->map_fstype, MNTTYPE_AUTOFS) != 0) &&
831 			(me->map_faked != TRUE)) {
832 			if ((rc = convert_mapent_to_automount(me, mapname,
833 			    mapopts)) != PARSE_OK)
834 				return (rc);
835 		}
836 		strcpy(w, (me->map_mntpnt+strlen(subdir)));
837 		strcpy(me->map_mntpnt, w);
838 		mp = me;
839 		me = me->map_next;
840 	}
841 
842 	if (trace > 3)
843 		trace_mapents("modify_mapents:", *mapents);
844 
845 	return (PARSE_OK);
846 }
847 
848 /*
849  * set_and_fake_mapent_mntlevel(hiernode *rootnode, char *subdir, char *key,
850  *			char *mapname, struct mapent **faked_mapents,
851  *			uint_t isdirect, char *mapopts, bool_t mount_access)
852  * sets the mapentry mount levels (depths) with respect to the subdir.
853  * Assigns a value of 0 to the new root. Finds the level1 directories by
854  * calling mark_*_level1_*(). Also cleans off extra /'s in level0 and
855  * level1 map_mntpnts. Note that one level below the new root is an existing
856  * mapentry if there's a mapentry (nfs mount) corresponding to the root,
857  * and the direct subdir set for the root, if there's no mapentry corresponding
858  * to the root (we install autodirs). Returns PARSE_OK or error value.
859  */
860 static int
861 set_and_fake_mapent_mntlevel(hiernode *rootnode, char *subdir, char *key,
862 		char *mapname, struct mapent **faked_mapents,
863 		uint_t isdirect, char *mapopts, bool_t mount_access)
864 {
865 	char dirname[MAXFILENAMELEN];
866 	char traversed_path[MAXPATHLEN]; /* used in building fake mapentries */
867 
868 	char *subdir_child = subdir;
869 	hiernode *prevnode = rootnode;
870 	hiernode *currnode = rootnode->subdir;
871 	int rc = PARSE_OK;
872 	traversed_path[0] = '\0';
873 
874 	/*
875 	 * find and mark the root by tracing down subdir. Use traversed_path
876 	 * to keep track of how far we go, while guaranteeing that it
877 	 * contains no '/' at the end. Took some mucking to get that right.
878 	 */
879 	if ((rc = get_dir_from_path(dirname, &subdir_child, sizeof (dirname)))
880 				!= PARSE_OK)
881 		return (rc);
882 
883 	if (dirname[0] != '\0')
884 		sprintf(traversed_path, "%s/%s", traversed_path, dirname);
885 
886 	prevnode = rootnode;
887 	currnode = rootnode->subdir;
888 	while (dirname[0] != '\0' && currnode != NULL) {
889 		if (strcmp(currnode->dirname, dirname) == 0) {
890 
891 			/* subdir is a child of currnode */
892 			prevnode = currnode;
893 			currnode = currnode->subdir;
894 
895 			if ((rc = get_dir_from_path(dirname, &subdir_child,
896 			    sizeof (dirname))) != PARSE_OK)
897 				return (rc);
898 			if (dirname[0] != '\0')
899 				sprintf(traversed_path, "%s/%s",
900 					traversed_path, dirname);
901 
902 		} else {
903 			/* try next leveldir */
904 			prevnode = currnode;
905 			currnode = currnode->leveldir;
906 		}
907 	}
908 
909 	if (dirname[0] != '\0') {
910 		if (verbose)
911 			syslog(LOG_ERR,
912 			"set_and_fake_mapent_mntlevel: subdir=%s error: map=%s",
913 			    subdir, mapname);
914 		return (PARSE_ERROR);
915 	}
916 
917 	/*
918 	 * see if level of root really points to a mapent and if
919 	 * have access to that filessystem - call appropriate
920 	 * routine to mark level 1 nodes, and build faked entries
921 	 */
922 	if (prevnode->mapent != NULL && mount_access == TRUE) {
923 		if (trace > 3)
924 			trace_prt(1, "  node mountpoint %s\t travpath=%s\n",
925 				prevnode->mapent->map_mntpnt, traversed_path);
926 
927 		/*
928 		 * Copy traversed path map_mntpnt to get rid of any extra
929 		 * '/' the map entry may contain.
930 		 */
931 		if (strlen(prevnode->mapent->map_mntpnt) <
932 				strlen(traversed_path)) { /* sanity check */
933 			if (verbose)
934 				syslog(LOG_ERR,
935 				"set_and_fake_mapent_mntlevel: path=%s error",
936 				    traversed_path);
937 			return (PARSE_ERROR);
938 		}
939 		if (strcmp(prevnode->mapent->map_mntpnt, traversed_path) != 0)
940 			strcpy(prevnode->mapent->map_mntpnt, traversed_path);
941 
942 		prevnode->mapent->map_mntlevel = 0; /* root level is 0 */
943 		if (currnode != NULL) {
944 			if ((rc = mark_level1_root(currnode,
945 			    traversed_path)) != PARSE_OK)
946 				return (rc);
947 		}
948 	} else if (currnode != NULL) {
949 		if (trace > 3)
950 			trace_prt(1, "  No rootnode, travpath=%s\n",
951 				traversed_path);
952 		if ((rc = mark_and_fake_level1_noroot(currnode,
953 		    traversed_path, key, mapname, faked_mapents, isdirect,
954 		    mapopts)) != PARSE_OK)
955 			return (rc);
956 	}
957 
958 	if (trace > 3) {
959 		trace_prt(1, "\n\tset_and_fake_mapent_mntlevel\n");
960 		trace_hierarchy(rootnode, 0);
961 	}
962 
963 	return (rc);
964 }
965 
966 
967 /*
968  * mark_level1_root(hiernode *node, char *traversed_path)
969  * marks nodes upto one level below the rootnode given by subdir
970  * recursively. Called if rootnode points to a mapent.
971  * In this routine, a level 1 node is considered to be the 1st existing
972  * mapentry below the root node, so there's no faking involved.
973  * Returns PARSE_OK or error value
974  */
975 static int
976 mark_level1_root(hiernode *node, char *traversed_path)
977 {
978 	/* ensure we touch all leveldirs */
979 	while (node) {
980 		/*
981 		 * mark node level as 1, if one exists - else walk down
982 		 * subdirs until we find one.
983 		 */
984 		if (node->mapent ==  NULL) {
985 			char w[MAXPATHLEN];
986 
987 			if (node->subdir != NULL) {
988 				sprintf(w, "%s/%s", traversed_path,
989 				    node->dirname);
990 				if (mark_level1_root(node->subdir, w)
991 						== PARSE_ERROR)
992 					return (PARSE_ERROR);
993 			} else {
994 				if (verbose) {
995 					syslog(LOG_ERR,
996 					"mark_level1_root: hierarchy error");
997 				}
998 				return (PARSE_ERROR);
999 			}
1000 		} else {
1001 			char w[MAXPATHLEN];
1002 
1003 			sprintf(w, "%s/%s", traversed_path, node->dirname);
1004 			if (trace > 3)
1005 				trace_prt(1, "  node mntpnt %s\t travpath %s\n",
1006 				    node->mapent->map_mntpnt, w);
1007 
1008 			/* replace mntpnt with travpath to clean extra '/' */
1009 			if (strlen(node->mapent->map_mntpnt) < strlen(w)) {
1010 				if (verbose) {
1011 					syslog(LOG_ERR,
1012 					"mark_level1_root: path=%s error",
1013 					    traversed_path);
1014 				}
1015 				return (PARSE_ERROR);
1016 			}
1017 			if (strcmp(node->mapent->map_mntpnt, w) != 0)
1018 				strcpy(node->mapent->map_mntpnt, w);
1019 			node->mapent->map_mntlevel = 1;
1020 		}
1021 		node = node->leveldir;
1022 	}
1023 	return (PARSE_OK);
1024 }
1025 
1026 /*
1027  * mark_and_fake_level1_noroot(hiernode *node, char *traversed_path,
1028  * 			char *key,char *mapname, struct mapent **faked_mapents,
1029  *			uint_t isdirect, char *mapopts)
1030  * Called if the root of the hierarchy does not point to a mapent. marks nodes
1031  * upto one physical level below the rootnode given by subdir. checks if
1032  * there's a real mapentry. If not, it builds a faked one (autonode) at that
1033  * point. The faked autonode is direct, with the map being the same as the
1034  * original one from which the call originated. Options are same as that of
1035  * the map and assigned in automount_opts(). Returns PARSE_OK or error value.
1036  */
1037 static int
1038 mark_and_fake_level1_noroot(hiernode *node, char *traversed_path,
1039 			char *key, char *mapname, struct mapent **faked_mapents,
1040 			uint_t isdirect, char *mapopts)
1041 {
1042 	struct mapent *me;
1043 	int rc = 0;
1044 	char faked_map_mntpnt[MAXPATHLEN];
1045 	char w1[MAXPATHLEN];
1046 	char w[MAXPATHLEN];
1047 
1048 	while (node != NULL) {
1049 		if (node->mapent != NULL) {
1050 			/*
1051 			 * existing mapentry at level 1 - copy travpath to
1052 			 * get rid of extra '/' in mntpnt
1053 			 */
1054 			sprintf(w, "%s/%s", traversed_path, node->dirname);
1055 			if (trace > 3)
1056 				trace_prt(1, "  node mntpnt=%s\t travpath=%s\n",
1057 				    node->mapent->map_mntpnt, w);
1058 			if (strlen(node->mapent->map_mntpnt) < strlen(w)) {
1059 				/* sanity check */
1060 				if (verbose)
1061 					syslog(LOG_ERR,
1062 					"mark_fake_level1_noroot:path=%s error",
1063 					    traversed_path);
1064 				return (PARSE_ERROR);
1065 			}
1066 			if (strcmp(node->mapent->map_mntpnt, w) != 0)
1067 				strcpy(node->mapent->map_mntpnt, w);
1068 			node->mapent->map_mntlevel = 1;
1069 		} else {
1070 			/*
1071 			 * build the faked autonode
1072 			 */
1073 			if ((me = (struct mapent *)malloc(sizeof (*me)))
1074 			    == NULL) {
1075 				syslog(LOG_ERR,
1076 				"mark_and_fake_level1_noroot: out of memory");
1077 				return (ENOMEM);
1078 			}
1079 			(void) memset((char *)me, 0, sizeof (*me));
1080 
1081 			if ((me->map_fs = (struct mapfs *)
1082 				malloc(sizeof (struct mapfs))) == NULL)
1083 				return (ENOMEM);
1084 			(void) memset(me->map_fs, 0, sizeof (struct mapfs));
1085 
1086 			if (isdirect) {
1087 				*w1 = '\0';
1088 			} else {
1089 				strcpy(w1, "/");
1090 				strcat(w1, key);
1091 			}
1092 			me->map_root = strdup(w1);
1093 
1094 			sprintf(faked_map_mntpnt, "%s/%s", traversed_path,
1095 				node->dirname);
1096 			me->map_mntpnt = strdup(faked_map_mntpnt);
1097 			me->map_fstype = strdup(MNTTYPE_AUTOFS);
1098 			me->map_mounter = strdup(MNTTYPE_AUTOFS);
1099 
1100 			/* set options */
1101 			if ((rc = automount_opts(&me->map_mntopts, mapopts))
1102 				!= PARSE_OK)
1103 				return (rc);
1104 			me->map_fs->mfs_dir = strdup(mapname);
1105 			me->map_mntlevel = 1;
1106 			me->map_modified = FALSE;
1107 			me->map_faked = TRUE;   /* mark as faked */
1108 			if (me->map_root == NULL ||
1109 			    me->map_mntpnt == NULL ||
1110 			    me->map_fstype == NULL ||
1111 			    me->map_mounter == NULL ||
1112 			    me->map_mntopts == NULL ||
1113 			    me->map_fs->mfs_dir == NULL) {
1114 				syslog(LOG_ERR,
1115 				"mark_and_fake_level1_noroot: out of memory");
1116 				free_mapent(*faked_mapents);
1117 				return (ENOMEM);
1118 			}
1119 
1120 			if (*faked_mapents == NULL)
1121 				*faked_mapents = me;
1122 			else {			/* attach to the head */
1123 				me->map_next = *faked_mapents;
1124 				*faked_mapents = me;
1125 			}
1126 			node->mapent = me;
1127 		}
1128 		node = node->leveldir;
1129 	}
1130 	return (rc);
1131 }
1132 
1133 /*
1134  * convert_mapent_to_automount(struct mapent *me, char *mapname,
1135  *				char *mapopts)
1136  * change the mapentry me to an automount - free fields first and NULL them
1137  * to avoid freeing again, while freeing the mapentry at a later stage.
1138  * Could have avoided freeing entries here as we don't really look at them.
1139  * Give the converted mapent entry the options that came with the map using
1140  * automount_opts(). Returns PARSE_OK or appropriate error value.
1141  */
1142 static int
1143 convert_mapent_to_automount(struct mapent *me, char *mapname,
1144 				char *mapopts)
1145 {
1146 	struct mapfs *mfs = me->map_fs;		/* assumes it exists */
1147 	int rc = PARSE_OK;
1148 
1149 	/* free relevant entries */
1150 	if (mfs->mfs_host) {
1151 		free(mfs->mfs_host);
1152 		mfs->mfs_host = NULL;
1153 	}
1154 	while (me->map_fs->mfs_next != NULL) {
1155 		mfs = me->map_fs->mfs_next;
1156 		if (mfs->mfs_host)
1157 			free(mfs->mfs_host);
1158 		if (mfs->mfs_dir)
1159 			free(mfs->mfs_dir);
1160 		me->map_fs->mfs_next = mfs->mfs_next;	/* nulls eventually */
1161 		free((void*)mfs);
1162 	}
1163 
1164 	/* replace relevant entries */
1165 	if (me->map_fstype)
1166 		free(me->map_fstype);
1167 	if ((me->map_fstype = strdup(MNTTYPE_AUTOFS)) == NULL)
1168 		goto alloc_failed;
1169 
1170 	if (me->map_mounter)
1171 		free(me->map_mounter);
1172 	if ((me->map_mounter = strdup(me->map_fstype)) == NULL)
1173 		goto alloc_failed;
1174 
1175 	if (me->map_fs->mfs_dir)
1176 		free(me->map_fs->mfs_dir);
1177 	if ((me->map_fs->mfs_dir = strdup(mapname)) == NULL)
1178 		goto alloc_failed;
1179 
1180 	/* set options */
1181 	if (me->map_mntopts)
1182 		free(me->map_mntopts);
1183 	if ((rc = automount_opts(&me->map_mntopts, mapopts)) != PARSE_OK)
1184 		return (rc);
1185 
1186 	/* mucked with this entry, set the map_modified field to TRUE */
1187 	me->map_modified = TRUE;
1188 
1189 	return (rc);
1190 
1191 alloc_failed:
1192 	syslog(LOG_ERR,
1193 		"convert_mapent_to_automount: Memory allocation failed");
1194 	return (ENOMEM);
1195 }
1196 
1197 /*
1198  * automount_opts(char **map_mntopts, char *mapopts)
1199  * modifies automount opts - gets rid of all "indirect" and "direct" strings
1200  * if they exist, and then adds a direct string to force a direct automount.
1201  * Rest of the mapopts stay intact. Returns PARSE_OK or appropriate error.
1202  */
1203 static int
1204 automount_opts(char **map_mntopts, char *mapopts)
1205 {
1206 	char *opts;
1207 	char *opt;
1208 	int len;
1209 	char *placeholder;
1210 	char buf[AUTOFS_MAXOPTSLEN];
1211 
1212 	char *addopt = "direct";
1213 
1214 	len = strlen(mapopts)+ strlen(addopt)+2;	/* +2 for ",", '\0' */
1215 	if (len > AUTOFS_MAXOPTSLEN) {
1216 		syslog(LOG_ERR,
1217 		"option string %s too long (max=%d)", mapopts,
1218 			AUTOFS_MAXOPTSLEN-8);
1219 		return (PARSE_ERROR);
1220 	}
1221 
1222 	if (((*map_mntopts) = ((char *)malloc(len))) == NULL) {
1223 		syslog(LOG_ERR,	"automount_opts: Memory allocation failed");
1224 		return (ENOMEM);
1225 	}
1226 	memset(*map_mntopts, 0, len);
1227 
1228 	strcpy(buf, mapopts);
1229 	opts = buf;
1230 	while ((opt = strtok_r(opts, ",", &placeholder)) != NULL) {
1231 		opts = NULL;
1232 
1233 		/* remove trailing and leading spaces */
1234 		while (isspace(*opt))
1235 			opt++;
1236 		len = strlen(opt)-1;
1237 		while (isspace(opt[len]))
1238 			opt[len--] = '\0';
1239 
1240 		/*
1241 		 * if direct or indirect found, get rid of it, else put it
1242 		 * back
1243 		 */
1244 		if ((strcmp(opt, "indirect") == 0) ||
1245 		    (strcmp(opt, "direct") == 0))
1246 			continue;
1247 		if (*map_mntopts[0] != '\0')
1248 			strcat(*map_mntopts, ",");
1249 		strcat(*map_mntopts, opt);
1250 	}
1251 
1252 	/* add the direct string at the end */
1253 	if (*map_mntopts[0] != '\0')
1254 		strcat(*map_mntopts,	",");
1255 	strcat(*map_mntopts, addopt);
1256 
1257 	return (PARSE_OK);
1258 }
1259 
1260 /*
1261  * parse_fsinfo(char *mapname, struct mapent *mapents)
1262  * parses the filesystem information stored in me->map_fsw and me->map_fswq
1263  * and calls appropriate filesystem parser.
1264  * Returns PARSE_OK or an appropriate error value.
1265  */
1266 static int
1267 parse_fsinfo(char *mapname, struct mapent *mapents)
1268 {
1269 	struct mapent *me = mapents;
1270 	char *bufp;
1271 	char *bufq;
1272 	int wordsz = MAXPATHLEN;
1273 	int err = 0;
1274 
1275 	while (me != NULL) {
1276 		bufp = "";
1277 		bufq = "";
1278 		if (strcmp(me->map_fstype, MNTTYPE_NFS) == 0) {
1279 			err = parse_nfs(mapname, me, me->map_fsw,
1280 				me->map_fswq, &bufp, &bufq, wordsz);
1281 		} else {
1282 			err = parse_special(me, me->map_fsw, me->map_fswq,
1283 				&bufp, &bufq, wordsz);
1284 		}
1285 
1286 		if (err != PARSE_OK || *me->map_fsw != '\0' ||
1287 		    *me->map_fswq != '\0') {
1288 			/* sanity check */
1289 			if (verbose)
1290 				syslog(LOG_ERR,
1291 				"parse_fsinfo: mount location error %s",
1292 				    me->map_fsw);
1293 			return (PARSE_ERROR);
1294 		}
1295 
1296 		me = me->map_next;
1297 	}
1298 
1299 	if (trace > 3) {
1300 		trace_mapents("parse_fsinfo:", mapents);
1301 	}
1302 
1303 	return (PARSE_OK);
1304 }
1305 
1306 /*
1307  * This function parses the map entry for a nfs type file system
1308  * The input is the string lp (and lq) which can be one of the
1309  * following forms:
1310  * a) host[(penalty)][,host[(penalty)]]... :/directory
1311  * b) host[(penalty)]:/directory[ host[(penalty)]:/directory]...
1312  * This routine constructs a mapfs link-list for each of
1313  * the hosts and the corresponding file system. The list
1314  * is then attatched to the mapent struct passed in.
1315  */
1316 int
1317 parse_nfs(mapname, me, fsw, fswq, lp, lq, wsize)
1318 	struct mapent *me;
1319 	char *mapname, *fsw, *fswq, **lp, **lq;
1320 	int wsize;
1321 {
1322 	struct mapfs *mfs, **mfsp;
1323 	char *wlp, *wlq;
1324 	char *hl, hostlist[1024], *hlq, hostlistq[1024];
1325 	char hostname_and_penalty[MXHOSTNAMELEN+5];
1326 	char *hn, *hnq, hostname[MXHOSTNAMELEN+1];
1327 	char dirname[MAXPATHLEN+1], subdir[MAXPATHLEN+1];
1328 	char qbuff[MAXPATHLEN+1], qbuff1[MAXPATHLEN+1];
1329 	char pbuff[10], pbuffq[10];
1330 	int penalty;
1331 	char w[MAXPATHLEN];
1332 	char wq[MAXPATHLEN];
1333 	int host_cnt;
1334 
1335 	mfsp = &me->map_fs;
1336 	*mfsp = NULL;
1337 
1338 	/*
1339 	 * there may be more than one entry in the map list. Get the
1340 	 * first one. Use temps to handle the word information and
1341 	 * copy back into fsw and fswq fields when done.
1342 	 */
1343 	*lp = fsw;
1344 	*lq = fswq;
1345 	if (getword(w, wq, lp, lq, ' ', wsize) == -1)
1346 		return (PARSE_ERROR);
1347 	while (*w && *w != '/') {
1348 		bool_t maybe_url;
1349 
1350 		maybe_url = TRUE;
1351 
1352 		wlp = w; wlq = wq;
1353 		if (getword(hostlist, hostlistq, &wlp, &wlq, ':',
1354 			    sizeof (hostlist)) == -1)
1355 			return (PARSE_ERROR);
1356 		if (!*hostlist)
1357 			goto bad_entry;
1358 
1359 		if (strcmp(hostlist, "nfs") != 0)
1360 			maybe_url = FALSE;
1361 
1362 		if (getword(dirname, qbuff, &wlp, &wlq, ':',
1363 					sizeof (dirname)) == -1)
1364 			return (PARSE_ERROR);
1365 		if (*dirname == '\0')
1366 			goto bad_entry;
1367 
1368 		if (maybe_url == TRUE && strncmp(dirname, "//", 2) != 0)
1369 			maybe_url = FALSE;
1370 
1371 		/*
1372 		 * See the next block comment ("Once upon a time ...") to
1373 		 * understand this. It turns the deprecated concept
1374 		 * of "subdir mounts" produced some useful code for handling
1375 		 * the possibility of a ":port#" in the URL.
1376 		 */
1377 		if (maybe_url == FALSE)
1378 			*subdir = '/';
1379 		else
1380 			*subdir = ':';
1381 
1382 		*qbuff = ' ';
1383 
1384 		/*
1385 		 * Once upon time, before autofs, there was support for
1386 		 * "subdir mounts". The idea was to "economize" the
1387 		 * number of mounts, so if you had a number of entries
1388 		 * all referring to a common subdirectory, e.g.
1389 		 *
1390 		 *	carol    seasons:/export/home11/carol
1391 		 *	ted	 seasons:/export/home11/ted
1392 		 *	alice	 seasons:/export/home11/alice
1393 		 *
1394 		 * then you could tell the automounter to mount a
1395 		 * common mountpoint which was delimited by the second
1396 		 * colon:
1397 		 *
1398 		 *	carol    seasons:/export/home11:carol
1399 		 *	ted	 seasons:/export/home11:ted
1400 		 *	alice	 seasons:/export/home11:alice
1401 		 *
1402 		 * The automounter would mount seasons:/export/home11
1403 		 * then for any other map entry that referenced the same
1404 		 * directory it would build a symbolic link that
1405 		 * appended the remainder of the path after the second
1406 		 * colon, i.e.  once the common subdir was mounted, then
1407 		 * other directories could be accessed just by link
1408 		 * building - no further mounts required.
1409 		 *
1410 		 * In theory the "mount saving" idea sounded good. In
1411 		 * practice the saving didn't amount to much and the
1412 		 * symbolic links confused people because the common
1413 		 * mountpoint had to have a pseudonym.
1414 		 *
1415 		 * To remain backward compatible with the existing
1416 		 * maps, we interpret a second colon as a slash.
1417 		 */
1418 		if (getword(subdir+1, qbuff+1, &wlp, &wlq, ':',
1419 				sizeof (subdir)) == -1)
1420 			return (PARSE_ERROR);
1421 
1422 		if (*(subdir+1))
1423 			(void) strcat(dirname, subdir);
1424 
1425 		hl = hostlist; hlq = hostlistq;
1426 
1427 		host_cnt = 0;
1428 		for (;;) {
1429 
1430 			if (getword(hostname_and_penalty, qbuff, &hl, &hlq, ',',
1431 				sizeof (hostname_and_penalty)) == -1)
1432 				return (PARSE_ERROR);
1433 			if (!*hostname_and_penalty)
1434 				break;
1435 
1436 			host_cnt++;
1437 			if (host_cnt > 1)
1438 				maybe_url = FALSE;
1439 
1440 			hn = hostname_and_penalty;
1441 			hnq = qbuff;
1442 			if (getword(hostname, qbuff1, &hn, &hnq, '(',
1443 				sizeof (hostname)) == -1)
1444 				return (PARSE_ERROR);
1445 			if (hostname[0] == '\0')
1446 				goto bad_entry;
1447 
1448 			if (strcmp(hostname, hostname_and_penalty) == 0) {
1449 				penalty = 0;
1450 			} else {
1451 				maybe_url = FALSE;
1452 				hn++; hnq++;
1453 				if (getword(pbuff, pbuffq, &hn, &hnq, ')',
1454 					sizeof (pbuff)) == -1)
1455 					return (PARSE_ERROR);
1456 				if (!*pbuff)
1457 					penalty = 0;
1458 				else
1459 					penalty = atoi(pbuff);
1460 			}
1461 			mfs = (struct mapfs *)malloc(sizeof (*mfs));
1462 			if (mfs == NULL) {
1463 				syslog(LOG_ERR,
1464 				"parse_nfs: Memory allocation failed");
1465 				return (PARSE_ERROR);
1466 			}
1467 			(void) memset(mfs, 0, sizeof (*mfs));
1468 			*mfsp = mfs;
1469 			mfsp = &mfs->mfs_next;
1470 
1471 			if (maybe_url == TRUE) {
1472 				char *host;
1473 				char *path;
1474 				char *sport;
1475 
1476 				host = dirname+2;
1477 				path = strchr(host, '/');
1478 				if (path == NULL) {
1479 					syslog(LOG_ERR,
1480 					"parse_nfs: illegal nfs url syntax: %s",
1481 					host);
1482 
1483 					return (PARSE_ERROR);
1484 				}
1485 				*path = '\0';
1486 				sport =  strchr(host, ':');
1487 
1488 				if (sport != NULL && sport < path) {
1489 					*sport = '\0';
1490 					mfs->mfs_port = atoi(sport+1);
1491 
1492 					if (mfs->mfs_port > USHRT_MAX) {
1493 						syslog(LOG_ERR,
1494 							"parse_nfs: invalid "
1495 							"port number (%d) in "
1496 							"NFS URL",
1497 							mfs->mfs_port);
1498 
1499 						return (PARSE_ERROR);
1500 					}
1501 
1502 				}
1503 
1504 				path++;
1505 				if (*path == '\0')
1506 					path = ".";
1507 
1508 				mfs->mfs_flags |= MFS_URL;
1509 
1510 				mfs->mfs_host = strdup(host);
1511 				mfs->mfs_dir = strdup(path);
1512 			} else {
1513 				mfs->mfs_host = strdup(hostname);
1514 				mfs->mfs_dir = strdup(dirname);
1515 			}
1516 
1517 			mfs->mfs_penalty = penalty;
1518 			if (mfs->mfs_host == NULL || mfs->mfs_dir == NULL) {
1519 				syslog(LOG_ERR,
1520 				"parse_nfs: Memory allocation failed");
1521 				return (PARSE_ERROR);
1522 			}
1523 		}
1524 		/*
1525 		 * We check host_cnt to make sure we haven't parsed an entry
1526 		 * with no host information.
1527 		 */
1528 		if (host_cnt == 0) {
1529 			syslog(LOG_ERR,
1530 			"parse_nfs: invalid host specified - bad entry "
1531 			"in map %s \"%s\"",
1532 			mapname, w);
1533 			return (PARSE_ERROR);
1534 		}
1535 		if (getword(w, wq, lp, lq, ' ', wsize) == -1)
1536 			return (PARSE_ERROR);
1537 	}
1538 
1539 	strcpy(fsw, w);
1540 	strcpy(fswq, wq);
1541 
1542 	return (PARSE_OK);
1543 
1544 bad_entry:
1545 	syslog(LOG_ERR, "parse_nfs: bad entry in map %s \"%s\"", mapname, w);
1546 	return (PARSE_ERROR);
1547 }
1548 
1549 static int
1550 parse_special(me, w, wq, lp, lq, wsize)
1551 	struct mapent *me;
1552 	char *w, *wq, **lp, **lq;
1553 	int wsize;
1554 {
1555 	char devname[MAXPATHLEN + 1], qbuf[MAXPATHLEN + 1];
1556 	char *wlp, *wlq;
1557 	struct mapfs *mfs;
1558 
1559 	wlp = w;
1560 	wlq = wq;
1561 	if (getword(devname, qbuf, &wlp, &wlq, ' ', sizeof (devname)) == -1)
1562 		return (PARSE_ERROR);
1563 	if (devname[0] == '\0')
1564 		return (PARSE_ERROR);
1565 
1566 	mfs = (struct mapfs *)malloc(sizeof (struct mapfs));
1567 	if (mfs == NULL)
1568 		return (PARSE_ERROR);
1569 	(void) memset(mfs, 0, sizeof (*mfs));
1570 
1571 	/*
1572 	 * A device name that begins with a slash could
1573 	 * be confused with a mountpoint path, hence use
1574 	 * a colon to escape a device string that begins
1575 	 * with a slash, e.g.
1576 	 *
1577 	 *	foo  -ro  /bar  foo:/bar
1578 	 * and
1579 	 *	foo  -ro  /dev/sr0
1580 	 *
1581 	 * would confuse the parser.  The second instance
1582 	 * must use a colon:
1583 	 *
1584 	 *	foo  -ro  :/dev/sr0
1585 	 */
1586 	mfs->mfs_dir = strdup(&devname[devname[0] == ':']);
1587 	if (mfs->mfs_dir == NULL)
1588 		return (PARSE_ERROR);
1589 	me->map_fs = mfs;
1590 	if (getword(w, wq, lp, lq, ' ', wsize) == -1)
1591 		return (PARSE_ERROR);
1592 	return (0);
1593 }
1594 
1595 /*
1596  * get_dir_from_path(char *dir, char **path, int dirsz)
1597  * gets the directory name dir from path for max string of length dirsz.
1598  * A modification of the getword routine. Assumes the delimiter is '/'
1599  * and that excess /'s are redundant.
1600  * Returns PARSE_OK or PARSE_ERROR
1601  */
1602 static int
1603 get_dir_from_path(char *dir, char **path, int dirsz)
1604 {
1605 	char *tmp = dir;
1606 	int count = dirsz;
1607 
1608 	if (dirsz <= 0) {
1609 		if (verbose)
1610 			syslog(LOG_ERR,
1611 			"get_dir_from_path: invalid directory size %d", dirsz);
1612 		return (PARSE_ERROR);
1613 	}
1614 
1615 	/* get rid of leading /'s in path */
1616 	while (**path == '/')
1617 		(*path)++;
1618 
1619 	/* now at a word or at the end of path */
1620 	while ((**path) && ((**path) != '/')) {
1621 		if (--count <= 0) {
1622 			*tmp = '\0';
1623 			syslog(LOG_ERR,
1624 			"get_dir_from_path: max pathlength exceeded %d", dirsz);
1625 			return (PARSE_ERROR);
1626 		}
1627 		*dir++ = *(*path)++;
1628 	}
1629 
1630 	*dir = '\0';
1631 
1632 	/* get rid of trailing /'s in path */
1633 	while (**path == '/')
1634 		(*path)++;
1635 
1636 	return (PARSE_OK);
1637 }
1638 
1639 /*
1640  * alloc_hiernode(hiernode **newnode, char *dirname)
1641  * allocates a new hiernode corresponding to a new directory entry
1642  * in the hierarchical structure, and passes a pointer to it back
1643  * to the calling program.
1644  * Returns PARSE_OK or appropriate error value.
1645  */
1646 static int
1647 alloc_hiernode(hiernode **newnode, char *dirname)
1648 {
1649 	if ((*newnode = (hiernode *)malloc(sizeof (hiernode))) == NULL) {
1650 		syslog(LOG_ERR,	"alloc_hiernode: Memory allocation failed");
1651 		return (ENOMEM);
1652 	}
1653 
1654 	memset(((char *)*newnode), 0, sizeof (hiernode));
1655 	strcpy(((*newnode)->dirname), dirname);
1656 	return (PARSE_OK);
1657 }
1658 
1659 /*
1660  * free_hiernode(hiernode *node)
1661  * frees the allocated hiernode given the head of the structure
1662  * recursively calls itself until it frees entire structure.
1663  * Returns nothing.
1664  */
1665 static void
1666 free_hiernode(hiernode *node)
1667 {
1668 	hiernode *currnode = node;
1669 	hiernode *prevnode = NULL;
1670 
1671 	while (currnode != NULL) {
1672 		if (currnode->subdir != NULL)
1673 			free_hiernode(currnode->subdir);
1674 		prevnode = currnode;
1675 		currnode = currnode->leveldir;
1676 		free((void*)prevnode);
1677 	}
1678 }
1679 
1680 /*
1681  * free_mapent(struct mapent *)
1682  * free the mapentry and its fields
1683  */
1684 void
1685 free_mapent(me)
1686 	struct mapent *me;
1687 {
1688 	struct mapfs *mfs;
1689 	struct mapent *m;
1690 
1691 	while (me) {
1692 		while (me->map_fs) {
1693 			mfs = me->map_fs;
1694 			if (mfs->mfs_host)
1695 				free(mfs->mfs_host);
1696 			if (mfs->mfs_dir)
1697 				free(mfs->mfs_dir);
1698 			if (mfs->mfs_args)
1699 				free(mfs->mfs_args);
1700 			if (mfs->mfs_nconf)
1701 				freenetconfigent(mfs->mfs_nconf);
1702 			me->map_fs = mfs->mfs_next;
1703 			free((char *)mfs);
1704 		}
1705 
1706 		if (me->map_root)
1707 			free(me->map_root);
1708 		if (me->map_mntpnt)
1709 			free(me->map_mntpnt);
1710 		if (me->map_mntopts)
1711 			free(me->map_mntopts);
1712 		if (me->map_fstype)
1713 			free(me->map_fstype);
1714 		if (me->map_mounter)
1715 			free(me->map_mounter);
1716 		if (me->map_fsw)
1717 			free(me->map_fsw);
1718 		if (me->map_fswq)
1719 			free(me->map_fswq);
1720 
1721 		m = me;
1722 		me = me->map_next;
1723 		free((char *)m);
1724 	}
1725 }
1726 
1727 /*
1728  * trace_mapents(struct mapent *mapents)
1729  * traces through the mapentry structure and prints it element by element
1730  * returns nothing
1731  */
1732 static void
1733 trace_mapents(char *s, struct mapent *mapents)
1734 {
1735 	struct mapfs  *mfs;
1736 	struct mapent *me;
1737 
1738 	trace_prt(1, "\n\t%s\n", s);
1739 	for (me = mapents; me; me = me->map_next) {
1740 		trace_prt(1, "  (%s,%s)\t %s%s -%s\n",
1741 			me->map_fstype ? me->map_fstype : "",
1742 			me->map_mounter ? me->map_mounter : "",
1743 			me->map_root  ? me->map_root : "",
1744 			me->map_mntpnt ? me->map_mntpnt : "",
1745 			me->map_mntopts ? me->map_mntopts : "");
1746 		for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next)
1747 			trace_prt(0, "\t\t%s:%s\n",
1748 				mfs->mfs_host ? mfs->mfs_host: "",
1749 				mfs->mfs_dir ? mfs->mfs_dir : "");
1750 
1751 		trace_prt(1, "\tme->map_fsw=%s\n",
1752 			me->map_fsw ? me->map_fsw:"",
1753 			me->map_fswq ? me->map_fsw:"");
1754 		trace_prt(1, "\t mntlevel=%d\t%s\t%s err=%d\n",
1755 			me->map_mntlevel,
1756 			me->map_modified ? "modify=TRUE":"modify=FALSE",
1757 			me->map_faked ? "faked=TRUE":"faked=FALSE",
1758 			me->map_err);
1759 	}
1760 }
1761 
1762 /*
1763  * trace_hierarchy(hiernode *node)
1764  * traces the allocated hiernode given the head of the structure
1765  * recursively calls itself until it traces entire structure.
1766  * the first call made at the root is made with a zero level.
1767  * nodelevel is simply used to print tab and make the tracing clean.
1768  * Returns nothing.
1769  */
1770 static void
1771 trace_hierarchy(hiernode *node, int nodelevel)
1772 {
1773 	hiernode *currnode = node;
1774 	int i;
1775 
1776 	while (currnode != NULL) {
1777 		if (currnode->subdir != NULL) {
1778 			for (i = 0; i < nodelevel; i++)
1779 				trace_prt(0, "\t");
1780 			trace_prt(0, "\t(%s, ",
1781 				currnode->dirname ? currnode->dirname :"");
1782 			if (currnode->mapent) {
1783 				trace_prt(0, "%d, %s)\n",
1784 					currnode->mapent->map_mntlevel,
1785 					currnode->mapent->map_mntopts ?
1786 					currnode->mapent->map_mntopts:"");
1787 			}
1788 			else
1789 				trace_prt(0, " ,)\n");
1790 			nodelevel++;
1791 			trace_hierarchy(currnode->subdir, nodelevel);
1792 		} else {
1793 			for (i = 0; i < nodelevel; i++)
1794 				trace_prt(0, "\t");
1795 			trace_prt(0, "\t(%s, ",
1796 				currnode->dirname ? currnode->dirname :"");
1797 			if (currnode->mapent) {
1798 				trace_prt(0, "%d, %s)\n",
1799 					currnode->mapent->map_mntlevel,
1800 					currnode->mapent->map_mntopts ?
1801 					currnode->mapent->map_mntopts:"");
1802 			}
1803 			else
1804 				trace_prt(0, ", )\n");
1805 		}
1806 		currnode = currnode->leveldir;
1807 	}
1808 }
1809 
1810 struct mapent *
1811 do_mapent_hosts(mapopts, host, isdirect)
1812 	char *mapopts, *host;
1813 	uint_t isdirect;
1814 {
1815 	CLIENT *cl;
1816 	struct mapent *me, *ms, *mp;
1817 	struct mapfs *mfs;
1818 	struct exportnode *ex = NULL;
1819 	struct exportnode *exlist, *texlist, **texp, *exnext;
1820 	struct timeval timeout;
1821 	enum clnt_stat clnt_stat;
1822 	char name[MAXPATHLEN];
1823 	char entryopts[MAXOPTSLEN];
1824 	char fstype[32], mounter[32];
1825 	int exlen, duplicate;
1826 	struct mnttab mb;	/* needed for hasmntopt() to get nfs version */
1827 	rpcvers_t nfsvers;	/* version in map options, 0 if not there */
1828 	rpcvers_t vers, versmin; /* used to negotiate nfs vers in pingnfs() */
1829 	int retries, delay;
1830 	int foundvers;
1831 
1832 	if (trace > 1)
1833 		trace_prt(1, "  do_mapent_hosts: host %s\n", host);
1834 
1835 	/* check for special case: host is me */
1836 
1837 	if (self_check(host)) {
1838 		ms = (struct mapent *)malloc(sizeof (*ms));
1839 		if (ms == NULL)
1840 			goto alloc_failed;
1841 		(void) memset((char *)ms, 0, sizeof (*ms));
1842 		(void) strcpy(fstype, MNTTYPE_NFS);
1843 		get_opts(mapopts, entryopts, fstype, NULL);
1844 		ms->map_mntopts = strdup(entryopts);
1845 		if (ms->map_mntopts == NULL)
1846 			goto alloc_failed;
1847 		ms->map_mounter = strdup(fstype);
1848 		if (ms->map_mounter == NULL)
1849 			goto alloc_failed;
1850 		ms->map_fstype = strdup(MNTTYPE_NFS);
1851 		if (ms->map_fstype == NULL)
1852 			goto alloc_failed;
1853 
1854 		if (isdirect)
1855 			name[0] = '\0';
1856 		else {
1857 			(void) strcpy(name, "/");
1858 			(void) strcat(name, host);
1859 		}
1860 		ms->map_root = strdup(name);
1861 		if (ms->map_root == NULL)
1862 			goto alloc_failed;
1863 		ms->map_mntpnt = strdup("");
1864 		if (ms->map_mntpnt == NULL)
1865 			goto alloc_failed;
1866 		mfs = (struct mapfs *)malloc(sizeof (*mfs));
1867 		if (mfs == NULL)
1868 			goto alloc_failed;
1869 		(void) memset((char *)mfs, 0, sizeof (*mfs));
1870 		ms->map_fs = mfs;
1871 		mfs->mfs_host = strdup(host);
1872 		if (mfs->mfs_host == NULL)
1873 			goto alloc_failed;
1874 		mfs->mfs_dir  = strdup("/");
1875 		if (mfs->mfs_dir == NULL)
1876 			goto alloc_failed;
1877 
1878 		/* initialize mntlevel and modify */
1879 		ms->map_mntlevel = -1;
1880 		ms->map_modified = FALSE;
1881 		ms->map_faked = FALSE;
1882 
1883 		if (trace > 1)
1884 			trace_prt(1,
1885 			"  do_mapent_hosts: self-host %s OK\n", host);
1886 
1887 		return (ms);
1888 	}
1889 
1890 	/*
1891 	 * Call pingnfs. Note that we can't have replicated hosts in /net.
1892 	 * XXX - we would like to avoid duplicating the across the wire calls
1893 	 * made here in nfsmount(). The pingnfs cache should help avoid it.
1894 	 */
1895 	mb.mnt_mntopts = mapopts;
1896 	foundvers = nopt(&mb, MNTOPT_VERS, (int *)&nfsvers);
1897 	if (!foundvers)
1898 		nfsvers = 0;
1899 	if (set_versrange(nfsvers, &vers, &versmin) != 0) {
1900 		syslog(LOG_ERR, "Incorrect NFS version specified for %s", host);
1901 		return ((struct mapent *)NULL);
1902 	}
1903 	if (pingnfs(host, get_retry(mapopts) + 1, &vers, versmin, 0, FALSE,
1904 	    NULL, NULL) != RPC_SUCCESS)
1905 		return ((struct mapent *)NULL);
1906 
1907 	retries = get_retry(mapopts);
1908 	delay = INITDELAY;
1909 retry:
1910 	/* get export list of host */
1911 	cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "circuit_v");
1912 	if (cl == NULL) {
1913 		cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "datagram_v");
1914 		if (cl == NULL) {
1915 			syslog(LOG_ERR,
1916 			"do_mapent_hosts: %s %s", host, clnt_spcreateerror(""));
1917 			return ((struct mapent *)NULL);
1918 		}
1919 
1920 	}
1921 #ifdef MALLOC_DEBUG
1922 	add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__);
1923 	add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
1924 		__FILE__, __LINE__);
1925 #endif
1926 
1927 	timeout.tv_usec = 0;
1928 	timeout.tv_sec  = 25;
1929 	if (clnt_stat = clnt_call(cl, MOUNTPROC_EXPORT, xdr_void, 0,
1930 				xdr_exports, (caddr_t)&ex, timeout)) {
1931 
1932 		if (retries-- > 0) {
1933 			clnt_destroy(cl);
1934 			DELAY(delay);
1935 			goto retry;
1936 		}
1937 
1938 		syslog(LOG_ERR,
1939 			"do_mapent_hosts: %s: export list: %s",
1940 			host, clnt_sperrno(clnt_stat));
1941 #ifdef MALLOC_DEBUG
1942 		drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__);
1943 		drop_alloc("AUTH_HANDLE", cl->cl_auth,
1944 			__FILE__, __LINE__);
1945 #endif
1946 		clnt_destroy(cl);
1947 		return ((struct mapent *)NULL);
1948 	}
1949 
1950 #ifdef MALLOC_DEBUG
1951 	drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__);
1952 	drop_alloc("AUTH_HANDLE", cl->cl_auth,
1953 		__FILE__, __LINE__);
1954 #endif
1955 	clnt_destroy(cl);
1956 
1957 	if (ex == NULL) {
1958 		if (trace > 1)
1959 			trace_prt(1,
1960 			    gettext("  getmapent_hosts: null export list\n"));
1961 		return ((struct mapent *)NULL);
1962 	}
1963 
1964 	/* now sort by length of names - to get mount order right */
1965 	exlist = ex;
1966 	texlist = NULL;
1967 #ifdef lint
1968 	exnext = NULL;
1969 #endif
1970 	for (; ex; ex = exnext) {
1971 		exnext = ex->ex_next;
1972 		exlen = strlen(ex->ex_dir);
1973 		duplicate = 0;
1974 		for (texp = &texlist; *texp; texp = &((*texp)->ex_next)) {
1975 			if (exlen < (int)strlen((*texp)->ex_dir))
1976 				break;
1977 			duplicate = (strcmp(ex->ex_dir, (*texp)->ex_dir) == 0);
1978 			if (duplicate) {
1979 				/* disregard duplicate entry */
1980 				freeex_ent(ex);
1981 				break;
1982 			}
1983 		}
1984 		if (!duplicate) {
1985 			ex->ex_next = *texp;
1986 			*texp = ex;
1987 		}
1988 	}
1989 	exlist = texlist;
1990 
1991 	/*
1992 	 * The following ugly chunk of code crept in as
1993 	 * a result of cachefs.  If it's a cachefs mount
1994 	 * of an nfs filesystem, then have it handled as
1995 	 * an nfs mount but have cachefs do the mount.
1996 	 */
1997 	(void) strcpy(fstype, MNTTYPE_NFS);
1998 	get_opts(mapopts, entryopts, fstype, NULL);
1999 	(void) strcpy(mounter, fstype);
2000 	if (strcmp(fstype, MNTTYPE_CACHEFS) == 0) {
2001 		struct mnttab m;
2002 		char *p;
2003 
2004 		m.mnt_mntopts = entryopts;
2005 		if ((p = hasmntopt(&m, "backfstype")) != NULL) {
2006 			int len = strlen(MNTTYPE_NFS);
2007 
2008 			p += 11;
2009 			if (strncmp(p, MNTTYPE_NFS, len) == 0 &&
2010 			    (p[len] == '\0' || p[len] == ',')) {
2011 				/*
2012 				 * Cached nfs mount
2013 				 */
2014 				(void) strcpy(fstype, MNTTYPE_NFS);
2015 				(void) strcpy(mounter, MNTTYPE_CACHEFS);
2016 			}
2017 		}
2018 	}
2019 
2020 	/* Now create a mapent from the export list */
2021 	ms = NULL;
2022 	me = NULL;
2023 
2024 	for (ex = exlist; ex; ex = ex->ex_next) {
2025 		mp = me;
2026 		me = (struct mapent *)malloc(sizeof (*me));
2027 		if (me == NULL)
2028 			goto alloc_failed;
2029 		(void) memset((char *)me, 0, sizeof (*me));
2030 
2031 		if (ms == NULL)
2032 			ms = me;
2033 		else
2034 			mp->map_next = me;
2035 
2036 		if (isdirect)
2037 			name[0] = '\0';
2038 		else {
2039 			(void) strcpy(name, "/");
2040 			(void) strcat(name, host);
2041 		}
2042 		me->map_root = strdup(name);
2043 		if (me->map_root == NULL)
2044 			goto alloc_failed;
2045 
2046 		*name = '\0';
2047 		if (strcmp(ex->ex_dir, "/") != 0) {
2048 			if (*(ex->ex_dir) != '/')
2049 				(void) strcpy(name, "/");
2050 			(void) strcat(name, ex->ex_dir);
2051 		}
2052 		me->map_mntpnt = strdup(name);
2053 		if (me->map_mntpnt == NULL)
2054 			goto alloc_failed;
2055 
2056 		me->map_fstype = strdup(fstype);
2057 		if (me->map_fstype == NULL)
2058 			goto alloc_failed;
2059 		me->map_mounter = strdup(mounter);
2060 		if (me->map_mounter == NULL)
2061 			goto alloc_failed;
2062 		me->map_mntopts = strdup(entryopts);
2063 		if (me->map_mntopts == NULL)
2064 			goto alloc_failed;
2065 
2066 		mfs = (struct mapfs *)malloc(sizeof (*mfs));
2067 		if (mfs == NULL)
2068 			goto alloc_failed;
2069 		(void) memset((char *)mfs, 0, sizeof (*mfs));
2070 		me->map_fs = mfs;
2071 		mfs->mfs_host = strdup(host);
2072 		if (mfs->mfs_host == NULL)
2073 			goto alloc_failed;
2074 		mfs->mfs_dir = strdup(ex->ex_dir);
2075 		if (mfs->mfs_dir == NULL)
2076 			goto alloc_failed;
2077 
2078 		/* initialize mntlevel and modify values */
2079 		me->map_mntlevel = -1;
2080 		me->map_modified = FALSE;
2081 		me->map_faked = FALSE;
2082 	}
2083 	freeex(exlist);
2084 
2085 	if (trace > 1)
2086 		trace_prt(1, "  do_mapent_hosts: host %s OK\n", host);
2087 
2088 	return (ms);
2089 
2090 alloc_failed:
2091 	syslog(LOG_ERR, "do_mapent_hosts: Memory allocation failed");
2092 	free_mapent(ms);
2093 	freeex(exlist);
2094 	return ((struct mapent *)NULL);
2095 }
2096 
2097 
2098 static void
2099 freeex_ent(ex)
2100 	struct exportnode *ex;
2101 {
2102 	struct groupnode *groups, *tmpgroups;
2103 
2104 	free(ex->ex_dir);
2105 	groups = ex->ex_groups;
2106 	while (groups) {
2107 		free(groups->gr_name);
2108 		tmpgroups = groups->gr_next;
2109 		free((char *)groups);
2110 		groups = tmpgroups;
2111 	}
2112 	free((char *)ex);
2113 }
2114 
2115 static void
2116 freeex(ex)
2117 	struct exportnode *ex;
2118 {
2119 	struct exportnode *tmpex;
2120 
2121 	while (ex) {
2122 		tmpex = ex->ex_next;
2123 		freeex_ent(ex);
2124 		ex = tmpex;
2125 	}
2126 }
2127 
2128 static const char uatfs_err[] = "submount under fstype=autofs not supported";
2129 /*
2130  * dump_mapent_err(struct mapent *me, char *key, char *mapname)
2131  * syslog appropriate error in mapentries.
2132  */
2133 static void dump_mapent_err(struct mapent *me, char *key, char *mapname)
2134 {
2135 	switch (me->map_err) {
2136 	case MAPENT_NOERR:
2137 		if (verbose)
2138 			syslog(LOG_ERR,
2139 			"map=%s key=%s mntpnt=%s: no error");
2140 		break;
2141 	case MAPENT_UATFS:
2142 		syslog(LOG_ERR,
2143 		"mountpoint %s in map %s key %s not mounted: %s",
2144 		    me->map_mntpnt, mapname, key, uatfs_err);
2145 		break;
2146 	default:
2147 		if (verbose)
2148 			syslog(LOG_ERR,
2149 			"map=%s key=%s mntpnt=%s: unknown mapentry error");
2150 	}
2151 }
2152