autofs-5.1.9 - update per-mount expire timeout on readmap

From: Ian Kent <raven@themaw.net>

Ensure read map requests are propagated to child submounts and update
the amd per-mount timeout.

Also update the logging text to remove the use of the word dentry as
it might not make sense to users.

Signed-off-by: Ian Kent <raven@themaw.net>
---
 CHANGELOG           |    1 
 daemon/master.c     |    2 -
 daemon/state.c      |    1 
 include/master.h    |    1 
 include/mounts.h    |    1 
 lib/mounts.c        |  122 +++++++++++++++++++++++++++++++++++++++++++++++++++
 modules/parse_amd.c |    6 +--
 7 files changed, 130 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 87bf6ebf2..f9432181f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -25,6 +25,7 @@
 - fix lookup search type in umount_subtree_mounts().
 - fix remount_active_mount() not remounting symlinks.
 - log when setting amd per-mount timeout.
+- update per-mount expire timeout on readmap.
 
 02/11/2023 autofs-5.1.9
 - fix kernel mount status notification.
diff --git a/daemon/master.c b/daemon/master.c
index 6bf67552e..bd7b16f90 100644
--- a/daemon/master.c
+++ b/daemon/master.c
@@ -1388,7 +1388,7 @@ static int master_do_mount(struct master_mapent *entry)
 	return 1;
 }
 
-static void check_update_map_sources(struct master_mapent *entry, int readall)
+void check_update_map_sources(struct master_mapent *entry, int readall)
 {
 	struct map_source *source, *last;
 	struct autofs_point *ap;
diff --git a/daemon/state.c b/daemon/state.c
index b7dfbc6da..4e26bc68f 100644
--- a/daemon/state.c
+++ b/daemon/state.c
@@ -432,6 +432,7 @@ static void *do_readmap(void *arg)
 		time_t timeout = get_exp_timeout(ap, ap->entry->maps);
 		ap->exp_runfreq = (timeout + CHECK_RATIO - 1) / CHECK_RATIO;
 		ops->timeout(ap->logopt, ap->ioctlfd, NULL, timeout);
+		update_mounted_mounts_timeout(ap, ap->path);
 		lookup_prune_cache(ap, now);
 		status = lookup_ghost(ap);
 	} else {
diff --git a/include/master.h b/include/master.h
index 38070bfc4..7b93c8e94 100644
--- a/include/master.h
+++ b/include/master.h
@@ -115,6 +115,7 @@ struct master *master_new(const char *, unsigned int, unsigned int);
 int master_read_master(struct master *, time_t);
 int master_notify_submount(struct autofs_point *, const char *path, enum states);
 void master_notify_state_change(struct master *, int);
+void check_update_map_sources(struct master_mapent *, int);
 int master_mount_mounts(struct master *, time_t);
 int dump_map(struct master *, const char *, const char *);
 int master_show_mounts(struct master *);
diff --git a/include/mounts.h b/include/mounts.h
index 0c711ee83..8b3acac51 100644
--- a/include/mounts.h
+++ b/include/mounts.h
@@ -197,6 +197,7 @@ const char *mount_type_str(unsigned int);
 void set_exp_timeout(struct autofs_point *ap, struct map_source *source, time_t timeout);
 time_t get_exp_timeout(struct autofs_point *ap, struct map_source *source);
 void notify_mount_result(struct autofs_point *, const char *, time_t, const char *);
+void update_mounted_mounts_timeout(struct autofs_point *, const char *);
 int try_remount(struct autofs_point *, struct mapent *, unsigned int);
 void set_indirect_mount_tree_catatonic(struct autofs_point *);
 void set_direct_mount_tree_catatonic(struct autofs_point *, struct mapent *);
diff --git a/lib/mounts.c b/lib/mounts.c
index b87e79a27..2c28a34b0 100644
--- a/lib/mounts.c
+++ b/lib/mounts.c
@@ -2605,6 +2605,128 @@ void notify_mount_result(struct autofs_point *ap,
 	return;
 }
 
+void update_mounted_mounts_timeout(struct autofs_point *ap, const char *path)
+{
+	struct ioctl_ops *ops = get_ioctl_ops();
+	struct dirent **de;
+	char buf[PATH_MAX + 1];
+	int n, size;
+
+	n = scandir(path, &de, 0, alphasort);
+	if (n < 0)
+		return;
+
+	size = sizeof(buf);
+
+	while (n--) {
+		unsigned int mounted = 0;
+		struct mnt_list *mnt;
+		int ret;
+
+		if (strcmp(de[n]->d_name, ".") == 0 ||
+		    strcmp(de[n]->d_name, "..") == 0) {
+			free(de[n]);
+			continue;
+		}
+
+		ret = cat_path(buf, size, path, de[n]->d_name);
+		if (!ret) {
+			do {
+				free(de[n]);
+			} while (n--);
+			free(de);
+			return;
+		}
+
+		ops->ismountpoint(ap->logopt, -1, buf, &mounted);
+		if (!mounted) {
+			struct dirent **de2;
+			int i, j;
+
+			i = j = scandir(buf, &de2, 0, alphasort);
+			if (i < 0) {
+				free(de[n]);
+				continue;
+			}
+			while (i--)
+				free(de2[i]);
+			free(de2);
+			if (j <= 2) {
+				free(de[n]);
+				continue;
+			}
+		}
+
+		/* For submounts we need to propogate the read map
+		 * request.
+		 */
+		mnt = mnts_find_submount(buf);
+		if (mnt) {
+			check_update_map_sources(mnt->ap->entry, 1);
+			mnts_put_mount(mnt);
+		}
+
+		mnt = mnts_find_amdmount(buf);
+		if (!mnt) {
+			free(de[n]);
+			continue;
+		}
+
+		/* For amd type auto mounts the timeout is the per-mount
+		 * timeout.
+		 */
+		if (mnt->amd_flags & AMD_MOUNT_TYPE_AUTO)
+			goto next;
+
+		/* No per-mount timeout set? */
+		if (!(mnt->amd_flags & AMD_MOUNT_OPT_MASK))
+			goto next;
+
+		/* The default in autofs is to always expire mounts according to
+		 * a timeout set in the autofs mount super block information
+		 * structure. But amd allows for differing expire timeouts on a
+		 * per-mount basis. It also has (context sensitive) options "unmount"
+		 * to say expire this mount and "nounmount" to say don't expire this
+		 * mount. In amd mounts these options are set by default according
+		 * to whether a mount should expire or not, for example a cd mount
+		 * is set "nounmount". Setting defaults like this is not used in the
+		 * autofs amd implementation because there's only one, little used,
+		 * removable file system available.
+		 *
+		 * But the "nounmount" and "utimeout" options can be useful.
+		 */
+		if (mnt->amd_flags & AMD_MOUNT_OPT_NOUNMOUNT) {
+			if (mnt->amd_utimeout)
+				warn(ap->logopt,
+				"non-zero timeout set, possible conflicting options");
+
+			/* "nounmount" option, don't expire this mount. */
+			if (ops) {
+				info(ap->logopt,
+				     "set amd per-mount expire timeout to 0 for %s",
+				     buf);
+				ops->timeout(ap->logopt, ap->ioctlfd, de[n]->d_name, 0);
+			}
+		} else if (mnt->amd_flags & AMD_MOUNT_OPT_UTIMEOUT) {
+			if (!mnt->amd_utimeout)
+				warn(ap->logopt,
+				"zero timeout set, possible conflicting options");
+
+			/* "utimeout" option, expire this mount according to a timeout. */
+			if (ops) {
+				info(ap->logopt,
+				     "set amd per-mount expire timeout to %d for %s",
+				     mnt->amd_utimeout, buf);
+				ops->timeout(ap->logopt, ap->ioctlfd, de[n]->d_name, mnt->amd_utimeout);
+			}
+		}
+next:
+		mnts_put_mount(mnt);
+		free(de[n]);
+	}
+	free(de);
+}
+
 static int do_remount_direct(struct autofs_point *ap,
 			     const unsigned int type, int fd, const char *path)
 {
diff --git a/modules/parse_amd.c b/modules/parse_amd.c
index bb32895e2..83e85f946 100644
--- a/modules/parse_amd.c
+++ b/modules/parse_amd.c
@@ -1765,7 +1765,7 @@ static int amd_mount(struct autofs_point *ap, const char *name,
 			if (ops) {
 				info(ap->logopt,
 				     "set amd per-mount expire timeout to 0 for %s",
-				     name);
+				     entry->path);
 				ops->timeout(ap->logopt, ap->ioctlfd, name, 0);
 			}
 		} else if (per_mnt_flags & AMD_MOUNT_OPT_UTIMEOUT) {
@@ -1776,8 +1776,8 @@ static int amd_mount(struct autofs_point *ap, const char *name,
 			/* "utimeout" option, expire this mount according to a timeout. */
 			if (ops) {
 				info(ap->logopt,
-				     "set amd per-dentry expire timeout to %d for %s",
-				     entry->utimeout, name);
+				     "set amd per-mount expire timeout to %d for %s",
+				     entry->utimeout, entry->path);
 				ops->timeout(ap->logopt, ap->ioctlfd, name, entry->utimeout);
 			}
 		}
