1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 *	autod_parse.c
24 *
25 *	Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
26 *	Use is subject to license terms.
27 */
28
29/*
30 * Portions Copyright 2007-2012 Apple Inc.
31 */
32
33#pragma ident	"@(#)autod_parse.c	1.31	05/06/08 SMI"
34
35#include <stdio.h>
36#include <ctype.h>
37#include <string.h>
38#include <syslog.h>
39#include <sys/types.h>
40#include <sys/stat.h>
41#include <sys/param.h>
42#include <errno.h>
43#include <pwd.h>
44#include <mntopts.h>
45#include <netinet/in.h>
46#include <netdb.h>
47#include <fstab.h>
48#include <locale.h>
49#include <stdlib.h>
50#include <unistd.h>
51#include <oncrpc/rpc.h>
52#include <rpcsvc/mount.h>
53#include <fcntl.h>
54#include <limits.h>
55
56#include "automount.h"
57#include "auto_mntopts.h"
58
59/*
60 * This structure is used to determine the hierarchical
61 * relationship between directories
62 */
63struct _hiernode
64{
65	char dirname[MAXFILENAMELEN+1];
66	struct _hiernode *subdir;
67	struct _hiernode *leveldir;
68	struct mapent *mapent;
69};
70typedef struct _hiernode hiernode;
71
72void free_mapent(struct mapent *);
73
74static int mapline_to_mapent(struct mapent **, struct mapline *, const char *,
75				const char *, const char *, char *, uint_t);
76static int hierarchical_sort(struct mapent *, hiernode **, const char *,
77			const char *);
78static int push_options(hiernode *, char *, const char *, int);
79static int set_mapent_opts(struct mapent *, char *, const char *);
80static int get_opts(const char *, char *, char *, size_t, bool_t *);
81static int fstype_opts(struct mapent *, const char *, const char *,
82			const char *);
83static int modify_mapents(struct mapent **, const char *, const char *,
84			const char *, hiernode *, const char *, uint_t, bool_t);
85static int set_and_fake_mapent_mntlevel(hiernode *, const char *, const char *,
86				const char *, struct mapent **, uint_t,
87				const char *, bool_t);
88static int mark_level1_root(hiernode *, char *);
89static int mark_and_fake_level1_noroot(hiernode *, const char *, const char *,
90				    const char *, struct mapent **, uint_t i,
91				    const char *);
92static int convert_mapent_to_automount(struct mapent *, const char *,
93				const char *);
94static int automount_opts(char **, const char *);
95static int parse_fsinfo(const char *, struct mapent *);
96static int is_nonnfs_url(char *, char *);
97static int parse_nfs(const char *, struct mapent *, char *, char *, char **,
98				char **);
99static int parse_special(struct mapent *, char *, char *, char **, char **,
100				int);
101static int get_dir_from_path(char *, const char **, int);
102static int alloc_hiernode(hiernode **, char *);
103static void free_hiernode(hiernode *);
104static void trace_mapents(char *, struct mapent *);
105static void trace_hierarchy(hiernode *, int);
106static struct mapent *do_mapent_hosts(const char *, const char *, uint_t,
107			int *);
108static void freeex_ent(struct exportnode *);
109static void freeex(struct exportnode *);
110static struct mapent *do_mapent_fstab(const char *, const char *, uint_t,
111				int *, int *);
112static struct mapent *do_mapent_static(const char *, uint_t, int *);
113static void dump_mapent_err(struct mapent *, const char *, const char *);
114
115#define	PARSE_OK	0
116#define	PARSE_ERROR	-1
117#define	MAX_FSLEN	32
118
119/*
120 * mapentry error type defininitions
121 */
122#define	MAPENT_NOERR	0
123#define	MAPENT_UATFS	1
124
125/*
126 * Make a copy of a string, if it's not too long, and save a pointer to
127 * it in the pointed-to location.  The maximum length includes the
128 * null terminator (e.g., a value of 2 means the longest string can
129 * have only 1 character).
130 *
131 * Return 0 on success, ENAMETOOLONG if the string is too long, and
132 * ENOMEM if we can't allocate memory for the copy.
133 */
134static int
135strldup(char **dstp, const char *str, size_t maxlen)
136{
137	size_t len;
138	char *dst;
139
140	len = strlen(str) + 1;
141	if (len > maxlen)
142		return (ENAMETOOLONG);
143	dst = malloc(len);
144	if (dst == NULL)
145		return (ENOMEM);
146	memcpy(dst, str, len);
147	*dstp = dst;
148	return (0);
149}
150
151/*
152 * Make a copy of a string, with a "/" preceding the string.
153 */
154static char *
155strprefix_slash(const char *str)
156{
157	size_t outstrsize;
158	char *outstr;
159
160	outstrsize = strlen(str) + 2;
161	outstr = malloc(outstrsize);
162	if (outstr == NULL)
163		return (NULL);
164	*outstr = '/';
165	memcpy(outstr + 1, str, outstrsize - 1);
166	return (outstr);
167}
168
169/*
170 * parse_entry(const char *key, const char *mapname, const char *mapopts,
171 *			const char *subdir, uint_t isdirect, int *node_type,
172 *			bool_t isrestricted, bool_t mount_access, int *err)
173 * Looks up the specified key in the specified map, and then parses the
174 * result to build a mapentry list containing the information for the
175 * mounts/lookups to be performed. Builds an intermediate mapentry list by
176 * processing the map entry, hierarchically sorts (builds a tree of) the
177 * list according to mountpoint. Then pushes options down the hierarchy,
178 * and fills in the mount file system. Finally, modifies the intermediate
179 * list depending on how far in the hierarchy the current request is (uses
180 * subdir). Deals with special cases of -hosts, -fstab, and -static map
181 * parsing.
182 *
183 * mapopts must be at most AUTOFS_MAXOPTSLEN bytes long (including the null
184 * terminator).
185 *
186 * Returns a pointer to the head of the mapentry list.
187 */
188struct mapent *
189parse_entry(const char *key, const char *mapname, const char *mapopts,
190			const char *subdir, uint_t isdirect, int *node_type,
191			bool_t isrestricted, bool_t mount_access, int *err)
192{
193	struct dir_entry *dirp;
194	char *stack[STACKSIZ];
195	char **stkptr = stack;
196	struct mapline ml;
197	bool_t iswildcard;
198	char defaultopts[AUTOFS_MAXOPTSLEN];
199
200	struct mapent *mapents = NULL;
201	struct mapent *me;
202	hiernode *rootnode = NULL;
203
204	/*
205	 * For now, assume the map entry is not a self-link in -fstab,
206	 * is not a wildcard match, and is not a trigger.
207	 */
208	if (node_type != NULL)
209		*node_type = 0;
210
211	/*
212	 * Check whether this is a special or regular map and, based
213	 * on that, do the appropriate lookup, select the appropriate
214	 * parser, and build the mapentry list.
215	 */
216	if (strcmp(mapname, "-hosts") == 0) {
217		/*
218		 * the /net parser - uses do_mapent_hosts to build mapents.
219		 * The mapopts are considered default for every entry, so we
220		 * don't push options down hierarchies.
221		 */
222		mapents = do_mapent_hosts(mapopts, key, isdirect, err);
223		if (mapents == NULL)		/* nothing to free */
224			return (mapents);
225
226		if (trace > 3)
227			trace_mapents("do_mapent_hosts:(return)", mapents);
228
229		*err = hierarchical_sort(mapents, &rootnode, key, mapname);
230		if (*err != 0)
231			goto parse_error_quiet;
232	} else if (strcmp(mapname, "-fstab") == 0) {
233		/*
234		 * the /Network/Servers parser - uses do_mapent_fstab to
235		 * build mapents.
236		 *
237		 * All entries in fstab have mount options ("ro" and
238		 * "rw", if nothing else), so the default mapopts are
239		 * ignored.
240		 */
241		mapents = do_mapent_fstab(mapopts, key, isdirect, node_type,
242		    err);
243		if (mapents == NULL)		/* nothing to free */
244			return (mapents);
245
246		if (trace > 3)
247			trace_mapents("do_mapent_fstab:(return)", mapents);
248
249		*err = hierarchical_sort(mapents, &rootnode, key, mapname);
250		if (*err != 0)
251			goto parse_error_quiet;
252	} else if (strcmp(mapname, "-static") == 0) {
253		/*
254		 * the static fstab parser - looks up the fstab entry
255		 * and builds mapents.
256		 * (We don't want mapline_to_mapent to process it,
257		 * as we don't want macro expansion to be done on
258		 * it.)
259		 *
260		 * All entries in fstab have mount options ("ro" and
261		 * "rw", if nothing else), so the default mapopts are
262		 * ignored.
263		 */
264		mapents = do_mapent_static(key, isdirect, err);
265		if (mapents == NULL)		/* nothing to free */
266			return (mapents);
267
268		if (trace > 3)
269			trace_mapents("do_mapent_static:(return)", mapents);
270
271		*err = hierarchical_sort(mapents, &rootnode, key, mapname);
272		if (*err != 0)
273			goto parse_error_quiet;
274	} else {
275		/*
276		 * All other maps.
277		 */
278
279		/* Is there an entry for that map/key in the readdir cache? */
280		dirp = rddir_entry_lookup(mapname, key);
281		if (dirp == NULL || dirp->line == NULL) {
282			/*
283			 * Either there's no entry, or the entry has a
284			 * name but no map line information.  We'll need
285			 * to look up the entry in the map.
286			 */
287
288			/* initialize the stack of open files for this thread */
289			stack_op(INIT, NULL, stack, &stkptr);
290
291			/*
292			 * Look up the entry in the map.
293			 */
294			switch (getmapent(key, mapname, &ml, stack, &stkptr,
295				&iswildcard, isrestricted)) {
296
297			case __NSW_SUCCESS:
298				/*
299				 * XXX - we failed to find this entry
300				 * in the readdir cache, but we did find
301				 * it in the map.  That means the readdir
302				 * cache is out of date for this map,
303				 * and we should flush the entry for
304				 * this map.
305				 */
306				break;
307			case __NSW_UNAVAIL:
308				if (verbose) {
309					syslog(LOG_ERR, "parse_entry: getmapent for map %s, key %s failed",
310					       mapname, key);
311                                }
312			case __NSW_NOTFOUND:
313				*err = ENOENT;	/* no such map entry */
314				return ((struct mapent *)NULL);	/* we failed to find it */
315			}
316		} else {
317			/*
318			 * Yes, and it has map line information.
319			 * Use it, rather than looking it up again
320			 * in the map.
321			 *
322			 * It's not a wildcard entry, as it corresponds
323			 * to an entry in the cache; wildcard entries
324			 * are not cached, as they're not returned by
325			 * readdir.
326			 */
327			if (CHECK_STRCPY(ml.linebuf, dirp->line, LINESZ)) {
328                             goto parse_error;
329                        }
330			if (CHECK_STRCPY(ml.lineqbuf, dirp->lineq, LINESZ)){
331                             goto parse_error;
332                        }
333			iswildcard = FALSE;
334		}
335
336		/*
337		 * Regular maps have no symlinks, but they *can* have
338		 * wildcard entries.  If this is a wildcard entry,
339		 * all references to it must trigger a mount, as that's
340		 * the only way to check whether it exists or not.
341		 */
342		if (node_type != NULL) {
343			if (iswildcard)
344				*node_type |= NT_FORCEMOUNT;
345		}
346
347		if (trace > 1)
348			trace_prt(1, "  mapline: %s\n", ml.linebuf);
349
350		*err = mapline_to_mapent(&mapents, &ml, key, mapname,
351		    mapopts, defaultopts, isdirect);
352		if (*err != 0)
353			goto parse_error;
354
355		if (mapents == NULL) {
356			*err = 0;
357			return (mapents);
358		}
359
360		*err = hierarchical_sort(mapents, &rootnode, key, mapname);
361		if (*err != 0)
362			goto parse_error_quiet;
363
364		*err = push_options(rootnode, defaultopts, mapopts,
365		    MAPENT_NOERR);
366		if (*err != 0)
367			goto parse_error;
368
369		if (trace > 3) {
370			trace_prt(1, "\tpush_options (return)\tdefault options=%s\n",
371				defaultopts);
372			trace_hierarchy(rootnode, 0);
373		};
374
375		*err = parse_fsinfo(mapname, mapents);
376		if (*err != 0)
377			goto parse_error;
378	}
379
380	/*
381	 * Modify the mapentry list. We *must* do this only after
382	 * the mapentry list is completely built (since we need to
383	 * have parse_fsinfo called first).
384	 */
385	*err = modify_mapents(&mapents, mapname, mapopts, subdir,
386	    rootnode, key, isdirect, mount_access);
387	if (*err != 0) {
388		/*
389		 * ENOENT means that something in subdir wasn't found
390		 * in the map entry list; that's just a user error.
391		 */
392		if (*err == ENOENT)
393			goto parse_error_quiet;
394		else
395			goto parse_error;
396	}
397
398	/*
399	 * XXX: its dangerous to use rootnode after modify mapents as
400	 * it may be pointing to mapents that have been freed
401	 */
402	if (rootnode != NULL)
403		free_hiernode(rootnode);
404
405	/*
406	 * Is this a directory that has any non-faked, non-modified
407	 * entries at level 0?
408	 *
409	 * If so, this is a trigger, as something should be mounted
410	 * on it; otherwise it isn't, as nothing should be mounted
411	 * directly on it (or it's a symlink).
412	 */
413	if (node_type != NULL && !(*node_type & NT_SYMLINK)) {
414		for (me = mapents; me; me = me->map_next) {
415			if (me->map_mntlevel == 0 &&
416			    !me->map_faked && !me->map_modified) {
417				*node_type |= NT_TRIGGER;
418				break;
419			}
420		}
421	}
422
423	return (mapents);
424
425parse_error:
426	syslog(LOG_ERR, "parse_entry: mapentry parse error: map=%s key=%s",
427	    mapname, key);
428parse_error_quiet:
429        if (mapents != NULL) {
430                free_mapent(mapents);
431        }
432	if (rootnode != NULL)
433		free_hiernode(rootnode);
434	return ((struct mapent *)NULL);
435}
436
437
438/*
439 * mapline_to_mapent(struct mapent **mapents, struct mapline *ml,
440 *		const char *key, const char *mapname, const char *mapopts,
441 *		char *defaultopts, uint_t isdirect)
442 * Parses the mapline information in ml word by word to build an intermediate
443 * mapentry list, which is passed back to the caller. The mapentries may have
444 * holes (example no options), as they are completed only later. The logic is
445 * awkward, but needed to provide the supported flexibility in the map entries.
446 * (especially the first line). Note that the key is the full pathname of the
447 * directory to be mounted in a direct map, and ml is the mapentry beyond key.
448 *
449 * mapopts must be at most MAXOPTSLEN bytes long (including the null
450 * terminator).
451 *
452 * defaultopts must be in a buffer at least MAXOPTSLEN bytes in size; we
453 * guarantee not to copy to it a string with more bytes than that (again,
454 * including the null terminator).
455 *
456 * Returns 0 or an appropriate error value.
457 */
458static int
459mapline_to_mapent(struct mapent **mapents, struct mapline *ml, const char *key,
460		const char *mapname, const char *mapopts,
461		char *defaultopts, uint_t isdirect)
462{
463	struct mapent *me = NULL;
464	struct mapent *mp;
465	char w[MAXPATHLEN];
466	char wq[MAXPATHLEN];
467	int implied;
468	int err;
469
470	char *lp = ml->linebuf;
471	char *lq = ml->lineqbuf;
472
473	/* do any macro expansions that are required to complete ml */
474	switch (macro_expand(key, lp, lq, LINESZ)) {
475
476	case MEXPAND_OK:
477		break;
478
479	case MEXPAND_LINE_TOO_LONG:
480		syslog(LOG_ERR,
481		"mapline_to_mapent: map %s: line too long (max %d chars)",
482			mapname, LINESZ - 1);
483		return (EIO);
484
485	case MEXPAND_VARNAME_TOO_LONG:
486		syslog(LOG_ERR,
487		"mapline_to_mapent: map %s: variable name too long",
488			mapname);
489		return (EIO);
490	}
491	if (trace > 3 && (strcmp(ml->linebuf, lp) != 0))
492		trace_prt(1, "  mapline_to_mapent: (expanded) mapline (%s,%s)\n",
493			ml->linebuf, ml->lineqbuf);
494
495	/* init the head of mapentry list to null */
496	*mapents = NULL;
497
498	/*
499	 * Get the first word - its either a '-' if default options provided,
500	 * a '/', if the mountroot is explicitly provided, or a mount filesystem
501	 * if the mountroot is implicit. Note that if the first word begins with
502	 * a '-' then the second must be read and it must be a mountpoint or a
503	 * mount filesystem. Use mapopts if no default opts are provided.
504	 */
505	if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1)
506		return (EIO);
507	if (*w == '-') {
508		if (strlcpy(defaultopts, w, MAXOPTSLEN) >= MAXOPTSLEN) {
509			syslog(LOG_ERR, "default options are too long");
510			return (EIO);
511		}
512		if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1)
513			return (EIO);
514	} else
515		if (CHECK_STRCPY(defaultopts, mapopts, MAXOPTSLEN)) {
516                        return (EIO);
517                }
518
519	implied = *w != '/'; /* implied is 1 only if '/' is implicit */
520	while (*w == '/' || implied) {
521		mp = me;
522		if ((me = (struct mapent *)malloc(sizeof (*me))) == NULL)
523			goto alloc_failed;
524		(void) memset((char *)me, 0, sizeof (*me));
525		if (*mapents == NULL)	/* special case of head */
526			*mapents = me;
527		else
528			mp->map_next = me;
529
530		/*
531		 * direct maps get an empty string as root - to be filled
532		 * by the entire path later. Indirect maps get /key as the
533		 * map root.
534		 */
535		if (isdirect)
536			me->map_root = strdup("");
537		else
538			me->map_root = strprefix_slash(key);
539		if (me->map_root == NULL)
540			goto alloc_failed;
541
542		/* mntpnt is empty for the mount root */
543		if (strcmp(w, "/") == 0 || implied)
544			me->map_mntpnt = strdup("");
545		else
546			me->map_mntpnt = strdup(w);
547		if (me->map_mntpnt == NULL)
548			goto alloc_failed;
549
550		/*
551		 * If implied, the word must be a mount filesystem,
552		 * and its already read in; also turn off implied - its
553		 * not applicable except for the mount root. Else,
554		 * read another (or two) words depending on if there's
555		 * an option.
556		 */
557		if (implied)   /* must be a mount filesystem */
558			implied = 0;
559		else {
560			if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1)
561				return (EIO);
562			if (w[0] == '-') {
563				/* mount options */
564				err = strldup(&me->map_mntopts, w, MAXOPTSLEN);
565				if (err == ENAMETOOLONG) {
566					syslog(LOG_ERR,
567					    "mount options for are too long");
568					return (EIO);
569				}
570				if (err == ENOMEM)
571					goto alloc_failed;
572				if (getword(w, wq, &lp, &lq, ' ',
573				    sizeof (w)) == -1)
574					return (EIO);
575			}
576		}
577
578		/*
579		 * must be a mount filesystem or a set of filesystems at
580		 * this point.
581		 */
582		if (w[0] == '\0' || w[0] == '-') {
583			syslog(LOG_ERR,
584			"mapline_to_mapent: bad location=%s map=%s key=%s",
585				w, mapname, key);
586			return (EIO);
587		}
588
589		/*
590		 * map_fsw and map_fswq hold information which will be
591		 * used to determine filesystem information at a later
592		 * point. This is required since we can only find out
593		 * about the mount file system after the directories
594		 * are hierarchically sorted and options have been pushed
595		 * down the hierarchies.
596		 */
597		if (((me->map_fsw = strdup(w)) == NULL) ||
598		    ((me->map_fswq = strdup(wq)) == NULL))
599			goto alloc_failed;
600
601		/*
602		 * the next word, if any, is either another mount point or a
603		 * mount filesystem if more than one server is listed.
604		 */
605		if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1)
606			return (EIO);
607		while (*w && *w != '/') {	/* more than 1 server listed */
608			char *fsw, *fswq;
609			if (asprintf(&fsw, "%s   %s", me->map_fsw, w) == -1)
610				goto alloc_failed;
611			free(me->map_fsw);
612			me->map_fsw = fsw;
613			if (asprintf(&fswq, "%s   %s", me->map_fswq, wq) == -1)
614				goto alloc_failed;
615			free(me->map_fswq);
616			me->map_fswq = fswq;
617			if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1)
618				return (EIO);
619		}
620
621		/* initialize flags */
622		me->map_mntlevel = -1;
623		me->map_modified = FALSE;
624		me->map_faked = FALSE;
625		me->map_err = MAPENT_NOERR;
626
627		me->map_next = NULL;
628	}
629
630	if (*mapents == NULL || w[0] != '\0') {	/* sanity check */
631		if (verbose) {
632			if (*mapents == NULL)
633				syslog(LOG_ERR,
634				"mapline_to_mapent: parsed with null mapents");
635			else
636				syslog(LOG_ERR,
637				"mapline_to_mapent: parsed nononempty w=%s", w);
638		}
639		return (EIO);
640	}
641
642	if (trace > 3)
643		trace_mapents("mapline_to_mapent:", *mapents);
644
645	return (0);
646
647alloc_failed:
648	syslog(LOG_ERR, "mapline_to_mapent: Memory allocation failed");
649	return (ENOMEM);
650}
651
652/*
653 * hierarchical_sort(struct mapent *mapents, hiernode **rootnode, char *key
654 *                   char *mapname)
655 * sorts the mntpnts in each mapent to build a hierarchy of nodes, with
656 * with the rootnode being the mount root. The hierarchy is setup as
657 * levels, and subdirs below each level. Provides a link from node to
658 * the relevant mapentry.
659 * Returns 0 or appropriate error value; logs a message on error.
660 */
661static int
662hierarchical_sort(struct mapent *mapents, hiernode **rootnode, const char *key,
663	const char *mapname)
664{
665	hiernode *prevnode, *currnode, *newnode;
666	const char *path;
667	char dirname[MAXFILENAMELEN];
668
669	int rc = 0;
670	struct mapent *me = mapents;
671
672	/* allocate the rootnode with a default path of "" */
673	*rootnode = NULL;
674	if ((rc = alloc_hiernode(rootnode, "")) != 0)
675		return (rc);
676
677	/*
678	 * walk through mapents - for each mapent, locate the position
679	 * within the hierarchy by walking across leveldirs, and
680	 * subdirs of matched leveldirs. Starts one level below
681	 * the root (assumes an implicit match with rootnode).
682	 * XXX - this could probably be done more cleanly using recursion.
683	 */
684	while (me != NULL) {
685
686		path = me->map_mntpnt;
687
688		if ((rc = get_dir_from_path(dirname, &path,
689		    sizeof (dirname))) != 0)
690			return (rc);
691
692		prevnode = *rootnode;
693		currnode = (*rootnode)->subdir;
694
695		while (dirname[0] != '\0') {
696			if (currnode != NULL) {
697				if (strcmp(currnode->dirname, dirname) == 0) {
698					/*
699					 * match found - mntpnt is a child of
700					 * this node
701					 */
702					prevnode = currnode;
703					currnode = currnode->subdir;
704				} else {
705					prevnode = currnode;
706					currnode = currnode->leveldir;
707
708					if (currnode == NULL) {
709						/*
710						 * No more leveldirs to match.
711						 * Add a new one
712						 */
713						if ((rc = alloc_hiernode
714							(&newnode, dirname))
715							!= 0)
716							return (rc);
717						prevnode->leveldir = newnode;
718						prevnode = newnode;
719						currnode = newnode->subdir;
720					} else {
721						/* try this leveldir */
722						continue;
723					}
724				}
725			} else {
726				/* no more subdirs to match. Add a new one */
727				if ((rc = alloc_hiernode(&newnode,
728				    dirname)) != 0)
729					return (rc);
730				prevnode->subdir = newnode;
731				prevnode = newnode;
732				currnode = newnode->subdir;
733			}
734			if ((rc = get_dir_from_path(dirname, &path,
735			    sizeof (dirname))) != 0)
736				return (rc);
737		}
738
739		if (prevnode->mapent != NULL) {
740			/* duplicate mntpoint found */
741			char *root;
742
743			if (strcmp(mapname, "-fstab") == 0) {
744				syslog(LOG_ERR,
745				    "Duplicate mounts for %s:%s in fstab",
746				    key, me->map_mntpnt);
747			} else {
748				root = me->map_root;
749				while (*root == '/')
750					root++;
751				syslog(LOG_ERR,
752				    "Duplicate submounts for %s%s in map %s, key %s",
753				    root, me->map_mntpnt, mapname, key);
754			}
755			return (EIO);
756		}
757
758		/* provide a pointer from node to mapent */
759		prevnode->mapent = me;
760		me = me->map_next;
761	}
762
763	if (trace > 3) {
764		trace_prt(1, "\thierarchical_sort:\n");
765		trace_hierarchy(*rootnode, 0);	/* 0 is rootnode's level */
766	}
767
768	return (rc);
769}
770
771/*
772 * push_options(hiernode *node, const char *defaultopts, const char *mapopts,
773 *		int err)
774 * Pushes the options down a hierarchical structure. Works recursively from the
775 * root, which is passed in on the first call. Uses a replacement policy.
776 * If a node points to a mapentry, and it has an option, then thats the option
777 * for that mapentry. Else, the node's mapent inherits the option from the
778 * default (which may be the global option for the entry or mapopts).
779 * err is useful in flagging entries with errors in pushing options.
780 * returns 0 or appropriate error value.
781 *
782 * defaultopts and mapopts must each be at most MAXOPTSLEN bytes long
783 * (including the null terminator).
784 */
785static int
786push_options(hiernode *node, char *defaultopts, const char *mapopts, int err)
787{
788	int rc = 0;
789	struct mapent *me = NULL;
790
791	/* ensure that all the dirs at a level are passed the default options */
792	while (node != NULL) {
793		me = node->mapent;
794		if (me != NULL) {	/* not all nodes point to a mapentry */
795			me->map_err = err;
796			if ((rc = set_mapent_opts(me, defaultopts, mapopts)) != 0)
797				return (rc);
798		}
799
800		/* push the options to subdirs */
801		if (node->subdir != NULL) {
802			if (node->mapent && node->mapent->map_fstype &&
803			    strcmp(node->mapent->map_fstype, MNTTYPE_AUTOFS) == 0)
804				err = MAPENT_UATFS;
805			if ((rc = push_options(node->subdir, defaultopts,
806				mapopts, err)) != 0)
807				return (rc);
808		}
809		node = node->leveldir;
810	}
811	return (rc);
812}
813
814#define	BACKFSTYPE "backfstype" /* used in cachefs options */
815#define	BACKFSTYPE_EQ "backfstype="
816#define	FSTYPE "fstype"
817#define	FSTYPE_EQ "fstype="
818#define	NO_OPTS ""
819
820/*
821 * set_mapent_opts(struct mapent *me, char *defaultopts, const char *mapopts)
822 * sets the mapentry's options, fstype and mounter fields by separating
823 * out the fstype part from the opts. Use default options if me->map_mntopts
824 * is NULL.  Note that defaultopts may be the same as mapopts.
825 *
826 * me->map_mntopts, defaultopts, and mapopts must each be at most
827 * AUTOFS_MAXOPTSLEN bytes long (including the null terminator).
828 *
829 * Returns 0 or appropriate error value.
830 */
831static int
832set_mapent_opts(struct mapent *me, char *defaultopts, const char *mapopts)
833{
834	char optsbuf[AUTOFS_MAXOPTSLEN];
835	char *opts;
836	char entryopts[AUTOFS_MAXOPTSLEN];
837	char fstype[MAX_FSLEN], mounter[MAX_FSLEN];
838	int rc = 0;
839	bool_t fstype_opt = FALSE;
840
841	/* set options to default options, if none exist for this entry */
842	if (me->map_mntopts == NULL) {
843		opts = defaultopts;
844		if (defaultopts == NULL) { /* NULL opts for entry */
845			/*
846			 * File system type not explicitly specified, as
847			 * no options were specified; it will be determined
848			 * later.
849			 */
850			me->map_fstype = NULL;
851			me->map_mounter = NULL;
852			return (0);
853		}
854	} else {
855		/*
856		 * Make a copy of map_mntopts, as we'll free it below.
857		 */
858		if (CHECK_STRCPY(optsbuf, me->map_mntopts, AUTOFS_MAXOPTSLEN)) {
859                        return (EIO);
860                }
861		opts = optsbuf;
862	}
863
864	if (*opts == '-')
865		opts++;
866
867	/* separate opts into fstype and (other) entrypopts */
868	if (!get_opts(opts, entryopts, fstype, sizeof (fstype), &fstype_opt)) {
869	    	syslog(LOG_ERR, "file system type is too long");
870		return (EIO);
871	}
872
873	/* replace any existing opts */
874	if (me->map_mntopts != NULL)
875		free(me->map_mntopts);
876	if ((me->map_mntopts = strdup(entryopts)) == NULL)
877		return (ENOMEM);
878
879	if (fstype_opt == TRUE) {
880		strlcpy(mounter, fstype, sizeof(mounter));
881
882#ifdef MNTTYPE_CACHEFS
883		/*
884		 * The following ugly chunk of code crept in as a result of
885		 * cachefs.  If it's a cachefs mount of an nfs filesystem, then
886		 * it's important to parse the nfs special field.  Otherwise,
887		 * just hand the special field to the fs-specific mount
888		 */
889		if (strcmp(fstype, MNTTYPE_CACHEFS) ==  0) {
890			struct mnttab m;
891			char *p;
892
893			m.mnt_mntopts = entryopts;
894			if ((p = hasmntopt(&m, BACKFSTYPE)) != NULL) {
895				int len = strlen(MNTTYPE_NFS);
896
897				p += strlen(BACKFSTYPE_EQ);
898
899				if (strncmp(p, MNTTYPE_NFS, len) ==  0 &&
900					(p[len] == '\0' || p[len] == ',')) {
901					/*
902					 * Cached nfs mount
903					 */
904					if (CHECK_STRCPY(fstype, MNTTYPE_NFS, sizeof (fstype))) {
905                                                return EIO;
906                                        }
907					if (CHECK_STRCPY(mounter, MNTTYPE_CACHEFS, sizeof (mounter))) {
908                                                return EIO;
909                                        }
910				}
911			}
912		}
913#endif
914
915		/*
916		 * If the child options are exactly fstype = somefs, i.e.
917		 * if no other options are specified, we need to do some
918		 * more option pushing work.
919		 */
920		if (strcmp(me->map_mntopts, NO_OPTS) == 0) {
921			free(me->map_mntopts);
922			me->map_mntopts = NULL;
923			if ((rc = fstype_opts(me, opts, defaultopts,
924			    mapopts)) != 0)
925				return (rc);
926		}
927
928		/*
929		 * File system type explicitly specified; set it.
930		 */
931		if (((me->map_fstype = strdup(fstype)) == NULL) ||
932			((me->map_mounter = strdup(mounter)) == NULL)) {
933			if (me->map_fstype != NULL)
934				free(me->map_fstype);
935			syslog(LOG_ERR, "set_mapent_opts: No memory");
936			return (ENOMEM);
937		}
938	} else {
939		/*
940		 * File system type not explicitly specified; it
941		 * will be determined later.
942		 */
943		me->map_fstype = NULL;
944		me->map_mounter = NULL;
945	}
946
947	return (rc);
948}
949
950/*
951 * Check the option string for an "fstype"
952 * option.  If found, return the fstype
953 * and the option string with the fstype
954 * option removed, e.g.
955 *
956 *  input:  "fstype=cachefs,ro,nosuid"
957 *  opts:   "ro,nosuid"
958 *  fstype: "cachefs"
959 *
960 * Also indicates if the fstype option was present
961 * by setting a flag, if the pointer to the flag
962 * is not NULL.
963 *
964 * input must be at most MAXOPTSLEN bytes long (including the null terminator).
965 *
966 * opts must point to a buffer at least MAXOPTSLEN bytes in size; we guarantee
967 * not to copy to it a string with more bytes than that (again, including the
968 * null terminator).
969 *
970 * 0 is returned if fstype would be more than fstype_size bytes long,
971 * otherwise 1 is returned.
972 */
973static int
974get_opts(input, opts, fstype, fstype_size, fstype_opt)
975	const char *input;
976	char *opts; 	/* output */
977	char *fstype;   /* output */
978	size_t fstype_size;
979	bool_t *fstype_opt;
980{
981	char *p, *pb;
982	char buf[MAXOPTSLEN];
983	char *placeholder;
984
985	*opts = '\0';
986	if (CHECK_STRCPY(buf, input, sizeof (buf))) {
987                return 0;
988        }
989	pb = buf;
990	while ((p = strtok_r(pb, ",", &placeholder)) != NULL) {
991		pb = NULL;
992		if (strncmp(p, FSTYPE_EQ, 7) == 0) {
993			if (fstype_opt != NULL)
994				*fstype_opt = TRUE;
995			if (strlcpy(fstype, p + 7, fstype_size) >= fstype_size)
996				return (0);
997		} else {
998			if (*opts) {
999				if (CHECK_STRCAT(opts, ",", MAXOPTSLEN)) {
1000                                        return 0;
1001                                }
1002                        }
1003			if (CHECK_STRCAT(opts, p, MAXOPTSLEN)) {
1004                                return 0;
1005                        }
1006		}
1007	}
1008	return (1);
1009}
1010
1011/*
1012 * fstype_opts(struct mapent *me, const char *opts, const char *defaultopts,
1013 *				const char *mapopts)
1014 * We need to push global options to the child entry if it is exactly
1015 * fstype=somefs.
1016 *
1017 * opts, defaultopts, and mapopts must each be at most AUTOFS_MAXOPTSLEN
1018 * bytes long (including the null terminator).
1019 */
1020static int
1021fstype_opts(struct mapent *me, const char *opts, const char *defaultopts,
1022				const char *mapopts)
1023{
1024	const char *optstopush;
1025	char pushopts[AUTOFS_MAXOPTSLEN];
1026	char pushentryopts[AUTOFS_MAXOPTSLEN];
1027	char pushfstype[MAX_FSLEN];
1028	int err;
1029
1030	if (defaultopts && *defaultopts == '-')
1031		defaultopts++;
1032
1033	/*
1034	 * the options to push are the global defaults for the entry,
1035	 * if they exist, or mapopts, if the global defaults for the
1036	 * entry does not exist.
1037	 */
1038	if (strcmp(defaultopts, opts) == 0) {
1039		if (*mapopts == '-')
1040			mapopts++;
1041		optstopush = mapopts;
1042	} else
1043		optstopush = defaultopts;
1044	if (!get_opts(optstopush, pushentryopts, pushfstype,
1045	    sizeof (pushfstype), NULL)) {
1046	    	syslog(LOG_ERR, "file system type is too long");
1047		return (EIO);
1048	}
1049	if (CHECK_STRCPY(pushopts, optstopush, sizeof (pushopts))) {
1050                return EIO;
1051        }
1052
1053#ifdef MNTTYPE_CACHEFS
1054	if (strcmp(pushfstype, MNTTYPE_CACHEFS) == 0)
1055		err = strldup(&me->map_mntopts, pushopts, MAXOPTSLEN);
1056	else
1057#endif
1058		err = strldup(&me->map_mntopts, pushentryopts, MAXOPTSLEN);
1059
1060	if (err == ENAMETOOLONG) {
1061		syslog(LOG_ERR, "mount options are too long");
1062		return (EIO);
1063	}
1064	if (err == ENOMEM) {
1065		syslog(LOG_ERR, "fstype_opts: No memory");
1066		return (ENOMEM);
1067	}
1068
1069	return (0);
1070}
1071
1072/*
1073 * modify_mapents(struct mapent **mapents, const char *mapname,
1074 *			const char *mapopts, const char *subdir,
1075 *			hiernode *rootnode, const char *key,
1076 *			uint_t isdirect, bool_t mount_access)
1077 * modifies the intermediate mapentry list into the final one, and passes
1078 * back a pointer to it. The final list may contain faked mapentries for
1079 * hiernodes that do not point to a mapentry, or converted mapentries, if
1080 * hiernodes that point to a mapentry need to be converted from nfs to autofs.
1081 * mounts. Entries that are not directly 1 level below the subdir are removed.
1082 * Returns 0, ENOENT, or EIO
1083 */
1084static int
1085modify_mapents(struct mapent **mapents, const char *mapname,
1086			const char *mapopts, const char *subdir,
1087			hiernode *rootnode, const char *key,
1088			uint_t isdirect, bool_t mount_access)
1089{
1090	struct mapent *mp = NULL;
1091	char w[MAXPATHLEN];
1092
1093	struct mapent *me;
1094	int rc = 0;
1095	struct mapent *faked_mapents = NULL;
1096
1097	/*
1098	 * correct the mapentry mntlevel from default -1 to level depending on
1099	 * position in hierarchy, and build any faked mapentries, if required
1100	 * at one level below the rootnode given by subdir.
1101	 */
1102	if ((rc = set_and_fake_mapent_mntlevel(rootnode, subdir, key, mapname,
1103		&faked_mapents, isdirect, mapopts, mount_access)) != 0)
1104		return (rc);
1105
1106	/*
1107	 * attaches faked mapents to real mapents list. Assumes mapents
1108	 * is not NULL.
1109	 */
1110	me = *mapents;
1111	while (me->map_next != NULL)
1112		me = me->map_next;
1113	me->map_next = faked_mapents;
1114
1115	/*
1116	 * get rid of nodes marked at level -1
1117	 */
1118	me = *mapents;
1119	while (me != NULL) {
1120		if ((me->map_mntlevel ==  -1) || (me->map_err) ||
1121			(mount_access == FALSE && me->map_mntlevel == 0)) {
1122			/*
1123			 * syslog any errors and free entry
1124			 */
1125			if (me->map_err)
1126				dump_mapent_err(me, key, mapname);
1127
1128			if (me ==  (*mapents)) {
1129				/* special case when head has to be freed */
1130				*mapents = me->map_next;
1131				if ((*mapents) ==  NULL) {
1132					/* something wierd happened */
1133					if (verbose)
1134						syslog(LOG_ERR,
1135						"modify_mapents: level error");
1136					return (EIO);
1137				}
1138
1139				/* separate out the node */
1140				me->map_next = NULL;
1141				free_mapent(me);
1142				me = *mapents;
1143			} else {
1144				mp->map_next = me->map_next;
1145				me->map_next = NULL;
1146				free_mapent(me);
1147				me = mp->map_next;
1148			}
1149			continue;
1150		}
1151
1152		/*
1153		 * convert level 1 mapents that are not already autonodes
1154		 * to autonodes
1155		 */
1156		if (me->map_mntlevel == 1 && me->map_fstype != NULL &&
1157			(strcmp(me->map_fstype, MNTTYPE_AUTOFS) != 0) &&
1158			(me->map_faked != TRUE)) {
1159			if ((rc = convert_mapent_to_automount(me, mapname,
1160			    mapopts)) != 0)
1161				return (rc);
1162		}
1163		if (CHECK_STRCPY(w, (me->map_mntpnt+strlen(subdir)), sizeof (w))) {
1164                        return EIO;
1165                }
1166		/* w is shorter than me->map_mntpnt, so this strcpy is safe */
1167		strcpy(me->map_mntpnt, w);
1168		mp = me;
1169		me = me->map_next;
1170	}
1171
1172	if (trace > 3)
1173		trace_mapents("modify_mapents:", *mapents);
1174
1175	return (0);
1176}
1177
1178/*
1179 * set_and_fake_mapent_mntlevel(hiernode *rootnode, const char *subdir,
1180 *		const char *key, const char *mapname,
1181 *		struct mapent **faked_mapents, uint_t isdirect,
1182 *		const char *mapopts, bool_t mount_access)
1183 * sets the mapentry mount levels (depths) with respect to the subdir.
1184 * Assigns a value of 0 to the new root. Finds the level1 directories by
1185 * calling mark_*_level1_*(). Also cleans off extra /'s in level0 and
1186 * level1 map_mntpnts. Note that one level below the new root is an existing
1187 * mapentry if there's a mapentry (nfs mount) corresponding to the root,
1188 * and the direct subdir set for the root, if there's no mapentry corresponding
1189 * to the root (we install autodirs). Returns 0 or error value.
1190 */
1191static int
1192set_and_fake_mapent_mntlevel(hiernode *rootnode, const char *subdir,
1193		const char *key, const char *mapname,
1194		struct mapent **faked_mapents, uint_t isdirect,
1195		const char *mapopts, bool_t mount_access)
1196{
1197	char dirname[MAXFILENAMELEN];
1198	char traversed_path[MAXPATHLEN]; /* used in building fake mapentries */
1199
1200	const char *subdir_child = subdir;
1201	hiernode *prevnode = rootnode;
1202	hiernode *currnode = rootnode->subdir;
1203	int rc = 0;
1204	traversed_path[0] = '\0';
1205
1206	/*
1207	 * find and mark the root by tracing down subdir. Use traversed_path
1208	 * to keep track of how far we go, while guaranteeing that it
1209	 * contains no '/' at the end. Took some mucking to get that right.
1210	 */
1211	if ((rc = get_dir_from_path(dirname, &subdir_child, sizeof (dirname)))
1212				!= 0)
1213		return (rc);
1214
1215	if (dirname[0] != '\0') {
1216		if (strlcat(traversed_path, "/", sizeof (traversed_path)) >=
1217		      sizeof (traversed_path) ||
1218		    strlcat(traversed_path, dirname, sizeof (traversed_path)) >=
1219		      sizeof (traversed_path)) {
1220			syslog(LOG_ERR, "set_and_fake_mapent_mntlevel: maximum path name length exceeded");
1221			return (EIO);
1222		}
1223	}
1224
1225	prevnode = rootnode;
1226	currnode = rootnode->subdir;
1227	while (dirname[0] != '\0' && currnode != NULL) {
1228		if (strcmp(currnode->dirname, dirname) == 0) {
1229
1230			/* subdir is a child of currnode */
1231			prevnode = currnode;
1232			currnode = currnode->subdir;
1233
1234			if ((rc = get_dir_from_path(dirname, &subdir_child,
1235			    sizeof (dirname))) != 0)
1236				return (rc);
1237			if (dirname[0] != '\0') {
1238				if (strlcat(traversed_path, "/", sizeof (traversed_path)) >=
1239				      sizeof (traversed_path) ||
1240				    strlcat(traversed_path, dirname, sizeof (traversed_path)) >=
1241				      sizeof (traversed_path)) {
1242					syslog(LOG_ERR,
1243					    "set_and_fake_mapent_mntlevel: maximum path name length exceeded");
1244					return (EIO);
1245				}
1246			}
1247		} else {
1248			/* try next leveldir */
1249			prevnode = currnode;
1250			currnode = currnode->leveldir;
1251		}
1252	}
1253
1254	if (dirname[0] != '\0') {
1255		/*
1256		 * We didn't find dirname in the map entry.
1257		 */
1258		if (verbose)
1259			syslog(LOG_ERR,
1260			"set_and_fake_mapent_mntlevel: subdir=%s error: %s not found in map=%s",
1261			    subdir, dirname, mapname);
1262		return (ENOENT);
1263	}
1264
1265	/*
1266	 * see if level of root really points to a mapent and if
1267	 * have access to that filessystem - call appropriate
1268	 * routine to mark level 1 nodes, and build faked entries
1269	 */
1270	if (prevnode->mapent != NULL && mount_access == TRUE) {
1271		if (trace > 3)
1272			trace_prt(1, "  node mountpoint %s\t travpath=%s\n",
1273				prevnode->mapent->map_mntpnt, traversed_path);
1274
1275		/*
1276		 * Copy traversed path map_mntpnt to get rid of any extra
1277		 * '/' the map entry may contain.
1278		 */
1279		if (strlen(prevnode->mapent->map_mntpnt) <
1280				strlen(traversed_path)) { /* sanity check */
1281			/*
1282			 * prevnode->mapent->map_mntpnt is too small to hold
1283			 * traversed_path.
1284			 */
1285			if (verbose)
1286				syslog(LOG_ERR,
1287				"set_and_fake_mapent_mntlevel: path=%s error",
1288				    traversed_path);
1289			return (EIO);
1290		}
1291		/*
1292		 * traversed_path is shorter than prevnode->mapent->map_mntpnt,
1293		 * so we can safely copy traversed_path to
1294		 * prevnode->mapent->map_mntpnt.
1295		 */
1296		if (strcmp(prevnode->mapent->map_mntpnt, traversed_path) != 0)
1297			strcpy(prevnode->mapent->map_mntpnt, traversed_path);
1298
1299		prevnode->mapent->map_mntlevel = 0; /* root level is 0 */
1300		if (currnode != NULL) {
1301			if ((rc = mark_level1_root(currnode,
1302			    traversed_path)) != 0)
1303				return (rc);
1304		}
1305	} else if (currnode != NULL) {
1306		if (trace > 3)
1307			trace_prt(1, "  No rootnode, travpath=%s\n", traversed_path);
1308		if ((rc = mark_and_fake_level1_noroot(currnode,
1309		    traversed_path, key, mapname, faked_mapents, isdirect,
1310		    mapopts)) != 0)
1311			return (rc);
1312	}
1313
1314	if (trace > 3) {
1315		trace_prt(1, "\tset_and_fake_mapent_mntlevel\n");
1316		trace_hierarchy(rootnode, 0);
1317	}
1318
1319	return (rc);
1320}
1321
1322
1323/*
1324 * mark_level1_root(hiernode *node, char *traversed_path)
1325 * marks nodes upto one level below the rootnode given by subdir
1326 * recursively. Called if rootnode points to a mapent.
1327 * In this routine, a level 1 node is considered to be the 1st existing
1328 * mapentry below the root node, so there's no faking involved.
1329 * Returns 0 or error value
1330 */
1331static int
1332mark_level1_root(hiernode *node, char *traversed_path)
1333{
1334	/* ensure we touch all leveldirs */
1335	while (node) {
1336		/*
1337		 * mark node level as 1, if one exists - else walk down
1338		 * subdirs until we find one.
1339		 */
1340		if (node->mapent ==  NULL) {
1341			char w[MAXPATHLEN];
1342
1343			if (node->subdir != NULL) {
1344				if (snprintf(w, sizeof (w), "%s/%s",
1345				    traversed_path, node->dirname) >=
1346				      (int)sizeof (w)) {
1347					syslog(LOG_ERR, "mark_level1_root: maximum path name length exceeded");
1348					return (EIO);
1349				}
1350				if (mark_level1_root(node->subdir, w) == EIO)
1351					return (EIO);
1352			} else {
1353				if (verbose) {
1354					syslog(LOG_ERR,
1355					"mark_level1_root: hierarchy error");
1356				}
1357				return (EIO);
1358			}
1359		} else {
1360			char w[MAXPATHLEN];
1361
1362			if (snprintf(w, sizeof (w), "%s/%s", traversed_path,
1363			    node->dirname) >= (int)sizeof (w)) {
1364				syslog(LOG_ERR, "mark_level1_root: maximum path name length exceeded");
1365				return (EIO);
1366			}
1367			if (trace > 3)
1368				trace_prt(1, "  node mntpnt %s\t travpath %s\n",
1369				    node->mapent->map_mntpnt, w);
1370
1371			/* replace mntpnt with travpath to clean extra '/' */
1372			if (strlen(node->mapent->map_mntpnt) < strlen(w)) {
1373				if (verbose) {
1374					/*
1375					 * node->mapent->map_mntpnt is too
1376					 * small to hold w.
1377					 */
1378					syslog(LOG_ERR,
1379					"mark_level1_root: path=%s error",
1380					    traversed_path);
1381				}
1382				return (EIO);
1383			}
1384			/*
1385			 * w is shorter than node->mapent->map_mntpnt,
1386			 * so we can safely copy w to node->mapent->map_mntpnt.
1387			 */
1388			if (strcmp(node->mapent->map_mntpnt, w) != 0)
1389				strcpy(node->mapent->map_mntpnt, w);
1390			node->mapent->map_mntlevel = 1;
1391		}
1392		node = node->leveldir;
1393	}
1394	return (0);
1395}
1396
1397/*
1398 * mark_and_fake_level1_noroot(hiernode *node, char *traversed_path,
1399 * 			char *key,char *mapname, struct mapent **faked_mapents,
1400 *			uint_t isdirect, char *mapopts)
1401 * Called if the root of the hierarchy does not point to a mapent. marks nodes
1402 * upto one physical level below the rootnode given by subdir. checks if
1403 * there's a real mapentry. If not, it builds a faked one (autonode) at that
1404 * point. The faked autonode is direct, with the map being the same as the
1405 * original one from which the call originated. Options are same as that of
1406 * the map and assigned in automount_opts(). Returns 0 or error value.
1407 */
1408static int
1409mark_and_fake_level1_noroot(hiernode *node, const char *traversed_path,
1410			const char *key, const char *mapname,
1411			struct mapent **faked_mapents, uint_t isdirect,
1412			const char *mapopts)
1413{
1414	struct mapent *me;
1415	int rc = 0;
1416	char w[MAXPATHLEN];
1417
1418	while (node != NULL) {
1419		me = NULL;
1420		if (node->mapent != NULL) {
1421			/*
1422			 * existing mapentry at level 1 - copy travpath to
1423			 * get rid of extra '/' in mntpnt
1424			 */
1425			if (snprintf(w, sizeof (w), "%s/%s", traversed_path,
1426			    node->dirname) >= (int)sizeof (w)) {
1427				syslog(LOG_ERR,
1428				"mark_fake_level1_noroot: maximum path name length exceeded");
1429				return (EIO);
1430			}
1431			if (trace > 3)
1432				trace_prt(1, "  node mntpnt=%s\t travpath=%s\n",
1433				    node->mapent->map_mntpnt, w);
1434			if (strlen(node->mapent->map_mntpnt) < strlen(w)) {
1435				/* sanity check */
1436				if (verbose) {
1437					/*
1438					 * node->mapent->map_mntpnt is too
1439					 * small to hold w.
1440					 */
1441					syslog(LOG_ERR,
1442					"mark_fake_level1_noroot:path=%s error",
1443					    traversed_path);
1444				}
1445				return (EIO);
1446			}
1447			/*
1448			 * w is shorter than node->mapent->map_mntpnt,
1449			 * so we can safely copy w to node->mapent->map_mntpnt.
1450			 */
1451			if (strcmp(node->mapent->map_mntpnt, w) != 0)
1452				strcpy(node->mapent->map_mntpnt, w);
1453			node->mapent->map_mntlevel = 1;
1454		} else {
1455			/*
1456			 * build the faked autonode
1457			 */
1458			if ((me = (struct mapent *)malloc(sizeof (*me)))
1459				== NULL)
1460				goto alloc_failed;
1461			(void) memset((char *)me, 0, sizeof (*me));
1462
1463			if ((me->map_fs = (struct mapfs *)
1464				malloc(sizeof (struct mapfs))) == NULL)
1465				goto alloc_failed;
1466			(void) memset(me->map_fs, 0, sizeof (struct mapfs));
1467
1468			if (isdirect)
1469				me->map_root = strdup("");
1470			else
1471				me->map_root = strprefix_slash(key);
1472			if (me->map_root == NULL)
1473				goto alloc_failed;
1474
1475			if (asprintf(&me->map_mntpnt, "%s/%s", traversed_path,
1476				node->dirname) == -1)
1477				goto alloc_failed;
1478			me->map_fstype = strdup(MNTTYPE_AUTOFS);
1479			me->map_mounter = strdup(MNTTYPE_AUTOFS);
1480
1481			/* set options */
1482			if ((rc = automount_opts(&me->map_mntopts, mapopts))
1483				!= 0) {
1484				free_mapent(me);
1485				return (rc);
1486			}
1487			me->map_fs->mfs_dir = strdup(mapname);
1488			me->map_mntlevel = 1;
1489			me->map_modified = FALSE;
1490			me->map_faked = TRUE;   /* mark as faked */
1491			if (me->map_root == NULL ||
1492			    me->map_mntpnt == NULL ||
1493			    me->map_fstype == NULL ||
1494			    me->map_mounter == NULL ||
1495			    me->map_mntopts == NULL ||
1496			    me->map_fs->mfs_dir == NULL)
1497				goto alloc_failed;
1498
1499			if (*faked_mapents == NULL)
1500				*faked_mapents = me;
1501			else {			/* attach to the head */
1502				me->map_next = *faked_mapents;
1503				*faked_mapents = me;
1504			}
1505			node->mapent = me;
1506		}
1507		node = node->leveldir;
1508	}
1509	return (rc);
1510
1511alloc_failed:
1512	syslog(LOG_ERR,	"mark_and_fake_level1_noroot: out of memory");
1513	if (me != NULL) {
1514		if (me->map_mounter != NULL)
1515			free(me->map_mounter);
1516		if (me->map_fstype != NULL)
1517			free(me->map_fstype);
1518		if (me->map_mntpnt != NULL)
1519			free(me->map_mntpnt);
1520		if (me->map_root != NULL)
1521			free(me->map_root);
1522		if (me->map_fs != NULL) {
1523			if (me->map_fs->mfs_dir != NULL)
1524				free(me->map_fs->mfs_dir);
1525			free(me->map_fs);
1526		}
1527		free(me);
1528	}
1529	free_mapent(*faked_mapents);
1530	return (ENOMEM);
1531}
1532
1533/*
1534 * convert_mapent_to_automount(struct mapent *me, const char *mapname,
1535 *				const char *mapopts)
1536 * change the mapentry me to an automount - free fields first and NULL them
1537 * to avoid freeing again, while freeing the mapentry at a later stage.
1538 * Could have avoided freeing entries here as we don't really look at them.
1539 * Give the converted mapent entry the options that came with the map using
1540 * automount_opts(). Returns 0 or appropriate error value.
1541 */
1542static int
1543convert_mapent_to_automount(struct mapent *me, const char *mapname,
1544				const char *mapopts)
1545{
1546	struct mapfs *mfs = me->map_fs;		/* assumes it exists */
1547	int rc = 0;
1548
1549	/* free relevant entries */
1550	if (mfs->mfs_host) {
1551		free(mfs->mfs_host);
1552		mfs->mfs_host = NULL;
1553	}
1554	while (me->map_fs->mfs_next != NULL) {
1555		mfs = me->map_fs->mfs_next;
1556		if (mfs->mfs_host)
1557			free(mfs->mfs_host);
1558		if (mfs->mfs_dir)
1559			free(mfs->mfs_dir);
1560		me->map_fs->mfs_next = mfs->mfs_next;	/* nulls eventually */
1561		free((void*)mfs);
1562	}
1563
1564	/* replace relevant entries */
1565	if (me->map_fstype)
1566		free(me->map_fstype);
1567	if ((me->map_fstype = strdup(MNTTYPE_AUTOFS)) == NULL)
1568		goto alloc_failed;
1569
1570	if (me->map_mounter)
1571		free(me->map_mounter);
1572	if ((me->map_mounter = strdup(me->map_fstype)) == NULL)
1573		goto alloc_failed;
1574
1575	if (me->map_fs->mfs_dir)
1576		free(me->map_fs->mfs_dir);
1577	if ((me->map_fs->mfs_dir = strdup(mapname)) == NULL)
1578		goto alloc_failed;
1579
1580	/* set options */
1581	if (me->map_mntopts) {
1582		free(me->map_mntopts);
1583		me->map_mntopts = NULL;
1584	}
1585	if ((rc = automount_opts(&me->map_mntopts, mapopts)) != 0)
1586		return (rc);
1587
1588	/* mucked with this entry, set the map_modified field to TRUE */
1589	me->map_modified = TRUE;
1590
1591	return (rc);
1592
1593alloc_failed:
1594	syslog(LOG_ERR,
1595		"convert_mapent_to_automount: Memory allocation failed");
1596	return (ENOMEM);
1597}
1598
1599/*
1600 * automount_opts(char **map_mntopts, const char *mapopts)
1601 * modifies automount opts - gets rid of all "indirect" and "direct" strings
1602 * if they exist, and then adds a direct string to force a direct automount.
1603 * Rest of the mapopts stay intact. Returns 0 or appropriate error.
1604 */
1605static int
1606automount_opts(char **map_mntopts, const char *mapopts)
1607{
1608	char *opts;
1609	char *opt;
1610	size_t len;
1611	char *placeholder;
1612	char buf[AUTOFS_MAXOPTSLEN];
1613
1614	char *addopt = "direct";
1615
1616	len = strlen(mapopts)+ strlen(addopt)+2;	/* +2 for ",", '\0' */
1617	if (len > AUTOFS_MAXOPTSLEN) {
1618		syslog(LOG_ERR,
1619		"option string %s too long (max=%d)", mapopts,
1620			AUTOFS_MAXOPTSLEN-8);
1621		return (EIO);
1622	}
1623
1624	if (((*map_mntopts) = ((char *)malloc(len))) == NULL) {
1625		syslog(LOG_ERR,	"automount_opts: Memory allocation failed");
1626		return (ENOMEM);
1627	}
1628	memset(*map_mntopts, 0, len);
1629
1630	strlcpy(buf, mapopts, sizeof(buf));
1631	opts = buf;
1632	while ((opt = strtok_r(opts, ",", &placeholder)) != NULL) {
1633		opts = NULL;
1634
1635		/* remove trailing and leading spaces */
1636		while (isspace(*opt))
1637			opt++;
1638		len = strlen(opt)-1;
1639		while (isspace(opt[len]))
1640			opt[len--] = '\0';
1641
1642		/*
1643		 * if direct or indirect found, get rid of it, else put it
1644		 * back
1645		 */
1646		if ((strcmp(opt, "indirect") == 0) ||
1647		    (strcmp(opt, "direct") == 0))
1648			continue;
1649		/*
1650		 * *map_mntopts is large enough to hold a copy of mapopts,
1651		 * so these strcat()s are safe.
1652		 */
1653		if (*map_mntopts[0] != '\0')
1654			strcat(*map_mntopts, ",");
1655		strcat(*map_mntopts, opt);
1656	}
1657
1658	/*
1659	 * Add the direct string at the end.
1660	 * *map_mntopts is large enough to hold a copy of mapopts, plus
1661	 * a comma, plus addopt, so these strcat()s are safe.
1662	 */
1663	if (*map_mntopts[0] != '\0')
1664		strcat(*map_mntopts,	",");
1665	strcat(*map_mntopts, addopt);
1666
1667	return (0);
1668}
1669
1670/*
1671 * parse_fsinfo(char *mapname, struct mapent *mapents)
1672 * parses the filesystem information stored in me->map_fsw and me->map_fswq
1673 * and calls appropriate filesystem parser.
1674 * Returns 0 or an appropriate error value.
1675 */
1676static int
1677parse_fsinfo(const char *mapname, struct mapent *mapents)
1678{
1679	struct mapent *me = mapents;
1680	char *bufp;
1681	char *bufq;
1682	int err = 0;
1683
1684	while (me != NULL) {
1685		bufp = "";
1686		bufq = "";
1687		if (me->map_fstype == NULL) {
1688			/*
1689			 * No file system type explicitly specified.
1690			 * If the thing to be mounted looks like
1691			 * a non-NFS URL, make the type "url", otherwise
1692			 * make it "nfs".
1693			 */
1694			if (is_nonnfs_url(me->map_fsw, me->map_fswq))
1695				me->map_fstype = strdup("url");
1696			else
1697				me->map_fstype = strdup("nfs");
1698		}
1699		if (strcmp(me->map_fstype, "nfs") == 0) {
1700			err = parse_nfs(mapname, me, me->map_fsw,
1701				me->map_fswq, &bufp, &bufq);
1702		} else {
1703			err = parse_special(me, me->map_fsw, me->map_fswq,
1704				&bufp, &bufq, MAXPATHLEN);
1705		}
1706
1707		if (err != 0 || *me->map_fsw != '\0' ||
1708		    *me->map_fswq != '\0') {
1709			/* sanity check */
1710			if (verbose)
1711				syslog(LOG_ERR,
1712				"parse_fsinfo: mount location error %s",
1713				    me->map_fsw);
1714			return (EIO);
1715		}
1716
1717		me = me->map_next;
1718	}
1719
1720	if (trace > 3) {
1721		trace_mapents("parse_fsinfo:", mapents);
1722	}
1723
1724	return (0);
1725}
1726
1727/*
1728 * Check whether the first token we see looks like a non-NFS URL or not.
1729 */
1730static int
1731is_nonnfs_url(fsw, fswq)
1732	char *fsw, *fswq;
1733{
1734	char tok1[MAXPATHLEN + 1], tok1q[MAXPATHLEN + 1];
1735	char *tok1p, *tok1qp;
1736	char c;
1737
1738	if (getword(tok1, tok1q, &fsw, &fswq, ' ', sizeof (tok1)) == -1)
1739		return (0);	/* error, doesn't look like anything */
1740
1741	tok1p = tok1;
1742	tok1qp = tok1q;
1743
1744	/*
1745	 * Scan until we find a character that doesn't belong in a URL
1746	 * scheme.  According to RFC 3986, "Scheme names consist of a
1747	 * sequence of characters beginning with a letter and followed
1748	 * by any combination of letters, digits, plus ("+"), period ("."),
1749	 * or hyphen ("-")."
1750	 *
1751	 * It's irrelevant whether the characters are quoted or not;
1752	 * quoting doesn't mean that the character is allowed to be part
1753	 * of a URL.
1754	 */
1755	while ((c = *tok1p) != '\0' && isascii(c) &&
1756	    (isalnum(c) || c == '+' || c == '.' || c == '-')) {
1757		tok1p++;
1758		tok1qp++;
1759	}
1760
1761	/*
1762	 * The character in question had better be a colon (quoted or not),
1763	 * otherwise this doesn't look like a URL.
1764	 */
1765	if (*tok1p != ':')
1766		return (0);	/* not a URL */
1767
1768	/*
1769	 * However, if it's a colon, that doesn't prove it's a URL; we
1770	 * could have server:/path.  Check whether the colon is followed
1771	 * by two slashes (quoted or not).
1772	 */
1773	if (*(tok1p + 1) != '/' || *(tok1p + 2) != '/')
1774		return (0);	/* not a URL */
1775
1776	/*
1777	 * scheme://...
1778	 * Probably a URL; is the scheme "nfs"?
1779	 */
1780	if (tok1p - tok1 != 3) {
1781		/*
1782		 * It's not 3 characters long, so it's not "nfs", so
1783		 * we have something that looks like a non-NFS URL.
1784		 */
1785		return (1);
1786	}
1787
1788	/*
1789	 * It's 3 characters long; is it "nfs" (case-insensitive)?
1790	 */
1791	if (strncasecmp(tok1, "nfs", 3) != 0)
1792		return (1);	/* no, so probably a non-NFS URL */
1793	return (0);	/* yes, so not a non-NFS URL */
1794}
1795
1796/*
1797 * This function parses the map entry for a nfs type file system
1798 * The input is the string lp (and lq) which can be one of the
1799 * following forms:
1800 * a) host[(penalty)][,host[(penalty)]]... :/directory
1801 * b) host[(penalty)]:/directory[ host[(penalty)]:/directory]...
1802 * This routine constructs a mapfs link-list for each of
1803 * the hosts and the corresponding file system. The list
1804 * is then attatched to the mapent struct passed in.
1805 */
1806static int
1807parse_nfs(mapname, me, fsw, fswq, lp, lq)
1808	struct mapent *me;
1809	const char *mapname;
1810	char *fsw, *fswq, **lp, **lq;
1811{
1812	struct mapfs *mfs, **mfsp;
1813	char *wlp, *wlq;
1814	char *hl, hostlist[1024], *hlq, hostlistq[1024];
1815	char hostname_and_penalty[MXHOSTNAMELEN+5];
1816	char *hn, *hnq, hostname[MXHOSTNAMELEN+1];
1817	char dirname[MAXPATHLEN+1], subdir[MAXPATHLEN+1];
1818	char qbuff[MAXPATHLEN+1], qbuff1[MAXPATHLEN+1];
1819	char pbuff[10], pbuffq[10];
1820	int penalty;
1821	char w[MAXPATHLEN];
1822	char wq[MAXPATHLEN];
1823	int host_cnt;
1824
1825	mfsp = &me->map_fs;
1826	*mfsp = NULL;
1827
1828	/*
1829	 * there may be more than one entry in the map list. Get the
1830	 * first one. Use temps to handle the word information and
1831	 * copy back into fsw and fswq fields when done.
1832	 */
1833	*lp = fsw;
1834	*lq = fswq;
1835	if (getword(w, wq, lp, lq, ' ', MAXPATHLEN) == -1)
1836		return (EIO);
1837	while (*w && *w != '/') {
1838		bool_t maybe_url;
1839
1840		maybe_url = TRUE;
1841
1842		wlp = w; wlq = wq;
1843		if (getword(hostlist, hostlistq, &wlp, &wlq, ':',
1844			    sizeof (hostlist)) == -1)
1845			return (EIO);
1846		if (!*hostlist) {
1847			syslog(LOG_ERR,
1848			"parse_nfs: host list empty in map %s \"%s\"",
1849			mapname, w);
1850			goto bad_entry;
1851		}
1852
1853		if (strcmp(hostlist, "nfs") != 0)
1854			maybe_url = FALSE;
1855
1856		if (getword(dirname, qbuff, &wlp, &wlq, ':',
1857					sizeof (dirname)) == -1)
1858			return (EIO);
1859		if (*dirname == '\0') {
1860			syslog(LOG_ERR,
1861			"parse_nfs: directory name empty in map %s \"%s\"",
1862			mapname, w);
1863			goto bad_entry;
1864		}
1865
1866		if (maybe_url == TRUE && strncmp(dirname, "//", 2) != 0)
1867			maybe_url = FALSE;
1868
1869		/*
1870		 * See the next block comment ("Once upon a time ...") to
1871		 * understand this. It turns the deprecated concept
1872		 * of "subdir mounts" produced some useful code for handling
1873		 * the possibility of a ":port#" in the URL.
1874		 */
1875		if (maybe_url == FALSE)
1876			*subdir = '/';
1877		else
1878			*subdir = ':';
1879
1880		*qbuff = ' ';
1881
1882		/*
1883		 * Once upon time, before autofs, there was support for
1884		 * "subdir mounts". The idea was to "economize" the
1885		 * number of mounts, so if you had a number of entries
1886		 * all referring to a common subdirectory, e.g.
1887		 *
1888		 *	carol    seasons:/export/home11/carol
1889		 *	ted	 seasons:/export/home11/ted
1890		 *	alice	 seasons:/export/home11/alice
1891		 *
1892		 * then you could tell the automounter to mount a
1893		 * common mountpoint which was delimited by the second
1894		 * colon:
1895		 *
1896		 *	carol    seasons:/export/home11:carol
1897		 *	ted	 seasons:/export/home11:ted
1898		 *	alice	 seasons:/export/home11:alice
1899		 *
1900		 * The automounter would mount seasons:/export/home11
1901		 * then for any other map entry that referenced the same
1902		 * directory it would build a symbolic link that
1903		 * appended the remainder of the path after the second
1904		 * colon, i.e.  once the common subdir was mounted, then
1905		 * other directories could be accessed just by link
1906		 * building - no further mounts required.
1907		 *
1908		 * In theory the "mount saving" idea sounded good. In
1909		 * practice the saving didn't amount to much and the
1910		 * symbolic links confused people because the common
1911		 * mountpoint had to have a pseudonym.
1912		 *
1913		 * To remain backward compatible with the existing
1914		 * maps, we interpret a second colon as a slash.
1915		 */
1916		if (getword(subdir+1, qbuff+1, &wlp, &wlq, ':',
1917				sizeof (subdir)) == -1)
1918			return (EIO);
1919
1920		if (*(subdir+1)) {
1921			if (strlcat(dirname, subdir, sizeof (dirname)) >=
1922			    sizeof (dirname)) {
1923				syslog(LOG_ERR, "directory+subdirectory is longer than MAXPATHLEN");
1924				return (EIO);
1925			}
1926		}
1927
1928		hl = hostlist; hlq = hostlistq;
1929
1930		host_cnt = 0;
1931		for (;;) {
1932
1933			if (getword(hostname_and_penalty, qbuff, &hl, &hlq, ',',
1934				sizeof (hostname_and_penalty)) == -1)
1935				return (EIO);
1936			if (!*hostname_and_penalty)
1937				break;
1938
1939			host_cnt++;
1940			if (host_cnt > 1)
1941				maybe_url = FALSE;
1942
1943			hn = hostname_and_penalty;
1944			hnq = qbuff;
1945			if (getword(hostname, qbuff1, &hn, &hnq, '(',
1946				sizeof (hostname)) == -1)
1947				return (EIO);
1948			if (hostname[0] == '\0') {
1949				syslog(LOG_ERR,
1950				"parse_nfs: host name empty in host list in map %s \"%s\"",
1951				mapname, w);
1952				goto bad_entry;
1953			}
1954
1955			if (strcmp(hostname, hostname_and_penalty) == 0) {
1956				penalty = 0;
1957			} else {
1958				maybe_url = FALSE;
1959				hn++; hnq++;
1960				if (getword(pbuff, pbuffq, &hn, &hnq, ')',
1961					sizeof (pbuff)) == -1)
1962					return (EIO);
1963				if (!*pbuff)
1964					penalty = 0;
1965				else
1966					penalty = atoi(pbuff);
1967			}
1968			mfs = (struct mapfs *)malloc(sizeof (*mfs));
1969			if (mfs == NULL) {
1970				syslog(LOG_ERR,
1971				"parse_nfs: Memory allocation failed");
1972				return (EIO);
1973			}
1974			(void) memset(mfs, 0, sizeof (*mfs));
1975			*mfsp = mfs;
1976			mfsp = &mfs->mfs_next;
1977
1978			if (maybe_url == TRUE) {
1979				char *host;
1980				char *path;
1981				char *sport;
1982
1983				host = dirname+2;
1984				path = strchr(host, '/');
1985				if (path == NULL) {
1986					syslog(LOG_ERR,
1987					"parse_nfs: illegal nfs url syntax: %s",
1988					host);
1989
1990					return (EIO);
1991				}
1992				*path = '\0';
1993				sport =  strchr(host, ':');
1994
1995				if (sport != NULL && sport < path) {
1996					*sport = '\0';
1997					mfs->mfs_port = atoi(sport+1);
1998
1999					if (mfs->mfs_port > USHRT_MAX) {
2000						syslog(LOG_ERR,
2001							"parse_nfs: invalid "
2002							"port number (%d) in "
2003							"NFS URL",
2004							mfs->mfs_port);
2005
2006						return (EIO);
2007					}
2008
2009				}
2010
2011				path++;
2012				if (*path == '\0')
2013					path = ".";
2014
2015				mfs->mfs_flags |= MFS_URL;
2016
2017				mfs->mfs_host = strdup(host);
2018				mfs->mfs_dir = strdup(path);
2019			} else {
2020				mfs->mfs_host = strdup(hostname);
2021				mfs->mfs_dir = strdup(dirname);
2022			}
2023
2024			mfs->mfs_penalty = penalty;
2025			if (mfs->mfs_host == NULL || mfs->mfs_dir == NULL) {
2026				syslog(LOG_ERR,
2027				"parse_nfs: Memory allocation failed");
2028				return (EIO);
2029			}
2030		}
2031		/*
2032		 * We check host_cnt to make sure we haven't parsed an entry
2033		 * with no host information.
2034		 */
2035		if (host_cnt == 0) {
2036			syslog(LOG_ERR,
2037			"parse_nfs: invalid host specified - bad entry "
2038			"in map %s \"%s\"",
2039			mapname, w);
2040			return (EIO);
2041		}
2042		if (getword(w, wq, lp, lq, ' ', MAXPATHLEN) == -1)
2043			return (EIO);
2044	}
2045
2046	/*
2047	 * w and wq are constructed from the input fsw and fswq,
2048	 * respectively, by extracting items, so they're guaranteed
2049	 * to be shorter than fsw and fswq, so these copies are safe.
2050	 */
2051	strcpy(fsw, w);
2052	strcpy(fswq, wq);
2053
2054	return (0);
2055
2056bad_entry:
2057	return (EIO);
2058}
2059
2060static int
2061parse_special(me, w, wq, lp, lq, wsize)
2062	struct mapent *me;
2063	char *w, *wq, **lp, **lq;
2064	int wsize;
2065{
2066	char mountname[MAXPATHLEN + 1], qbuf[MAXPATHLEN + 1];
2067	char *wlp, *wlq;
2068	struct mapfs *mfs;
2069
2070	wlp = w;
2071	wlq = wq;
2072	if (getword(mountname, qbuf, &wlp, &wlq, ' ', sizeof (mountname)) == -1)
2073		return (EIO);
2074	if (mountname[0] == '\0')
2075		return (EIO);
2076
2077	mfs = (struct mapfs *)malloc(sizeof (struct mapfs));
2078	if (mfs == NULL) {
2079		syslog(LOG_ERR, "parse_special: Memory allocation failed");
2080		return (EIO);
2081	}
2082	(void) memset(mfs, 0, sizeof (*mfs));
2083
2084	/*
2085	 * A device name that begins with a slash could
2086	 * be confused with a mountpoint path, hence use
2087	 * a colon to escape a device string that begins
2088	 * with a slash, e.g.
2089	 *
2090	 *	foo  -ro  /bar  foo:/bar
2091	 * and
2092	 *	foo  -ro  /dev/sr0
2093	 *
2094	 * would confuse the parser.  The second instance
2095	 * must use a colon:
2096	 *
2097	 *	foo  -ro  :/dev/sr0
2098	 */
2099	mfs->mfs_dir = strdup(&mountname[mountname[0] == ':']);
2100	if (mfs->mfs_dir == NULL) {
2101		syslog(LOG_ERR, "parse_special: Memory allocation failed");
2102		return (EIO);
2103	}
2104	me->map_fs = mfs;
2105	if (getword(w, wq, lp, lq, ' ', wsize) == -1)
2106		return (EIO);
2107	return (0);
2108}
2109
2110/*
2111 * get_dir_from_path(char *dir, char **path, int dirsz)
2112 * gets the directory name dir from path for max string of length dirsz.
2113 * A modification of the getword routine. Assumes the delimiter is '/'
2114 * and that excess /'s are redundant.
2115 * Returns 0 or EIO
2116 */
2117static int
2118get_dir_from_path(char *dir, const char **path, int dirsz)
2119{
2120	char *tmp = dir;
2121	int count = dirsz;
2122
2123	if (dirsz <= 0) {
2124		if (verbose)
2125			syslog(LOG_ERR,
2126			"get_dir_from_path: invalid directory size %d", dirsz);
2127		return (EIO);
2128	}
2129
2130	/* get rid of leading /'s in path */
2131	while (**path == '/')
2132		(*path)++;
2133
2134	/* now at a word or at the end of path */
2135	while ((**path) && ((**path) != '/')) {
2136		if (--count <= 0) {
2137			*tmp = '\0';
2138			syslog(LOG_ERR,
2139			"get_dir_from_path: max pathlength exceeded %d", dirsz);
2140			return (EIO);
2141		}
2142		*dir++ = *(*path)++;
2143	}
2144
2145	*dir = '\0';
2146
2147	/* get rid of trailing /'s in path */
2148	while (**path == '/')
2149		(*path)++;
2150
2151	return (0);
2152}
2153
2154/*
2155 * alloc_hiernode(hiernode **newnode, char *dirname)
2156 * allocates a new hiernode corresponding to a new directory entry
2157 * in the hierarchical structure, and passes a pointer to it back
2158 * to the calling program.
2159 * Returns 0 or appropriate error value.
2160 */
2161static int
2162alloc_hiernode(hiernode **newnode, char *dirname)
2163{
2164	if ((*newnode = (hiernode *)malloc(sizeof (hiernode))) == NULL) {
2165		syslog(LOG_ERR,	"alloc_hiernode: Memory allocation failed");
2166		return (ENOMEM);
2167	}
2168
2169	memset(((char *)*newnode), 0, sizeof (hiernode));
2170	if (CHECK_STRCPY((*newnode)->dirname, dirname, sizeof ((*newnode)->dirname))) {
2171                return EIO;
2172        }
2173	return (0);
2174}
2175
2176/*
2177 * free_hiernode(hiernode *node)
2178 * frees the allocated hiernode given the head of the structure
2179 * recursively calls itself until it frees entire structure.
2180 * Returns nothing.
2181 */
2182static void
2183free_hiernode(hiernode *node)
2184{
2185	hiernode *currnode = node;
2186	hiernode *prevnode = NULL;
2187
2188	while (currnode != NULL) {
2189		if (currnode->subdir != NULL)
2190			free_hiernode(currnode->subdir);
2191		prevnode = currnode;
2192		currnode = currnode->leveldir;
2193		free((void*)prevnode);
2194	}
2195}
2196
2197/*
2198 * free_mapent(struct mapent *)
2199 * free the mapentry and its fields
2200 */
2201void
2202free_mapent(me)
2203	struct mapent *me;
2204{
2205	struct mapfs *mfs;
2206	struct mapent *m;
2207
2208	while (me) {
2209		while (me->map_fs) {
2210			mfs = me->map_fs;
2211			if (mfs->mfs_host)
2212				free(mfs->mfs_host);
2213			if (mfs->mfs_dir)
2214				free(mfs->mfs_dir);
2215			if (mfs->mfs_args)
2216				free(mfs->mfs_args);
2217			me->map_fs = mfs->mfs_next;
2218			free((char *)mfs);
2219		}
2220
2221		if (me->map_root)
2222			free(me->map_root);
2223		if (me->map_mntpnt)
2224			free(me->map_mntpnt);
2225		if (me->map_mntopts)
2226			free(me->map_mntopts);
2227		if (me->map_fstype)
2228			free(me->map_fstype);
2229		if (me->map_mounter)
2230			free(me->map_mounter);
2231		if (me->map_fsw)
2232			free(me->map_fsw);
2233		if (me->map_fswq)
2234			free(me->map_fswq);
2235
2236		m = me;
2237		me = me->map_next;
2238		free((char *)m);
2239	}
2240}
2241
2242/*
2243 * trace_mapents(struct mapent *mapents)
2244 * traces through the mapentry structure and prints it element by element
2245 * returns nothing
2246 */
2247static void
2248trace_mapents(char *s, struct mapent *mapents)
2249{
2250	struct mapfs  *mfs;
2251	struct mapent *me;
2252
2253	trace_prt(1, "\t%s\n", s);
2254	for (me = mapents; me; me = me->map_next) {
2255		trace_prt(1, "\t    (%s,%s)\t %s%s -%s\n",
2256			me->map_fstype ? me->map_fstype : "",
2257			me->map_mounter ? me->map_mounter : "",
2258			me->map_root  ? me->map_root : "",
2259			me->map_mntpnt ? me->map_mntpnt : "",
2260			me->map_mntopts ? me->map_mntopts : "");
2261		for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next)
2262			trace_prt(0, "\t\t%s:%s\n",
2263				mfs->mfs_host ? mfs->mfs_host: "",
2264				mfs->mfs_dir ? mfs->mfs_dir : "");
2265
2266		trace_prt(1, "\t\tme->map_fsw=%s me->map_fswq=%s mntlevel=%d %s %s err=%d\n",
2267			me->map_fsw ? me->map_fsw:"",
2268			me->map_fswq ? me->map_fswq:"",
2269			me->map_mntlevel,
2270			me->map_modified ? "modify=TRUE":"modify=FALSE",
2271			me->map_faked ? "faked=TRUE":"faked=FALSE",
2272			me->map_err);
2273	}
2274}
2275
2276/*
2277 * trace_hierarchy(hiernode *node)
2278 * traces the allocated hiernode given the head of the structure
2279 * recursively calls itself until it traces entire structure.
2280 * the first call made at the root is made with a zero level.
2281 * nodelevel is simply used to print tab and make the tracing clean.
2282 * Returns nothing.
2283 */
2284static void
2285trace_hierarchy(hiernode *node, int nodelevel)
2286{
2287	char tabs[16];
2288	int i;
2289
2290	tabs[0] = '\0';
2291	for (i = 0; i < nodelevel; i++)
2292		strlcat(tabs, "\t", 16);
2293
2294	while (node != NULL) {
2295		if (node->mapent) {
2296			trace_prt(1, "%s(\"%s\", %d, %s)\n", tabs,
2297				node->dirname ? node->dirname :"-null-",
2298				node->mapent->map_mntlevel,
2299				node->mapent->map_mntopts ?  node->mapent->map_mntopts:"");
2300		} else
2301			trace_prt(1, "%s(\"%s\")\n", tabs, node->dirname ? node->dirname :"-null-");
2302
2303		if (node->subdir)
2304			trace_hierarchy(node->subdir, nodelevel + 1);
2305
2306		node = node->leveldir;
2307	}
2308}
2309
2310static const struct mntopt mopts_vers[] = {
2311	MOPT_VERS,
2312	{ NULL,		0, 0, 0 }
2313};
2314
2315static int
2316clnt_stat_to_errno(enum clnt_stat clnt_stat)
2317{
2318	switch (clnt_stat) {
2319
2320	case RPC_UNKNOWNHOST:
2321		return (ENOENT);	/* no such host = no such directory */
2322
2323	case RPC_TIMEDOUT:
2324		return (ETIMEDOUT);
2325
2326	default:
2327		return (EIO);
2328	}
2329}
2330
2331/*
2332 * mapopts must be at most MAXOPTSLEN bytes long (including the null
2333 * terminator).
2334 */
2335struct mapent *
2336do_mapent_hosts(mapopts, host, isdirect, err)
2337	const char *mapopts, *host;
2338	uint_t isdirect;
2339	int *err;
2340{
2341	CLIENT *cl;
2342	struct mapent *me, *ms = NULL, *mp;
2343	struct mapfs *mfs;
2344	struct exportnode *ex = NULL;
2345	struct exportnode *exlist = NULL, *texlist, **texp, *exnext;
2346	struct timeval timeout = {10, 0};	// sec,usec
2347	enum clnt_stat clnt_stat;
2348	char entryopts[MAXOPTSLEN];
2349	char fstype[MAX_FSLEN], mounter[MAX_FSLEN];
2350	size_t exlen;
2351	int duplicate;
2352	mntoptparse_t mop;
2353	int flags;
2354	int altflags;
2355	long optval;
2356	rpcvers_t nfsvers;	/* version in map options, 0 if not there */
2357	rpcvers_t vers, versmin; /* used to negotiate nfs vers in pingnfs() */
2358	int retries, delay;
2359
2360	if (trace > 1)
2361		trace_prt(1, "  do_mapent_hosts: host %s\n", host);
2362
2363	/*
2364	 * Check whether the host has a name that begins with ".";
2365	 * if so, it's not a valid host name, and we return ENOENT.
2366	 * That way, we avoid doing host name lookups for various
2367	 * dot-file names, e.g. ".localized" and ".DS_Store".
2368	 */
2369	if (host[0] == '.') {
2370		*err = ENOENT;
2371		return ((struct mapent *)NULL);
2372	}
2373
2374	/*
2375	 * XXX - this appears to assume that you can get *all* the
2376	 * file systems - or, at least, all the local file systems -
2377	 * from this machine simply by mounting / with the loopback
2378	 * file system.  We don't yet have a loopback file system,
2379	 * so we have to mount individual NFS exports.
2380	 */
2381#ifdef HAVE_LOFS
2382	/* check for special case: host is me */
2383
2384	if (self_check(host)) {
2385		ms = (struct mapent *)malloc(sizeof (*ms));
2386		if (ms == NULL)
2387			goto alloc_failed;
2388		(void) memset((char *)ms, 0, sizeof (*ms));
2389		if (CHECK_STRCPY(fstype, MNTTYPE_NFS, sizeof (fstype))) {
2390                        *err = EIO;
2391                        return ((struct mapent *)NULL);
2392                }
2393
2394		if (!get_opts(mapopts, entryopts, fstype, sizeof (fstype), NULL))
2395			goto fstype_too_long;
2396		ms->map_mntopts = strdup(entryopts);
2397		if (ms->map_mntopts == NULL)
2398			goto alloc_failed;
2399		ms->map_mounter = strdup(fstype);
2400		if (ms->map_mounter == NULL)
2401			goto alloc_failed;
2402		ms->map_fstype = strdup(MNTTYPE_NFS);
2403		if (ms->map_fstype == NULL)
2404			goto alloc_failed;
2405
2406		if (isdirect)
2407			ms->map_root = strdup("");
2408		else
2409			ms->map_root = strprefix_slash(host);
2410		if (ms->map_root == NULL)
2411			goto alloc_failed;
2412		ms->map_mntpnt = strdup("");
2413		if (ms->map_mntpnt == NULL)
2414			goto alloc_failed;
2415		mfs = (struct mapfs *)malloc(sizeof (*mfs));
2416		if (mfs == NULL)
2417			goto alloc_failed;
2418		(void) memset((char *)mfs, 0, sizeof (*mfs));
2419		ms->map_fs = mfs;
2420		mfs->mfs_host = strdup(host);
2421		if (mfs->mfs_host == NULL)
2422			goto alloc_failed;
2423		mfs->mfs_dir  = strdup("/");
2424		if (mfs->mfs_dir == NULL)
2425			goto alloc_failed;
2426
2427		/* initialize mntlevel and modify */
2428		ms->map_mntlevel = -1;
2429		ms->map_modified = FALSE;
2430		ms->map_faked = FALSE;
2431
2432		if (trace > 1)
2433			trace_prt(1, "  do_mapent_hosts: self-host %s OK\n", host);
2434
2435		*err = 0;
2436		return (ms);
2437	}
2438#endif
2439
2440	/*
2441	 * Call pingnfs. Note that we can't have replicated hosts in /net.
2442	 * XXX - we would like to avoid duplicating the across the wire calls
2443	 * made here in nfsmount(). The pingnfs cache should help avoid it.
2444	 */
2445	flags = altflags = 0;
2446	getmnt_silent = 1;
2447	mop = getmntopts(mapopts, mopts_vers, &flags, &altflags);
2448	if (mop == NULL) {
2449		syslog(LOG_ERR, "Couldn't parse mount options \"%s\" for %s: %m",
2450		    mapopts, host);
2451		*err = EIO;
2452		return ((struct mapent *)NULL);
2453	}
2454	if (altflags & (NFS_MNT_VERS|NFS_MNT_NFSVERS)) {
2455		optval = get_nfs_vers(mop, altflags);
2456		if (optval == 0) {
2457			syslog(LOG_ERR, "Invalid NFS version number for %s", host);
2458			freemntopts(mop);
2459			*err = EIO;
2460			return ((struct mapent *)NULL);
2461		}
2462		nfsvers = (rpcvers_t)optval;
2463	} else
2464		nfsvers = 0;
2465	freemntopts(mop);
2466	if (set_versrange(nfsvers, &vers, &versmin) != 0) {
2467		syslog(LOG_ERR, "Incorrect NFS version specified for %s", host);
2468		*err = EIO;
2469		return ((struct mapent *)NULL);
2470	}
2471	clnt_stat = pingnfs(host, &vers, versmin, 0, NULL, NULL);
2472	if (clnt_stat != RPC_SUCCESS) {
2473		*err = clnt_stat_to_errno(clnt_stat);
2474		return ((struct mapent *)NULL);
2475	}
2476
2477	retries = get_retry(mapopts);
2478	delay = INITDELAY;
2479retry:
2480	/* get export list of host */
2481	cl = clnt_create_timeout((char *)host, MOUNTPROG, MOUNTVERS, "tcp", &timeout);
2482	if (cl == NULL) {
2483		cl = clnt_create_timeout((char *)host, MOUNTPROG, MOUNTVERS, "udp", &timeout);
2484		if (cl == NULL) {
2485			syslog(LOG_ERR,
2486			"do_mapent_hosts: %s %s", host, clnt_spcreateerror(""));
2487			*err = clnt_stat_to_errno(rpc_createerr.cf_stat);
2488			return ((struct mapent *)NULL);
2489		}
2490
2491	}
2492#ifdef MALLOC_DEBUG
2493	add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__);
2494	add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
2495		__FILE__, __LINE__);
2496#endif
2497
2498	if ((clnt_stat = clnt_call(cl, MOUNTPROC_EXPORT, (xdrproc_t)xdr_void, 0,
2499		(xdrproc_t)xdr_exports, (caddr_t)&ex, timeout)) != RPC_SUCCESS) {
2500
2501		if (retries-- > 0) {
2502			clnt_destroy(cl);
2503			DO_DELAY(delay);
2504			goto retry;
2505		}
2506
2507		syslog(LOG_ERR,
2508			"do_mapent_hosts: %s: export list: %s",
2509			host, clnt_sperrno(clnt_stat));
2510#ifdef MALLOC_DEBUG
2511		drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__);
2512		drop_alloc("AUTH_HANDLE", cl->cl_auth,
2513			__FILE__, __LINE__);
2514#endif
2515		clnt_destroy(cl);
2516		*err = clnt_stat_to_errno(clnt_stat);
2517		return ((struct mapent *)NULL);
2518	}
2519
2520#ifdef MALLOC_DEBUG
2521	drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__);
2522	drop_alloc("AUTH_HANDLE", cl->cl_auth,
2523		__FILE__, __LINE__);
2524#endif
2525	clnt_destroy(cl);
2526
2527	if (ex == NULL) {
2528		if (trace > 1)
2529			trace_prt(1, "  getmapent_hosts: null export list\n");
2530		*err = 0;
2531		return ((struct mapent *)NULL);
2532	}
2533
2534	/* now sort by length of names - to get mount order right */
2535	exlist = ex;
2536	texlist = NULL;
2537#ifdef lint
2538	exnext = NULL;
2539#endif
2540	for (; ex; ex = exnext) {
2541		exnext = ex->ex_next;
2542		exlen = strlen(ex->ex_dir);
2543		duplicate = 0;
2544		for (texp = &texlist; *texp; texp = &((*texp)->ex_next)) {
2545			if (exlen < strlen((*texp)->ex_dir))
2546				break;
2547			duplicate = (strcmp(ex->ex_dir, (*texp)->ex_dir) == 0);
2548			if (duplicate) {
2549				/* disregard duplicate entry */
2550				freeex_ent(ex);
2551				break;
2552			}
2553		}
2554		if (!duplicate) {
2555			ex->ex_next = *texp;
2556			*texp = ex;
2557		}
2558	}
2559	exlist = texlist;
2560
2561	/*
2562	 * The following ugly chunk of code crept in as
2563	 * a result of cachefs.  If it's a cachefs mount
2564	 * of an nfs filesystem, then have it handled as
2565	 * an nfs mount but have cachefs do the mount.
2566	 */
2567	if (CHECK_STRCPY(fstype, MNTTYPE_NFS, sizeof (fstype))) {
2568		goto fstype_too_long;
2569	}
2570
2571	if (!get_opts(mapopts, entryopts, fstype, sizeof (fstype), NULL))
2572		goto fstype_too_long;
2573	(void) strlcpy(mounter, fstype, sizeof(mounter));
2574#ifdef MNTTYPE_CACHEFS
2575	if (strcmp(fstype, MNTTYPE_CACHEFS) == 0) {
2576		struct mnttab m;
2577		char *p;
2578
2579		m.mnt_mntopts = entryopts;
2580		if ((p = hasmntopt(&m, "backfstype")) != NULL) {
2581			int len = strlen(MNTTYPE_NFS);
2582
2583			p += 11;
2584			if (strncmp(p, MNTTYPE_NFS, len) == 0 &&
2585			    (p[len] == '\0' || p[len] == ',')) {
2586				/*
2587				 * Cached nfs mount
2588				 */
2589				if (CHECK_STRCPY(fstype, MNTTYPE_NFS, sizeof (fstype))) {
2590                                        goto fstype_too_long;
2591                                }
2592				if (CHECK_STRCPY(mounter, MNTTYPE_CACHEFS, sizeof (mounter))){
2593                                        goto fstype_too_long;
2594                                }
2595			}
2596		}
2597	}
2598#endif
2599
2600	/* Now create a mapent from the export list */
2601	ms = NULL;
2602	me = NULL;
2603
2604	for (ex = exlist; ex; ex = ex->ex_next) {
2605		mp = me;
2606		me = (struct mapent *)malloc(sizeof (*me));
2607		if (me == NULL)
2608			goto alloc_failed;
2609		(void) memset((char *)me, 0, sizeof (*me));
2610
2611		if (ms == NULL)
2612			ms = me;
2613		else
2614			mp->map_next = me;
2615
2616		if (isdirect)
2617			me->map_root = strdup("");
2618		else
2619			me->map_root = strprefix_slash(host);
2620		if (me->map_root == NULL)
2621			goto alloc_failed;
2622
2623		if (strcmp(ex->ex_dir, "/") != 0) {
2624			if (*(ex->ex_dir) != '/')
2625				me->map_mntpnt = strprefix_slash(ex->ex_dir);
2626			else
2627				me->map_mntpnt = strdup(ex->ex_dir);
2628		} else
2629			me->map_mntpnt = strdup("");
2630		if (me->map_mntpnt == NULL)
2631			goto alloc_failed;
2632
2633		me->map_fstype = strdup(fstype);
2634		if (me->map_fstype == NULL)
2635			goto alloc_failed;
2636		me->map_mounter = strdup(mounter);
2637		if (me->map_mounter == NULL)
2638			goto alloc_failed;
2639		me->map_mntopts = strdup(entryopts);
2640		if (me->map_mntopts == NULL)
2641			goto alloc_failed;
2642
2643		mfs = (struct mapfs *)malloc(sizeof (*mfs));
2644		if (mfs == NULL)
2645			goto alloc_failed;
2646		(void) memset((char *)mfs, 0, sizeof (*mfs));
2647		me->map_fs = mfs;
2648		mfs->mfs_host = strdup(host);
2649		if (mfs->mfs_host == NULL)
2650			goto alloc_failed;
2651		mfs->mfs_dir = strdup(ex->ex_dir);
2652		if (mfs->mfs_dir == NULL)
2653			goto alloc_failed;
2654
2655		/* initialize mntlevel and modify values */
2656		me->map_mntlevel = -1;
2657		me->map_modified = FALSE;
2658		me->map_faked = FALSE;
2659	}
2660	freeex(exlist);
2661
2662	if (trace > 1)
2663		trace_prt(1, "  do_mapent_hosts: host %s OK\n", host);
2664
2665	*err = 0;
2666	return (ms);
2667
2668alloc_failed:
2669	syslog(LOG_ERR, "do_mapent_hosts: Memory allocation failed");
2670	free_mapent(ms);
2671	freeex(exlist);
2672	*err = ENOMEM;
2673	return ((struct mapent *)NULL);
2674
2675fstype_too_long:
2676	syslog(LOG_ERR, "do_mapent_hosts: File system type is too long");
2677	free_mapent(ms);
2678	freeex(exlist);
2679	*err = EIO;
2680	return ((struct mapent *)NULL);
2681}
2682
2683
2684static void
2685freeex_ent(ex)
2686	struct exportnode *ex;
2687{
2688	struct groupnode *group, *tmpgroups;
2689
2690	free(ex->ex_dir);
2691	group = ex->ex_groups;
2692	while (group) {
2693		free(group->gr_name);
2694		tmpgroups = group->gr_next;
2695		free((char *)group);
2696		group = tmpgroups;
2697	}
2698	free((char *)ex);
2699}
2700
2701static void
2702freeex(ex)
2703	struct exportnode *ex;
2704{
2705	struct exportnode *tmpex;
2706
2707	while (ex) {
2708		tmpex = ex->ex_next;
2709		freeex_ent(ex);
2710		ex = tmpex;
2711	}
2712}
2713
2714struct create_mapent_args {
2715	uint_t		isdirect;
2716	const char	*host;
2717	struct mapent	*ms;
2718	struct mapent	*me;
2719};
2720
2721static int
2722create_mapent(struct fstabnode *fst, void *arg)
2723{
2724	struct create_mapent_args *args = arg;
2725	struct mapent *me, *mp;
2726	struct mapfs *mfs;
2727
2728	mp = args->me;
2729	me = (struct mapent *)malloc(sizeof (*me));
2730	if (me == NULL)
2731		goto alloc_failed;
2732	(void) memset((char *)me, 0, sizeof (*me));
2733	args->me = me;
2734
2735	if (args->isdirect)
2736		me->map_root = strdup("");
2737	else
2738		me->map_root = strprefix_slash(args->host);
2739	if (me->map_root == NULL)
2740		goto alloc_failed;
2741
2742	if (strcmp(fst->fst_dir, "/") == 0)
2743		me->map_mntpnt = strdup("");
2744	else {
2745		if (*(fst->fst_dir) != '/')
2746			me->map_mntpnt = strprefix_slash(fst->fst_dir);
2747		else
2748			me->map_mntpnt = strdup(fst->fst_dir);
2749	}
2750	if (me->map_mntpnt == NULL)
2751		goto alloc_failed;
2752
2753	me->map_fstype = strdup(fst->fst_vfstype);
2754	if (me->map_fstype == NULL)
2755		goto alloc_failed;
2756	me->map_mounter = strdup(fst->fst_vfstype);
2757	if (me->map_mounter == NULL)
2758		goto alloc_failed;
2759	me->map_mntopts = strdup(fst->fst_mntops);
2760	if (me->map_mntopts == NULL)
2761		goto alloc_failed;
2762
2763	mfs = (struct mapfs *)malloc(sizeof (*mfs));
2764	if (mfs == NULL)
2765		goto alloc_failed;
2766	(void) memset((char *)mfs, 0, sizeof (*mfs));
2767	me->map_fs = mfs;
2768	mfs->mfs_host = strdup(args->host);
2769	if (mfs->mfs_host == NULL)
2770		goto alloc_failed;
2771	if (strcmp(fst->fst_vfstype, "url") == 0) {
2772		/* Use the URL. */
2773		mfs->mfs_dir = strdup(fst->fst_url);
2774	} else
2775		mfs->mfs_dir = strdup(fst->fst_dir);
2776	if (mfs->mfs_dir == NULL)
2777		goto alloc_failed;
2778
2779	/* initialize mntlevel and modify values */
2780	me->map_mntlevel = -1;
2781	me->map_modified = FALSE;
2782	me->map_faked = FALSE;
2783
2784	if (args->ms == NULL)
2785		args->ms = me;
2786	else
2787		mp->map_next = me;
2788
2789	return (0);
2790
2791alloc_failed:
2792	free_mapent(me);
2793	return (ENOMEM);
2794}
2795
2796/*
2797 * mapopts must be at most MAXOPTSLEN bytes long (including the null
2798 * terminator).
2799 */
2800struct mapent *
2801do_mapent_fstab(mapopts, host, isdirect, node_type, err)
2802	const char *mapopts, *host;
2803	uint_t isdirect;
2804	int *node_type;
2805	int *err;
2806{
2807	struct mapent *ms = NULL;
2808	struct mapfs *mfs = NULL;
2809	char entryopts[MAXOPTSLEN];
2810	char fstype[MAX_FSLEN];
2811	struct create_mapent_args args;
2812
2813	if (trace > 1)
2814		trace_prt(1, "  do_mapent_fstab: host %s\n", host);
2815
2816	/*
2817	 * Check for special case: host is me.
2818	 * We check based on the name, as was done for the selflink
2819	 * in the old automounter; we don't check based on the
2820	 * IP address, as that's expensive (it requires that we
2821	 * resolve the IP address of the server, which is really
2822	 * expensive the first time it's done, as it's likely not
2823	 * to be in the DNS resolver's cache).  The host_is_us()
2824	 * check handles multi-homed hosts (it checks against the
2825	 * names corresponding to *all* this host's IP addresses)
2826	 * and handles local names (it checks against this host's
2827	 * .local name, if it has one).
2828	 */
2829
2830	if (host_is_us(host, strlen(host))) {
2831		ms = (struct mapent *)malloc(sizeof (*ms));
2832		if (ms == NULL)
2833			goto alloc_failed;
2834		(void) memset((char *)ms, 0, sizeof (*ms));
2835		if (CHECK_STRCPY(fstype, MNTTYPE_NFS, sizeof (fstype))) {
2836                        goto fstype_too_long;
2837                }
2838		if (!get_opts(mapopts, entryopts, fstype, sizeof (fstype), NULL))
2839			goto fstype_too_long;
2840		ms->map_mntopts = strdup(entryopts);
2841		if (ms->map_mntopts == NULL)
2842			goto alloc_failed;
2843		ms->map_mounter = strdup(fstype);
2844		if (ms->map_mounter == NULL)
2845			goto alloc_failed;
2846		ms->map_fstype = strdup(MNTTYPE_NFS);
2847		if (ms->map_fstype == NULL)
2848			goto alloc_failed;
2849
2850		if (isdirect)
2851			ms->map_root = strdup("");
2852		else
2853			ms->map_root = strprefix_slash(host);
2854		if (ms->map_root == NULL)
2855			goto alloc_failed;
2856		ms->map_mntpnt = strdup("");
2857		if (ms->map_mntpnt == NULL)
2858			goto alloc_failed;
2859		mfs = (struct mapfs *)malloc(sizeof (*mfs));
2860		if (mfs == NULL)
2861			goto alloc_failed;
2862		(void) memset((char *)mfs, 0, sizeof (*mfs));
2863		ms->map_fs = mfs;
2864		mfs->mfs_host = strdup(host);
2865		if (mfs->mfs_host == NULL)
2866			goto alloc_failed;
2867		mfs->mfs_dir  = strdup("/");
2868		if (mfs->mfs_dir == NULL)
2869			goto alloc_failed;
2870
2871		/* initialize mntlevel and modify */
2872		ms->map_mntlevel = -1;
2873		ms->map_modified = FALSE;
2874		ms->map_faked = FALSE;
2875
2876		if (trace > 1)
2877			trace_prt(1, "  do_mapent_fstab: self-host %s OK\n", host);
2878
2879		/*
2880		 * This is a symlink to /.
2881		 */
2882		if (node_type != NULL)
2883			*node_type = NT_SYMLINK;
2884
2885		*err = 0;
2886		return (ms);
2887	}
2888
2889	/*
2890	 * Create a mapent from the list of fstab map entries
2891	 * that refer to host.
2892	 */
2893	args.isdirect = isdirect;
2894	args.host = host;
2895	args.ms = NULL;
2896	args.me = NULL;
2897	*err = fstab_process_host(host, create_mapent, &args);
2898	if (*err != 0) {
2899		/*
2900		 * -1 means "there's no entry for that host".
2901		 */
2902		if (*err == -1)
2903			*err = ENOENT;
2904		else
2905			syslog(LOG_ERR, "do_mapent_fstab: Memory allocation failed");
2906		return ((struct mapent *)NULL);
2907	}
2908
2909	if (trace > 1)
2910		trace_prt(1, "  do_mapent_fstab: host %s OK\n", host);
2911
2912	return (args.ms);
2913
2914alloc_failed:
2915	syslog(LOG_ERR, "do_mapent_fstab: Memory allocation failed");
2916        if (mfs) {
2917                free(mfs);
2918        }
2919        if (ms) {
2920                free_mapent(ms);
2921        }
2922	*err = ENOMEM;
2923	return ((struct mapent *)NULL);
2924
2925fstype_too_long:
2926	syslog(LOG_ERR, "do_mapent_hosts: File system type is too long");
2927        if (ms) {
2928                free_mapent(ms);
2929        }
2930	*err = EIO;
2931	return ((struct mapent *)NULL);
2932}
2933
2934
2935struct mapent *
2936do_mapent_static(key, isdirect, err)
2937	const char *key;
2938	uint_t isdirect;
2939	int *err;
2940{
2941	struct staticmap *static_ent;
2942	struct mapent *me;
2943	struct mapfs *mfs;
2944
2945	/*
2946	 * Look up the fstab entry with the specified key
2947	 * as its mount point.
2948	 */
2949	static_ent = get_staticmap_entry(key);
2950	if (static_ent == NULL) {
2951		*err = ENOENT;
2952		return (NULL);
2953	}
2954
2955	if ((me = (struct mapent *)malloc(sizeof (*me))) == NULL)
2956		goto alloc_failed;
2957	memset((char *)me, 0, sizeof (*me));
2958	/*
2959	 * direct maps get an empty string as root - to be filled
2960	 * by the entire path later. Indirect maps get /key as the
2961	 * map root.
2962	 */
2963	if (isdirect)
2964		me->map_root = strdup("");
2965	else
2966		me->map_root = strprefix_slash(key);
2967	if (me->map_root == NULL)
2968		goto alloc_failed;
2969
2970	me->map_mntpnt = strdup("");
2971	if (me->map_mntpnt == NULL)
2972		goto alloc_failed;
2973	me->map_fstype = strdup(static_ent->vfstype);
2974	if (me->map_fstype == NULL)
2975		goto alloc_failed;
2976	me->map_mounter = strdup(static_ent->vfstype);
2977	if (me->map_mounter == NULL)
2978		goto alloc_failed;
2979	me->map_mntopts = strdup(static_ent->mntops);
2980	if (me->map_mntopts == NULL)
2981		goto alloc_failed;
2982	me->map_fsw = strdup("");
2983	if (me->map_fsw == NULL)
2984		goto alloc_failed;
2985	me->map_fswq = strdup("");
2986	if (me->map_fswq == NULL)
2987		goto alloc_failed;
2988
2989	/* initialize flags */
2990	me->map_mntlevel = -1;
2991	me->map_modified = FALSE;
2992	me->map_faked = FALSE;
2993	me->map_err = MAPENT_NOERR;
2994
2995	/* Add the one and only mapfs entry. */
2996	mfs = (struct mapfs *)malloc(sizeof (*mfs));
2997	if (mfs == NULL)
2998		goto alloc_failed;
2999	(void) memset((char *)mfs, 0, sizeof (*mfs));
3000	me->map_fs = mfs;
3001	mfs->mfs_host = strdup(static_ent->host);
3002	if (mfs->mfs_host == NULL)
3003		goto alloc_failed;
3004	mfs->mfs_dir = strdup(static_ent->spec);
3005	if (mfs->mfs_dir == NULL)
3006		goto alloc_failed;
3007
3008	me->map_next = NULL;
3009
3010	release_staticmap_entry(static_ent);
3011	*err = 0;
3012	return (me);
3013
3014alloc_failed:
3015	release_staticmap_entry(static_ent);
3016	syslog(LOG_ERR, "do_mapent_static: Memory allocation failed");
3017	free_mapent(me);
3018	*err = ENOMEM;
3019	return (NULL);
3020}
3021
3022static const char uatfs_err[] = "submount under fstype=autofs not supported";
3023/*
3024 * dump_mapent_err(struct mapent *me, const char *key, const char *mapname)
3025 * syslog appropriate error in mapentries.
3026 */
3027static void dump_mapent_err(struct mapent *me, const char *key,
3028			const char *mapname)
3029{
3030	switch (me->map_err) {
3031	case MAPENT_NOERR:
3032		if (verbose)
3033			syslog(LOG_ERR,
3034			"map=%s key=%s mntpnt=%s: no error",
3035			mapname, key, me->map_mntpnt);
3036		break;
3037	case MAPENT_UATFS:
3038		syslog(LOG_ERR,
3039		"mountpoint %s in map %s key %s not mounted: %s",
3040		    me->map_mntpnt, mapname, key, uatfs_err);
3041		break;
3042	default:
3043		if (verbose)
3044			syslog(LOG_ERR,
3045			"map=%s key=%s mntpnt=%s: unknown mapentry error",
3046			mapname, key, me->map_mntpnt);
3047	}
3048}
3049