autofs-5.1.9 - add ability to disable negative cache

From: Ian Kent <raven@themaw.net>

Sometimes it's necessary to disable the negative caching for map entries
so add a flag to do that.

Signed-off-by: Ian Kent <raven@themaw.net>
---
 CHANGELOG           |    1 +
 include/automount.h |    5 ++++
 lib/cache.c         |   70 ++++++++++++++++++++++++++++++++++++---------------
 3 files changed, 56 insertions(+), 20 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 3c0b8f0fd..f2d13424a 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -102,6 +102,7 @@
 - improve handling of missing map entry for expire request.
 - don't open code negative update in lookup.
 - make negative timeout check consistent.
+- add ability to disable negative cache.
 
 02/11/2023 autofs-5.1.9
 - fix kernel mount status notification.
diff --git a/include/automount.h b/include/automount.h
index bb71e53d0..a9480768e 100644
--- a/include/automount.h
+++ b/include/automount.h
@@ -217,6 +217,8 @@ struct mapent *cache_partial_match(struct mapent_cache *mc, const char *prefix);
 struct mapent *cache_partial_match_wild(struct mapent_cache *mc, const char *prefix);
 int cache_add(struct mapent_cache *mc, struct map_source *ms, const char *key, const char *mapent, time_t age);
 int cache_update_offset(struct mapent_cache *mc, const char *mkey, const char *key, const char *mapent, time_t age);
+void cache_disable_negative(struct mapent *me);
+void cache_enable_negative(struct mapent *me);
 int cache_negative_expired(struct mapent *me);
 int cache_lookup_negative(struct mapent *me, const char *key);
 void cache_update_negative(struct mapent_cache *mc, struct map_source *ms, const char *key, time_t timeout);
@@ -552,6 +554,9 @@ struct kernel_mod_version {
 /* Flag to show we have mounted the offset mount trigger */
 #define MOUNT_FLAG_OFFSET_MOUNTED	0x2000
 
+/* Flag to show disable negative caching for the map entry */
+#define MOUNT_FLAG_DISABLE_NEGATIVE	0x4000
+
 struct autofs_point {
 	pthread_t thid;
 	char *path;			/* Mount point name */
diff --git a/lib/cache.c b/lib/cache.c
index d266c1ec9..dde5ef6fb 100644
--- a/lib/cache.c
+++ b/lib/cache.c
@@ -625,6 +625,20 @@ int cache_update_offset(struct mapent_cache *mc, const char *mkey, const char *k
 	return ret; 
 }
 
+void cache_disable_negative(struct mapent *me)
+{
+	if (!(me->flags & MOUNT_FLAG_DISABLE_NEGATIVE)) {
+		me->flags |= MOUNT_FLAG_DISABLE_NEGATIVE;
+		me->status = 0;
+	}
+}
+
+void cache_enable_negative(struct mapent *me)
+{
+	if (me->flags & MOUNT_FLAG_DISABLE_NEGATIVE)
+		me->flags &= ~MOUNT_FLAG_DISABLE_NEGATIVE;
+}
+
 int cache_negative_expired(struct mapent *me)
 {
 	if (me->status >= monotonic_time(NULL))
@@ -632,36 +646,49 @@ int cache_negative_expired(struct mapent *me)
 	return 1;
 }
 
+static void cache_restore_mapent(struct mapent_cache *mc, const char *key)
+{
+	struct mapent *me;
+
+	cache_writelock(mc);
+	me = cache_lookup_distinct(mc, key);
+	if (me && !me->mapent) {
+		if (cache_pop_mapent(me) == CHE_FAIL)
+			cache_delete(mc, key);
+	}
+	cache_unlock(mc);
+}
+
 /* Called with cache_lock held for map entry me and is released
  * on return.
+ * Note: Must be called only on lookup so that expired negative
+ * entry can be restored.
  */
 int cache_lookup_negative(struct mapent *me, const char *key)
 {
+	struct mapent_cache *mc = me->mc;
+
+	if (me->flags & MOUNT_FLAG_DISABLE_NEGATIVE) {
+		cache_unlock(mc);
+		cache_restore_mapent(mc, key);
+		return CHE_OK;
+	}
+
 	if (!cache_negative_expired(me)) {
-		cache_unlock(me->mc);
+		cache_unlock(mc);
 		return CHE_UNAVAIL;
 	} else {
-		struct mapent_cache *smc = me->mc;
-		struct mapent *sme;
-
-		if (me->mapent)
-			cache_unlock(smc);
-		else {
-			cache_unlock(smc);
-			cache_writelock(smc);
-			sme = cache_lookup_distinct(smc, key);
-			/* Negative timeout expired for non-existent entry. */
-			if (sme && !sme->mapent) {
-				if (cache_pop_mapent(sme) == CHE_FAIL)
-					cache_delete(smc, key);
-			}
-			cache_unlock(smc);
-		}
+		cache_unlock(mc);
+		cache_restore_mapent(mc, key);
 	}
 
 	return CHE_OK;
 }
 
+/*
+ * Note: Must be called only on lookup failure so that entry location
+ * can be saved and entry set negative.
+ */
 void cache_update_negative(struct mapent_cache *mc,
 			   struct map_source *ms, const char *key,
 			   time_t timeout)
@@ -675,10 +702,13 @@ void cache_update_negative(struct mapent_cache *mc,
 		return;
 
 	me = cache_lookup_distinct(mc, key);
-	if (me)
-		rv = cache_push_mapent(me, NULL);
-	else
+	if (!me)
 		rv = cache_update(mc, ms, key, NULL, now);
+	else {
+		if (me->flags & MOUNT_FLAG_DISABLE_NEGATIVE)
+			return;
+		rv = cache_push_mapent(me, NULL);
+	}
 	if (rv != CHE_FAIL) {
 		me = cache_lookup_distinct(mc, key);
 		if (me)
