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