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