| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include "automount.h" |
| #include "nsswitch.h" |
| |
| static void nsslist_cleanup(void *arg) |
| { |
| struct list_head *nsslist = (struct list_head *) arg; |
| if (!list_empty(nsslist)) |
| free_sources(nsslist); |
| return; |
| } |
| |
| static int do_read_master(struct master *master, char *type, time_t age) |
| { |
| struct lookup_mod *lookup; |
| const char *argv[2]; |
| int argc; |
| int status; |
| |
| argc = 1; |
| argv[0] = master->name; |
| argv[1] = NULL; |
| |
| status = open_lookup(type, "", NULL, argc, argv, &lookup); |
| if (status != NSS_STATUS_SUCCESS) |
| return status; |
| |
| status = lookup->lookup_read_master(master, age, lookup->context); |
| |
| close_lookup(lookup); |
| |
| return status; |
| } |
| |
| static char *find_map_path(struct autofs_point *ap, struct map_source *map) |
| { |
| const char *mname = map->argv[0]; |
| unsigned int mlen = strlen(mname); |
| char *tok, *ptr = NULL; |
| char *path = NULL; |
| char *search_path; |
| struct stat st; |
| |
| |
| |
| |
| |
| |
| |
| |
| search_path = strdup(AUTOFS_MAP_DIR); |
| if (map->flags & MAP_FLAG_FORMAT_AMD) { |
| struct autofs_point *pap = ap; |
| char *tmp; |
| |
| |
| |
| |
| |
| |
| while (pap->parent) |
| pap = pap->parent; |
| tmp = conf_amd_get_search_path(pap->path); |
| if (tmp) { |
| if (search_path) |
| free(search_path); |
| search_path = tmp; |
| } |
| } |
| if (!search_path) |
| return NULL; |
| |
| tok = strtok_r(search_path, ":", &ptr); |
| while (tok) { |
| char *this = malloc(strlen(tok) + mlen + 2); |
| if (!this) { |
| free(search_path); |
| return NULL; |
| } |
| strcpy(this, tok); |
| strcat(this, "/"); |
| strcat(this, mname); |
| if (!stat(this, &st)) { |
| path = this; |
| break; |
| } |
| free(this); |
| tok = strtok_r(NULL, ":", &ptr); |
| } |
| |
| free(search_path); |
| return path; |
| } |
| |
| static int read_master_map(struct master *master, char *type, time_t age) |
| { |
| unsigned int logopt = master->logopt; |
| char *path, *save_name; |
| int result; |
| |
| if (strcasecmp(type, "files")) { |
| return do_read_master(master, type, age); |
| } |
| |
| |
| |
| |
| |
| |
| |
| if (strchr(master->name, '/')) { |
| error(logopt, "relative path invalid in files map name"); |
| return NSS_STATUS_NOTFOUND; |
| } |
| |
| path = malloc(strlen(AUTOFS_MAP_DIR) + strlen(master->name) + 2); |
| if (!path) |
| return NSS_STATUS_UNKNOWN; |
| |
| strcpy(path, AUTOFS_MAP_DIR); |
| strcat(path, "/"); |
| strcat(path, master->name); |
| |
| save_name = master->name; |
| master->name = path; |
| |
| result = do_read_master(master, type, age); |
| |
| master->name = save_name; |
| free(path); |
| |
| return result; |
| } |
| |
| int lookup_nss_read_master(struct master *master, time_t age) |
| { |
| unsigned int logopt = master->logopt; |
| struct list_head nsslist; |
| struct list_head *head, *p; |
| int result = NSS_STATUS_UNKNOWN; |
| |
| |
| if (*master->name == '/') { |
| if (*(master->name + 1) == '/') { |
| debug(logopt, "reading master ldap %s", master->name); |
| result = do_read_master(master, "ldap", age); |
| } else { |
| debug(logopt, "reading master file %s", master->name); |
| result = do_read_master(master, "file", age); |
| } |
| |
| if (result == NSS_STATUS_UNAVAIL) |
| master->read_fail = 1; |
| |
| return result; |
| } else { |
| char *name = master->name; |
| char *tmp; |
| |
| |
| tmp = strchr(name, ':'); |
| if (tmp) { |
| char source[10]; |
| |
| memset(source, 0, 10); |
| if ((!strncmp(name, "file", 4) && |
| (name[4] == ',' || name[4] == ':')) || |
| (!strncmp(name, "yp", 2) && |
| (name[2] == ',' || name[2] == ':')) || |
| (!strncmp(name, "nis", 3) && |
| (name[3] == ',' || name[3] == ':')) || |
| (!strncmp(name, "nisplus", 7) && |
| (name[7] == ',' || name[7] == ':')) || |
| (!strncmp(name, "ldap", 4) && |
| (name[4] == ',' || name[4] == ':')) || |
| (!strncmp(name, "ldaps", 5) && |
| (name[5] == ',' || name[5] == ':')) || |
| (!strncmp(name, "sss", 3) || |
| (name[3] == ',' || name[3] == ':')) || |
| (!strncmp(name, "dir", 3) && |
| (name[3] == ',' || name[3] == ':'))) { |
| strncpy(source, name, tmp - name); |
| |
| |
| |
| |
| |
| |
| if (strncmp(name, "ldap", 4)) { |
| master->name = tmp + 1; |
| debug(logopt, "reading master %s %s", |
| source, master->name); |
| } else { |
| master->name = name; |
| debug(logopt, "reading master %s %s", |
| source, tmp + 1); |
| } |
| |
| result = do_read_master(master, source, age); |
| master->name = name; |
| |
| if (result == NSS_STATUS_UNAVAIL) |
| master->read_fail = 1; |
| |
| return result; |
| } |
| } |
| } |
| |
| INIT_LIST_HEAD(&nsslist); |
| |
| result = nsswitch_parse(&nsslist); |
| if (result) { |
| if (!list_empty(&nsslist)) |
| free_sources(&nsslist); |
| error(logopt, "can't to read name service switch config."); |
| return NSS_STATUS_UNAVAIL; |
| } |
| |
| |
| result = NSS_STATUS_SUCCESS; |
| head = &nsslist; |
| list_for_each(p, head) { |
| struct nss_source *this; |
| int status; |
| |
| this = list_entry(p, struct nss_source, list); |
| |
| if (strncmp(this->source, "files", 5) && |
| strncmp(this->source, "nis", 3) && |
| strncmp(this->source, "nisplus", 7) && |
| strncmp(this->source, "ldap", 4) && |
| strncmp(this->source, "sss", 3)) |
| continue; |
| |
| debug(logopt, |
| "reading master %s %s", this->source, master->name); |
| |
| result = read_master_map(master, this->source, age); |
| |
| |
| |
| |
| |
| |
| |
| |
| if (result != NSS_STATUS_SUCCESS && |
| !master->depth && !defaults_master_set()) { |
| char *tmp = strchr(master->name, '.'); |
| if (tmp) { |
| debug(logopt, |
| "%s not found, replacing '.' with '_'", |
| master->name); |
| *tmp = '_'; |
| result = read_master_map(master, this->source, age); |
| if (result != NSS_STATUS_SUCCESS) |
| *tmp = '.'; |
| } |
| } |
| |
| |
| if (result == NSS_STATUS_TRYAGAIN) { |
| result = NSS_STATUS_SUCCESS; |
| continue; |
| } |
| |
| if (result == NSS_STATUS_UNKNOWN || |
| result == NSS_STATUS_NOTFOUND) { |
| debug(logopt, "no map - continuing to next source"); |
| result = NSS_STATUS_SUCCESS; |
| continue; |
| } |
| |
| if (result == NSS_STATUS_UNAVAIL) |
| master->read_fail = 1; |
| |
| status = check_nss_result(this, result); |
| if (status >= 0) { |
| free_sources(&nsslist); |
| return status; |
| } |
| } |
| |
| if (!list_empty(&nsslist)) |
| free_sources(&nsslist); |
| |
| return result; |
| } |
| |
| static int do_read_map(struct autofs_point *ap, struct map_source *map, time_t age) |
| { |
| struct lookup_mod *lookup; |
| int status; |
| |
| lookup = NULL; |
| master_source_writelock(ap->entry); |
| if (!map->lookup) { |
| status = open_lookup(map->type, "", map->format, |
| map->argc, map->argv, &lookup); |
| if (status != NSS_STATUS_SUCCESS) { |
| master_source_unlock(ap->entry); |
| debug(ap->logopt, |
| "lookup module %s open failed", map->type); |
| return status; |
| } |
| map->lookup = lookup; |
| } else { |
| lookup = map->lookup; |
| status = lookup->lookup_reinit(map->format, |
| map->argc, map->argv, |
| &lookup->context); |
| if (status) |
| warn(ap->logopt, |
| "lookup module %s reinit failed", map->type); |
| } |
| master_source_unlock(ap->entry); |
| |
| if (!map->stale) |
| return NSS_STATUS_SUCCESS; |
| |
| master_source_current_wait(ap->entry); |
| ap->entry->current = map; |
| |
| status = lookup->lookup_read_map(ap, age, lookup->context); |
| |
| if (status != NSS_STATUS_SUCCESS) |
| map->stale = 0; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if (status == NSS_STATUS_UNKNOWN || |
| (ap->type == LKP_INDIRECT && status == NSS_STATUS_UNAVAIL)) |
| return NSS_STATUS_SUCCESS; |
| |
| return status; |
| } |
| |
| static int read_file_source_instance(struct autofs_point *ap, struct map_source *map, time_t age) |
| { |
| struct map_source *instance; |
| char src_file[] = "file"; |
| char src_prog[] = "program"; |
| struct stat st; |
| char *type, *format; |
| |
| if (stat(map->argv[0], &st) == -1) { |
| warn(ap->logopt, "file map %s not found", map->argv[0]); |
| return NSS_STATUS_NOTFOUND; |
| } |
| |
| if (!S_ISREG(st.st_mode)) |
| return NSS_STATUS_NOTFOUND; |
| |
| if (st.st_mode & __S_IEXEC) |
| type = src_prog; |
| else |
| type = src_file; |
| |
| format = map->format; |
| |
| instance = master_find_source_instance(map, type, format, 0, NULL); |
| if (!instance) { |
| int argc = map->argc; |
| const char **argv = map->argv; |
| instance = master_add_source_instance(map, type, format, age, argc, argv); |
| if (!instance) |
| return NSS_STATUS_UNAVAIL; |
| instance->recurse = map->recurse; |
| instance->depth = map->depth; |
| } |
| instance->stale = map->stale; |
| |
| return do_read_map(ap, instance, age); |
| } |
| |
| static int read_source_instance(struct autofs_point *ap, struct map_source *map, const char *type, time_t age) |
| { |
| struct map_source *instance; |
| const char *format; |
| |
| format = map->format; |
| |
| instance = master_find_source_instance(map, type, format, 0, NULL); |
| if (!instance) { |
| int argc = map->argc; |
| const char **argv = map->argv; |
| instance = master_add_source_instance(map, type, format, age, argc, argv); |
| if (!instance) |
| return NSS_STATUS_UNAVAIL; |
| instance->recurse = map->recurse; |
| instance->depth = map->depth; |
| } |
| instance->stale = map->stale; |
| |
| return do_read_map(ap, instance, age); |
| } |
| |
| static void argv_cleanup(void *arg) |
| { |
| struct map_source *tmap = (struct map_source *) arg; |
| |
| free_argv(tmap->argc, tmap->argv); |
| return; |
| } |
| |
| static int lookup_map_read_map(struct autofs_point *ap, |
| struct map_source *map, time_t age) |
| { |
| char *path; |
| |
| if (!map->argv[0]) { |
| if (!strcmp(map->type, "hosts")) |
| return do_read_map(ap, map, age); |
| return NSS_STATUS_UNKNOWN; |
| } |
| |
| |
| |
| |
| |
| |
| if (strncmp(map->type, "file", 4)) |
| return do_read_map(ap, map, age); |
| |
| if (map->argv[0][0] == '/') |
| return do_read_map(ap, map, age); |
| |
| path = find_map_path(ap, map); |
| if (!path) |
| return NSS_STATUS_UNKNOWN; |
| |
| if (map->argc >= 1) { |
| if (map->argv[0]) |
| free((char *) map->argv[0]); |
| map->argv[0] = path; |
| } else { |
| error(ap->logopt, "invalid arguments for autofs_point"); |
| free(path); |
| return NSS_STATUS_UNKNOWN; |
| } |
| |
| return do_read_map(ap, map, age); |
| } |
| |
| static enum nsswitch_status read_map_source(struct nss_source *this, |
| struct autofs_point *ap, struct map_source *map, time_t age) |
| { |
| enum nsswitch_status result; |
| struct map_source tmap; |
| char *path; |
| |
| if (strcasecmp(this->source, "files")) { |
| return read_source_instance(ap, map, this->source, age); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| if (strchr(map->argv[0], '/')) { |
| error(ap->logopt, "relative path invalid in files map name"); |
| return NSS_STATUS_NOTFOUND; |
| } |
| |
| this->source[4] = '\0'; |
| tmap.flags = map->flags; |
| tmap.type = this->source; |
| tmap.format = map->format; |
| tmap.name = map->name; |
| tmap.lookup = map->lookup; |
| tmap.mc = map->mc; |
| tmap.instance = map->instance; |
| tmap.exp_timeout = map->exp_timeout; |
| tmap.recurse = map->recurse; |
| tmap.depth = map->depth; |
| tmap.stale = map->stale; |
| tmap.argc = 0; |
| tmap.argv = NULL; |
| |
| path = find_map_path(ap, map); |
| if (!path) |
| return NSS_STATUS_UNKNOWN; |
| |
| if (map->argc >= 1) { |
| tmap.argc = map->argc; |
| tmap.argv = copy_argv(map->argc, map->argv); |
| if (!tmap.argv) { |
| error(ap->logopt, "failed to copy args"); |
| free(path); |
| return NSS_STATUS_UNKNOWN; |
| } |
| if (tmap.argv[0]) |
| free((char *) tmap.argv[0]); |
| tmap.argv[0] = path; |
| } else { |
| error(ap->logopt, "invalid arguments for autofs_point"); |
| free(path); |
| return NSS_STATUS_UNKNOWN; |
| } |
| |
| pthread_cleanup_push(argv_cleanup, &tmap); |
| result = read_file_source_instance(ap, &tmap, age); |
| pthread_cleanup_pop(1); |
| |
| if (!map->instance) |
| map->instance = tmap.instance; |
| |
| return result; |
| } |
| |
| int lookup_nss_read_map(struct autofs_point *ap, struct map_source *source, time_t age) |
| { |
| struct master_mapent *entry = ap->entry; |
| struct list_head nsslist; |
| struct list_head *head, *p; |
| struct nss_source *this; |
| struct map_source *map; |
| enum nsswitch_status status; |
| unsigned int at_least_one = 0; |
| int result = 0; |
| |
| |
| |
| |
| |
| |
| if (source) |
| map = source; |
| else |
| map = entry->maps; |
| while (map) { |
| |
| if (!map->stale || entry->age > map->age) { |
| map = map->next; |
| continue; |
| } |
| |
| if (map->type) { |
| if (!strncmp(map->type, "multi", 5)) |
| debug(ap->logopt, "reading multi map"); |
| else |
| debug(ap->logopt, |
| "reading map %s %s", |
| map->type, map->argv[0]); |
| result = lookup_map_read_map(ap, map, age); |
| map = map->next; |
| continue; |
| } |
| |
| |
| if (map->argv && *map->argv[0] == '/') { |
| if (*(map->argv[0] + 1) == '/') { |
| char *tmp = strdup("ldap"); |
| if (!tmp) { |
| map = map->next; |
| continue; |
| } |
| map->type = tmp; |
| debug(ap->logopt, |
| "reading map %s %s", tmp, map->argv[0]); |
| result = do_read_map(ap, map, age); |
| } else { |
| debug(ap->logopt, |
| "reading map file %s", map->argv[0]); |
| result = read_file_source_instance(ap, map, age); |
| } |
| map = map->next; |
| continue; |
| } |
| |
| INIT_LIST_HEAD(&nsslist); |
| |
| pthread_cleanup_push(nsslist_cleanup, &nsslist); |
| status = nsswitch_parse(&nsslist); |
| pthread_cleanup_pop(0); |
| if (status) { |
| error(ap->logopt, |
| "can't to read name service switch config."); |
| result = 1; |
| break; |
| } |
| |
| pthread_cleanup_push(nsslist_cleanup, &nsslist); |
| head = &nsslist; |
| list_for_each(p, head) { |
| this = list_entry(p, struct nss_source, list); |
| |
| if (map->flags & MAP_FLAG_FORMAT_AMD && |
| !strcmp(this->source, "sss")) { |
| warn(ap->logopt, |
| "source sss is not available for amd maps."); |
| continue; |
| } |
| |
| debug(ap->logopt, |
| "reading map %s %s", this->source, map->argv[0]); |
| |
| result = read_map_source(this, ap, map, age); |
| if (result == NSS_STATUS_UNKNOWN) |
| continue; |
| |
| |
| |
| if (result == NSS_STATUS_UNAVAIL) |
| map->stale = 0; |
| |
| if (result == NSS_STATUS_SUCCESS) { |
| at_least_one = 1; |
| result = NSS_STATUS_TRYAGAIN; |
| } |
| |
| status = check_nss_result(this, result); |
| if (status >= 0) { |
| map = NULL; |
| break; |
| } |
| |
| result = NSS_STATUS_SUCCESS; |
| } |
| pthread_cleanup_pop(1); |
| |
| if (!map) |
| break; |
| |
| map = map->next; |
| } |
| |
| if (!result || at_least_one) |
| return 1; |
| |
| return 0; |
| } |
| |
| static char *make_browse_path(unsigned int logopt, |
| const char *root, const char *key, |
| const char *prefix) |
| { |
| unsigned int l_prefix; |
| unsigned int k_len, r_len; |
| char *k_start; |
| char *path; |
| |
| k_start = (char *) key; |
| k_len = strlen(key); |
| l_prefix = 0; |
| |
| if (prefix) { |
| l_prefix = strlen(prefix); |
| |
| if (l_prefix > k_len) |
| return NULL; |
| |
| |
| |
| |
| |
| if (strncmp(key, prefix, l_prefix)) |
| return NULL; |
| |
| |
| k_start += l_prefix; |
| } |
| |
| |
| if (strchr(k_start, '/')) |
| return NULL; |
| |
| r_len = strlen(root); |
| |
| if ((r_len + strlen(k_start)) > KEY_MAX_LEN) |
| return NULL; |
| |
| path = malloc(r_len + k_len + 2); |
| if (!path) { |
| warn(logopt, "failed to allocate full path"); |
| return NULL; |
| } |
| |
| sprintf(path, "%s/%s", root, k_start); |
| |
| return path; |
| } |
| |
| int lookup_ghost(struct autofs_point *ap, const char *root) |
| { |
| struct master_mapent *entry = ap->entry; |
| struct map_source *map; |
| struct mapent_cache *mc; |
| struct mapent *me; |
| char buf[MAX_ERR_BUF]; |
| struct stat st; |
| char *fullpath; |
| int ret; |
| |
| if (!strcmp(ap->path, "/-")) |
| return LKP_FAIL | LKP_DIRECT; |
| |
| if (!(ap->flags & MOUNT_FLAG_GHOST)) |
| return LKP_INDIRECT; |
| |
| pthread_cleanup_push(master_source_lock_cleanup, entry); |
| master_source_readlock(entry); |
| map = entry->maps; |
| while (map) { |
| |
| |
| |
| |
| if (entry->age > map->age) { |
| map = map->next; |
| continue; |
| } |
| |
| mc = map->mc; |
| pthread_cleanup_push(cache_lock_cleanup, mc); |
| cache_readlock(mc); |
| me = cache_enumerate(mc, NULL); |
| while (me) { |
| |
| |
| |
| |
| |
| if (!me->mapent) |
| goto next; |
| |
| |
| |
| |
| if (strchr(me->key, '*')) |
| goto next; |
| |
| |
| |
| |
| if (*me->key == '/') { |
| if (map->flags & MAP_FLAG_FORMAT_AMD) |
| goto next; |
| |
| |
| if (list_empty(&me->multi_list)) |
| error(ap->logopt, |
| "invalid key %s", me->key); |
| goto next; |
| } |
| |
| fullpath = make_browse_path(ap->logopt, |
| root, me->key, ap->pref); |
| if (!fullpath) |
| goto next; |
| |
| ret = stat(fullpath, &st); |
| if (ret == -1 && errno != ENOENT) { |
| char *estr = strerror_r(errno, buf, MAX_ERR_BUF); |
| warn(ap->logopt, "stat error %s", estr); |
| free(fullpath); |
| goto next; |
| } |
| |
| |
| if (!ret) { |
| |
| |
| |
| |
| debug(ap->logopt, "me->dev %d me->ino %d", me->dev, me->ino); |
| free(fullpath); |
| goto next; |
| } |
| |
| ret = mkdir_path(fullpath, mp_mode); |
| if (ret < 0 && errno != EEXIST) { |
| char *estr = strerror_r(errno, buf, MAX_ERR_BUF); |
| warn(ap->logopt, |
| "mkdir_path %s failed: %s", fullpath, estr); |
| free(fullpath); |
| goto next; |
| } |
| |
| if (stat(fullpath, &st) != -1) { |
| me->dev = st.st_dev; |
| me->ino = st.st_ino; |
| } |
| |
| free(fullpath); |
| next: |
| me = cache_enumerate(mc, me); |
| } |
| pthread_cleanup_pop(1); |
| map = map->next; |
| } |
| pthread_cleanup_pop(1); |
| |
| return LKP_INDIRECT; |
| } |
| |
| int do_lookup_mount(struct autofs_point *ap, struct map_source *map, const char *name, int name_len) |
| { |
| struct lookup_mod *lookup; |
| int status; |
| |
| if (!map->lookup) { |
| status = open_lookup(map->type, "", |
| map->format, map->argc, map->argv, &lookup); |
| if (status != NSS_STATUS_SUCCESS) { |
| debug(ap->logopt, |
| "lookup module %s open failed", map->type); |
| return status; |
| } |
| map->lookup = lookup; |
| } |
| |
| lookup = map->lookup; |
| |
| master_source_current_wait(ap->entry); |
| ap->entry->current = map; |
| |
| status = lookup->lookup_mount(ap, name, name_len, lookup->context); |
| |
| return status; |
| } |
| |
| static int lookup_amd_instance(struct autofs_point *ap, |
| struct map_source *map, |
| const char *name, int name_len) |
| { |
| struct map_source *instance; |
| struct amd_entry *entry; |
| const char *argv[2]; |
| const char **pargv = NULL; |
| int argc = 0; |
| struct mapent *me; |
| char *m_key; |
| |
| me = cache_lookup_distinct(map->mc, name); |
| if (!me || !me->multi) { |
| error(ap->logopt, "expected multi mount entry not found"); |
| return NSS_STATUS_UNKNOWN; |
| } |
| |
| m_key = malloc(strlen(ap->path) + strlen(me->multi->key) + 2); |
| if (!m_key) { |
| error(ap->logopt, |
| "failed to allocate storage for search key"); |
| return NSS_STATUS_UNKNOWN; |
| } |
| |
| strcpy(m_key, ap->path); |
| strcat(m_key, "/"); |
| strcat(m_key, me->multi->key); |
| entry = master_find_amdmount(ap, m_key); |
| free(m_key); |
| |
| if (!entry) { |
| error(ap->logopt, "expected amd mount entry not found"); |
| return NSS_STATUS_UNKNOWN; |
| } |
| |
| if (strcmp(entry->type, "host")) { |
| error(ap->logopt, "unexpected map type %s", entry->type); |
| return NSS_STATUS_UNKNOWN; |
| } |
| |
| if (entry->opts && *entry->opts) { |
| argv[0] = entry->opts; |
| argv[1] = NULL; |
| pargv = argv; |
| argc = 1; |
| } |
| |
| instance = master_find_source_instance(map, "hosts", "sun", argc, pargv); |
| |
| if (!instance && map->instance) { |
| struct map_source *next = map->instance; |
| while (next) { |
| instance = master_find_source_instance(next, |
| "hosts", "sun", argc, pargv); |
| if (instance) |
| break; |
| next = next->next; |
| } |
| } |
| if (!instance) { |
| error(ap->logopt, "expected hosts map instance not found"); |
| return NSS_STATUS_UNKNOWN; |
| } |
| |
| return do_lookup_mount(ap, instance, name, name_len); |
| } |
| |
| static int lookup_name_file_source_instance(struct autofs_point *ap, struct map_source *map, const char *name, int name_len) |
| { |
| struct map_source *instance; |
| char src_file[] = "file"; |
| char src_prog[] = "program"; |
| time_t age = monotonic_time(NULL); |
| struct stat st; |
| char *type, *format; |
| |
| if (*name == '/' && map->flags & MAP_FLAG_FORMAT_AMD) |
| return lookup_amd_instance(ap, map, name, name_len); |
| |
| if (stat(map->argv[0], &st) == -1) { |
| debug(ap->logopt, "file map not found"); |
| return NSS_STATUS_NOTFOUND; |
| } |
| |
| if (!S_ISREG(st.st_mode)) |
| return NSS_STATUS_NOTFOUND; |
| |
| if (st.st_mode & __S_IEXEC) |
| type = src_prog; |
| else |
| type = src_file; |
| |
| format = map->format; |
| |
| instance = master_find_source_instance(map, type, format, 0, NULL); |
| if (!instance) { |
| int argc = map->argc; |
| const char **argv = map->argv; |
| instance = master_add_source_instance(map, type, format, age, argc, argv); |
| if (!instance) |
| return NSS_STATUS_NOTFOUND; |
| instance->recurse = map->recurse; |
| instance->depth = map->depth; |
| } |
| |
| return do_lookup_mount(ap, instance, name, name_len); |
| } |
| |
| static int lookup_name_source_instance(struct autofs_point *ap, struct map_source *map, const char *type, const char *name, int name_len) |
| { |
| struct map_source *instance; |
| const char *format; |
| time_t age = monotonic_time(NULL); |
| |
| if (*name == '/' && map->flags & MAP_FLAG_FORMAT_AMD) |
| return lookup_amd_instance(ap, map, name, name_len); |
| |
| format = map->format; |
| |
| instance = master_find_source_instance(map, type, format, 0, NULL); |
| if (!instance) { |
| int argc = map->argc; |
| const char **argv = map->argv; |
| instance = master_add_source_instance(map, type, format, age, argc, argv); |
| if (!instance) |
| return NSS_STATUS_NOTFOUND; |
| instance->recurse = map->recurse; |
| instance->depth = map->depth; |
| } |
| |
| return do_lookup_mount(ap, instance, name, name_len); |
| } |
| |
| static int do_name_lookup_mount(struct autofs_point *ap, |
| struct map_source *map, |
| const char *name, int name_len) |
| { |
| char *path; |
| |
| if (!map->argv[0]) { |
| if (!strcmp(map->type, "hosts")) |
| return do_lookup_mount(ap, map, name, name_len); |
| return NSS_STATUS_UNKNOWN; |
| } |
| |
| if (*name == '/' && map->flags & MAP_FLAG_FORMAT_AMD) |
| return lookup_amd_instance(ap, map, name, name_len); |
| |
| |
| |
| |
| |
| |
| if (strncmp(map->type, "file", 4)) |
| return do_lookup_mount(ap, map, name, name_len); |
| |
| if (map->argv[0][0] == '/') |
| return do_lookup_mount(ap, map, name, name_len); |
| |
| path = find_map_path(ap, map); |
| if (!path) |
| return NSS_STATUS_UNKNOWN; |
| |
| if (map->argc >= 1) { |
| if (map->argv[0]) |
| free((char *) map->argv[0]); |
| map->argv[0] = path; |
| } else { |
| error(ap->logopt, "invalid arguments for autofs_point"); |
| free(path); |
| return NSS_STATUS_UNKNOWN; |
| } |
| |
| return do_lookup_mount(ap, map, name, name_len); |
| } |
| |
| static enum nsswitch_status lookup_map_name(struct nss_source *this, |
| struct autofs_point *ap, struct map_source *map, |
| const char *name, int name_len) |
| { |
| enum nsswitch_status result; |
| struct map_source tmap; |
| char *path; |
| |
| if (strcasecmp(this->source, "files")) |
| return lookup_name_source_instance(ap, map, |
| this->source, name, name_len); |
| |
| |
| |
| |
| |
| |
| |
| |
| if (strchr(map->argv[0], '/')) { |
| error(ap->logopt, "relative path invalid in files map name"); |
| return NSS_STATUS_NOTFOUND; |
| } |
| |
| this->source[4] = '\0'; |
| tmap.flags = map->flags; |
| tmap.type = this->source; |
| tmap.format = map->format; |
| tmap.name = map->name; |
| tmap.mc = map->mc; |
| tmap.instance = map->instance; |
| tmap.exp_timeout = map->exp_timeout; |
| tmap.recurse = map->recurse; |
| tmap.depth = map->depth; |
| tmap.argc = 0; |
| tmap.argv = NULL; |
| |
| path = find_map_path(ap, map); |
| if (!path) |
| return NSS_STATUS_UNKNOWN; |
| |
| if (map->argc >= 1) { |
| tmap.argc = map->argc; |
| tmap.argv = copy_argv(map->argc, map->argv); |
| if (!tmap.argv) { |
| error(ap->logopt, "failed to copy args"); |
| free(path); |
| return NSS_STATUS_UNKNOWN; |
| } |
| if (tmap.argv[0]) |
| free((char *) tmap.argv[0]); |
| tmap.argv[0] = path; |
| } else { |
| error(ap->logopt, "invalid arguments for autofs_point"); |
| free(path); |
| return NSS_STATUS_UNKNOWN; |
| } |
| |
| result = lookup_name_file_source_instance(ap, &tmap, name, name_len); |
| |
| if (!map->instance) |
| map->instance = tmap.instance; |
| |
| |
| free_argv(tmap.argc, tmap.argv); |
| |
| return result; |
| } |
| |
| static void update_negative_cache(struct autofs_point *ap, struct map_source *source, const char *name) |
| { |
| struct master_mapent *entry = ap->entry; |
| struct map_source *map; |
| struct mapent *me; |
| |
| |
| if (source && source->depth) |
| return; |
| |
| |
| if (strlen(name) == 1 && *name == '*') |
| return; |
| |
| |
| me = lookup_source_mapent(ap, name, LKP_DISTINCT); |
| if (me) |
| |
| |
| |
| |
| cache_unlock(me->mc); |
| else { |
| if (!defaults_disable_not_found_message()) { |
| |
| |
| |
| |
| |
| |
| logmsg("key \"%s\" not found in map source(s).", name); |
| } |
| |
| |
| if (source) |
| map = source; |
| else |
| map = entry->maps; |
| if (map) { |
| time_t now = monotonic_time(NULL); |
| int rv = CHE_FAIL; |
| |
| cache_writelock(map->mc); |
| me = cache_lookup_distinct(map->mc, name); |
| if (me) |
| rv = cache_push_mapent(me, NULL); |
| else |
| rv = cache_update(map->mc, map, name, NULL, now); |
| if (rv != CHE_FAIL) { |
| me = cache_lookup_distinct(map->mc, name); |
| if (me) |
| me->status = now + ap->negative_timeout; |
| } |
| cache_unlock(map->mc); |
| } |
| } |
| return; |
| } |
| |
| int lookup_nss_mount(struct autofs_point *ap, struct map_source *source, const char *name, int name_len) |
| { |
| struct master_mapent *entry = ap->entry; |
| struct list_head nsslist; |
| struct list_head *head, *p; |
| struct nss_source *this; |
| struct map_source *map; |
| enum nsswitch_status status; |
| int result = NSS_STATUS_UNKNOWN; |
| |
| |
| |
| |
| |
| |
| pthread_cleanup_push(master_source_lock_cleanup, entry); |
| master_source_readlock(entry); |
| if (source) |
| map = source; |
| else |
| map = entry->maps; |
| while (map) { |
| |
| |
| |
| |
| if (entry->age > map->age) { |
| status = NSS_STATUS_UNAVAIL; |
| map = map->next; |
| continue; |
| } |
| |
| sched_yield(); |
| |
| if (map->type) { |
| result = do_name_lookup_mount(ap, map, name, name_len); |
| if (result == NSS_STATUS_SUCCESS) |
| break; |
| |
| map = map->next; |
| continue; |
| } |
| |
| |
| if (*map->argv[0] == '/') { |
| if (*(map->argv[0] + 1) == '/') { |
| char *tmp = strdup("ldap"); |
| if (!tmp) { |
| map = map->next; |
| status = NSS_STATUS_TRYAGAIN; |
| continue; |
| } |
| map->type = tmp; |
| result = do_lookup_mount(ap, map, name, name_len); |
| } else |
| result = lookup_name_file_source_instance(ap, map, name, name_len); |
| |
| if (result == NSS_STATUS_SUCCESS) |
| break; |
| |
| map = map->next; |
| continue; |
| } |
| |
| INIT_LIST_HEAD(&nsslist); |
| |
| status = nsswitch_parse(&nsslist); |
| if (status) { |
| error(ap->logopt, |
| "can't to read name service switch config."); |
| result = 1; |
| break; |
| } |
| |
| head = &nsslist; |
| list_for_each(p, head) { |
| this = list_entry(p, struct nss_source, list); |
| |
| if (map->flags & MAP_FLAG_FORMAT_AMD && |
| !strcmp(this->source, "sss")) { |
| warn(ap->logopt, |
| "source sss is not available for amd maps."); |
| result = NSS_STATUS_UNAVAIL; |
| continue; |
| } |
| |
| result = lookup_map_name(this, ap, map, name, name_len); |
| |
| if (result == NSS_STATUS_UNKNOWN) |
| continue; |
| |
| status = check_nss_result(this, result); |
| if (status >= 0) { |
| map = NULL; |
| break; |
| } |
| } |
| |
| if (!list_empty(&nsslist)) |
| free_sources(&nsslist); |
| |
| if (!map) |
| break; |
| |
| map = map->next; |
| } |
| if (ap->state != ST_INIT) |
| send_map_update_request(ap); |
| |
| |
| |
| |
| |
| |
| if (result == NSS_STATUS_NOTFOUND || result == NSS_STATUS_UNAVAIL) |
| update_negative_cache(ap, source, name); |
| pthread_cleanup_pop(1); |
| |
| return !result; |
| } |
| |
| static void lookup_close_lookup_instances(struct map_source *map) |
| { |
| struct map_source *instance; |
| |
| instance = map->instance; |
| while (instance) { |
| lookup_close_lookup_instances(instance); |
| instance = instance->next; |
| } |
| |
| if (map->lookup) { |
| close_lookup(map->lookup); |
| map->lookup = NULL; |
| } |
| } |
| |
| void lookup_close_lookup(struct autofs_point *ap) |
| { |
| struct map_source *map; |
| |
| map = ap->entry->maps; |
| if (!map) |
| return; |
| |
| while (map) { |
| lookup_close_lookup_instances(map); |
| map = map->next; |
| } |
| |
| return; |
| } |
| |
| static char *make_fullpath(struct autofs_point *ap, const char *key) |
| { |
| char *path = NULL; |
| int l; |
| |
| if (*key != '/') |
| path = make_browse_path(ap->logopt, ap->path, key, ap->pref); |
| else { |
| l = strlen(key) + 1; |
| if (l > KEY_MAX_LEN) |
| goto out; |
| path = malloc(l); |
| if (!path) |
| goto out; |
| strcpy(path, key); |
| } |
| out: |
| return path; |
| } |
| |
| void lookup_prune_one_cache(struct autofs_point *ap, struct mapent_cache *mc, time_t age) |
| { |
| struct mapent *me, *this; |
| char *path; |
| int status = CHE_FAIL; |
| |
| me = cache_enumerate(mc, NULL); |
| while (me) { |
| struct mapent *valid; |
| char *key = NULL, *next_key = NULL; |
| |
| if (me->age >= age) { |
| |
| |
| |
| |
| |
| |
| |
| if (me->mapent || cache_lookup(mc, "*")) |
| me->status = 0; |
| me = cache_enumerate(mc, me); |
| continue; |
| } |
| |
| key = strdup(me->key); |
| me = cache_enumerate(mc, me); |
| |
| if (!key || strchr(key, '*')) { |
| if (key) |
| free(key); |
| continue; |
| } |
| |
| path = make_fullpath(ap, key); |
| if (!path) { |
| warn(ap->logopt, "can't malloc storage for path"); |
| free(key); |
| continue; |
| } |
| |
| |
| |
| |
| |
| |
| |
| valid = lookup_source_valid_mapent(ap, key, LKP_DISTINCT); |
| if (valid && valid->mc == mc) { |
| |
| |
| |
| |
| cache_unlock(valid->mc); |
| valid = NULL; |
| } |
| if (!valid && |
| is_mounted(_PATH_MOUNTED, path, MNTS_REAL)) { |
| debug(ap->logopt, |
| "prune check posponed, %s mounted", path); |
| free(key); |
| free(path); |
| continue; |
| } |
| if (valid) |
| cache_unlock(valid->mc); |
| |
| if (me) |
| next_key = strdup(me->key); |
| |
| cache_unlock(mc); |
| |
| cache_writelock(mc); |
| this = cache_lookup_distinct(mc, key); |
| if (!this) { |
| cache_unlock(mc); |
| goto next; |
| } |
| |
| if (valid) |
| cache_delete(mc, key); |
| else if (!is_mounted(_PROC_MOUNTS, path, MNTS_AUTOFS)) { |
| dev_t devid = ap->dev; |
| status = CHE_FAIL; |
| if (ap->type == LKP_DIRECT) |
| devid = this->dev; |
| if (this->ioctlfd == -1) |
| status = cache_delete(mc, key); |
| if (status != CHE_FAIL) { |
| if (ap->type == LKP_INDIRECT) { |
| if (ap->flags & MOUNT_FLAG_GHOST) |
| rmdir_path(ap, path, devid); |
| } else |
| rmdir_path(ap, path, devid); |
| } |
| } |
| cache_unlock(mc); |
| |
| next: |
| cache_readlock(mc); |
| if (next_key) { |
| me = cache_lookup_distinct(mc, next_key); |
| free(next_key); |
| } |
| free(key); |
| free(path); |
| } |
| |
| return; |
| } |
| |
| int lookup_prune_cache(struct autofs_point *ap, time_t age) |
| { |
| struct master_mapent *entry = ap->entry; |
| struct map_source *map; |
| |
| pthread_cleanup_push(master_source_lock_cleanup, entry); |
| master_source_readlock(entry); |
| |
| map = entry->maps; |
| while (map) { |
| |
| if (!map->stale && !check_stale_instances(map)) { |
| map = map->next; |
| continue; |
| } |
| pthread_cleanup_push(cache_lock_cleanup, map->mc); |
| cache_readlock(map->mc); |
| lookup_prune_one_cache(ap, map->mc, age); |
| pthread_cleanup_pop(1); |
| clear_stale_instances(map); |
| map->stale = 0; |
| map = map->next; |
| } |
| |
| pthread_cleanup_pop(1); |
| |
| return 1; |
| } |
| |
| |
| struct mapent *lookup_source_valid_mapent(struct autofs_point *ap, const char *key, unsigned int type) |
| { |
| struct master_mapent *entry = ap->entry; |
| struct map_source *map; |
| struct mapent_cache *mc; |
| struct mapent *me = NULL; |
| |
| map = entry->maps; |
| while (map) { |
| |
| |
| |
| |
| if (ap->entry->age > map->age) { |
| map = map->next; |
| continue; |
| } |
| |
| mc = map->mc; |
| cache_readlock(mc); |
| if (type == LKP_DISTINCT) |
| me = cache_lookup_distinct(mc, key); |
| else |
| me = cache_lookup(mc, key); |
| if (me) |
| break; |
| cache_unlock(mc); |
| map = map->next; |
| } |
| |
| return me; |
| } |
| |
| |
| struct mapent *lookup_source_mapent(struct autofs_point *ap, const char *key, unsigned int type) |
| { |
| struct master_mapent *entry = ap->entry; |
| struct map_source *map; |
| struct mapent_cache *mc; |
| struct mapent *me = NULL; |
| |
| map = entry->maps; |
| while (map) { |
| mc = map->mc; |
| cache_readlock(mc); |
| if (type == LKP_DISTINCT) |
| me = cache_lookup_distinct(mc, key); |
| else |
| me = cache_lookup(mc, key); |
| if (me) |
| break; |
| cache_unlock(mc); |
| map = map->next; |
| } |
| |
| if (me && me->mc != mc) |
| error(LOGOPT_ANY, "mismatching mc in cache", me->key); |
| |
| return me; |
| } |
| |
| int lookup_source_close_ioctlfd(struct autofs_point *ap, const char *key) |
| { |
| struct master_mapent *entry = ap->entry; |
| struct map_source *map; |
| struct mapent_cache *mc; |
| struct mapent *me; |
| int ret = 0; |
| |
| map = entry->maps; |
| while (map) { |
| mc = map->mc; |
| cache_readlock(mc); |
| me = cache_lookup_distinct(mc, key); |
| if (me) { |
| if (me->ioctlfd != -1) { |
| struct ioctl_ops *ops = get_ioctl_ops(); |
| ops->close(ap->logopt, me->ioctlfd); |
| me->ioctlfd = -1; |
| } |
| cache_unlock(mc); |
| ret = 1; |
| break; |
| } |
| cache_unlock(mc); |
| map = map->next; |
| } |
| |
| return ret; |
| } |
| |