6198 Let's EOL cachefs
[illumos-gate.git] / usr / src / cmd / fs.d / autofs / autod_parse.c
blobfa3f9ca08a460b30894d70fb3ec944ded5587af0
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * autod_parse.c
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.
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"
52 * This structure is used to determine the hierarchical
53 * relationship between directories
55 typedef struct _hiernode {
56 char dirname[MAXFILENAMELEN+1];
57 struct _hiernode *subdir;
58 struct _hiernode *leveldir;
59 struct mapent *mapent;
60 } hiernode;
62 void free_mapent(struct mapent *);
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 *);
95 #define PARSE_OK 0
96 #define PARSE_ERROR -1
97 #define MAX_FSLEN 32
100 * mapentry error type defininitions
102 #define MAPENT_NOERR 0
103 #define MAPENT_UATFS 1
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.
117 struct mapent *
118 parse_entry(char *key, char *mapname, char *mapopts, struct mapline *ml,
119 char *subdir, uint_t isdirect, bool_t mount_access)
121 char *p;
122 char defaultopts[AUTOFS_MAXOPTSLEN];
124 struct mapent *mapents = NULL;
125 hiernode *rootnode = NULL;
126 char *lp = ml->linebuf;
128 if (trace > 1)
129 trace_prt(1, " mapline: %s\n", ml->linebuf);
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.
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);
145 * select the appropriate parser, and build the mapentry list
147 if (strcmp(lp, "-hosts") == 0) {
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.
153 mapents = do_mapent_hosts(mapopts, key, isdirect);
154 if (mapents == NULL) /* nothing to free */
155 return (mapents);
157 if (trace > 3)
158 trace_mapents("do_mapent_hosts:(return)", mapents);
160 if (hierarchical_sort(mapents, &rootnode, key, mapname)
161 != PARSE_OK)
162 goto parse_error;
163 } else {
165 * all other parsing
167 if (mapline_to_mapent(&mapents, ml, key, mapname,
168 mapopts, defaultopts, isdirect) != PARSE_OK)
169 goto parse_error;
171 if (mapents == NULL)
172 return (mapents);
174 if (hierarchical_sort(mapents, &rootnode, key, mapname)
175 != PARSE_OK)
176 goto parse_error;
178 if (push_options(rootnode, defaultopts, mapopts,
179 MAPENT_NOERR) != PARSE_OK)
180 goto parse_error;
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);
188 if (parse_fsinfo(mapname, mapents) != PARSE_OK)
189 goto parse_error;
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).
197 if (modify_mapents(&mapents, mapname, mapopts, subdir,
198 rootnode, key, isdirect, mount_access) != PARSE_OK)
199 goto parse_error;
202 * XXX: its dangerous to use rootnode after modify mapents as
203 * it may be pointing to mapents that have been freed
205 if (rootnode != NULL)
206 free_hiernode(rootnode);
208 return (mapents);
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);
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.
232 static int
233 mapline_to_mapent(struct mapent **mapents, struct mapline *ml, char *key,
234 char *mapname, char *mapopts, char *defaultopts,
235 uint_t isdirect)
237 struct mapent *me = NULL;
238 struct mapent *mp;
239 char w[MAXPATHLEN];
240 char wq[MAXPATHLEN];
241 char w1[MAXPATHLEN];
242 int implied;
244 char *lp = ml->linebuf;
245 char *lq = ml->lineqbuf;
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);
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);
259 /* init the head of mapentry list to null */
260 *mapents = NULL;
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.
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);
279 * implied is true if there is no '/'
280 * We need the same code path if we have an smbfs mount.
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;
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().
299 if (isdirect) {
300 *w1 = '\0';
301 } else {
302 strcpy(w1, "/");
303 strcat(w1, key);
305 if ((me->map_root = strdup(w1)) == NULL)
306 goto alloc_failed;
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;
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.
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);
339 * must be a mount filesystem or a set of filesystems at
340 * this point.
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);
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.
357 if (((me->map_fsw = strdup(w)) == NULL) ||
358 ((me->map_fswq = strdup(wq)) == NULL))
359 goto alloc_failed;
362 * the next word, if any, is either another mount point or a
363 * mount filesystem if more than one server is listed.
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);
386 /* initialize flags */
387 me->map_mntlevel = -1;
388 me->map_modified = FALSE;
389 me->map_faked = FALSE;
390 me->map_err = MAPENT_NOERR;
392 me->map_next = NULL;
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);
404 return (PARSE_ERROR);
407 if (trace > 3)
408 trace_mapents("mapline_to_mapent:", *mapents);
410 return (PARSE_OK);
412 alloc_failed:
413 syslog(LOG_ERR, "mapline_to_mapent: Memory allocation failed");
414 return (ENOMEM);
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
426 static int
427 hierarchical_sort(struct mapent *mapents, hiernode **rootnode, char *key,
428 char *mapname)
430 hiernode *prevnode, *currnode, *newnode;
431 char *path;
432 char dirname[MAXFILENAMELEN];
434 int rc = PARSE_OK;
435 struct mapent *me = mapents;
437 /* allocate the rootnode with a default path of "" */
438 *rootnode = NULL;
439 if ((rc = alloc_hiernode(rootnode, "")) != PARSE_OK)
440 return (rc);
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.
449 while (me != NULL) {
451 path = me->map_mntpnt;
453 if ((rc = get_dir_from_path(dirname, &path,
454 sizeof (dirname))) != PARSE_OK)
455 return (rc);
457 prevnode = *rootnode;
458 currnode = (*rootnode)->subdir;
460 while (dirname[0] != '\0') {
461 if (currnode != NULL) {
462 if (strcmp(currnode->dirname, dirname) == 0) {
464 * match found - mntpnt is a child of
465 * this node
467 prevnode = currnode;
468 currnode = currnode->subdir;
469 } else {
470 prevnode = currnode;
471 currnode = currnode->leveldir;
473 if (currnode == NULL) {
475 * No more leveldirs to match.
476 * Add a new one
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;
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;
499 if ((rc = get_dir_from_path(dirname, &path,
500 sizeof (dirname))) != PARSE_OK)
501 return (rc);
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);
512 /* provide a pointer from node to mapent */
513 prevnode->mapent = me;
514 me = me->map_next;
517 if (trace > 3) {
518 trace_prt(1, "\n\thierarchical_sort:\n");
519 trace_hierarchy(*rootnode, 0); /* 0 is rootnode's level */
522 return (rc);
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.
535 static int
536 push_options(hiernode *node, char *defaultopts, char *mapopts, int err)
538 int rc = PARSE_OK;
539 struct mapent *me = NULL;
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);
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);
560 node = node->leveldir;
562 return (rc);
565 #define FSTYPE "fstype"
566 #define FSTYPE_EQ "fstype="
567 #define NO_OPTS ""
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.
577 static int
578 set_mapent_opts(struct mapent *me, char *opts, char *defaultopts,
579 char *mapopts)
581 char entryopts[AUTOFS_MAXOPTSLEN];
582 char fstype[MAX_FSLEN], mounter[MAX_FSLEN];
583 int rc = PARSE_OK;
584 bool_t fstype_opt = FALSE;
586 strcpy(fstype, MNTTYPE_NFS); /* default */
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;
596 if (*opts == '-')
597 opts++;
599 /* separate opts into fstype and (other) entrypopts */
600 get_opts(opts, entryopts, fstype, &fstype_opt);
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);
610 * child options are exactly fstype = somefs, we need to do some
611 * more option pushing work.
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);
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);
630 return (rc);
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.
639 * input: "fstype=nfs,ro,nosuid"
640 * opts: "ro,nosuid"
641 * fstype: "nfs"
643 * Also indicates if the fstype option was present
644 * by setting a flag, if the pointer to the flag
645 * is not NULL.
647 static void
648 get_opts(input, opts, fstype, fstype_opt)
649 char *input;
650 char *opts; /* output */
651 char *fstype; /* output */
652 bool_t *fstype_opt;
654 char *p, *pb;
655 char buf[MAXOPTSLEN];
656 char *placeholder;
658 *opts = '\0';
659 (void) strcpy(buf, input);
660 pb = buf;
661 while (p = (char *)strtok_r(pb, ",", &placeholder)) {
662 pb = NULL;
663 if (strncmp(p, FSTYPE_EQ, 7) == 0) {
664 if (fstype_opt != NULL)
665 *fstype_opt = TRUE;
666 (void) strcpy(fstype, p + 7);
667 } else {
668 if (*opts)
669 (void) strcat(opts, ",");
670 (void) strcat(opts, p);
676 * fstype_opts(struct mapent *me, char *opts, char *defaultopts,
677 * char *mapopts)
678 * We need to push global options to the child entry if it is exactly
679 * fstype=somefs.
681 static int
682 fstype_opts(struct mapent *me, char *opts, char *defaultopts,
683 char *mapopts)
685 char pushentryopts[AUTOFS_MAXOPTSLEN];
686 char pushfstype[MAX_FSLEN];
688 if (defaultopts && *defaultopts == '-')
689 defaultopts++;
692 * the options to push are the global defaults for the entry,
693 * if they exist, or mapopts, if the global defaults for the
694 * entry does not exist.
696 if (strcmp(defaultopts, opts) == 0) {
697 if (*mapopts == '-')
698 mapopts++;
699 get_opts(mapopts, pushentryopts, pushfstype, NULL);
700 } else {
701 get_opts(defaultopts, pushentryopts, pushfstype, NULL);
704 me->map_mntopts = strdup(pushentryopts);
706 if (!me->map_mntopts) {
707 syslog(LOG_ERR, "fstype_opts: No memory");
708 return (ENOMEM);
711 return (PARSE_OK);
715 * modify_mapents(struct mapent **mapents, char *mapname,
716 * char *mapopts, char *subdir, hiernode *rootnode,
717 * char *key, uint_t isdirect, bool_t mount_access)
718 * modifies the intermediate mapentry list into the final one, and passes
719 * back a pointer to it. The final list may contain faked mapentries for
720 * hiernodes that do not point to a mapentry, or converted mapentries, if
721 * hiernodes that point to a mapentry need to be converted from nfs to autofs.
722 * mounts. Entries that are not directly 1 level below the subdir are removed.
723 * Returns PARSE_OK or PARSE_ERROR
725 static int
726 modify_mapents(struct mapent **mapents, char *mapname,
727 char *mapopts, char *subdir, hiernode *rootnode,
728 char *key, uint_t isdirect, bool_t mount_access)
730 struct mapent *mp = NULL;
731 char w[MAXPATHLEN];
733 struct mapent *me;
734 int rc = PARSE_OK;
735 struct mapent *faked_mapents = NULL;
738 * correct the mapentry mntlevel from default -1 to level depending on
739 * position in hierarchy, and build any faked mapentries, if required
740 * at one level below the rootnode given by subdir.
742 if ((rc = set_and_fake_mapent_mntlevel(rootnode, subdir, key, mapname,
743 &faked_mapents, isdirect, mapopts, mount_access)) != PARSE_OK)
744 return (rc);
747 * attaches faked mapents to real mapents list. Assumes mapents
748 * is not NULL.
750 me = *mapents;
751 while (me->map_next != NULL)
752 me = me->map_next;
753 me->map_next = faked_mapents;
756 * get rid of nodes marked at level -1
758 me = *mapents;
759 while (me != NULL) {
760 if ((me->map_mntlevel == -1) || (me->map_err) ||
761 (mount_access == FALSE && me->map_mntlevel == 0)) {
763 * syslog any errors and free entry
765 if (me->map_err)
766 dump_mapent_err(me, key, mapname);
768 if (me == (*mapents)) {
769 /* special case when head has to be freed */
770 *mapents = me->map_next;
771 if ((*mapents) == NULL) {
772 /* something wierd happened */
773 if (verbose)
774 syslog(LOG_ERR,
775 "modify_mapents: level error");
776 return (PARSE_ERROR);
779 /* separate out the node */
780 me->map_next = NULL;
781 free_mapent(me);
782 me = *mapents;
783 } else {
784 mp->map_next = me->map_next;
785 me->map_next = NULL;
786 free_mapent(me);
787 me = mp->map_next;
789 continue;
793 * convert level 1 mapents that are not already autonodes
794 * to autonodes
796 if (me->map_mntlevel == 1 &&
797 (strcmp(me->map_fstype, MNTTYPE_AUTOFS) != 0) &&
798 (me->map_faked != TRUE)) {
799 if ((rc = convert_mapent_to_automount(me, mapname,
800 mapopts)) != PARSE_OK)
801 return (rc);
803 strcpy(w, (me->map_mntpnt+strlen(subdir)));
804 strcpy(me->map_mntpnt, w);
805 mp = me;
806 me = me->map_next;
809 if (trace > 3)
810 trace_mapents("modify_mapents:", *mapents);
812 return (PARSE_OK);
816 * set_and_fake_mapent_mntlevel(hiernode *rootnode, char *subdir, char *key,
817 * char *mapname, struct mapent **faked_mapents,
818 * uint_t isdirect, char *mapopts, bool_t mount_access)
819 * sets the mapentry mount levels (depths) with respect to the subdir.
820 * Assigns a value of 0 to the new root. Finds the level1 directories by
821 * calling mark_*_level1_*(). Also cleans off extra /'s in level0 and
822 * level1 map_mntpnts. Note that one level below the new root is an existing
823 * mapentry if there's a mapentry (nfs mount) corresponding to the root,
824 * and the direct subdir set for the root, if there's no mapentry corresponding
825 * to the root (we install autodirs). Returns PARSE_OK or error value.
827 static int
828 set_and_fake_mapent_mntlevel(hiernode *rootnode, char *subdir, char *key,
829 char *mapname, struct mapent **faked_mapents,
830 uint_t isdirect, char *mapopts, bool_t mount_access)
832 char dirname[MAXFILENAMELEN];
833 char traversed_path[MAXPATHLEN]; /* used in building fake mapentries */
835 char *subdir_child = subdir;
836 hiernode *prevnode = rootnode;
837 hiernode *currnode = rootnode->subdir;
838 int rc = PARSE_OK;
839 traversed_path[0] = '\0';
842 * find and mark the root by tracing down subdir. Use traversed_path
843 * to keep track of how far we go, while guaranteeing that it
844 * contains no '/' at the end. Took some mucking to get that right.
846 if ((rc = get_dir_from_path(dirname, &subdir_child, sizeof (dirname)))
847 != PARSE_OK)
848 return (rc);
850 if (dirname[0] != '\0')
851 sprintf(traversed_path, "%s/%s", traversed_path, dirname);
853 prevnode = rootnode;
854 currnode = rootnode->subdir;
855 while (dirname[0] != '\0' && currnode != NULL) {
856 if (strcmp(currnode->dirname, dirname) == 0) {
858 /* subdir is a child of currnode */
859 prevnode = currnode;
860 currnode = currnode->subdir;
862 if ((rc = get_dir_from_path(dirname, &subdir_child,
863 sizeof (dirname))) != PARSE_OK)
864 return (rc);
865 if (dirname[0] != '\0')
866 sprintf(traversed_path, "%s/%s",
867 traversed_path, dirname);
869 } else {
870 /* try next leveldir */
871 prevnode = currnode;
872 currnode = currnode->leveldir;
876 if (dirname[0] != '\0') {
877 if (verbose)
878 syslog(LOG_ERR,
879 "set_and_fake_mapent_mntlevel: subdir=%s error: map=%s",
880 subdir, mapname);
881 return (PARSE_ERROR);
885 * see if level of root really points to a mapent and if
886 * have access to that filessystem - call appropriate
887 * routine to mark level 1 nodes, and build faked entries
889 if (prevnode->mapent != NULL && mount_access == TRUE) {
890 if (trace > 3)
891 trace_prt(1, " node mountpoint %s\t travpath=%s\n",
892 prevnode->mapent->map_mntpnt, traversed_path);
895 * Copy traversed path map_mntpnt to get rid of any extra
896 * '/' the map entry may contain.
898 if (strlen(prevnode->mapent->map_mntpnt) <
899 strlen(traversed_path)) { /* sanity check */
900 if (verbose)
901 syslog(LOG_ERR,
902 "set_and_fake_mapent_mntlevel: path=%s error",
903 traversed_path);
904 return (PARSE_ERROR);
906 if (strcmp(prevnode->mapent->map_mntpnt, traversed_path) != 0)
907 strcpy(prevnode->mapent->map_mntpnt, traversed_path);
909 prevnode->mapent->map_mntlevel = 0; /* root level is 0 */
910 if (currnode != NULL) {
911 if ((rc = mark_level1_root(currnode,
912 traversed_path)) != PARSE_OK)
913 return (rc);
915 } else if (currnode != NULL) {
916 if (trace > 3)
917 trace_prt(1, " No rootnode, travpath=%s\n",
918 traversed_path);
919 if ((rc = mark_and_fake_level1_noroot(currnode,
920 traversed_path, key, mapname, faked_mapents, isdirect,
921 mapopts)) != PARSE_OK)
922 return (rc);
925 if (trace > 3) {
926 trace_prt(1, "\n\tset_and_fake_mapent_mntlevel\n");
927 trace_hierarchy(rootnode, 0);
930 return (rc);
935 * mark_level1_root(hiernode *node, char *traversed_path)
936 * marks nodes upto one level below the rootnode given by subdir
937 * recursively. Called if rootnode points to a mapent.
938 * In this routine, a level 1 node is considered to be the 1st existing
939 * mapentry below the root node, so there's no faking involved.
940 * Returns PARSE_OK or error value
942 static int
943 mark_level1_root(hiernode *node, char *traversed_path)
945 /* ensure we touch all leveldirs */
946 while (node) {
948 * mark node level as 1, if one exists - else walk down
949 * subdirs until we find one.
951 if (node->mapent == NULL) {
952 char w[MAXPATHLEN];
954 if (node->subdir != NULL) {
955 sprintf(w, "%s/%s", traversed_path,
956 node->dirname);
957 if (mark_level1_root(node->subdir, w)
958 == PARSE_ERROR)
959 return (PARSE_ERROR);
960 } else {
961 if (verbose) {
962 syslog(LOG_ERR,
963 "mark_level1_root: hierarchy error");
965 return (PARSE_ERROR);
967 } else {
968 char w[MAXPATHLEN];
970 sprintf(w, "%s/%s", traversed_path, node->dirname);
971 if (trace > 3)
972 trace_prt(1, " node mntpnt %s\t travpath %s\n",
973 node->mapent->map_mntpnt, w);
975 /* replace mntpnt with travpath to clean extra '/' */
976 if (strlen(node->mapent->map_mntpnt) < strlen(w)) {
977 if (verbose) {
978 syslog(LOG_ERR,
979 "mark_level1_root: path=%s error",
980 traversed_path);
982 return (PARSE_ERROR);
984 if (strcmp(node->mapent->map_mntpnt, w) != 0)
985 strcpy(node->mapent->map_mntpnt, w);
986 node->mapent->map_mntlevel = 1;
988 node = node->leveldir;
990 return (PARSE_OK);
994 * mark_and_fake_level1_noroot(hiernode *node, char *traversed_path,
995 * char *key,char *mapname, struct mapent **faked_mapents,
996 * uint_t isdirect, char *mapopts)
997 * Called if the root of the hierarchy does not point to a mapent. marks nodes
998 * upto one physical level below the rootnode given by subdir. checks if
999 * there's a real mapentry. If not, it builds a faked one (autonode) at that
1000 * point. The faked autonode is direct, with the map being the same as the
1001 * original one from which the call originated. Options are same as that of
1002 * the map and assigned in automount_opts(). Returns PARSE_OK or error value.
1004 static int
1005 mark_and_fake_level1_noroot(hiernode *node, char *traversed_path,
1006 char *key, char *mapname, struct mapent **faked_mapents,
1007 uint_t isdirect, char *mapopts)
1009 struct mapent *me;
1010 int rc = 0;
1011 char faked_map_mntpnt[MAXPATHLEN];
1012 char w1[MAXPATHLEN];
1013 char w[MAXPATHLEN];
1015 while (node != NULL) {
1016 if (node->mapent != NULL) {
1018 * existing mapentry at level 1 - copy travpath to
1019 * get rid of extra '/' in mntpnt
1021 sprintf(w, "%s/%s", traversed_path, node->dirname);
1022 if (trace > 3)
1023 trace_prt(1, " node mntpnt=%s\t travpath=%s\n",
1024 node->mapent->map_mntpnt, w);
1025 if (strlen(node->mapent->map_mntpnt) < strlen(w)) {
1026 /* sanity check */
1027 if (verbose)
1028 syslog(LOG_ERR,
1029 "mark_fake_level1_noroot:path=%s error",
1030 traversed_path);
1031 return (PARSE_ERROR);
1033 if (strcmp(node->mapent->map_mntpnt, w) != 0)
1034 strcpy(node->mapent->map_mntpnt, w);
1035 node->mapent->map_mntlevel = 1;
1036 } else {
1038 * build the faked autonode
1040 if ((me = (struct mapent *)malloc(sizeof (*me)))
1041 == NULL) {
1042 syslog(LOG_ERR,
1043 "mark_and_fake_level1_noroot: out of memory");
1044 return (ENOMEM);
1046 (void) memset((char *)me, 0, sizeof (*me));
1048 if ((me->map_fs = (struct mapfs *)
1049 malloc(sizeof (struct mapfs))) == NULL)
1050 return (ENOMEM);
1051 (void) memset(me->map_fs, 0, sizeof (struct mapfs));
1053 if (isdirect) {
1054 *w1 = '\0';
1055 } else {
1056 strcpy(w1, "/");
1057 strcat(w1, key);
1059 me->map_root = strdup(w1);
1061 sprintf(faked_map_mntpnt, "%s/%s", traversed_path,
1062 node->dirname);
1063 me->map_mntpnt = strdup(faked_map_mntpnt);
1064 me->map_fstype = strdup(MNTTYPE_AUTOFS);
1065 me->map_mounter = strdup(MNTTYPE_AUTOFS);
1067 /* set options */
1068 if ((rc = automount_opts(&me->map_mntopts, mapopts))
1069 != PARSE_OK)
1070 return (rc);
1071 me->map_fs->mfs_dir = strdup(mapname);
1072 me->map_mntlevel = 1;
1073 me->map_modified = FALSE;
1074 me->map_faked = TRUE; /* mark as faked */
1075 if (me->map_root == NULL ||
1076 me->map_mntpnt == NULL ||
1077 me->map_fstype == NULL ||
1078 me->map_mounter == NULL ||
1079 me->map_mntopts == NULL ||
1080 me->map_fs->mfs_dir == NULL) {
1081 syslog(LOG_ERR,
1082 "mark_and_fake_level1_noroot: out of memory");
1083 free_mapent(*faked_mapents);
1084 return (ENOMEM);
1087 if (*faked_mapents == NULL)
1088 *faked_mapents = me;
1089 else { /* attach to the head */
1090 me->map_next = *faked_mapents;
1091 *faked_mapents = me;
1093 node->mapent = me;
1095 node = node->leveldir;
1097 return (rc);
1101 * convert_mapent_to_automount(struct mapent *me, char *mapname,
1102 * char *mapopts)
1103 * change the mapentry me to an automount - free fields first and NULL them
1104 * to avoid freeing again, while freeing the mapentry at a later stage.
1105 * Could have avoided freeing entries here as we don't really look at them.
1106 * Give the converted mapent entry the options that came with the map using
1107 * automount_opts(). Returns PARSE_OK or appropriate error value.
1109 static int
1110 convert_mapent_to_automount(struct mapent *me, char *mapname,
1111 char *mapopts)
1113 struct mapfs *mfs = me->map_fs; /* assumes it exists */
1114 int rc = PARSE_OK;
1116 /* free relevant entries */
1117 if (mfs->mfs_host) {
1118 free(mfs->mfs_host);
1119 mfs->mfs_host = NULL;
1121 while (me->map_fs->mfs_next != NULL) {
1122 mfs = me->map_fs->mfs_next;
1123 if (mfs->mfs_host)
1124 free(mfs->mfs_host);
1125 if (mfs->mfs_dir)
1126 free(mfs->mfs_dir);
1127 me->map_fs->mfs_next = mfs->mfs_next; /* nulls eventually */
1128 free((void*)mfs);
1131 /* replace relevant entries */
1132 if (me->map_fstype)
1133 free(me->map_fstype);
1134 if ((me->map_fstype = strdup(MNTTYPE_AUTOFS)) == NULL)
1135 goto alloc_failed;
1137 if (me->map_mounter)
1138 free(me->map_mounter);
1139 if ((me->map_mounter = strdup(me->map_fstype)) == NULL)
1140 goto alloc_failed;
1142 if (me->map_fs->mfs_dir)
1143 free(me->map_fs->mfs_dir);
1144 if ((me->map_fs->mfs_dir = strdup(mapname)) == NULL)
1145 goto alloc_failed;
1147 /* set options */
1148 if (me->map_mntopts)
1149 free(me->map_mntopts);
1150 if ((rc = automount_opts(&me->map_mntopts, mapopts)) != PARSE_OK)
1151 return (rc);
1153 /* mucked with this entry, set the map_modified field to TRUE */
1154 me->map_modified = TRUE;
1156 return (rc);
1158 alloc_failed:
1159 syslog(LOG_ERR,
1160 "convert_mapent_to_automount: Memory allocation failed");
1161 return (ENOMEM);
1165 * automount_opts(char **map_mntopts, char *mapopts)
1166 * modifies automount opts - gets rid of all "indirect" and "direct" strings
1167 * if they exist, and then adds a direct string to force a direct automount.
1168 * Rest of the mapopts stay intact. Returns PARSE_OK or appropriate error.
1170 static int
1171 automount_opts(char **map_mntopts, char *mapopts)
1173 char *opts;
1174 char *opt;
1175 int len;
1176 char *placeholder;
1177 char buf[AUTOFS_MAXOPTSLEN];
1179 char *addopt = "direct";
1181 len = strlen(mapopts)+ strlen(addopt)+2; /* +2 for ",", '\0' */
1182 if (len > AUTOFS_MAXOPTSLEN) {
1183 syslog(LOG_ERR,
1184 "option string %s too long (max=%d)", mapopts,
1185 AUTOFS_MAXOPTSLEN-8);
1186 return (PARSE_ERROR);
1189 if (((*map_mntopts) = ((char *)malloc(len))) == NULL) {
1190 syslog(LOG_ERR, "automount_opts: Memory allocation failed");
1191 return (ENOMEM);
1193 memset(*map_mntopts, 0, len);
1195 strcpy(buf, mapopts);
1196 opts = buf;
1197 while ((opt = strtok_r(opts, ",", &placeholder)) != NULL) {
1198 opts = NULL;
1200 /* remove trailing and leading spaces */
1201 while (isspace(*opt))
1202 opt++;
1203 len = strlen(opt)-1;
1204 while (isspace(opt[len]))
1205 opt[len--] = '\0';
1208 * if direct or indirect found, get rid of it, else put it
1209 * back
1211 if ((strcmp(opt, "indirect") == 0) ||
1212 (strcmp(opt, "direct") == 0))
1213 continue;
1214 if (*map_mntopts[0] != '\0')
1215 strcat(*map_mntopts, ",");
1216 strcat(*map_mntopts, opt);
1219 /* add the direct string at the end */
1220 if (*map_mntopts[0] != '\0')
1221 strcat(*map_mntopts, ",");
1222 strcat(*map_mntopts, addopt);
1224 return (PARSE_OK);
1228 * parse_fsinfo(char *mapname, struct mapent *mapents)
1229 * parses the filesystem information stored in me->map_fsw and me->map_fswq
1230 * and calls appropriate filesystem parser.
1231 * Returns PARSE_OK or an appropriate error value.
1233 static int
1234 parse_fsinfo(char *mapname, struct mapent *mapents)
1236 struct mapent *me = mapents;
1237 char *bufp;
1238 char *bufq;
1239 int wordsz = MAXPATHLEN;
1240 int err = 0;
1242 while (me != NULL) {
1243 bufp = "";
1244 bufq = "";
1245 if (strcmp(me->map_fstype, MNTTYPE_NFS) == 0) {
1246 err = parse_nfs(mapname, me, me->map_fsw,
1247 me->map_fswq, &bufp, &bufq, wordsz);
1248 } else {
1249 err = parse_special(me, me->map_fsw, me->map_fswq,
1250 &bufp, &bufq, wordsz);
1253 if (err != PARSE_OK || *me->map_fsw != '\0' ||
1254 *me->map_fswq != '\0') {
1255 /* sanity check */
1256 if (verbose)
1257 syslog(LOG_ERR,
1258 "parse_fsinfo: mount location error %s",
1259 me->map_fsw);
1260 return (PARSE_ERROR);
1263 me = me->map_next;
1266 if (trace > 3) {
1267 trace_mapents("parse_fsinfo:", mapents);
1270 return (PARSE_OK);
1274 * This function parses the map entry for a nfs type file system
1275 * The input is the string lp (and lq) which can be one of the
1276 * following forms:
1277 * a) host[(penalty)][,host[(penalty)]]... :/directory
1278 * b) host[(penalty)]:/directory[ host[(penalty)]:/directory]...
1279 * This routine constructs a mapfs link-list for each of
1280 * the hosts and the corresponding file system. The list
1281 * is then attatched to the mapent struct passed in.
1284 parse_nfs(mapname, me, fsw, fswq, lp, lq, wsize)
1285 struct mapent *me;
1286 char *mapname, *fsw, *fswq, **lp, **lq;
1287 int wsize;
1289 struct mapfs *mfs, **mfsp;
1290 char *wlp, *wlq;
1291 char *hl, hostlist[1024], *hlq, hostlistq[1024];
1292 char hostname_and_penalty[MXHOSTNAMELEN+5];
1293 char *hn, *hnq, hostname[MXHOSTNAMELEN+1];
1294 char dirname[MAXPATHLEN+1], subdir[MAXPATHLEN+1];
1295 char qbuff[MAXPATHLEN+1], qbuff1[MAXPATHLEN+1];
1296 char pbuff[10], pbuffq[10];
1297 int penalty;
1298 char w[MAXPATHLEN];
1299 char wq[MAXPATHLEN];
1300 int host_cnt;
1302 mfsp = &me->map_fs;
1303 *mfsp = NULL;
1306 * there may be more than one entry in the map list. Get the
1307 * first one. Use temps to handle the word information and
1308 * copy back into fsw and fswq fields when done.
1310 *lp = fsw;
1311 *lq = fswq;
1312 if (getword(w, wq, lp, lq, ' ', wsize) == -1)
1313 return (PARSE_ERROR);
1314 while (*w && *w != '/') {
1315 bool_t maybe_url;
1317 maybe_url = TRUE;
1319 wlp = w; wlq = wq;
1320 if (getword(hostlist, hostlistq, &wlp, &wlq, ':',
1321 sizeof (hostlist)) == -1)
1322 return (PARSE_ERROR);
1323 if (!*hostlist)
1324 goto bad_entry;
1326 if (strcmp(hostlist, "nfs") != 0)
1327 maybe_url = FALSE;
1329 if (getword(dirname, qbuff, &wlp, &wlq, ':',
1330 sizeof (dirname)) == -1)
1331 return (PARSE_ERROR);
1332 if (*dirname == '\0')
1333 goto bad_entry;
1335 if (maybe_url == TRUE && strncmp(dirname, "//", 2) != 0)
1336 maybe_url = FALSE;
1339 * See the next block comment ("Once upon a time ...") to
1340 * understand this. It turns the deprecated concept
1341 * of "subdir mounts" produced some useful code for handling
1342 * the possibility of a ":port#" in the URL.
1344 if (maybe_url == FALSE)
1345 *subdir = '/';
1346 else
1347 *subdir = ':';
1349 *qbuff = ' ';
1352 * Once upon time, before autofs, there was support for
1353 * "subdir mounts". The idea was to "economize" the
1354 * number of mounts, so if you had a number of entries
1355 * all referring to a common subdirectory, e.g.
1357 * carol seasons:/export/home11/carol
1358 * ted seasons:/export/home11/ted
1359 * alice seasons:/export/home11/alice
1361 * then you could tell the automounter to mount a
1362 * common mountpoint which was delimited by the second
1363 * colon:
1365 * carol seasons:/export/home11:carol
1366 * ted seasons:/export/home11:ted
1367 * alice seasons:/export/home11:alice
1369 * The automounter would mount seasons:/export/home11
1370 * then for any other map entry that referenced the same
1371 * directory it would build a symbolic link that
1372 * appended the remainder of the path after the second
1373 * colon, i.e. once the common subdir was mounted, then
1374 * other directories could be accessed just by link
1375 * building - no further mounts required.
1377 * In theory the "mount saving" idea sounded good. In
1378 * practice the saving didn't amount to much and the
1379 * symbolic links confused people because the common
1380 * mountpoint had to have a pseudonym.
1382 * To remain backward compatible with the existing
1383 * maps, we interpret a second colon as a slash.
1385 if (getword(subdir+1, qbuff+1, &wlp, &wlq, ':',
1386 sizeof (subdir)) == -1)
1387 return (PARSE_ERROR);
1389 if (*(subdir+1))
1390 (void) strcat(dirname, subdir);
1392 hl = hostlist; hlq = hostlistq;
1394 host_cnt = 0;
1395 for (;;) {
1397 if (getword(hostname_and_penalty, qbuff, &hl, &hlq, ',',
1398 sizeof (hostname_and_penalty)) == -1)
1399 return (PARSE_ERROR);
1400 if (!*hostname_and_penalty)
1401 break;
1403 host_cnt++;
1404 if (host_cnt > 1)
1405 maybe_url = FALSE;
1407 hn = hostname_and_penalty;
1408 hnq = qbuff;
1409 if (getword(hostname, qbuff1, &hn, &hnq, '(',
1410 sizeof (hostname)) == -1)
1411 return (PARSE_ERROR);
1412 if (hostname[0] == '\0')
1413 goto bad_entry;
1415 if (strcmp(hostname, hostname_and_penalty) == 0) {
1416 penalty = 0;
1417 } else {
1418 maybe_url = FALSE;
1419 hn++; hnq++;
1420 if (getword(pbuff, pbuffq, &hn, &hnq, ')',
1421 sizeof (pbuff)) == -1)
1422 return (PARSE_ERROR);
1423 if (!*pbuff)
1424 penalty = 0;
1425 else
1426 penalty = atoi(pbuff);
1428 mfs = (struct mapfs *)malloc(sizeof (*mfs));
1429 if (mfs == NULL) {
1430 syslog(LOG_ERR,
1431 "parse_nfs: Memory allocation failed");
1432 return (PARSE_ERROR);
1434 (void) memset(mfs, 0, sizeof (*mfs));
1435 *mfsp = mfs;
1436 mfsp = &mfs->mfs_next;
1438 if (maybe_url == TRUE) {
1439 char *host;
1440 char *path;
1441 char *sport;
1443 host = dirname+2;
1444 path = strchr(host, '/');
1445 if (path == NULL) {
1446 syslog(LOG_ERR,
1447 "parse_nfs: illegal nfs url syntax: %s",
1448 host);
1450 return (PARSE_ERROR);
1452 *path = '\0';
1453 sport = strchr(host, ':');
1455 if (sport != NULL && sport < path) {
1456 *sport = '\0';
1457 mfs->mfs_port = atoi(sport+1);
1459 if (mfs->mfs_port > USHRT_MAX) {
1460 syslog(LOG_ERR,
1461 "parse_nfs: invalid "
1462 "port number (%d) in "
1463 "NFS URL",
1464 mfs->mfs_port);
1466 return (PARSE_ERROR);
1471 path++;
1472 if (*path == '\0')
1473 path = ".";
1475 mfs->mfs_flags |= MFS_URL;
1477 mfs->mfs_host = strdup(host);
1478 mfs->mfs_dir = strdup(path);
1479 } else {
1480 mfs->mfs_host = strdup(hostname);
1481 mfs->mfs_dir = strdup(dirname);
1484 mfs->mfs_penalty = penalty;
1485 if (mfs->mfs_host == NULL || mfs->mfs_dir == NULL) {
1486 syslog(LOG_ERR,
1487 "parse_nfs: Memory allocation failed");
1488 return (PARSE_ERROR);
1492 * We check host_cnt to make sure we haven't parsed an entry
1493 * with no host information.
1495 if (host_cnt == 0) {
1496 syslog(LOG_ERR,
1497 "parse_nfs: invalid host specified - bad entry "
1498 "in map %s \"%s\"",
1499 mapname, w);
1500 return (PARSE_ERROR);
1502 if (getword(w, wq, lp, lq, ' ', wsize) == -1)
1503 return (PARSE_ERROR);
1506 strcpy(fsw, w);
1507 strcpy(fswq, wq);
1509 return (PARSE_OK);
1511 bad_entry:
1512 syslog(LOG_ERR, "parse_nfs: bad entry in map %s \"%s\"", mapname, w);
1513 return (PARSE_ERROR);
1516 static int
1517 parse_special(me, w, wq, lp, lq, wsize)
1518 struct mapent *me;
1519 char *w, *wq, **lp, **lq;
1520 int wsize;
1522 char devname[MAXPATHLEN + 1], qbuf[MAXPATHLEN + 1];
1523 char *wlp, *wlq;
1524 struct mapfs *mfs;
1526 wlp = w;
1527 wlq = wq;
1528 if (getword(devname, qbuf, &wlp, &wlq, ' ', sizeof (devname)) == -1)
1529 return (PARSE_ERROR);
1530 if (devname[0] == '\0')
1531 return (PARSE_ERROR);
1533 mfs = (struct mapfs *)malloc(sizeof (struct mapfs));
1534 if (mfs == NULL)
1535 return (PARSE_ERROR);
1536 (void) memset(mfs, 0, sizeof (*mfs));
1539 * A device name that begins with a slash could
1540 * be confused with a mountpoint path, hence use
1541 * a colon to escape a device string that begins
1542 * with a slash, e.g.
1544 * foo -ro /bar foo:/bar
1545 * and
1546 * foo -ro /dev/sr0
1548 * would confuse the parser. The second instance
1549 * must use a colon:
1551 * foo -ro :/dev/sr0
1553 mfs->mfs_dir = strdup(&devname[devname[0] == ':']);
1554 if (mfs->mfs_dir == NULL)
1555 return (PARSE_ERROR);
1556 me->map_fs = mfs;
1557 if (getword(w, wq, lp, lq, ' ', wsize) == -1)
1558 return (PARSE_ERROR);
1559 return (0);
1563 * get_dir_from_path(char *dir, char **path, int dirsz)
1564 * gets the directory name dir from path for max string of length dirsz.
1565 * A modification of the getword routine. Assumes the delimiter is '/'
1566 * and that excess /'s are redundant.
1567 * Returns PARSE_OK or PARSE_ERROR
1569 static int
1570 get_dir_from_path(char *dir, char **path, int dirsz)
1572 char *tmp = dir;
1573 int count = dirsz;
1575 if (dirsz <= 0) {
1576 if (verbose)
1577 syslog(LOG_ERR,
1578 "get_dir_from_path: invalid directory size %d", dirsz);
1579 return (PARSE_ERROR);
1582 /* get rid of leading /'s in path */
1583 while (**path == '/')
1584 (*path)++;
1586 /* now at a word or at the end of path */
1587 while ((**path) && ((**path) != '/')) {
1588 if (--count <= 0) {
1589 *tmp = '\0';
1590 syslog(LOG_ERR,
1591 "get_dir_from_path: max pathlength exceeded %d", dirsz);
1592 return (PARSE_ERROR);
1594 *dir++ = *(*path)++;
1597 *dir = '\0';
1599 /* get rid of trailing /'s in path */
1600 while (**path == '/')
1601 (*path)++;
1603 return (PARSE_OK);
1607 * alloc_hiernode(hiernode **newnode, char *dirname)
1608 * allocates a new hiernode corresponding to a new directory entry
1609 * in the hierarchical structure, and passes a pointer to it back
1610 * to the calling program.
1611 * Returns PARSE_OK or appropriate error value.
1613 static int
1614 alloc_hiernode(hiernode **newnode, char *dirname)
1616 if ((*newnode = (hiernode *)malloc(sizeof (hiernode))) == NULL) {
1617 syslog(LOG_ERR, "alloc_hiernode: Memory allocation failed");
1618 return (ENOMEM);
1621 memset(((char *)*newnode), 0, sizeof (hiernode));
1622 strcpy(((*newnode)->dirname), dirname);
1623 return (PARSE_OK);
1627 * free_hiernode(hiernode *node)
1628 * frees the allocated hiernode given the head of the structure
1629 * recursively calls itself until it frees entire structure.
1630 * Returns nothing.
1632 static void
1633 free_hiernode(hiernode *node)
1635 hiernode *currnode = node;
1636 hiernode *prevnode = NULL;
1638 while (currnode != NULL) {
1639 if (currnode->subdir != NULL)
1640 free_hiernode(currnode->subdir);
1641 prevnode = currnode;
1642 currnode = currnode->leveldir;
1643 free((void*)prevnode);
1648 * free_mapent(struct mapent *)
1649 * free the mapentry and its fields
1651 void
1652 free_mapent(me)
1653 struct mapent *me;
1655 struct mapfs *mfs;
1656 struct mapent *m;
1658 while (me) {
1659 while (me->map_fs) {
1660 mfs = me->map_fs;
1661 if (mfs->mfs_host)
1662 free(mfs->mfs_host);
1663 if (mfs->mfs_dir)
1664 free(mfs->mfs_dir);
1665 if (mfs->mfs_args)
1666 free(mfs->mfs_args);
1667 if (mfs->mfs_nconf)
1668 freenetconfigent(mfs->mfs_nconf);
1669 me->map_fs = mfs->mfs_next;
1670 free((char *)mfs);
1673 if (me->map_root)
1674 free(me->map_root);
1675 if (me->map_mntpnt)
1676 free(me->map_mntpnt);
1677 if (me->map_mntopts)
1678 free(me->map_mntopts);
1679 if (me->map_fstype)
1680 free(me->map_fstype);
1681 if (me->map_mounter)
1682 free(me->map_mounter);
1683 if (me->map_fsw)
1684 free(me->map_fsw);
1685 if (me->map_fswq)
1686 free(me->map_fswq);
1688 m = me;
1689 me = me->map_next;
1690 free((char *)m);
1695 * trace_mapents(struct mapent *mapents)
1696 * traces through the mapentry structure and prints it element by element
1697 * returns nothing
1699 static void
1700 trace_mapents(char *s, struct mapent *mapents)
1702 struct mapfs *mfs;
1703 struct mapent *me;
1705 trace_prt(1, "\n\t%s\n", s);
1706 for (me = mapents; me; me = me->map_next) {
1707 trace_prt(1, " (%s,%s)\t %s%s -%s\n",
1708 me->map_fstype ? me->map_fstype : "",
1709 me->map_mounter ? me->map_mounter : "",
1710 me->map_root ? me->map_root : "",
1711 me->map_mntpnt ? me->map_mntpnt : "",
1712 me->map_mntopts ? me->map_mntopts : "");
1713 for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next)
1714 trace_prt(0, "\t\t%s:%s\n",
1715 mfs->mfs_host ? mfs->mfs_host: "",
1716 mfs->mfs_dir ? mfs->mfs_dir : "");
1718 trace_prt(1, "\tme->map_fsw=%s\n",
1719 me->map_fsw ? me->map_fsw:"",
1720 me->map_fswq ? me->map_fsw:"");
1721 trace_prt(1, "\t mntlevel=%d\t%s\t%s err=%d\n",
1722 me->map_mntlevel,
1723 me->map_modified ? "modify=TRUE":"modify=FALSE",
1724 me->map_faked ? "faked=TRUE":"faked=FALSE",
1725 me->map_err);
1730 * trace_hierarchy(hiernode *node)
1731 * traces the allocated hiernode given the head of the structure
1732 * recursively calls itself until it traces entire structure.
1733 * the first call made at the root is made with a zero level.
1734 * nodelevel is simply used to print tab and make the tracing clean.
1735 * Returns nothing.
1737 static void
1738 trace_hierarchy(hiernode *node, int nodelevel)
1740 hiernode *currnode = node;
1741 int i;
1743 while (currnode != NULL) {
1744 if (currnode->subdir != NULL) {
1745 for (i = 0; i < nodelevel; i++)
1746 trace_prt(0, "\t");
1747 trace_prt(0, "\t(%s, ",
1748 currnode->dirname ? currnode->dirname :"");
1749 if (currnode->mapent) {
1750 trace_prt(0, "%d, %s)\n",
1751 currnode->mapent->map_mntlevel,
1752 currnode->mapent->map_mntopts ?
1753 currnode->mapent->map_mntopts:"");
1755 else
1756 trace_prt(0, " ,)\n");
1757 nodelevel++;
1758 trace_hierarchy(currnode->subdir, nodelevel);
1759 } else {
1760 for (i = 0; i < nodelevel; i++)
1761 trace_prt(0, "\t");
1762 trace_prt(0, "\t(%s, ",
1763 currnode->dirname ? 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:"");
1770 else
1771 trace_prt(0, ", )\n");
1773 currnode = currnode->leveldir;
1777 struct mapent *
1778 do_mapent_hosts(mapopts, host, isdirect)
1779 char *mapopts, *host;
1780 uint_t isdirect;
1782 CLIENT *cl;
1783 struct mapent *me, *ms, *mp;
1784 struct mapfs *mfs;
1785 struct exportnode *ex = NULL;
1786 struct exportnode *exlist, *texlist, **texp, *exnext;
1787 struct timeval timeout;
1788 enum clnt_stat clnt_stat;
1789 char name[MAXPATHLEN];
1790 char entryopts[MAXOPTSLEN];
1791 char fstype[32], mounter[32];
1792 int exlen, duplicate;
1793 struct mnttab mb; /* needed for hasmntopt() to get nfs version */
1794 rpcvers_t nfsvers; /* version in map options, 0 if not there */
1795 rpcvers_t vers, versmin; /* used to negotiate nfs vers in pingnfs() */
1796 int retries, delay;
1797 int foundvers;
1799 if (trace > 1)
1800 trace_prt(1, " do_mapent_hosts: host %s\n", host);
1802 /* check for special case: host is me */
1804 if (self_check(host)) {
1805 ms = (struct mapent *)malloc(sizeof (*ms));
1806 if (ms == NULL)
1807 goto alloc_failed;
1808 (void) memset((char *)ms, 0, sizeof (*ms));
1809 (void) strcpy(fstype, MNTTYPE_NFS);
1810 get_opts(mapopts, entryopts, fstype, NULL);
1811 ms->map_mntopts = strdup(entryopts);
1812 if (ms->map_mntopts == NULL)
1813 goto alloc_failed;
1814 ms->map_mounter = strdup(fstype);
1815 if (ms->map_mounter == NULL)
1816 goto alloc_failed;
1817 ms->map_fstype = strdup(MNTTYPE_NFS);
1818 if (ms->map_fstype == NULL)
1819 goto alloc_failed;
1821 if (isdirect)
1822 name[0] = '\0';
1823 else {
1824 (void) strcpy(name, "/");
1825 (void) strcat(name, host);
1827 ms->map_root = strdup(name);
1828 if (ms->map_root == NULL)
1829 goto alloc_failed;
1830 ms->map_mntpnt = strdup("");
1831 if (ms->map_mntpnt == NULL)
1832 goto alloc_failed;
1833 mfs = (struct mapfs *)malloc(sizeof (*mfs));
1834 if (mfs == NULL)
1835 goto alloc_failed;
1836 (void) memset((char *)mfs, 0, sizeof (*mfs));
1837 ms->map_fs = mfs;
1838 mfs->mfs_host = strdup(host);
1839 if (mfs->mfs_host == NULL)
1840 goto alloc_failed;
1841 mfs->mfs_dir = strdup("/");
1842 if (mfs->mfs_dir == NULL)
1843 goto alloc_failed;
1845 /* initialize mntlevel and modify */
1846 ms->map_mntlevel = -1;
1847 ms->map_modified = FALSE;
1848 ms->map_faked = FALSE;
1850 if (trace > 1)
1851 trace_prt(1,
1852 " do_mapent_hosts: self-host %s OK\n", host);
1854 return (ms);
1858 * Call pingnfs. Note that we can't have replicated hosts in /net.
1859 * XXX - we would like to avoid duplicating the across the wire calls
1860 * made here in nfsmount(). The pingnfs cache should help avoid it.
1862 mb.mnt_mntopts = mapopts;
1863 foundvers = nopt(&mb, MNTOPT_VERS, (int *)&nfsvers);
1864 if (!foundvers)
1865 nfsvers = 0;
1866 if (set_versrange(nfsvers, &vers, &versmin) != 0) {
1867 syslog(LOG_ERR, "Incorrect NFS version specified for %s", host);
1868 return ((struct mapent *)NULL);
1870 if (pingnfs(host, get_retry(mapopts) + 1, &vers, versmin, 0, FALSE,
1871 NULL, NULL) != RPC_SUCCESS)
1872 return ((struct mapent *)NULL);
1874 retries = get_retry(mapopts);
1875 delay = INITDELAY;
1876 retry:
1877 /* get export list of host */
1878 cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "circuit_v");
1879 if (cl == NULL) {
1880 cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "datagram_v");
1881 if (cl == NULL) {
1882 syslog(LOG_ERR,
1883 "do_mapent_hosts: %s %s", host, clnt_spcreateerror(""));
1884 return ((struct mapent *)NULL);
1888 #ifdef MALLOC_DEBUG
1889 add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__);
1890 add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
1891 __FILE__, __LINE__);
1892 #endif
1894 timeout.tv_usec = 0;
1895 timeout.tv_sec = 25;
1896 if (clnt_stat = clnt_call(cl, MOUNTPROC_EXPORT, xdr_void, 0,
1897 xdr_exports, (caddr_t)&ex, timeout)) {
1899 if (retries-- > 0) {
1900 clnt_destroy(cl);
1901 DELAY(delay);
1902 goto retry;
1905 syslog(LOG_ERR,
1906 "do_mapent_hosts: %s: export list: %s",
1907 host, clnt_sperrno(clnt_stat));
1908 #ifdef MALLOC_DEBUG
1909 drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__);
1910 drop_alloc("AUTH_HANDLE", cl->cl_auth,
1911 __FILE__, __LINE__);
1912 #endif
1913 clnt_destroy(cl);
1914 return ((struct mapent *)NULL);
1917 #ifdef MALLOC_DEBUG
1918 drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__);
1919 drop_alloc("AUTH_HANDLE", cl->cl_auth,
1920 __FILE__, __LINE__);
1921 #endif
1922 clnt_destroy(cl);
1924 if (ex == NULL) {
1925 if (trace > 1)
1926 trace_prt(1,
1927 gettext(" getmapent_hosts: null export list\n"));
1928 return ((struct mapent *)NULL);
1931 /* now sort by length of names - to get mount order right */
1932 exlist = ex;
1933 texlist = NULL;
1934 #ifdef lint
1935 exnext = NULL;
1936 #endif
1937 for (; ex; ex = exnext) {
1938 exnext = ex->ex_next;
1939 exlen = strlen(ex->ex_dir);
1940 duplicate = 0;
1941 for (texp = &texlist; *texp; texp = &((*texp)->ex_next)) {
1942 if (exlen < (int)strlen((*texp)->ex_dir))
1943 break;
1944 duplicate = (strcmp(ex->ex_dir, (*texp)->ex_dir) == 0);
1945 if (duplicate) {
1946 /* disregard duplicate entry */
1947 freeex_ent(ex);
1948 break;
1951 if (!duplicate) {
1952 ex->ex_next = *texp;
1953 *texp = ex;
1956 exlist = texlist;
1958 (void) strcpy(fstype, MNTTYPE_NFS);
1959 get_opts(mapopts, entryopts, fstype, NULL);
1960 (void) strcpy(mounter, fstype);
1962 /* Now create a mapent from the export list */
1963 ms = NULL;
1964 me = NULL;
1966 for (ex = exlist; ex; ex = ex->ex_next) {
1967 mp = me;
1968 me = (struct mapent *)malloc(sizeof (*me));
1969 if (me == NULL)
1970 goto alloc_failed;
1971 (void) memset((char *)me, 0, sizeof (*me));
1973 if (ms == NULL)
1974 ms = me;
1975 else
1976 mp->map_next = me;
1978 if (isdirect)
1979 name[0] = '\0';
1980 else {
1981 (void) strcpy(name, "/");
1982 (void) strcat(name, host);
1984 me->map_root = strdup(name);
1985 if (me->map_root == NULL)
1986 goto alloc_failed;
1988 *name = '\0';
1989 if (strcmp(ex->ex_dir, "/") != 0) {
1990 if (*(ex->ex_dir) != '/')
1991 (void) strcpy(name, "/");
1992 (void) strcat(name, ex->ex_dir);
1994 me->map_mntpnt = strdup(name);
1995 if (me->map_mntpnt == NULL)
1996 goto alloc_failed;
1998 me->map_fstype = strdup(fstype);
1999 if (me->map_fstype == NULL)
2000 goto alloc_failed;
2001 me->map_mounter = strdup(mounter);
2002 if (me->map_mounter == NULL)
2003 goto alloc_failed;
2004 me->map_mntopts = strdup(entryopts);
2005 if (me->map_mntopts == NULL)
2006 goto alloc_failed;
2008 mfs = (struct mapfs *)malloc(sizeof (*mfs));
2009 if (mfs == NULL)
2010 goto alloc_failed;
2011 (void) memset((char *)mfs, 0, sizeof (*mfs));
2012 me->map_fs = mfs;
2013 mfs->mfs_host = strdup(host);
2014 if (mfs->mfs_host == NULL)
2015 goto alloc_failed;
2016 mfs->mfs_dir = strdup(ex->ex_dir);
2017 if (mfs->mfs_dir == NULL)
2018 goto alloc_failed;
2020 /* initialize mntlevel and modify values */
2021 me->map_mntlevel = -1;
2022 me->map_modified = FALSE;
2023 me->map_faked = FALSE;
2025 freeex(exlist);
2027 if (trace > 1)
2028 trace_prt(1, " do_mapent_hosts: host %s OK\n", host);
2030 return (ms);
2032 alloc_failed:
2033 syslog(LOG_ERR, "do_mapent_hosts: Memory allocation failed");
2034 free_mapent(ms);
2035 freeex(exlist);
2036 return ((struct mapent *)NULL);
2040 static void
2041 freeex_ent(ex)
2042 struct exportnode *ex;
2044 struct groupnode *groups, *tmpgroups;
2046 free(ex->ex_dir);
2047 groups = ex->ex_groups;
2048 while (groups) {
2049 free(groups->gr_name);
2050 tmpgroups = groups->gr_next;
2051 free((char *)groups);
2052 groups = tmpgroups;
2054 free((char *)ex);
2057 static void
2058 freeex(ex)
2059 struct exportnode *ex;
2061 struct exportnode *tmpex;
2063 while (ex) {
2064 tmpex = ex->ex_next;
2065 freeex_ent(ex);
2066 ex = tmpex;
2070 static const char uatfs_err[] = "submount under fstype=autofs not supported";
2072 * dump_mapent_err(struct mapent *me, char *key, char *mapname)
2073 * syslog appropriate error in mapentries.
2075 static void dump_mapent_err(struct mapent *me, char *key, char *mapname)
2077 switch (me->map_err) {
2078 case MAPENT_NOERR:
2079 if (verbose)
2080 syslog(LOG_ERR,
2081 "map=%s key=%s mntpnt=%s: no error");
2082 break;
2083 case MAPENT_UATFS:
2084 syslog(LOG_ERR,
2085 "mountpoint %s in map %s key %s not mounted: %s",
2086 me->map_mntpnt, mapname, key, uatfs_err);
2087 break;
2088 default:
2089 if (verbose)
2090 syslog(LOG_ERR,
2091 "map=%s key=%s mntpnt=%s: unknown mapentry error");