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