2 * Copyright (c) 1997-2006 Erez Zadok
3 * Copyright (c) 1989 Jan-Simon Pendry
4 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1989 The Regents of the University of California.
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgment:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * File: am-utils/amd/mapc.c
50 #endif /* HAVE_CONFIG_H */
55 * Make a duplicate reference to an existing map
57 #define mapc_dup(m) ((m)->refc++, (m))
61 * default, none, incremental, all, regexp
62 * MAPC_RE implies MAPC_ALL and must be numerically
65 #define MAPC_DFLT 0x000
66 #define MAPC_NONE 0x001
67 #define MAPC_INC 0x002
68 #define MAPC_ROOT 0x004
69 #define MAPC_ALL 0x010
70 #define MAPC_CACHE_MASK 0x0ff
71 #define MAPC_SYNC 0x100
74 # define MAPC_RE 0x020
75 # define MAPC_ISRE(m) ((m)->alloc == MAPC_RE)
76 #else /* not HAVE_REGEXEC */
77 # define MAPC_ISRE(m) FALSE
78 #endif /* not HAVE_REGEXEC */
87 static struct opt_tab mapc_opt[] =
90 {"default", MAPC_DFLT},
92 {"mapdefault", MAPC_DFLT},
97 #endif /* HAVE_REGEXEC */
105 static char wildcard[] = "*";
110 typedef struct map_type map_type;
112 char *name; /* Name of this map type */
113 init_fn *init; /* Initialization */
114 reload_fn *reload; /* Reload or fill */
115 isup_fn *isup; /* Is service up or not? (1=up, 0=down) */
116 search_fn *search; /* Search for new entry */
117 mtime_fn *mtime; /* Find modify time */
118 int def_alloc; /* Default allocation mode */
124 static mnt_map *root_map;
129 qelem map_list_head = {&map_list_head, &map_list_head};
135 /* forward definitions */
136 static const char *get_full_path(const char *map, const char *path, const char *type);
137 static int mapc_meta_search(mnt_map *, char *, char **, int);
138 static void mapc_sync(mnt_map *);
139 static void mapc_clear(mnt_map *);
142 static int root_init(mnt_map *, char *, time_t *);
145 static int error_init(mnt_map *, char *, time_t *);
146 static int error_reload(mnt_map *, char *, add_fn *);
147 static int error_search(mnt_map *, char *, char *, char **, time_t *);
148 static int error_mtime(mnt_map *, char *, time_t *);
151 #ifdef HAVE_MAP_PASSWD
152 extern int passwd_init(mnt_map *, char *, time_t *);
153 extern int passwd_search(mnt_map *, char *, char *, char **, time_t *);
154 #endif /* HAVE_MAP_PASSWD */
157 #ifdef HAVE_MAP_HESIOD
158 extern int amu_hesiod_init(mnt_map *, char *map, time_t *tp);
159 extern int hesiod_isup(mnt_map *, char *);
160 extern int hesiod_search(mnt_map *, char *, char *, char **, time_t *);
161 #endif /* HAVE_MAP_HESIOD */
165 extern int amu_ldap_init(mnt_map *, char *map, time_t *tp);
166 extern int amu_ldap_search(mnt_map *, char *, char *, char **, time_t *);
167 extern int amu_ldap_mtime(mnt_map *, char *, time_t *);
168 #endif /* HAVE_MAP_LDAP */
171 #ifdef HAVE_MAP_UNION
172 extern int union_init(mnt_map *, char *, time_t *);
173 extern int union_search(mnt_map *, char *, char *, char **, time_t *);
174 extern int union_reload(mnt_map *, char *, add_fn *);
175 #endif /* HAVE_MAP_UNION */
177 /* Network Information Service PLUS (NIS+) */
178 #ifdef HAVE_MAP_NISPLUS
179 extern int nisplus_init(mnt_map *, char *, time_t *);
180 extern int nisplus_reload(mnt_map *, char *, add_fn *);
181 extern int nisplus_search(mnt_map *, char *, char *, char **, time_t *);
182 extern int nisplus_mtime(mnt_map *, char *, time_t *);
183 #endif /* HAVE_MAP_NISPLUS */
185 /* Network Information Service (YP, Yellow Pages) */
187 extern int nis_init(mnt_map *, char *, time_t *);
188 extern int nis_reload(mnt_map *, char *, add_fn *);
189 extern int nis_isup(mnt_map *, char *);
190 extern int nis_search(mnt_map *, char *, char *, char **, time_t *);
191 extern int nis_mtime(mnt_map *, char *, time_t *);
192 #endif /* HAVE_MAP_NIS */
196 extern int ndbm_init(mnt_map *, char *, time_t *);
197 extern int ndbm_search(mnt_map *, char *, char *, char **, time_t *);
198 extern int ndbm_mtime(mnt_map *, char *, time_t *);
199 #endif /* HAVE_MAP_NDBM */
201 /* EXECUTABLE MAPS */
203 extern int exec_init(mnt_map *, char *, time_t *);
204 extern int exec_search(mnt_map *, char *, char *, char **, time_t *);
205 #endif /* HAVE_MAP_EXEC */
209 extern int file_init_or_mtime(mnt_map *, char *, time_t *);
210 extern int file_reload(mnt_map *, char *, add_fn *);
211 extern int file_search(mnt_map *, char *, char *, char **, time_t *);
212 #endif /* HAVE_MAP_FILE */
215 /* note that the choice of MAPC_{INC,ALL} will affect browsable_dirs */
216 static map_type maptypes[] =
222 NULL, /* isup function */
227 #ifdef HAVE_MAP_PASSWD
232 NULL, /* isup function */
237 #endif /* HAVE_MAP_PASSWD */
238 #ifdef HAVE_MAP_HESIOD
243 hesiod_isup, /* is Hesiod up or not? */
248 #endif /* HAVE_MAP_HESIOD */
254 NULL, /* isup function */
259 #endif /* HAVE_MAP_LDAP */
260 #ifdef HAVE_MAP_UNION
265 NULL, /* isup function */
270 #endif /* HAVE_MAP_UNION */
271 #ifdef HAVE_MAP_NISPLUS
276 NULL, /* isup function */
281 #endif /* HAVE_MAP_NISPLUS */
287 nis_isup, /* is NIS up or not? */
292 #endif /* HAVE_MAP_NIS */
298 NULL, /* isup function */
303 #endif /* HAVE_MAP_NDBM */
309 NULL, /* isup function */
314 #endif /* HAVE_MAP_FILE */
320 NULL, /* isup function */
325 #endif /* HAVE_MAP_EXEC */
330 NULL, /* isup function */
346 for (i = 0; (j = *key++); i += j) ;
353 mapc_showtypes(char *buf, size_t l)
355 map_type *mt=NULL, *lastmt;
358 i = sizeof(maptypes) / sizeof(maptypes[0]);
359 lastmt = maptypes + i;
361 for (mt = maptypes; mt < lastmt; mt++) {
362 xstrlcat(buf, mt->name, l);
363 if (mt == (lastmt-1))
364 break; /* if last one, don't do xstrlcat's that follows */
365 linesize += strlen(mt->name);
367 xstrlcat(buf, ", ", l);
372 xstrlcat(buf, "\n\t\t ", l);
379 * Check if a map of a certain type exists.
380 * Return 1 (true) if exists, 0 (false) if not.
383 mapc_type_exists(const char *type)
390 mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]);
392 if (STREQ(type, mt->name))
395 return 0; /* not found anywhere */
400 * Add key and val to the map m.
401 * key and val are assumed to be safe copies
404 mapc_add_kv(mnt_map *m, char *key, char *val)
408 int hash = kvhash_of(key);
411 #endif /* HAVE_REGEXEC */
413 dlog("add_kv: %s -> %s", key, val);
417 char pattern[MAXPATHLEN];
421 * Make sure the string is bound to the start and end
423 xsnprintf(pattern, sizeof(pattern), "^%s$", key);
424 retval = regcomp(&re, pattern, REG_ICASE);
428 /* XXX: this code was recently ported, and must be tested -Erez */
430 regerror(retval, &re, errstr, 256);
431 plog(XLOG_USER, "error compiling RE \"%s\": %s", pattern, errstr);
435 #endif /* HAVE_REGEXEC */
437 h = &m->kvhash[hash];
438 n = ALLOC(struct kv);
441 memcpy(&n->re, &re, sizeof(regex_t));
442 #endif /* HAVE_REGEXEC */
450 mapc_repl_kv(mnt_map *m, char *key, char *val)
455 * Compute the hash table offset
457 k = m->kvhash[kvhash_of(key)];
460 * Scan the linked list for the key
462 while (k && !FSTREQ(k->key, key))
469 mapc_add_kv(m, key, val);
475 * Search a map for a key.
476 * Calls map specific search routine.
477 * While map is out of date, keep re-syncing.
480 search_map(mnt_map *m, char *key, char **valp)
485 rc = (*m->search) (m, m->map_name, key, valp, &m->modify);
487 plog(XLOG_MAP, "Re-synchronizing cache for map %s", m->map_name);
497 * Do a wildcard lookup in the map and
501 mapc_find_wildcard(mnt_map *m)
504 * Attempt to find the wildcard entry
506 int rc = search_map(m, wildcard, &m->wildcard);
515 * Attempt to reload without losing current data by switching the hashes
517 * If reloading was needed and succeeded, return 1; else return 0.
520 mapc_reload_map(mnt_map *m)
523 kv *maphash[NKVHASH], *tmphash[NKVHASH];
526 error = (*m->mtime) (m, m->map_name, &t);
532 * skip reloading maps that have not been modified, unless
533 * amq -f was used (do_mapc_reload is 0)
535 if (m->reloads != 0 && do_mapc_reload != 0) {
536 if (t <= m->modify) {
537 plog(XLOG_INFO, "reload of map %s is not needed (in sync)", m->map_name);
538 dlog("map %s last load time is %d, last modify time is %d",
539 m->map_name, (int) m->modify, (int) t);
544 /* copy the old hash and zero the map */
545 memcpy((voidp) maphash, (voidp) m->kvhash, sizeof(m->kvhash));
546 memset((voidp) m->kvhash, 0, sizeof(m->kvhash));
548 dlog("calling map reload on %s", m->map_name);
549 error = (*m->reload) (m, m->map_name, mapc_add_kv);
552 plog(XLOG_FATAL, "first time load of map %s failed!", m->map_name);
554 plog(XLOG_ERROR, "reload of map %s failed - using old values",
557 memcpy((voidp) m->kvhash, (voidp) maphash, sizeof(m->kvhash));
559 if (m->reloads++ == 0)
560 plog(XLOG_INFO, "first time load of map %s succeeded", m->map_name);
562 plog(XLOG_INFO, "reload #%d of map %s succeeded",
563 m->reloads, m->map_name);
564 memcpy((voidp) tmphash, (voidp) m->kvhash, sizeof(m->kvhash));
565 memcpy((voidp) m->kvhash, (voidp) maphash, sizeof(m->kvhash));
567 memcpy((voidp) m->kvhash, (voidp) tmphash, sizeof(m->kvhash));
573 dlog("calling mapc_search for wildcard");
574 error = mapc_search(m, wildcard, &m->wildcard);
585 mapc_create(char *map, char *opt, const char *type)
587 mnt_map *m = ALLOC(struct mnt_map);
592 cmdoption(opt, mapc_opt, &alloc);
595 * If using a configuration file, and the map_type is defined, then look
596 * for it, in the maptypes array. If found, initialize the map using that
597 * map_type. If not found, return error. If no map_type was defined,
598 * default to cycling through all maptypes.
600 if (use_conf_file && type) {
601 /* find what type of map this one is */
603 mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]);
605 if (STREQ(type, mt->name)) {
606 plog(XLOG_INFO, "initializing amd.conf map %s of type %s", map, type);
607 if ((*mt->init) (m, map, &modify) == 0) {
610 plog(XLOG_ERROR, "failed to initialize map %s", map);
611 error_init(m, map, &modify);
615 } /* end of "for (mt =" loop */
617 } else { /* cycle through all known maptypes */
620 * not using amd conf file or using it by w/o specifying map type
623 mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]);
625 dlog("trying to initialize map %s of type %s ...", map, mt->name);
626 if ((*mt->init) (m, map, &modify) == 0) {
630 } /* end of "if (use_conf_file && (colpos = strchr ..." statement */
632 /* assert: mt in maptypes */
634 m->flags = alloc & ~MAPC_CACHE_MASK;
635 alloc &= MAPC_CACHE_MASK;
637 if (alloc == MAPC_DFLT)
638 alloc = mt->def_alloc;
642 plog(XLOG_USER, "Ambiguous map cache type \"%s\"; using \"inc\"", opt);
644 /* fall-through... */
652 * If there is no support for reload and it was requested
653 * then back off to incremental instead.
655 if (mt->reload == error_reload) {
656 plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"all\"; using \"inc\"", mt->name);
663 if (mt->reload == error_reload) {
664 plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"re\"", mt->name);
665 mt = &maptypes[sizeof(maptypes) / sizeof(maptypes[0]) - 1];
666 /* assert: mt->name == "error" */
669 #endif /* HAVE_REGEXEC */
672 dlog("Map for %s coming from maptype %s", map, mt->name);
675 m->reload = mt->reload;
678 m->search = alloc >= MAPC_ALL ? error_search : mt->search;
679 m->mtime = mt->mtime;
680 memset((voidp) m->kvhash, 0, sizeof(m->kvhash));
681 m->map_name = strdup(map);
685 /* Unfortunately with current code structure, this cannot be initialized here */
689 * synchronize cache with reality
698 * Free the cached data in a map
701 mapc_clear(mnt_map *m)
706 * For each of the hash slots, chain
707 * along free'ing the data.
709 for (i = 0; i < NKVHASH; i++) {
710 kv *k = m->kvhash[i];
722 * Zero the hash slots
724 memset((voidp) m->kvhash, 0, sizeof(m->kvhash));
727 * Free the wildcard if it exists
737 * Find a map, or create one if it does not exist
740 mapc_find(char *map, char *opt, const char *maptype)
745 * Search the list of known maps to see if
746 * it has already been loaded. If it is found
747 * then return a duplicate reference to it.
748 * Otherwise make a new map as required and
749 * add it to the list of maps
751 ITER(m, mnt_map, &map_list_head)
752 if (STREQ(m->map_name, map))
754 m = mapc_create(map, opt, maptype);
755 ins_que(&m->hdr, &map_list_head);
765 mapc_free(opaque_t arg)
767 mnt_map *m = (mnt_map *) arg;
770 * Decrement the reference count.
771 * If the reference count hits zero
772 * then throw the map away.
774 if (m && --m->refc == 0) {
784 * Search the map for the key. Put a safe (malloc'ed) copy in *pval or
785 * return an error code
788 mapc_meta_search(mnt_map *m, char *key, char **pval, int recurse)
797 plog(XLOG_ERROR, "Null map request for %s", key);
801 if (m->flags & MAPC_SYNC) {
806 error = (*m->mtime) (m, m->map_name, &t);
807 if (error || t > m->modify) {
808 plog(XLOG_INFO, "Map %s is out of date", m->map_name);
815 * Compute the hash table offset
817 k = m->kvhash[kvhash_of(key)];
820 * Scan the linked list for the key
822 while (k && !FSTREQ(k->key, key))
828 else if (recurse == MREC_FULL) {
830 * Try for an RE match against the entire map.
831 * Note that this will be done in a "random"
836 for (i = 0; i < NKVHASH; i++) {
841 /* XXX: this code was recently ported, and must be tested -Erez */
842 retval = regexec(&k->re, key, 0, 0, 0);
843 if (retval == 0) { /* succeeded */
845 } else { /* failed to match, log error */
849 regerror(retval, &k->re, errstr, 256);
850 plog(XLOG_USER, "error matching RE \"%s\" against \"%s\": %s",
851 key, k->key, errstr);
859 #endif /* HAVE_REGEXEC */
862 * If found then take a copy
866 *pval = strdup(k->val);
869 } else if (m->alloc >= MAPC_ALL) {
871 * If the entire map is cached then this
872 * key does not exist.
877 * Otherwise search the map. If we are
878 * in incremental mode then add the key
881 error = search_map(m, key, pval);
882 if (!error && m->alloc == MAPC_INC)
883 mapc_add_kv(m, strdup(key), strdup(*pval));
887 * If an error, and a wildcard exists,
888 * and the key is not internal then
889 * return a copy of the wildcard.
892 if (recurse == MREC_FULL && !MAPC_ISRE(m)) {
893 char wildname[MAXPATHLEN];
898 * Keep chopping sub-directories from the RHS
899 * and replacing with "/ *" and repeat the lookup.
901 * "src/gnu/gcc" -> "src / gnu / *" -> "src / *"
903 xstrlcpy(wildname, key, sizeof(wildname));
904 while (error && (subp = strrchr(wildname, '/'))) {
906 * sizeof space left in subp is sizeof wildname minus what's left
907 * after the strchr above returned a pointer inside wildname into
910 xstrlcpy(subp, "/*", sizeof(wildname) - (subp - wildname));
911 dlog("mapc recurses on %s", wildname);
912 error = mapc_meta_search(m, wildname, pval, MREC_PART);
917 if (error > 0 && m->wildcard) {
918 *pval = strdup(m->wildcard);
928 mapc_search(mnt_map *m, char *key, char **pval)
930 return mapc_meta_search(m, key, pval, MREC_FULL);
935 * Get map cache in sync with physical representation
938 mapc_sync(mnt_map *m)
940 int need_mtime_update = 0;
942 if (m->alloc == MAPC_ROOT)
943 return; /* nothing to do */
945 /* do not clear map if map service is down */
947 if (!((*m->isup)(m, m->map_name))) {
948 plog(XLOG_ERROR, "mapc_sync: map %s is down: not clearing map", m->map_name);
953 if (m->alloc >= MAPC_ALL) {
954 /* mapc_reload_map() always works */
955 need_mtime_update = mapc_reload_map(m);
959 * Attempt to find the wildcard entry
961 mapc_find_wildcard(m);
962 need_mtime_update = 1; /* because mapc_clear always works */
966 * To be safe, update the mtime of the mnt_map's own node, so that the
967 * kernel will flush all of its cached entries.
969 if (need_mtime_update && m->cfm) {
970 am_node *mp = find_ap(m->cfm->cfm_dir);
972 clocktime(&mp->am_fattr.na_mtime);
974 plog(XLOG_ERROR, "cannot find map %s to update its mtime",
982 * Reload all the maps
983 * Called when Amd gets hit by a SIGHUP.
992 * Throw away the existing information.
996 ITER(m, mnt_map, &map_list_head)
1003 * The root map is used to bootstrap amd.
1004 * All the require top-level mounts are added
1005 * into the root map and then the map is iterated
1006 * and a lookup is done on all the mount points.
1007 * This causes the top level mounts to be automounted.
1010 root_init(mnt_map *m, char *map, time_t *tp)
1012 *tp = clocktime(NULL);
1013 return STREQ(map, ROOT_MAP) ? 0 : ENOENT;
1018 * Add a new entry to the root map
1020 * dir - directory (key)
1021 * opts - mount options
1023 * cfm - optional amd configuration file map section structure
1026 root_newmap(const char *dir, const char *opts, const char *map, const cf_map_t *cfm)
1028 char str[MAXPATHLEN];
1031 * First make sure we have a root map to talk about...
1034 root_map = mapc_find(ROOT_MAP, "mapdefault", NULL);
1037 * Then add the entry...
1041 * Here I plug in the code to process other amd.conf options like
1042 * map_type, search_path, and flags (browsable_dirs, mount_type).
1047 xsnprintf(str, sizeof(str),
1048 "cache:=mapdefault;type:=toplvl;mount_type:=%s;fs:=\"%s\"",
1049 cfm->cfm_flags & CFM_MOUNT_TYPE_AUTOFS ? "autofs" : "nfs",
1050 get_full_path(map, cfm->cfm_search_path, cfm->cfm_type));
1051 if (opts && opts[0] != '\0') {
1052 xstrlcat(str, ";", sizeof(str));
1053 xstrlcat(str, opts, sizeof(str));
1055 if (cfm->cfm_flags & CFM_BROWSABLE_DIRS_FULL)
1056 xstrlcat(str, ";opts:=rw,fullybrowsable", sizeof(str));
1057 if (cfm->cfm_flags & CFM_BROWSABLE_DIRS)
1058 xstrlcat(str, ";opts:=rw,browsable", sizeof(str));
1059 if (cfm->cfm_type) {
1060 xstrlcat(str, ";maptype:=", sizeof(str));
1061 xstrlcat(str, cfm->cfm_type, sizeof(str));
1064 xstrlcpy(str, opts, sizeof(str));
1068 xsnprintf(str, sizeof(str),
1069 "cache:=mapdefault;type:=toplvl;fs:=\"%s\";%s",
1070 map, opts ? opts : "");
1072 xstrlcpy(str, opts, sizeof(str));
1074 mapc_repl_kv(root_map, strdup((char *)dir), strdup(str));
1079 mapc_keyiter(mnt_map *m, key_fun *fn, opaque_t arg)
1084 for (i = 0; i < NKVHASH; i++) {
1085 kv *k = m->kvhash[i];
1087 (*fn) (k->key, arg);
1098 * Iterate on the root map and call (*fn)() on the key of all the nodes.
1099 * Returns the number of entries in the root map.
1102 root_keyiter(key_fun *fn, opaque_t arg)
1105 int c = mapc_keyiter(root_map, fn, arg);
1117 error_init(mnt_map *m, char *map, time_t *tp)
1119 plog(XLOG_USER, "No source data for map %s", map);
1127 error_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp)
1134 error_reload(mnt_map *m, char *map, add_fn *fn)
1141 error_mtime(mnt_map *m, char *map, time_t *tp)
1150 * Return absolute path of map, searched in a type-specific path.
1151 * Note: uses a static buffer for returned data.
1154 get_full_path(const char *map, const char *path, const char *type)
1156 char component[MAXPATHLEN], *str;
1157 static char full_path[MAXPATHLEN];
1160 /* for now, only file-type search paths are implemented */
1161 if (type && !STREQ(type, "file"))
1164 /* if null map, return it */
1168 /* if map includes a '/', return it (absolute or relative path) */
1169 if (strchr(map, '/'))
1172 /* if path is empty, return map */
1176 /* now break path into components, and search in each */
1177 xstrlcpy(component, path, sizeof(component));
1179 str = strtok(component, ":");
1181 xstrlcpy(full_path, str, sizeof(full_path));
1182 len = strlen(full_path);
1183 if (full_path[len - 1] != '/') /* add trailing "/" if needed */
1184 xstrlcat(full_path, "/", sizeof(full_path));
1185 xstrlcat(full_path, map, sizeof(full_path));
1186 if (access(full_path, R_OK) == 0)
1188 str = strtok(NULL, ":");
1191 return map; /* if found nothing, return map */