[CRIU] [PATCH 5/6] [v2] mnt: try to split a mount tree to restore over-mounted mounts

Andrei Vagin avagin at openvz.org
Wed Sep 21 23:16:40 PDT 2016


From: Andrei Vagin <avagin at virtuozzo.com>

If a mount overmounts something else, we can try to restore it
separetly and then move it to the right places after restoring
all mounts.

In this patch if we see that a mount is overmounts something,
we create a new directory in the root yard and restore this
mount and its sub-tree in this directory.

https://bugs.openvz.org/browse/OVZ-6778

v2: add more comments
    rename roots_mp into roots_yard_mp

Signed-off-by: Andrei Vagin <avagin at virtuozzo.com>
---
 criu/mount.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 153 insertions(+), 1 deletion(-)

diff --git a/criu/mount.c b/criu/mount.c
index a64671f..ccbe1eb 100644
--- a/criu/mount.c
+++ b/criu/mount.c
@@ -734,7 +734,8 @@ static int validate_mounts(struct mount_info *info, bool for_dump)
 			}
 		}
 skip_fstype:
-		if (does_mnt_overmount(m)) {
+		if (does_mnt_overmount(m) &&
+		    !list_empty(&m->parent->mnt_share)) {
 			pr_err("Unable to handle mounts under %d:%s\n",
 					m->mnt_id, m->mountpoint);
 			return -1;
@@ -2913,6 +2914,143 @@ static int do_umount_one(struct mount_info *mi)
 	return 0;
 }
 
+/*
+ * If a mount overmounts other mounts, it is restored  separetly in the roots
+ * yard and then moved to the right place.
+ *
+ * mnt_remap_entry is created for each such mount and it's added into
+ * mnt_remap_list. The origin mount point is replaced on a new one in
+ * roots_yard where it will be restored. The remapped mount will be
+ * moved to the right places after restoring all mounts.
+ */
+
+static inline int print_ns_root(struct ns_id *ns, int remap_id, char *buf, int bs);
+static int get_mp_mountpoint(char *mountpoint, struct mount_info *mi, char *root, int root_len);
+
+static LIST_HEAD(mnt_remap_list);
+static int remap_id;
+
+struct mnt_remap_entry {
+	struct mount_info *child; /* child is remaped into the root yards */
+	struct mount_info *parent; /* the origin parent for the child*/
+	struct list_head node;
+};
+
+static int do_mnt_remap(struct mount_info *m)
+{
+	int len;
+
+	if (m->nsid->type == NS_OTHER) {
+		/*
+		 * m->mountpoint already contains a roots_yard prefix and
+		 * it has a fixed size, so it can be just replaced.
+		 */
+		len = print_ns_root(m->nsid, remap_id, m->mountpoint, PATH_MAX);
+		m->mountpoint[len] = '/';
+	} else if (m->nsid->type == NS_ROOT) {
+		char root[PATH_MAX], *mp, *ns_mp;
+		int len, ret;
+
+		/*
+		 * Allocate a new path in the roots yard. m->mountpoint in the
+		 * root namespace doesn't have a roots_yard prefix, so its
+		 * size has to be changed and a new storage has to be
+		 * allocated.
+		 */
+		mp = m->mountpoint; ns_mp = m->ns_mountpoint;
+
+		len = print_ns_root(m->nsid, remap_id, root, PATH_MAX);
+
+		ret = get_mp_mountpoint(ns_mp, m, root, len);
+		if (ret < 0)
+			return ret;
+		xfree(mp);
+	} else
+		BUG();
+	return 0;
+}
+
+static int remap_mnt(struct mount_info *m)
+{
+	struct mnt_remap_entry *r;
+
+	if (!does_mnt_overmount(m))
+		return 0;
+
+	BUG_ON(!m->parent || !list_empty(&m->parent->mnt_share));
+
+	r = xmalloc(sizeof(struct mnt_remap_entry));
+	if (!r)
+		return -1;
+
+	r->child = m;
+	list_add(&r->node, &mnt_remap_list);
+
+	return 0;
+}
+
+static int handle_overmounts(struct mount_info *root)
+{
+	struct mnt_remap_entry *r;
+	struct mount_info *m;
+
+	/* It's imposiable to change a tree without interrupting
+	 * enumeration, so on the first step mounts are added
+	 * into mnt_remap_list and then they are connected to root_yard_mp.
+	 */
+	if (mnt_tree_for_each(root, remap_mnt))
+		return -1;
+
+	/* Move remapped mounts to root_yard */
+	list_for_each_entry(r, &mnt_remap_list, node) {
+		m = r->child;
+		r->parent = m->parent;
+		m->parent = root_yard_mp;
+		list_del(&m->siblings);
+		list_add(&m->siblings, &root_yard_mp->children);
+
+		remap_id++;
+		mnt_tree_for_each(m, do_mnt_remap);
+		pr_debug("Restore the %d mount in %s\n", m->mnt_id, m->mountpoint);
+	}
+
+	return 0;
+}
+
+/* Move remapped mounts to places where they have to be */
+static int move_back_mnt_remaps()
+{
+	struct mnt_remap_entry *r;
+
+	list_for_each_entry(r, &mnt_remap_list, node) {
+		struct mount_info *m = r->child;
+		char path[PATH_MAX];
+		int len;
+
+		if (m->nsid->type == NS_ROOT) {
+			path[0] = '.';
+			strncpy(path + 1, m->ns_mountpoint, PATH_MAX - 1);
+		} else {
+			strncpy(path, m->mountpoint, PATH_MAX);
+			len = print_ns_root(m->nsid, 0, path, PATH_MAX);
+			path[len] = '/';
+		}
+
+		pr_debug("Move mount %s -> %s\n", m->mountpoint, path);
+		if (mount(m->mountpoint, path, NULL, MS_MOVE, NULL)) {
+			pr_perror("Unable to move mount %s -> %s", m->mountpoint, path);
+			return -1;
+		}
+
+		/* Insert child back to its place in the tree */
+		list_del(&r->child->siblings);
+		list_add(&r->child->siblings, &r->parent->children);
+		r->child->parent = r->parent;
+	}
+
+	return 0;
+}
+
 static int cr_pivot_root(char *root)
 {
 	char tmp_dir_tmpl[] = "crtools-put-root.XXXXXX";
@@ -3394,6 +3532,7 @@ void fini_restore_mntns(void)
  */
 static int populate_roots_yard(void)
 {
+	struct mnt_remap_entry *r;
 	char path[PATH_MAX];
 	struct ns_id *nsid;
 
@@ -3414,6 +3553,13 @@ static int populate_roots_yard(void)
 		}
 	}
 
+	list_for_each_entry(r, &mnt_remap_list, node) {
+		if (mkdirpat(AT_FDCWD, r->child->mountpoint)) {
+			pr_perror("Unable to create %s", r->child->mountpoint);
+			return -1;
+		}
+	}
+
 	return 0;
 }
 
@@ -3462,6 +3608,9 @@ static int populate_mnt_ns(void)
 	if (validate_mounts(mntinfo, false))
 		return -1;
 
+	if (handle_overmounts(pms))
+		return -1;
+
 	/*
 	 * Set properties for the root before mounting a root yard,
 	 * otherwise the root yard can be propagated into the host
@@ -3479,6 +3628,9 @@ static int populate_mnt_ns(void)
 	ret = mnt_tree_for_each(pms, do_mount_one);
 	mnt_tree_for_each(pms, do_close_one);
 
+	if (ret == 0 && move_back_mnt_remaps())
+		return -1;
+
 	if (umount_clean_path())
 		return -1;
 	return ret;
-- 
2.7.4



More information about the CRIU mailing list