autod_parse.c revision 1160694128cd3980cc06abe31af529a887efd310
1N/A * The contents of this file are subject to the terms of the 1N/A * Common Development and Distribution License, Version 1.0 only 1N/A * (the "License"). You may not use this file except in compliance 1N/A * See the License for the specific language governing permissions 1N/A * and limitations under the License. 1N/A * When distributing Covered Code, include this CDDL HEADER in each 1N/A * If applicable, add the following below this CDDL HEADER, with the 1N/A * fields enclosed by brackets "[]" replaced with your own identifying 1N/A * information: Portions Copyright [yyyy] [name of copyright owner] 1N/A * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 1N/A * Use is subject to license terms. 1N/A#
pragma ident "%Z%%M% %I% %E% SMI" 1N/A * This structure is used to determine the hierarchical 1N/A * relationship between directories 1N/A * mapentry error type defininitions 1N/A * parse_entry(char *key, char *mapname, char *mapopts, struct mapline *ml, 1N/A * char *subdir, uint_t isdirect, bool_t mount_access) 1N/A * Parses the data in ml to build a mapentry list containing the information 1N/A * by processing ml, hierarchically sorts (builds a tree of) the list according 1N/A * to mountpoint. Then pushes options down the hierarchy, and fills in the mount 1N/A * file system. Finally, modifies the intermediate list depending on how far 1N/A * in the hierarchy the current request is (uses subdir). Deals with special 1N/A * case of /net map parsing. 1N/A * Returns a pointer to the head of the mapentry list. 1N/A * Assure the key is only one token long. 1N/A * This prevents options from sneaking in through the 1N/A for (p =
key; *p !=
'\0'; p++) {
1N/A * select the appropriate parser, and build the mapentry list 1N/A * the /net parser - uses do_mapent_hosts to build mapents. 1N/A * The mapopts are considered default for every entry, so we 1N/A * don't push options down hierarchies. 1N/A * Modify the mapentry list. We *must* do this only after 1N/A * the mapentry list is completely built (since we need to 1N/A * have parse_fsinfo called first). 1N/A * XXX: its dangerous to use rootnode after modify mapents as 1N/A * it may be pointing to mapents that have been freed 1N/A * mapline_to_mapent(struct mapent **mapents, struct mapline *ml, 1N/A * char *key, char *mapname, char *mapopts, char *defaultopts, 1N/A * Parses the mapline information in ml word by word to build an intermediate 1N/A * mapentry list, which is passed back to the caller. The mapentries may have 1N/A * holes (example no options), as they are completed only later. The logic is 1N/A * awkward, but needed to provide the supported flexibility in the map entries. 1N/A * (especially the first line). Note that the key is the full pathname of the 1N/A * directory to be mounted in a direct map, and ml is the mapentry beyond key. 1N/A * Returns PARSE_OK or an appropriate error value. 1N/A /* do any macro expansions that are required to complete ml */ 1N/A "mapline_to_mapent: map %s: line too long (max %d chars)",
1N/A " mapline_to_mapent: (expanded) mapline (%s,%s)\n",
1N/A /* init the head of mapentry list to null */ 1N/A * Get the first word - its either a '-' if default options provided, 1N/A * a '/', if the mountroot is implicitly provided, or a mount filesystem 1N/A * if the mountroot is implicit. Note that if the first word begins with 1N/A * a '-' then the second must be read and it must be a mountpoint or a 1N/A * mount filesystem. Use mapopts if no default opts are provided. 1N/A implied = *w !=
'/';
/* implied is 1 only if '/' is implicit */ 1N/A * direct maps get an empty string as root - to be filled 1N/A * by the entire path later. Indirect maps get /key as the 1N/A * map root. Note that xfn maps don't care about the root 1N/A * - they override it in getmapent_fn(). 1N/A /* mntpnt is empty for the mount root */ 1N/A * If implied, the word must be a mount filesystem, 1N/A * and its already read in; also turn off implied - its 1N/A * not applicable except for the mount root. Else, 1N/A * read another (or two) words depending on if there's 1N/A * must be a mount filesystem or a set of filesystems at 1N/A if (w[0] ==
'\0' || w[0] ==
'-') {
1N/A "mapline_to_mapent: bad location=%s map=%s key=%s",
1N/A * map_fsw and map_fswq hold information which will be 1N/A * used to determine filesystem information at a later 1N/A * point. This is required since we can only find out 1N/A * about the mount file system after the directories 1N/A * are hierarchically sorted and options have been pushed 1N/A * down the hierarchies. 1N/A * the next word, if any, is either another mount point or a 1N/A * mount filesystem if more than one server is listed. 1N/A while (*w && *w !=
'/') {
/* more than 1 server listed */ 1N/A /* initialize flags */ 1N/A "mapline_to_mapent: parsed with null mapents");
1N/A "mapline_to_mapent: parsed nononempty w=%s", w);
1N/A * hierarchical_sort(struct mapent *mapents, hiernode **rootnode, char *key 1N/A * sorts the mntpnts in each mapent to build a hierarchy of nodes, with 1N/A * with the rootnode being the mount root. The hierarchy is setup as 1N/A * levels, and subdirs below each level. Provides a link from node to 1N/A * the relevant mapentry. 1N/A * Returns PARSE_OK or appropriate error value 1N/A /* allocate the rootnode with a default path of "" */ 1N/A * walk through mapents - for each mapent, locate the position 1N/A * within the hierarchy by walking across leveldirs, and 1N/A * subdirs of matched leveldirs. Starts one level below 1N/A * the root (assumes an implicit match with rootnode). 1N/A * XXX - this could probably be done more cleanly using recursion. 1N/A * match found - mntpnt is a child of 1N/A * No more leveldirs to match. 1N/A /* try this leveldir */ 1N/A /* no more subdirs to match. Add a new one */ 1N/A /* duplicate mntpoint found */ 1N/A "hierarchical_sort: duplicate mntpnt map=%s key=%s",
1N/A /* provide a pointer from node to mapent */ 1N/A * push_options(hiernode *node, char *opts, char *mapopts, int err) 1N/A * Pushes the options down a hierarchical structure. Works recursively from the 1N/A * root, which is passed in on the first call. Uses a replacement policy. 1N/A * If a node points to a mapentry, and it has an option, then thats the option 1N/A * for that mapentry. Else, the node's mapent inherits the option from the 1N/A * default (which may be the global option for the entry or mapopts). 1N/A * err is useful in flagging entries with errors in pushing options. 1N/A * returns PARSE_OK or appropriate error value. 1N/A /* ensure that all the dirs at a level are passed the default options */ 1N/A if (
me !=
NULL) {
/* not all nodes point to a mapentry */ 1N/A /* push the options to subdirs */ 1N/A * set_mapent_opts(struct mapent *me, char *opts, char *defaultopts, 1N/A * sets the mapentry's options, fstype and mounter fields by separating 1N/A * out the fstype part from the opts. Use default options if opts is NULL. 1N/A * Note taht defaultopts may be the same as mapopts. 1N/A * Returns PARSE_OK or appropriate error value. 1N/A /* set options to default options, if none exist for this entry */ 1N/A /* separate opts into fstype and (other) entrypopts */ 1N/A /* replace any existing opts */ 1N/A * The following ugly chunk of code crept in as a result of 1N/A * cachefs. If it's a cachefs mount of an nfs filesystem, then 1N/A * it's important to parse the nfs special field. Otherwise, 1N/A * just hand the special field to the fs-specific mount 1N/A * child options are exactly fstype = somefs, we need to do some 1N/A * more option pushing work. 1N/A * Check the option string for an "fstype" 1N/A * option. If found, return the fstype 1N/A * and the option string with the fstype 1N/A * option removed, e.g. 1N/A * input: "fstype=cachefs,ro,nosuid" 1N/A * Also indicates if the fstype option was present 1N/A * by setting a flag, if the pointer to the flag * fstype_opts(struct mapent *me, char *opts, char *defaultopts, * We need to push global options to the child entry if it is exactly * the options to push are the global defaults for the entry, * if they exist, or mapopts, if the global defaults for the * modify_mapents(struct mapent **mapents, char *mapname, * char *mapopts, char *subdir, hiernode *rootnode, * char *key, uint_t isdirect, bool_t mount_access) * modifies the intermediate mapentry list into the final one, and passes * back a pointer to it. The final list may contain faked mapentries for * hiernodes that do not point to a mapentry, or converted mapentries, if * hiernodes that point to a mapentry need to be converted from nfs to autofs. * mounts. Entries that are not directly 1 level below the subdir are removed. * Returns PARSE_OK or PARSE_ERROR * correct the mapentry mntlevel from default -1 to level depending on * position in hierarchy, and build any faked mapentries, if required * at one level below the rootnode given by subdir. * attaches faked mapents to real mapents list. Assumes mapents * get rid of nodes marked at level -1 * syslog any errors and free entry /* special case when head has to be freed */ /* something wierd happened */ "modify_mapents: level error");
/* separate out the node */ * convert level 1 mapents that are not already autonodes * 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) * sets the mapentry mount levels (depths) with respect to the subdir. * Assigns a value of 0 to the new root. Finds the level1 directories by * calling mark_*_level1_*(). Also cleans off extra /'s in level0 and * level1 map_mntpnts. Note that one level below the new root is an existing * mapentry if there's a mapentry (nfs mount) corresponding to the root, * and the direct subdir set for the root, if there's no mapentry corresponding * to the root (we install autodirs). Returns PARSE_OK or error value. * find and mark the root by tracing down subdir. Use traversed_path * to keep track of how far we go, while guaranteeing that it * contains no '/' at the end. Took some mucking to get that right. /* subdir is a child of currnode */ "set_and_fake_mapent_mntlevel: subdir=%s error: map=%s",
* see if level of root really points to a mapent and if * have access to that filessystem - call appropriate * routine to mark level 1 nodes, and build faked entries trace_prt(
1,
" node mountpoint %s\t travpath=%s\n",
* Copy traversed path map_mntpnt to get rid of any extra * '/' the map entry may contain. "set_and_fake_mapent_mntlevel: path=%s error",
trace_prt(
1,
"\n\tset_and_fake_mapent_mntlevel\n");
* mark_level1_root(hiernode *node, char *traversed_path) * marks nodes upto one level below the rootnode given by subdir * recursively. Called if rootnode points to a mapent. * In this routine, a level 1 node is considered to be the 1st existing * mapentry below the root node, so there's no faking involved. * Returns PARSE_OK or error value /* ensure we touch all leveldirs */ * mark node level as 1, if one exists - else walk down * subdirs until we find one. "mark_level1_root: hierarchy error");
trace_prt(
1,
" node mntpnt %s\t travpath %s\n",
/* replace mntpnt with travpath to clean extra '/' */ "mark_level1_root: path=%s error",
* mark_and_fake_level1_noroot(hiernode *node, char *traversed_path, * char *key,char *mapname, struct mapent **faked_mapents, * uint_t isdirect, char *mapopts) * Called if the root of the hierarchy does not point to a mapent. marks nodes * upto one physical level below the rootnode given by subdir. checks if * there's a real mapentry. If not, it builds a faked one (autonode) at that * point. The faked autonode is direct, with the map being the same as the * original one from which the call originated. Options are same as that of * the map and assigned in automount_opts(). Returns PARSE_OK or error value. * existing mapentry at level 1 - copy travpath to * get rid of extra '/' in mntpnt trace_prt(
1,
" node mntpnt=%s\t travpath=%s\n",
"mark_fake_level1_noroot:path=%s error",
* build the faked autonode "mark_and_fake_level1_noroot: out of memory");
"mark_and_fake_level1_noroot: out of memory");
else {
/* attach to the head */ * convert_mapent_to_automount(struct mapent *me, char *mapname, * change the mapentry me to an automount - free fields first and NULL them * to avoid freeing again, while freeing the mapentry at a later stage. * Could have avoided freeing entries here as we don't really look at them. * Give the converted mapent entry the options that came with the map using * automount_opts(). Returns PARSE_OK or appropriate error value. /* free relevant entries */ /* replace relevant entries */ /* mucked with this entry, set the map_modified field to TRUE */ "convert_mapent_to_automount: Memory allocation failed");
* automount_opts(char **map_mntopts, char *mapopts) * modifies automount opts - gets rid of all "indirect" and "direct" strings * if they exist, and then adds a direct string to force a direct automount. * Rest of the mapopts stay intact. Returns PARSE_OK or appropriate error. "option string %s too long (max=%d)",
mapopts,
/* remove trailing and leading spaces */ * if direct or indirect found, get rid of it, else put it /* add the direct string at the end */ * parse_fsinfo(char *mapname, struct mapent *mapents) * parses the filesystem information stored in me->map_fsw and me->map_fswq * and calls appropriate filesystem parser. * Returns PARSE_OK or an appropriate error value. "parse_fsinfo: mount location error %s",
* This function parses the map entry for a nfs type file system * The input is the string lp (and lq) which can be one of the * a) host[(penalty)][,host[(penalty)]]... :/directory * b) host[(penalty)]:/directory[ host[(penalty)]:/directory]... * This routine constructs a mapfs link-list for each of * the hosts and the corresponding file system. The list * is then attatched to the mapent struct passed in. * there may be more than one entry in the map list. Get the * first one. Use temps to handle the word information and * copy back into fsw and fswq fields when done. while (*w && *w !=
'/') {
* See the next block comment ("Once upon a time ...") to * understand this. It turns the deprecated concept * of "subdir mounts" produced some useful code for handling * the possibility of a ":port#" in the URL. * Once upon time, before autofs, there was support for * "subdir mounts". The idea was to "economize" the * number of mounts, so if you had a number of entries * all referring to a common subdirectory, e.g. * then you could tell the automounter to mount a * common mountpoint which was delimited by the second * then for any other map entry that referenced the same * directory it would build a symbolic link that * appended the remainder of the path after the second * colon, i.e. once the common subdir was mounted, then * other directories could be accessed just by link * building - no further mounts required. * In theory the "mount saving" idea sounded good. In * practice the saving didn't amount to much and the * symbolic links confused people because the common * mountpoint had to have a pseudonym. * To remain backward compatible with the existing * maps, we interpret a second colon as a slash. "parse_nfs: Memory allocation failed");
"parse_nfs: illegal nfs url syntax: %s",
"parse_nfs: Memory allocation failed");
* We check host_cnt to make sure we haven't parsed an entry * with no host information. "parse_nfs: invalid host specified - bad entry " * A device name that begins with a slash could * be confused with a mountpoint path, hence use * a colon to escape a device string that begins * would confuse the parser. The second instance * get_dir_from_path(char *dir, char **path, int dirsz) * gets the directory name dir from path for max string of length dirsz. * A modification of the getword routine. Assumes the delimiter is '/' * and that excess /'s are redundant. * Returns PARSE_OK or PARSE_ERROR "get_dir_from_path: invalid directory size %d",
dirsz);
/* get rid of leading /'s in path */ /* now at a word or at the end of path */ "get_dir_from_path: max pathlength exceeded %d",
dirsz);
/* get rid of trailing /'s in path */ * alloc_hiernode(hiernode **newnode, char *dirname) * allocates a new hiernode corresponding to a new directory entry * in the hierarchical structure, and passes a pointer to it back * to the calling program. * Returns PARSE_OK or appropriate error value. * free_hiernode(hiernode *node) * frees the allocated hiernode given the head of the structure * recursively calls itself until it frees entire structure. * free_mapent(struct mapent *) * free the mapentry and its fields * trace_mapents(struct mapent *mapents) * traces through the mapentry structure and prints it element by element trace_prt(
1,
"\t mntlevel=%d\t%s\t%s err=%d\n",
* trace_hierarchy(hiernode *node) * traces the allocated hiernode given the head of the structure * recursively calls itself until it traces entire structure. * the first call made at the root is made with a zero level. * nodelevel is simply used to print tab and make the tracing clean. struct mnttab mb;
/* needed for hasmntopt() to get nfs version */ /* check for special case: host is me */ /* initialize mntlevel and modify */ " do_mapent_hosts: self-host %s OK\n",
host);
* Call pingnfs. Note that we can't have replicated hosts in /net. * XXX - we would like to avoid duplicating the across the wire calls * made here in nfsmount(). The pingnfs cache should help avoid it. /* get export list of host */ "do_mapent_hosts: %s: export list: %s",
gettext(
" getmapent_hosts: null export list\n"));
/* now sort by length of names - to get mount order right */ /* disregard duplicate entry */ * The following ugly chunk of code crept in as * a result of cachefs. If it's a cachefs mount * of an nfs filesystem, then have it handled as * an nfs mount but have cachefs do the mount. (p[
len] ==
'\0' || p[
len] ==
',')) {
/* Now create a mapent from the export list */ /* initialize mntlevel and modify values */ static const char uatfs_err[] =
"submount under fstype=autofs not supported";
* dump_mapent_err(struct mapent *me, char *key, char *mapname) * syslog appropriate error in mapentries. "map=%s key=%s mntpnt=%s: no error");
"mountpoint %s in map %s key %s not mounted: %s",
"map=%s key=%s mntpnt=%s: unknown mapentry error");