autofs/autofs-5.1.7-add-mount-and-umount-offsets-functions.patch
DistroBaker a5adb69dac Merged update from upstream sources
This is an automated DistroBaker update from upstream sources.
If you do not know what this is about or would like to opt out,
contact the OSCI team.

Source: https://src.fedoraproject.org/rpms/autofs.git#25aaf0b69441b4e7370a195cbf1c7988d0abef3d
2021-03-26 02:05:45 +00:00

311 lines
8.5 KiB
Diff

autofs-5.1.7 - add mount and umount offsets functions
From: Ian Kent <raven@themaw.net>
Add tree_mapent_mount_offsets() and tree_mapent_umount_offsets() to
the mapent tree handling implementation.
Signed-off-by: Ian Kent <raven@themaw.net>
---
CHANGELOG | 1
include/mounts.h | 2
lib/mounts.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 263 insertions(+)
diff --git a/CHANGELOG b/CHANGELOG
index 0bd6f181..892f7581 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -39,6 +39,7 @@
- fix mount_fullpath().
- add tree_mapent_cleanup_offsets().
- add set_offset_tree_catatonic().
+- add mount and umount offsets functions.
25/01/2021 autofs-5.1.7
- make bind mounts propagation slave by default.
diff --git a/include/mounts.h b/include/mounts.h
index 5441ee0e..e56f80ba 100644
--- a/include/mounts.h
+++ b/include/mounts.h
@@ -172,6 +172,8 @@ struct tree_node *tree_mapent_root(struct mapent *me);
int tree_mapent_add_node(struct mapent_cache *mc, const char *base, const char *key);
int tree_mapent_delete_offsets(struct mapent_cache *mc, const char *key);
void tree_mapent_cleanup_offsets(struct mapent *oe);
+int tree_mapent_mount_offsets(struct mapent *oe, int nonstrict);
+int tree_mapent_umount_offsets(struct mapent *oe, int nonstrict);
int unlink_mount_tree(struct autofs_point *ap, const char *mp);
void free_mnt_list(struct mnt_list *list);
int is_mounted(const char *mp, unsigned int type);
diff --git a/lib/mounts.c b/lib/mounts.c
index f075a27e..f7c29475 100644
--- a/lib/mounts.c
+++ b/lib/mounts.c
@@ -1692,6 +1692,266 @@ void tree_mapent_cleanup_offsets(struct mapent *oe)
}
}
+static int tree_mapent_rmdir_path_offset(struct autofs_point *ap, struct mapent *oe)
+{
+ struct mapent *mm_root = MAPENT(MAPENT_ROOT(oe));
+ char *dir, *path;
+ unsigned int split;
+ int ret;
+
+ if (ap->type == LKP_DIRECT)
+ return rmdir_path(ap, oe->key, mm_root->dev);
+
+ dir = strdup(oe->key);
+
+ if (ap->flags & MOUNT_FLAG_GHOST)
+ split = ap->len + mm_root->len + 1;
+ else
+ split = ap->len;
+
+ dir[split] = '\0';
+ path = &dir[split + 1];
+
+ if (chdir(dir) == -1) {
+ error(ap->logopt, "failed to chdir to %s", dir);
+ free(dir);
+ return -1;
+ }
+
+ ret = rmdir_path(ap, path, ap->dev);
+
+ free(dir);
+
+ if (chdir("/") == -1)
+ error(ap->logopt, "failed to chdir to /");
+
+ return ret;
+}
+
+static int tree_mapent_mount_offset(struct mapent *oe, void *ptr)
+{
+ struct traverse_subtree_context *ctxt = ptr;
+ struct autofs_point *ap = ctxt->ap;
+ int ret;
+
+ debug(ap->logopt, "mount offset %s", oe->key);
+
+ ret = mount_autofs_offset(ap, oe);
+ if (ret < MOUNT_OFFSET_OK) {
+ if (ret != MOUNT_OFFSET_IGNORE) {
+ warn(ap->logopt, "failed to mount offset");
+ return 0;
+ } else {
+ debug(ap->logopt,
+ "ignoring \"nohide\" trigger %s", oe->key);
+ /*
+ * Ok, so we shouldn't modify the mapent but
+ * mount requests are blocked at a point above
+ * this and expire only uses the mapent key or
+ * holds the cache write lock.
+ */
+ free(oe->mapent);
+ oe->mapent = NULL;
+ }
+ }
+
+ return 1;
+}
+
+static int tree_mapent_umount_offset(struct mapent *oe, void *ptr)
+{
+ struct traverse_subtree_context *ctxt = ptr;
+ struct autofs_point *ap = ctxt->ap;
+ int ret = 1;
+
+ /*
+ * Check for and umount subtree offsets resulting from
+ * nonstrict mount fail.
+ */
+ ret = tree_mapent_umount_offsets(oe, ctxt->strict);
+ if (!ret)
+ return 0;
+
+ /*
+ * If an offset that has an active mount has been removed
+ * from the multi-mount we don't want to attempt to trigger
+ * mounts for it. Obviously this is because it has been
+ * removed, but less obvious is the potential strange
+ * behaviour that can result if we do try and mount it
+ * again after it's been expired. For example, if an NFS
+ * file system is no longer exported and is later umounted
+ * it can be mounted again without any error message but
+ * shows as an empty directory. That's going to confuse
+ * people for sure.
+ *
+ * If the mount cannot be umounted (the process is now
+ * using a stale mount) the offset needs to be invalidated
+ * so no further mounts will be attempted but the offset
+ * cache entry must remain so expires can continue to
+ * attempt to umount it. If the mount can be umounted and
+ * the offset is removed, at least for NFS we will get
+ * ESTALE errors when attempting list the directory.
+ */
+ if (oe->ioctlfd != -1 ||
+ is_mounted(oe->key, MNTS_REAL)) {
+ if (umount_ent(ap, oe->key) &&
+ is_mounted(oe->key, MNTS_REAL)) {
+ debug(ap->logopt,
+ "offset %s has active mount, invalidate",
+ oe->key);
+ /*
+ * Ok, so we shouldn't modify the mapent but
+ * mount requests are blocked at a point above
+ * this and expire only uses the mapent key or
+ * holds the cache write lock.
+ */
+ if (oe->mapent) {
+ free(oe->mapent);
+ oe->mapent = NULL;
+ }
+ return 0;
+ }
+ }
+
+ /* Don't bother if there's noting to umount. */
+ if (!is_mounted(oe->key, MNTS_AUTOFS))
+ goto done;
+
+ debug(ap->logopt, "umount offset %s", oe->key);
+
+ if (umount_autofs_offset(ap, oe)) {
+ warn(ap->logopt, "failed to umount offset");
+ ret = 0;
+ } else {
+ struct stat st;
+ int ret;
+
+ if (!(oe->flags & MOUNT_FLAG_DIR_CREATED))
+ goto done;
+
+ /*
+ * An error due to partial directory removal is
+ * ok so only try and remount the offset if the
+ * actual mount point still exists.
+ */
+ ret = tree_mapent_rmdir_path_offset(ap, oe);
+ if (ret == -1 && !stat(oe->key, &st)) {
+ ret = tree_mapent_mount_offset(oe, ctxt);
+ /* But we did origianlly create this */
+ oe->flags |= MOUNT_FLAG_DIR_CREATED;
+ }
+ }
+done:
+ return ret;
+}
+
+static int tree_mapent_mount_offsets_work(struct tree_node *n, void *ptr)
+{
+ struct traverse_subtree_context *ctxt = ptr;
+ struct mapent *oe = MAPENT(n);
+ struct mapent *mm_root = MAPENT(MAPENT_ROOT(oe));
+ struct autofs_point *ap = ctxt->ap;
+ int ret;
+
+ if (!oe->mapent)
+ return 1;
+
+ /* Stale offset, no longer present in the mapent */
+ if (oe->age != mm_root->age) {
+ /* Best effort */
+ tree_mapent_umount_offset(oe, ctxt);
+ return 1;
+ }
+
+ ret = tree_mapent_mount_offset(oe, ctxt);
+
+ /*
+ * If re-constructing a multi-mount it's necessary to walk
+ * into nested mounts, unlike the usual "mount only what's
+ * needed as you go" behavior.
+ */
+ if (ap->state == ST_READMAP && ap->flags & MOUNT_FLAG_REMOUNT) {
+ if (oe->ioctlfd != -1 ||
+ is_mounted(oe->key, MNTS_REAL))
+ /* Best effort */
+ tree_mapent_mount_offsets(oe, !ctxt->strict);
+ }
+
+ return ret;
+}
+
+int tree_mapent_mount_offsets(struct mapent *oe, int nonstrict)
+{
+ struct tree_node *base = MAPENT_NODE(oe);
+ struct traverse_subtree_context ctxt = {
+ .ap = oe->mc->ap,
+ .base = base,
+ .strict = !nonstrict,
+ };
+
+ return tree_mapent_traverse_subtree(base,
+ tree_mapent_mount_offsets_work, &ctxt);
+}
+
+static int tree_mapent_umount_offsets_work(struct tree_node *n, void *ptr)
+{
+ struct mapent *oe = MAPENT(n);
+
+ return tree_mapent_umount_offset(oe, ptr);
+}
+
+int tree_mapent_umount_offsets(struct mapent *oe, int nonstrict)
+{
+ struct tree_node *base = MAPENT_NODE(oe);
+ struct autofs_point *ap = oe->mc->ap;
+ struct traverse_subtree_context ctxt = {
+ .ap = ap,
+ .base = base,
+ .strict = !nonstrict,
+ };
+ int ret;
+
+ ret = tree_mapent_traverse_subtree(base,
+ tree_mapent_umount_offsets_work, &ctxt);
+ if (ret && tree_mapent_is_root(oe)) {
+ char mp[PATH_MAX + 1];
+
+ /*
+ * The map entry cache stores mapent keys. For indirect
+ * mount maps they are single direcory components so when
+ * one of these keys is the root of a multi-mount the mount
+ * path must be constructed.
+ */
+ if (!mount_fullpath(mp, PATH_MAX, ap->path, oe->key)) {
+ error(ap->logopt, "mount path is too long");
+ return 0;
+ }
+
+ /*
+ * Special case.
+ * If we can't umount the root container then we can't
+ * delete the offsets from the cache and we need to put
+ * the offset triggers back.
+ */
+ if (is_mounted(mp, MNTS_REAL)) {
+ info(ap->logopt, "unmounting dir = %s", mp);
+ if (umount_ent(ap, mp) &&
+ is_mounted(mp, MNTS_REAL)) {
+ if (!tree_mapent_mount_offsets(oe, 1))
+ warn(ap->logopt,
+ "failed to remount offset triggers");
+ return 0;
+ }
+ }
+
+ /* check for mounted mount entry and remove it if found */
+ mnts_remove_mount(mp, MNTS_MOUNTED);
+
+ }
+
+ return ret;
+}
+
/* From glibc decode_name() */
/* Since the values in a line are separated by spaces, a name cannot
* contain a space. Therefore some programs encode spaces in names