305 lines
8.3 KiB
Diff
305 lines
8.3 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(+)
|
||
|
|
||
|
--- autofs-5.1.4.orig/CHANGELOG
|
||
|
+++ autofs-5.1.4/CHANGELOG
|
||
|
@@ -39,6 +39,7 @@
|
||
|
- fix mount_fullpath().
|
||
|
- add tree_mapent_cleanup_offsets().
|
||
|
- add set_offset_tree_catatonic().
|
||
|
+- add mount and umount offsets functions.
|
||
|
|
||
|
xx/xx/2018 autofs-5.1.5
|
||
|
- fix flag file permission.
|
||
|
--- autofs-5.1.4.orig/include/mounts.h
|
||
|
+++ autofs-5.1.4/include/mounts.h
|
||
|
@@ -172,6 +172,8 @@ struct tree_node *tree_mapent_root(struc
|
||
|
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);
|
||
|
--- autofs-5.1.4.orig/lib/mounts.c
|
||
|
+++ autofs-5.1.4/lib/mounts.c
|
||
|
@@ -1692,6 +1692,266 @@ void tree_mapent_cleanup_offsets(struct
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+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
|