From 2f7f09e93516653bfe64b498add4ec631ab88412 Mon Sep 17 00:00:00 2001
From: Ian Kent <raven@themaw.net>
Date: Wed, 27 Sep 2017 09:09:01 +0800
Subject: [PATCH] autofs-5.1.3 - only take master map mutex for master map
 update
References: bsc#1191625

When a map read is done it's neccessary to re-initialize the lookup
context which requires a write lock to be taken on the master map
entry source. Currently a map re-read also takes the master map lock
which will block new lookups.

If the lookup module thinks the map has been modified it will queue
a map read which will take these locks.

Now, when a bind mount (or symlink) is triggered by a lookup it's
necessary to trigger automounts that are included in the target path
for the dependent path to be valid.

When the target path triggers automounts in this way and refers to the
same master map entry, and a map re-read is scheduled and started and
manages to take the master map lock before the dependent lookup gets
past the master map lock check, the dependent path lookup will deadlock.

But the master map lock is meant to gaurd against master map entries
going away during lookups so isn't really needed for map reads as long
as the master map lock is held during the update of the mounted
automounts.

Signed-off-by: Ian Kent <raven@themaw.net>
---
 CHANGELOG      |  1 +
 daemon/state.c |  3 ---
 lib/master.c   | 45 +++++++++++++++++++++++++++++++++++++--------
 3 files changed, 38 insertions(+), 11 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index b3c5539..13bbce0 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,6 @@
 24/05/2016 autofs-5.1.3
 =======================
+- only take master map mutex for master map update.
 - fix release date in CHANGELOG.
 - build: check for clock_gettime in librt.
 - fix compiler warning in try_remount().
diff --git a/daemon/state.c b/daemon/state.c
index 72bba6a..cf835e0 100644
--- a/daemon/state.c
+++ b/daemon/state.c
@@ -484,12 +484,9 @@ static void *do_readmap(void *arg)
 
 	info(ap->logopt, "re-reading map for %s", ap->path);
 
-	pthread_cleanup_push(master_mutex_lock_cleanup, NULL);
-	master_mutex_lock();
 	status = lookup_nss_read_map(ap, NULL, now);
 	if (!status)
 		pthread_exit(NULL);
-	pthread_cleanup_pop(1);
 
 	if (ap->type == LKP_INDIRECT) {
 		struct ioctl_ops *ops = get_ioctl_ops();
diff --git a/lib/master.c b/lib/master.c
index 22b1522..a144a39 100644
--- a/lib/master.c
+++ b/lib/master.c
@@ -1090,6 +1090,39 @@ next:
 	free(paths);
 }
 
+static void wait_for_lookups_and_lock(struct master *master)
+{
+	struct list_head *p, *head;
+	int status;
+
+again:
+	master_mutex_lock();
+
+	head = &master->mounts;
+	p = head->next;
+	while (p != head) {
+		struct master_mapent *this;
+
+		this = list_entry(p, struct master_mapent, list);
+
+		status = pthread_rwlock_trywrlock(&this->source_lock);
+		if (status) {
+			struct timespec t = { 0, 200000000 };
+			struct timespec r;
+
+			master_mutex_unlock();
+
+			while (nanosleep(&t, &r) == -1 && errno == EINTR)
+				memcpy(&t, &r, sizeof(struct timespec));
+
+			goto again;
+		}
+		master_source_unlock(this);
+
+		p = p->next;
+	}
+}
+
 int master_read_master(struct master *master, time_t age, int readall)
 {
 	unsigned int logopt = master->logopt;
@@ -1099,7 +1132,7 @@ int master_read_master(struct master *master, time_t age, int readall)
 	 * We need to clear and re-populate the null map entry cache
 	 * before alowing anyone else to use it.
 	 */
-	master_mutex_lock();
+	wait_for_lookups_and_lock(master);
 	if (master->nc) {
 		cache_writelock(master->nc);
 		nc = master->nc;
@@ -1119,21 +1152,19 @@ int master_read_master(struct master *master, time_t age, int readall)
 	lookup_nss_read_master(master, age);
 	cache_unlock(nc);
 	master_add_amd_mount_section_mounts(master, age);
-	master_mutex_unlock();
 
 	if (!master->read_fail)
 		master_mount_mounts(master, age, readall);
 	else {
 		master->read_fail = 0;
 		/* HUP signal sets readall == 1 only */
-		if (!readall)
+		if (!readall) {
+			master_mutex_unlock();
 			return 0;
-		else
+		} else
 			master_mount_mounts(master, age, readall);
 	}
 
-	master_mutex_lock();
-
 	if (list_empty(&master->mounts))
 		warn(logopt, "no mounts in table");
 
@@ -1423,7 +1454,6 @@ int master_mount_mounts(struct master *master, time_t age, int readall)
 	int cur_state;
 
 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
-	master_mutex_lock();
 
 	head = &master->mounts;
 	p = head->next;
@@ -1511,7 +1541,6 @@ cont:
 		}
 	}
 
-	master_mutex_unlock();
 	pthread_setcancelstate(cur_state, NULL);
 
 	return 1;
-- 
2.26.2

