autofs-5.0.1 - fix null cache race From: Ian Kent <raven@themaw.net> The null map entry cache scope is across the entire master map but it is used by individual master map entries during master map re-read and subsequest updates resulting form it. The current null cache locking doesn't properly account for this. To resolve this, when we re-read the master, map we need to block access to the null cache until the master map has been read and the null cache updated. --- daemon/automount.c | 4 +++- include/automount.h | 1 + lib/cache.c | 33 ++++++++++++++++++++++++++------- lib/master.c | 28 ++++++++++++++++++++-------- lib/master_parse.y | 5 ----- 5 files changed, 50 insertions(+), 21 deletions(-) --- autofs-5.0.1.orig/daemon/automount.c +++ autofs-5.0.1/daemon/automount.c @@ -1254,14 +1254,16 @@ static int do_hup_signal(struct master * if (status) fatal(status); + master_mutex_lock(); if (master->reading) { status = pthread_mutex_unlock(&mrc.mutex); if (status) fatal(status); + master_mutex_unlock(); return 1; } - master->reading = 1; + master_mutex_unlock(); status = pthread_create(&thid, &th_attr_detached, do_read_master, NULL); if (status) { --- autofs-5.0.1.orig/include/automount.h +++ autofs-5.0.1/include/automount.h @@ -192,6 +192,7 @@ void cache_multi_writelock(struct mapent void cache_multi_unlock(struct mapent *me); int cache_delete_offset_list(struct mapent_cache *mc, const char *key); void cache_release(struct map_source *map); +void cache_clean_null_cache(struct mapent_cache *mc); void cache_release_null_cache(struct master *master); struct mapent *cache_enumerate(struct mapent_cache *mc, struct mapent *me); char *cache_get_offset(const char *prefix, char *offset, int start, struct list_head *head, struct list_head **pos); --- autofs-5.0.1.orig/lib/cache.c +++ autofs-5.0.1/lib/cache.c @@ -230,15 +230,38 @@ struct mapent_cache *cache_init(struct a return mc; } +void cache_clean_null_cache(struct mapent_cache *mc) +{ + struct mapent *me, *next; + int i; + + for (i = 0; i < mc->size; i++) { + me = mc->hash[i]; + if (me == NULL) + continue; + next = me->next; + free(me->key); + if (me->mapent) + free(me->mapent); + free(me); + + while (next != NULL) { + me = next; + next = me->next; + free(me->key); + free(me); + } + } + + return; +} + struct mapent_cache *cache_init_null_cache(struct master *master) { struct mapent_cache *mc; unsigned int i; int status; - if (master->nc) - cache_release_null_cache(master); - mc = malloc(sizeof(struct mapent_cache)); if (!mc) return NULL; @@ -266,8 +289,6 @@ struct mapent_cache *cache_init_null_cac if (status) fatal(status); - cache_writelock(mc); - for (i = 0; i < mc->size; i++) { mc->hash[i] = NULL; INIT_LIST_HEAD(&mc->ino_index[i]); @@ -276,8 +297,6 @@ struct mapent_cache *cache_init_null_cac mc->ap = NULL; mc->map = NULL; - cache_unlock(mc); - return mc; } --- autofs-5.0.1.orig/lib/master.c +++ autofs-5.0.1/lib/master.c @@ -814,17 +814,29 @@ int master_read_master(struct master *ma unsigned int logopt = master->logopt; struct mapent_cache *nc; - nc = cache_init_null_cache(master); - if (!nc) { - error(logopt, - "failed to init null map cache for %s", master->name); - return 0; + /* + * We need to clear and re-populate the null map entry cache + * before alowing anyone else to use it. + */ + if (master->nc) { + cache_writelock(master->nc); + nc = master->nc; + cache_clean_null_cache(nc); + } else { + nc = cache_init_null_cache(master); + if (!nc) { + error(logopt, + "failed to init null map cache for %s", + master->name); + return 0; + } + cache_writelock(nc); + master->nc = nc; } - master->nc = nc; - master_init_scan(); - lookup_nss_read_master(master, age); + cache_unlock(nc); + master_mount_mounts(master, age, readall); master_mutex_lock(); --- autofs-5.0.1.orig/lib/master_parse.y +++ autofs-5.0.1/lib/master_parse.y @@ -743,21 +743,16 @@ int master_parse_entry(const char *buffe /* Add null map entries to the null map cache */ if (type && !strcmp(type, "null")) { - cache_writelock(nc); cache_update(nc, NULL, path, NULL, lineno); - cache_unlock(nc); local_free_vars(); return 1; } /* Ignore all subsequent matching nulled entries */ - cache_readlock(nc); if (cache_lookup_distinct(nc, path)) { - cache_unlock(nc); local_free_vars(); return 1; } - cache_unlock(nc); if (debug || verbose) { logopt = (debug ? LOGOPT_DEBUG : 0);