Home | History | Annotate | Download | only in autofs
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  *	autod_parse.c
     23  *
     24  *	Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     25  *	Use is subject to license terms.
     26  */
     27 
     28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     29 
     30 #include <stdio.h>
     31 #include <ctype.h>
     32 #include <string.h>
     33 #include <syslog.h>
     34 #include <sys/types.h>
     35 #include <sys/stat.h>
     36 #include <sys/param.h>
     37 #include <errno.h>
     38 #include <pwd.h>
     39 #include <netinet/in.h>
     40 #include <netdb.h>
     41 #include <sys/tiuser.h>
     42 #include <locale.h>
     43 #include <stdlib.h>
     44 #include <unistd.h>
     45 #include <thread.h>
     46 #include <rpc/rpc.h>
     47 #include <rpcsvc/mount.h>
     48 #include <fcntl.h>
     49 #include <limits.h>
     50 #include "automount.h"
     51 
     52 /*
     53  * This structure is used to determine the hierarchical
     54  * relationship between directories
     55  */
     56 typedef struct _hiernode {
     57 	char dirname[MAXFILENAMELEN+1];
     58 	struct _hiernode *subdir;
     59 	struct _hiernode *leveldir;
     60 	struct mapent *mapent;
     61 } hiernode;
     62 
     63 void free_mapent(struct mapent *);
     64 
     65 static int mapline_to_mapent(struct mapent **, struct mapline *, char *, char *,
     66 				char *, char *, uint_t);
     67 static int hierarchical_sort(struct mapent *, hiernode **, char *, char *);
     68 static int push_options(hiernode *, char *, char *, int);
     69 static int set_mapent_opts(struct mapent *, char *, char *, char *);
     70 static void get_opts(char *, char *, char *, bool_t *);
     71 static int fstype_opts(struct mapent *, char *, char *, char *);
     72 static int modify_mapents(struct mapent **, char *, char *, char *, hiernode *,
     73 			char *, uint_t, bool_t);
     74 static int set_and_fake_mapent_mntlevel(hiernode *, char *, char *, char *,
     75 				struct mapent **, uint_t, char *, bool_t);
     76 static int mark_level1_root(hiernode *, char *);
     77 static int mark_and_fake_level1_noroot(hiernode *, char *, char *, char *,
     78 				    struct mapent **, uint_t i, char *);
     79 static int convert_mapent_to_automount(struct mapent *, char *, char *);
     80 static int automount_opts(char **, char *);
     81 static int parse_fsinfo(char *, struct mapent *);
     82 static int parse_nfs(char *, struct mapent *, char *, char *, char **, char **,
     83 				int);
     84 static int parse_special(struct mapent *, char *, char *, char **, char **,
     85 				int);
     86 static int get_dir_from_path(char *, char **, int);
     87 static int alloc_hiernode(hiernode **, char *);
     88 static void free_hiernode(hiernode *);
     89 static void trace_mapents(char *, struct mapent *);
     90 static void trace_hierarchy(hiernode *, int);
     91 static struct mapent *do_mapent_hosts(char *, char *, uint_t);
     92 static void freeex_ent(struct exportnode *);
     93 static void freeex(struct exportnode *);
     94 static void dump_mapent_err(struct mapent *, char *, char *);
     95 
     96 #define	PARSE_OK	0
     97 #define	PARSE_ERROR	-1
     98 #define	MAX_FSLEN	32
     99 
    100 /*
    101  * mapentry error type defininitions
    102  */
    103 #define	MAPENT_NOERR	0
    104 #define	MAPENT_UATFS	1
    105 
    106 /*
    107  * parse_entry(char *key, char *mapname, char *mapopts, struct mapline *ml,
    108  *			char *subdir, uint_t isdirect, bool_t mount_access)
    109  * Parses the data in ml to build a mapentry list containing the information
    110  * for the mounts/lookups to be performed. Builds an intermediate mapentry list
    111  * by processing ml, hierarchically sorts (builds a tree of) the list according
    112  * to mountpoint. Then pushes options down the hierarchy, and fills in the mount
    113  * file system. Finally, modifies the intermediate list depending on how far
    114  * in the hierarchy the current request is (uses subdir). Deals with special
    115  * case of /net map parsing.
    116  * Returns a pointer to the head of the mapentry list.
    117  */
    118 struct mapent *
    119 parse_entry(char *key, char *mapname, char *mapopts, struct mapline *ml,
    120 			char *subdir, uint_t isdirect, bool_t mount_access)
    121 {
    122 	char *p;
    123 	char defaultopts[AUTOFS_MAXOPTSLEN];
    124 
    125 	struct mapent *mapents = NULL;
    126 	hiernode *rootnode = NULL;
    127 	char *lp = ml->linebuf;
    128 
    129 	if (trace > 1)
    130 		trace_prt(1, "  mapline: %s\n", ml->linebuf);
    131 
    132 	/*
    133 	 * Assure the key is only one token long.
    134 	 * This prevents options from sneaking in through the
    135 	 * command line or corruption of /etc/mnttab.
    136 	 */
    137 	for (p = key; *p != '\0'; p++) {
    138 		if (isspace(*p)) {
    139 			syslog(LOG_ERR,
    140 			"parse_entry: bad key in map %s: %s", mapname, key);
    141 			return ((struct mapent *)NULL);
    142 		}
    143 	}
    144 
    145 	/*
    146 	 * select the appropriate parser, and build the mapentry list
    147 	 */
    148 	if (strcmp(lp, "-hosts") == 0) {
    149 		/*
    150 		 * the /net parser - uses do_mapent_hosts to build mapents.
    151 		 * The mapopts are considered default for every entry, so we
    152 		 * don't push options down hierarchies.
    153 		 */
    154 		mapents = do_mapent_hosts(mapopts, key, isdirect);
    155 		if (mapents == NULL)		/* nothing to free */
    156 			return (mapents);
    157 
    158 		if (trace > 3)
    159 			trace_mapents("do_mapent_hosts:(return)", mapents);
    160 
    161 		if (hierarchical_sort(mapents, &rootnode, key, mapname)
    162 		    != PARSE_OK)
    163 			goto parse_error;
    164 	} else {
    165 		/*
    166 		 * all other parsing
    167 		 */
    168 		if (mapline_to_mapent(&mapents, ml, key, mapname,
    169 		    mapopts, defaultopts, isdirect) != PARSE_OK)
    170 			goto parse_error;
    171 
    172 		if (mapents == NULL)
    173 			return (mapents);
    174 
    175 		if (hierarchical_sort(mapents, &rootnode, key, mapname)
    176 		    != PARSE_OK)
    177 			goto parse_error;
    178 
    179 		if (push_options(rootnode, defaultopts, mapopts,
    180 		    MAPENT_NOERR) != PARSE_OK)
    181 			goto parse_error;
    182 
    183 		if (trace > 3) {
    184 			trace_prt(1, "\n\tpush_options (return)\n");
    185 			trace_prt(0, "\tdefault options=%s\n", defaultopts);
    186 			trace_hierarchy(rootnode, 0);
    187 		};
    188 
    189 		if (parse_fsinfo(mapname, mapents) != PARSE_OK)
    190 			goto parse_error;
    191 	}
    192 
    193 	/*
    194 	 * Modify the mapentry list. We *must* do this only after
    195 	 * the mapentry list is completely built (since we need to
    196 	 * have parse_fsinfo called first).
    197 	 */
    198 	if (modify_mapents(&mapents, mapname, mapopts, subdir,
    199 	    rootnode, key, isdirect, mount_access) != PARSE_OK)
    200 		goto parse_error;
    201 
    202 	/*
    203 	 * XXX: its dangerous to use rootnode after modify mapents as
    204 	 * it may be pointing to mapents that have been freed
    205 	 */
    206 	if (rootnode != NULL)
    207 		free_hiernode(rootnode);
    208 
    209 	return (mapents);
    210 
    211 parse_error:
    212 	syslog(LOG_ERR, "parse_entry: mapentry parse error: map=%s key=%s",
    213 	    mapname, key);
    214 	free_mapent(mapents);
    215 	if (rootnode != NULL)
    216 		free_hiernode(rootnode);
    217 	return ((struct mapent *)NULL);
    218 }
    219 
    220 
    221 /*
    222  * mapline_to_mapent(struct mapent **mapents, struct mapline *ml,
    223  *		char *key, char *mapname, char *mapopts, char *defaultopts,
    224  *              uint_t isdirect)
    225  * Parses the mapline information in ml word by word to build an intermediate
    226  * mapentry list, which is passed back to the caller. The mapentries may have
    227  * holes (example no options), as they are completed only later. The logic is
    228  * awkward, but needed to provide the supported flexibility in the map entries.
    229  * (especially the first line). Note that the key is the full pathname of the
    230  * directory to be mounted in a direct map, and ml is the mapentry beyond key.
    231  * Returns PARSE_OK or an appropriate error value.
    232  */
    233 static int
    234 mapline_to_mapent(struct mapent **mapents, struct mapline *ml, char *key,
    235 		char *mapname, char *mapopts, char *defaultopts,
    236 		uint_t isdirect)
    237 {
    238 	struct mapent *me = NULL;
    239 	struct mapent *mp;
    240 	char w[MAXPATHLEN];
    241 	char wq[MAXPATHLEN];
    242 	char w1[MAXPATHLEN];
    243 	int implied;
    244 
    245 	char *lp = ml->linebuf;
    246 	char *lq = ml->lineqbuf;
    247 
    248 	/* do any macro expansions that are required to complete ml */
    249 	if (macro_expand(key, lp, lq, LINESZ)) {
    250 		syslog(LOG_ERR,
    251 		"mapline_to_mapent: map %s: line too long (max %d chars)",
    252 		    mapname, LINESZ - 1);
    253 		return (PARSE_ERROR);
    254 	}
    255 	if (trace > 3 && (strcmp(ml->linebuf, lp) != 0))
    256 		trace_prt(1,
    257 		    "  mapline_to_mapent: (expanded) mapline (%s,%s)\n",
    258 		    ml->linebuf, ml->lineqbuf);
    259 
    260 	/* init the head of mapentry list to null */
    261 	*mapents = NULL;
    262 
    263 	/*
    264 	 * Get the first word - its either a '-' if default options provided,
    265 	 * a '/', if the mountroot is implicitly provided, or a mount filesystem
    266 	 * if the mountroot is implicit. Note that if the first word begins with
    267 	 * a '-' then the second must be read and it must be a mountpoint or a
    268 	 * mount filesystem. Use mapopts if no default opts are provided.
    269 	 */
    270 	if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1)
    271 		return (PARSE_ERROR);
    272 	if (*w == '-') {
    273 		strcpy(defaultopts, w);
    274 		if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1)
    275 			return (PARSE_ERROR);
    276 	} else
    277 		strcpy(defaultopts, mapopts);
    278 
    279 	/*
    280 	 * implied is true if there is no '/' (the usual NFS case)
    281 	 * or if there are two slashes (the smbfs case)
    282 	 */
    283 	implied = ((*w != '/') || (*(w+1) == '/'));
    284 	while (*w == '/' || implied) {
    285 		mp = me;
    286 		if ((me = (struct mapent *)malloc(sizeof (*me))) == NULL)
    287 			goto alloc_failed;
    288 		(void) memset((char *)me, 0, sizeof (*me));
    289 		if (*mapents == NULL)	/* special case of head */
    290 			*mapents = me;
    291 		else
    292 			mp->map_next = me;
    293 
    294 		/*
    295 		 * direct maps get an empty string as root - to be filled
    296 		 * by the entire path later. Indirect maps get /key as the
    297 		 * map root. Note that xfn maps don't care about the root
    298 		 * - they override it in getmapent_fn().
    299 		 */
    300 		if (isdirect) {
    301 			*w1 = '\0';
    302 		} else {
    303 			strcpy(w1, "/");
    304 			strcat(w1, key);
    305 		}
    306 		if ((me->map_root = strdup(w1)) == NULL)
    307 			goto alloc_failed;
    308 
    309 		/* mntpnt is empty for the mount root */
    310 		if (strcmp(w, "/") == 0 || implied)
    311 			me->map_mntpnt = strdup("");
    312 		else
    313 			me->map_mntpnt = strdup(w);
    314 		if (me->map_mntpnt == NULL)
    315 			goto alloc_failed;
    316 
    317 		/*
    318 		 * If implied, the word must be a mount filesystem,
    319 		 * and its already read in; also turn off implied - its
    320 		 * not applicable except for the mount root. Else,
    321 		 * read another (or two) words depending on if there's
    322 		 * an option.
    323 		 */
    324 		if (implied)   /* must be a mount filesystem */
    325 			implied = 0;
    326 		else {
    327 			if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1)
    328 				return (PARSE_ERROR);
    329 			if (w[0] == '-') {
    330 				/* mount options */
    331 				if ((me->map_mntopts = strdup(w)) == NULL)
    332 					goto alloc_failed;
    333 				if (getword(w, wq, &lp, &lq, ' ',
    334 				    sizeof (w)) == -1)
    335 					return (PARSE_ERROR);
    336 			}
    337 		}
    338 
    339 		/*
    340 		 * must be a mount filesystem or a set of filesystems at
    341 		 * this point.
    342 		 */
    343 		if (w[0] == '\0' || w[0] == '-') {
    344 			syslog(LOG_ERR,
    345 			"mapline_to_mapent: bad location=%s map=%s key=%s",
    346 			    w, mapname, key);
    347 			return (PARSE_ERROR);
    348 		}
    349 
    350 		/*
    351 		 * map_fsw and map_fswq hold information which will be
    352 		 * used to determine filesystem information at a later
    353 		 * point. This is required since we can only find out
    354 		 * about the mount file system after the directories
    355 		 * are hierarchically sorted and options have been pushed
    356 		 * down the hierarchies.
    357 		 */
    358 		if (((me->map_fsw = strdup(w)) == NULL) ||
    359 		    ((me->map_fswq = strdup(wq)) == NULL))
    360 			goto alloc_failed;
    361 
    362 		/*
    363 		 * the next word, if any, is either another mount point or a
    364 		 * mount filesystem if more than one server is listed.
    365 		 */
    366 		if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1)
    367 			return (PARSE_ERROR);
    368 		while (*w && *w != '/') {	/* more than 1 server listed */
    369 			int len;
    370 			char *fsw, *fswq;
    371 			len = strlen(me->map_fsw) + strlen(w) + 4;
    372 			if ((fsw = (char *)malloc(len)) == NULL)
    373 				goto alloc_failed;
    374 			sprintf(fsw, "%s   %s", me->map_fsw, w);
    375 			free(me->map_fsw);
    376 			me->map_fsw = fsw;
    377 			len = strlen(me->map_fswq) + strlen(wq) + 4;
    378 			if ((fswq = (char *)malloc(len)) == NULL)
    379 				goto alloc_failed;
    380 			sprintf(fswq, "%s   %s", me->map_fswq, wq);
    381 			free(me->map_fswq);
    382 			me->map_fswq = fswq;
    383 			if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1)
    384 				return (PARSE_ERROR);
    385 		}
    386 
    387 		/* initialize flags */
    388 		me->map_mntlevel = -1;
    389 		me->map_modified = FALSE;
    390 		me->map_faked = FALSE;
    391 		me->map_err = MAPENT_NOERR;
    392 
    393 		me->map_next = NULL;
    394 	}
    395 
    396 	if (*mapents == NULL || w[0] != '\0') {	/* sanity check */
    397 		if (verbose) {
    398 			if (*mapents == NULL)
    399 				syslog(LOG_ERR,
    400 				"mapline_to_mapent: parsed with null mapents");
    401 			else
    402 				syslog(LOG_ERR,
    403 				"mapline_to_mapent: parsed nononempty w=%s", w);
    404 		}
    405 		return (PARSE_ERROR);
    406 	}
    407 
    408 	if (trace > 3)
    409 		trace_mapents("mapline_to_mapent:", *mapents);
    410 
    411 	return (PARSE_OK);
    412 
    413 alloc_failed:
    414 	syslog(LOG_ERR, "mapline_to_mapent: Memory allocation failed");
    415 	return (ENOMEM);
    416 }
    417 
    418 /*
    419  * hierarchical_sort(struct mapent *mapents, hiernode **rootnode, char *key
    420  *                   char *mapname)
    421  * sorts the mntpnts in each mapent to build a hierarchy of nodes, with
    422  * with the rootnode being the mount root. The hierarchy is setup as
    423  * levels, and subdirs below each level. Provides a link from node to
    424  * the relevant mapentry.
    425  * Returns PARSE_OK or appropriate error value
    426  */
    427 static int
    428 hierarchical_sort(struct mapent *mapents, hiernode **rootnode, char *key,
    429 	char *mapname)
    430 {
    431 	hiernode *prevnode, *currnode, *newnode;
    432 	char *path;
    433 	char dirname[MAXFILENAMELEN];
    434 
    435 	int rc = PARSE_OK;
    436 	struct mapent *me = mapents;
    437 
    438 	/* allocate the rootnode with a default path of "" */
    439 	*rootnode = NULL;
    440 	if ((rc = alloc_hiernode(rootnode, "")) != PARSE_OK)
    441 		return (rc);
    442 
    443 	/*
    444 	 * walk through mapents - for each mapent, locate the position
    445 	 * within the hierarchy by walking across leveldirs, and
    446 	 * subdirs of matched leveldirs. Starts one level below
    447 	 * the root (assumes an implicit match with rootnode).
    448 	 * XXX - this could probably be done more cleanly using recursion.
    449 	 */
    450 	while (me != NULL) {
    451 
    452 		path = me->map_mntpnt;
    453 
    454 		if ((rc = get_dir_from_path(dirname, &path,
    455 		    sizeof (dirname))) != PARSE_OK)
    456 			return (rc);
    457 
    458 		prevnode = *rootnode;
    459 		currnode = (*rootnode)->subdir;
    460 
    461 		while (dirname[0] != '\0') {
    462 			if (currnode != NULL) {
    463 				if (strcmp(currnode->dirname, dirname) == 0) {
    464 					/*
    465 					 * match found - mntpnt is a child of
    466 					 * this node
    467 					 */
    468 					prevnode = currnode;
    469 					currnode = currnode->subdir;
    470 				} else {
    471 					prevnode = currnode;
    472 					currnode = currnode->leveldir;
    473 
    474 					if (currnode == NULL) {
    475 						/*
    476 						 * No more leveldirs to match.
    477 						 * Add a new one
    478 						 */
    479 						if ((rc = alloc_hiernode
    480 						    (&newnode, dirname))
    481 						    != PARSE_OK)
    482 							return (rc);
    483 						prevnode->leveldir = newnode;
    484 						prevnode = newnode;
    485 						currnode = newnode->subdir;
    486 					} else {
    487 						/* try this leveldir */
    488 						continue;
    489 					}
    490 				}
    491 			} else {
    492 				/* no more subdirs to match. Add a new one */
    493 				if ((rc = alloc_hiernode(&newnode,
    494 				    dirname)) != PARSE_OK)
    495 					return (rc);
    496 				prevnode->subdir = newnode;
    497 				prevnode = newnode;
    498 				currnode = newnode->subdir;
    499 			}
    500 			if ((rc = get_dir_from_path(dirname, &path,
    501 			    sizeof (dirname))) != PARSE_OK)
    502 				return (rc);
    503 		}
    504 
    505 		if (prevnode->mapent != NULL) {
    506 			/* duplicate mntpoint found */
    507 			syslog(LOG_ERR,
    508 			"hierarchical_sort: duplicate mntpnt map=%s key=%s",
    509 			    mapname, key);
    510 			return (PARSE_ERROR);
    511 		}
    512 
    513 		/* provide a pointer from node to mapent */
    514 		prevnode->mapent = me;
    515 		me = me->map_next;
    516 	}
    517 
    518 	if (trace > 3) {
    519 		trace_prt(1, "\n\thierarchical_sort:\n");
    520 		trace_hierarchy(*rootnode, 0);	/* 0 is rootnode's level */
    521 	}
    522 
    523 	return (rc);
    524 }
    525 
    526 /*
    527  * push_options(hiernode *node, char *opts, char *mapopts, int err)
    528  * Pushes the options down a hierarchical structure. Works recursively from the
    529  * root, which is passed in on the first call. Uses a replacement policy.
    530  * If a node points to a mapentry, and it has an option, then thats the option
    531  * for that mapentry. Else, the node's mapent inherits the option from the
    532  * default (which may be the global option for the entry or mapopts).
    533  * err is useful in flagging entries with errors in pushing options.
    534  * returns PARSE_OK or appropriate error value.
    535  */
    536 static int
    537 push_options(hiernode *node, char *defaultopts, char *mapopts, int err)
    538 {
    539 	int rc = PARSE_OK;
    540 	struct mapent *me = NULL;
    541 
    542 	/* ensure that all the dirs at a level are passed the default options */
    543 	while (node != NULL) {
    544 		me = node->mapent;
    545 		if (me != NULL) {	/* not all nodes point to a mapentry */
    546 			me->map_err = err;
    547 			if ((rc = set_mapent_opts(me, me->map_mntopts,
    548 			    defaultopts, mapopts)) != PARSE_OK)
    549 				return (rc);
    550 		}
    551 
    552 		/* push the options to subdirs */
    553 		if (node->subdir != NULL) {
    554 			if (node->mapent && strcmp(node->mapent->map_fstype,
    555 			    MNTTYPE_AUTOFS) == 0)
    556 				err = MAPENT_UATFS;
    557 			if ((rc = push_options(node->subdir, defaultopts,
    558 			    mapopts, err)) != PARSE_OK)
    559 				return (rc);
    560 		}
    561 		node = node->leveldir;
    562 	}
    563 	return (rc);
    564 }
    565 
    566 #define	BACKFSTYPE "backfstype" /* used in cachefs options */
    567 #define	BACKFSTYPE_EQ "backfstype="
    568 #define	FSTYPE "fstype"
    569 #define	FSTYPE_EQ "fstype="
    570 #define	NO_OPTS ""
    571 
    572 /*
    573  * set_mapent_opts(struct mapent *me, char *opts, char *defaultopts,
    574  *			char *mapopts)
    575  * sets the mapentry's options, fstype and mounter fields by separating
    576  * out the fstype part from the opts. Use default options if opts is NULL.
    577  * Note taht defaultopts may be the same as mapopts.
    578  * Returns PARSE_OK or appropriate error value.
    579  */
    580 static int
    581 set_mapent_opts(struct mapent *me, char *opts, char *defaultopts,
    582 		char *mapopts)
    583 {
    584 	char entryopts[AUTOFS_MAXOPTSLEN];
    585 	char fstype[MAX_FSLEN], mounter[MAX_FSLEN];
    586 	int rc = PARSE_OK;
    587 	bool_t fstype_opt = FALSE;
    588 
    589 	strcpy(fstype, MNTTYPE_NFS);		/* default */
    590 
    591 	/* set options to default options, if none exist for this entry */
    592 	if (opts == NULL) {
    593 		opts = defaultopts;
    594 		if (defaultopts == NULL) { /* NULL opts for entry */
    595 			strcpy(mounter, fstype);
    596 			goto done;
    597 		}
    598 	}
    599 	if (*opts == '-')
    600 		opts++;
    601 
    602 	/* separate opts into fstype and (other) entrypopts */
    603 	get_opts(opts,	entryopts, fstype, &fstype_opt);
    604 
    605 	/* replace any existing opts */
    606 	if (me->map_mntopts != NULL)
    607 		free(me->map_mntopts);
    608 	if ((me->map_mntopts = strdup(entryopts)) == NULL)
    609 		return (ENOMEM);
    610 	strcpy(mounter,	fstype);
    611 
    612 	/*
    613 	 * The following ugly chunk of code crept in as a result of
    614 	 * cachefs.  If it's a cachefs mount of an nfs filesystem, then
    615 	 * it's important to parse the nfs special field.  Otherwise,
    616 	 * just hand the special field to the fs-specific mount
    617 	 */
    618 	if (strcmp(fstype, MNTTYPE_CACHEFS) ==  0) {
    619 		struct mnttab m;
    620 		char *p;
    621 
    622 		m.mnt_mntopts = entryopts;
    623 		if ((p = hasmntopt(&m, BACKFSTYPE)) != NULL) {
    624 			int len = strlen(MNTTYPE_NFS);
    625 
    626 			p += strlen(BACKFSTYPE_EQ);
    627 
    628 			if (strncmp(p, MNTTYPE_NFS, len) ==  0 &&
    629 			    (p[len] == '\0' || p[len] == ',')) {
    630 				/*
    631 				 * Cached nfs mount
    632 				 */
    633 				(void) strcpy(fstype, MNTTYPE_NFS);
    634 				(void) strcpy(mounter, MNTTYPE_CACHEFS);
    635 			}
    636 		}
    637 	}
    638 
    639 	/*
    640 	 * child options are exactly fstype = somefs, we need to do some
    641 	 * more option pushing work.
    642 	 */
    643 	if (fstype_opt == TRUE &&
    644 	    (strcmp(me->map_mntopts, NO_OPTS) == 0)) {
    645 		free(me->map_mntopts);
    646 		if ((rc = fstype_opts(me, opts, defaultopts,
    647 		    mapopts)) != PARSE_OK)
    648 			return (rc);
    649 	}
    650 
    651 done:
    652 	if (((me->map_fstype = strdup(fstype)) == NULL) ||
    653 	    ((me->map_mounter = strdup(mounter)) == NULL)) {
    654 		if (me->map_fstype != NULL)
    655 			free(me->map_fstype);
    656 		syslog(LOG_ERR, "set_mapent_opts: No memory");
    657 		return (ENOMEM);
    658 	}
    659 
    660 	return (rc);
    661 }
    662 
    663 /*
    664  * Check the option string for an "fstype"
    665  * option.  If found, return the fstype
    666  * and the option string with the fstype
    667  * option removed, e.g.
    668  *
    669  *  input:  "fstype=cachefs,ro,nosuid"
    670  *  opts:   "ro,nosuid"
    671  *  fstype: "cachefs"
    672  *
    673  * Also indicates if the fstype option was present
    674  * by setting a flag, if the pointer to the flag
    675  * is not NULL.
    676  */
    677 static void
    678 get_opts(input, opts, fstype, fstype_opt)
    679 	char *input;
    680 	char *opts; 	/* output */
    681 	char *fstype;   /* output */
    682 	bool_t *fstype_opt;
    683 {
    684 	char *p, *pb;
    685 	char buf[MAXOPTSLEN];
    686 	char *placeholder;
    687 
    688 	*opts = '\0';
    689 	(void) strcpy(buf, input);
    690 	pb = buf;
    691 	while (p = (char *)strtok_r(pb, ",", &placeholder)) {
    692 		pb = NULL;
    693 		if (strncmp(p, FSTYPE_EQ, 7) == 0) {
    694 			if (fstype_opt != NULL)
    695 				*fstype_opt = TRUE;
    696 			(void) strcpy(fstype, p + 7);
    697 		} else {
    698 			if (*opts)
    699 				(void) strcat(opts, ",");
    700 			(void) strcat(opts, p);
    701 		}
    702 	}
    703 }
    704 
    705 /*
    706  * fstype_opts(struct mapent *me, char *opts, char *defaultopts,
    707  *				char *mapopts)
    708  * We need to push global options to the child entry if it is exactly
    709  * fstype=somefs.
    710  */
    711 static int
    712 fstype_opts(struct mapent *me, char *opts, char *defaultopts,
    713 				char *mapopts)
    714 {
    715 	char pushopts[AUTOFS_MAXOPTSLEN];
    716 	char pushentryopts[AUTOFS_MAXOPTSLEN];
    717 	char pushfstype[MAX_FSLEN];
    718 
    719 	if (defaultopts && *defaultopts == '-')
    720 		defaultopts++;
    721 
    722 	/*
    723 	 * the options to push are the global defaults for the entry,
    724 	 * if they exist, or mapopts, if the global defaults for the
    725 	 * entry does not exist.
    726 	 */
    727 	if (strcmp(defaultopts, opts) == 0) {
    728 		if (*mapopts == '-')
    729 			mapopts++;
    730 		get_opts(mapopts, pushentryopts, pushfstype, NULL);
    731 		strcpy(pushopts, mapopts);
    732 	} else {
    733 		get_opts(defaultopts, pushentryopts, pushfstype, NULL);
    734 		strcpy(pushopts, defaultopts);
    735 	}
    736 
    737 	if (strcmp(pushfstype, MNTTYPE_CACHEFS) == 0)
    738 		me->map_mntopts = strdup(pushopts);
    739 	else
    740 		me->map_mntopts = strdup(pushentryopts);
    741 
    742 	if (!me->map_mntopts) {
    743 		syslog(LOG_ERR, "fstype_opts: No memory");
    744 		return (ENOMEM);
    745 	}
    746 
    747 	return (PARSE_OK);
    748 }
    749 
    750 /*
    751  * modify_mapents(struct mapent **mapents, char *mapname,
    752  *			char *mapopts, char *subdir, hiernode *rootnode,
    753  * 			char *key, uint_t isdirect, bool_t mount_access)
    754  * modifies the intermediate mapentry list into the final one, and passes
    755  * back a pointer to it. The final list may contain faked mapentries for
    756  * hiernodes that do not point to a mapentry, or converted mapentries, if
    757  * hiernodes that point to a mapentry need to be converted from nfs to autofs.
    758  * mounts. Entries that are not directly 1 level below the subdir are removed.
    759  * Returns PARSE_OK or PARSE_ERROR
    760  */
    761 static int
    762 modify_mapents(struct mapent **mapents, char *mapname,
    763 			char *mapopts, char *subdir, hiernode *rootnode,
    764 			char *key, uint_t isdirect, bool_t mount_access)
    765 {
    766 	struct mapent *mp = NULL;
    767 	char w[MAXPATHLEN];
    768 
    769 	struct mapent *me;
    770 	int rc = PARSE_OK;
    771 	struct mapent *faked_mapents = NULL;
    772 
    773 	/*
    774 	 * correct the mapentry mntlevel from default -1 to level depending on
    775 	 * position in hierarchy, and build any faked mapentries, if required
    776 	 * at one level below the rootnode given by subdir.
    777 	 */
    778 	if ((rc = set_and_fake_mapent_mntlevel(rootnode, subdir, key, mapname,
    779 	    &faked_mapents, isdirect, mapopts, mount_access)) != PARSE_OK)
    780 		return (rc);
    781 
    782 	/*
    783 	 * attaches faked mapents to real mapents list. Assumes mapents
    784 	 * is not NULL.
    785 	 */
    786 	me = *mapents;
    787 	while (me->map_next != NULL)
    788 		me = me->map_next;
    789 	me->map_next = faked_mapents;
    790 
    791 	/*
    792 	 * get rid of nodes marked at level -1
    793 	 */
    794 	me = *mapents;
    795 	while (me != NULL) {
    796 		if ((me->map_mntlevel ==  -1) || (me->map_err) ||
    797 		    (mount_access == FALSE && me->map_mntlevel == 0)) {
    798 			/*
    799 			 * syslog any errors and free entry
    800 			 */
    801 			if (me->map_err)
    802 				dump_mapent_err(me, key, mapname);
    803 
    804 			if (me ==  (*mapents)) {
    805 				/* special case when head has to be freed */
    806 				*mapents = me->map_next;
    807 				if ((*mapents) ==  NULL) {
    808 					/* something wierd happened */
    809 					if (verbose)
    810 						syslog(LOG_ERR,
    811 						"modify_mapents: level error");
    812 					return (PARSE_ERROR);
    813 				}
    814 
    815 				/* separate out the node */
    816 				me->map_next = NULL;
    817 				free_mapent(me);
    818 				me = *mapents;
    819 			} else {
    820 				mp->map_next = me->map_next;
    821 				me->map_next = NULL;
    822 				free_mapent(me);
    823 				me = mp->map_next;
    824 			}
    825 			continue;
    826 		}
    827 
    828 		/*
    829 		 * convert level 1 mapents that are not already autonodes
    830 		 * to autonodes
    831 		 */
    832 		if (me->map_mntlevel == 1 &&
    833 		    (strcmp(me->map_fstype, MNTTYPE_AUTOFS) != 0) &&
    834 		    (me->map_faked != TRUE)) {
    835 			if ((rc = convert_mapent_to_automount(me, mapname,
    836 			    mapopts)) != PARSE_OK)
    837 				return (rc);
    838 		}
    839 		strcpy(w, (me->map_mntpnt+strlen(subdir)));
    840 		strcpy(me->map_mntpnt, w);
    841 		mp = me;
    842 		me = me->map_next;
    843 	}
    844 
    845 	if (trace > 3)
    846 		trace_mapents("modify_mapents:", *mapents);
    847 
    848 	return (PARSE_OK);
    849 }
    850 
    851 /*
    852  * set_and_fake_mapent_mntlevel(hiernode *rootnode, char *subdir, char *key,
    853  *			char *mapname, struct mapent **faked_mapents,
    854  *			uint_t isdirect, char *mapopts, bool_t mount_access)
    855  * sets the mapentry mount levels (depths) with respect to the subdir.
    856  * Assigns a value of 0 to the new root. Finds the level1 directories by
    857  * calling mark_*_level1_*(). Also cleans off extra /'s in level0 and
    858  * level1 map_mntpnts. Note that one level below the new root is an existing
    859  * mapentry if there's a mapentry (nfs mount) corresponding to the root,
    860  * and the direct subdir set for the root, if there's no mapentry corresponding
    861  * to the root (we install autodirs). Returns PARSE_OK or error value.
    862  */
    863 static int
    864 set_and_fake_mapent_mntlevel(hiernode *rootnode, char *subdir, char *key,
    865 		char *mapname, struct mapent **faked_mapents,
    866 		uint_t isdirect, char *mapopts, bool_t mount_access)
    867 {
    868 	char dirname[MAXFILENAMELEN];
    869 	char traversed_path[MAXPATHLEN]; /* used in building fake mapentries */
    870 
    871 	char *subdir_child = subdir;
    872 	hiernode *prevnode = rootnode;
    873 	hiernode *currnode = rootnode->subdir;
    874 	int rc = PARSE_OK;
    875 	traversed_path[0] = '\0';
    876 
    877 	/*
    878 	 * find and mark the root by tracing down subdir. Use traversed_path
    879 	 * to keep track of how far we go, while guaranteeing that it
    880 	 * contains no '/' at the end. Took some mucking to get that right.
    881 	 */
    882 	if ((rc = get_dir_from_path(dirname, &subdir_child, sizeof (dirname)))
    883 	    != PARSE_OK)
    884 		return (rc);
    885 
    886 	if (dirname[0] != '\0')
    887 		sprintf(traversed_path, "%s/%s", traversed_path, dirname);
    888 
    889 	prevnode = rootnode;
    890 	currnode = rootnode->subdir;
    891 	while (dirname[0] != '\0' && currnode != NULL) {
    892 		if (strcmp(currnode->dirname, dirname) == 0) {
    893 
    894 			/* subdir is a child of currnode */
    895 			prevnode = currnode;
    896 			currnode = currnode->subdir;
    897 
    898 			if ((rc = get_dir_from_path(dirname, &subdir_child,
    899 			    sizeof (dirname))) != PARSE_OK)
    900 				return (rc);
    901 			if (dirname[0] != '\0')
    902 				sprintf(traversed_path, "%s/%s",
    903 				    traversed_path, dirname);
    904 
    905 		} else {
    906 			/* try next leveldir */
    907 			prevnode = currnode;
    908 			currnode = currnode->leveldir;
    909 		}
    910 	}
    911 
    912 	if (dirname[0] != '\0') {
    913 		if (verbose)
    914 			syslog(LOG_ERR,
    915 			"set_and_fake_mapent_mntlevel: subdir=%s error: map=%s",
    916 			    subdir, mapname);
    917 		return (PARSE_ERROR);
    918 	}
    919 
    920 	/*
    921 	 * see if level of root really points to a mapent and if
    922 	 * have access to that filessystem - call appropriate
    923 	 * routine to mark level 1 nodes, and build faked entries
    924 	 */
    925 	if (prevnode->mapent != NULL && mount_access == TRUE) {
    926 		if (trace > 3)
    927 			trace_prt(1, "  node mountpoint %s\t travpath=%s\n",
    928 			    prevnode->mapent->map_mntpnt, traversed_path);
    929 
    930 		/*
    931 		 * Copy traversed path map_mntpnt to get rid of any extra
    932 		 * '/' the map entry may contain.
    933 		 */
    934 		if (strlen(prevnode->mapent->map_mntpnt) <
    935 		    strlen(traversed_path)) { /* sanity check */
    936 			if (verbose)
    937 				syslog(LOG_ERR,
    938 				"set_and_fake_mapent_mntlevel: path=%s error",
    939 				    traversed_path);
    940 			return (PARSE_ERROR);
    941 		}
    942 		if (strcmp(prevnode->mapent->map_mntpnt, traversed_path) != 0)
    943 			strcpy(prevnode->mapent->map_mntpnt, traversed_path);
    944 
    945 		prevnode->mapent->map_mntlevel = 0; /* root level is 0 */
    946 		if (currnode != NULL) {
    947 			if ((rc = mark_level1_root(currnode,
    948 			    traversed_path)) != PARSE_OK)
    949 				return (rc);
    950 		}
    951 	} else if (currnode != NULL) {
    952 		if (trace > 3)
    953 			trace_prt(1, "  No rootnode, travpath=%s\n",
    954 			    traversed_path);
    955 		if ((rc = mark_and_fake_level1_noroot(currnode,
    956 		    traversed_path, key, mapname, faked_mapents, isdirect,
    957 		    mapopts)) != PARSE_OK)
    958 			return (rc);
    959 	}
    960 
    961 	if (trace > 3) {
    962 		trace_prt(1, "\n\tset_and_fake_mapent_mntlevel\n");
    963 		trace_hierarchy(rootnode, 0);
    964 	}
    965 
    966 	return (rc);
    967 }
    968 
    969 
    970 /*
    971  * mark_level1_root(hiernode *node, char *traversed_path)
    972  * marks nodes upto one level below the rootnode given by subdir
    973  * recursively. Called if rootnode points to a mapent.
    974  * In this routine, a level 1 node is considered to be the 1st existing
    975  * mapentry below the root node, so there's no faking involved.
    976  * Returns PARSE_OK or error value
    977  */
    978 static int
    979 mark_level1_root(hiernode *node, char *traversed_path)
    980 {
    981 	/* ensure we touch all leveldirs */
    982 	while (node) {
    983 		/*
    984 		 * mark node level as 1, if one exists - else walk down
    985 		 * subdirs until we find one.
    986 		 */
    987 		if (node->mapent ==  NULL) {
    988 			char w[MAXPATHLEN];
    989 
    990 			if (node->subdir != NULL) {
    991 				sprintf(w, "%s/%s", traversed_path,
    992 				    node->dirname);
    993 				if (mark_level1_root(node->subdir, w)
    994 				    == PARSE_ERROR)
    995 					return (PARSE_ERROR);
    996 			} else {
    997 				if (verbose) {
    998 					syslog(LOG_ERR,
    999 					"mark_level1_root: hierarchy error");
   1000 				}
   1001 				return (PARSE_ERROR);
   1002 			}
   1003 		} else {
   1004 			char w[MAXPATHLEN];
   1005 
   1006 			sprintf(w, "%s/%s", traversed_path, node->dirname);
   1007 			if (trace > 3)
   1008 				trace_prt(1, "  node mntpnt %s\t travpath %s\n",
   1009 				    node->mapent->map_mntpnt, w);
   1010 
   1011 			/* replace mntpnt with travpath to clean extra '/' */
   1012 			if (strlen(node->mapent->map_mntpnt) < strlen(w)) {
   1013 				if (verbose) {
   1014 					syslog(LOG_ERR,
   1015 					"mark_level1_root: path=%s error",
   1016 					    traversed_path);
   1017 				}
   1018 				return (PARSE_ERROR);
   1019 			}
   1020 			if (strcmp(node->mapent->map_mntpnt, w) != 0)
   1021 				strcpy(node->mapent->map_mntpnt, w);
   1022 			node->mapent->map_mntlevel = 1;
   1023 		}
   1024 		node = node->leveldir;
   1025 	}
   1026 	return (PARSE_OK);
   1027 }
   1028 
   1029 /*
   1030  * mark_and_fake_level1_noroot(hiernode *node, char *traversed_path,
   1031  * 			char *key,char *mapname, struct mapent **faked_mapents,
   1032  *			uint_t isdirect, char *mapopts)
   1033  * Called if the root of the hierarchy does not point to a mapent. marks nodes
   1034  * upto one physical level below the rootnode given by subdir. checks if
   1035  * there's a real mapentry. If not, it builds a faked one (autonode) at that
   1036  * point. The faked autonode is direct, with the map being the same as the
   1037  * original one from which the call originated. Options are same as that of
   1038  * the map and assigned in automount_opts(). Returns PARSE_OK or error value.
   1039  */
   1040 static int
   1041 mark_and_fake_level1_noroot(hiernode *node, char *traversed_path,
   1042 			char *key, char *mapname, struct mapent **faked_mapents,
   1043 			uint_t isdirect, char *mapopts)
   1044 {
   1045 	struct mapent *me;
   1046 	int rc = 0;
   1047 	char faked_map_mntpnt[MAXPATHLEN];
   1048 	char w1[MAXPATHLEN];
   1049 	char w[MAXPATHLEN];
   1050 
   1051 	while (node != NULL) {
   1052 		if (node->mapent != NULL) {
   1053 			/*
   1054 			 * existing mapentry at level 1 - copy travpath to
   1055 			 * get rid of extra '/' in mntpnt
   1056 			 */
   1057 			sprintf(w, "%s/%s", traversed_path, node->dirname);
   1058 			if (trace > 3)
   1059 				trace_prt(1, "  node mntpnt=%s\t travpath=%s\n",
   1060 				    node->mapent->map_mntpnt, w);
   1061 			if (strlen(node->mapent->map_mntpnt) < strlen(w)) {
   1062 				/* sanity check */
   1063 				if (verbose)
   1064 					syslog(LOG_ERR,
   1065 					"mark_fake_level1_noroot:path=%s error",
   1066 					    traversed_path);
   1067 				return (PARSE_ERROR);
   1068 			}
   1069 			if (strcmp(node->mapent->map_mntpnt, w) != 0)
   1070 				strcpy(node->mapent->map_mntpnt, w);
   1071 			node->mapent->map_mntlevel = 1;
   1072 		} else {
   1073 			/*
   1074 			 * build the faked autonode
   1075 			 */
   1076 			if ((me = (struct mapent *)malloc(sizeof (*me)))
   1077 			    == NULL) {
   1078 				syslog(LOG_ERR,
   1079 				"mark_and_fake_level1_noroot: out of memory");
   1080 				return (ENOMEM);
   1081 			}
   1082 			(void) memset((char *)me, 0, sizeof (*me));
   1083 
   1084 			if ((me->map_fs = (struct mapfs *)
   1085 			    malloc(sizeof (struct mapfs))) == NULL)
   1086 				return (ENOMEM);
   1087 			(void) memset(me->map_fs, 0, sizeof (struct mapfs));
   1088 
   1089 			if (isdirect) {
   1090 				*w1 = '\0';
   1091 			} else {
   1092 				strcpy(w1, "/");
   1093 				strcat(w1, key);
   1094 			}
   1095 			me->map_root = strdup(w1);
   1096 
   1097 			sprintf(faked_map_mntpnt, "%s/%s", traversed_path,
   1098 			    node->dirname);
   1099 			me->