[5/9] mount: put all mounts which propagate from each other to a list

Submitted by Pavel Tikhomirov on July 10, 2018, 4:02 p.m.

Details

Message ID 20180710160231.1292-6-ptikhomirov@virtuozzo.com
State Accepted
Series "mount: better handling of mount propagation"
Commit 64d8b1cfa6194124e7564fc2748939e7409a1dda
Headers show

Commit Message

Pavel Tikhomirov July 10, 2018, 4:02 p.m.
These information will help improving the restore of tricky mounts
configurations.

Function same_propagation_group checks if two mounts were created
simultaneousely through shared mount propagation, and the main part of
these - they should be in exaclty the same place inside the share of
their parents.

Function root_path_from_parent prints the mountpoint path
relative to the root of the parent's share, by first substracting
parent's mountpoint from our mountpoint and second prepending parents
root path (relative to the root of it's file system), e.g:

id	parent_id	root	mountpoint
1	0		/	/
2	1		/	/parent_a
3	1		/dir	/parent_b
4	2		/	/parent_a/dir/a
5	3		/	/parent_b/a

(Let 2 and 3 be a shared group)

For mount 4 root_path_from_parent gives:
"/parent_a/dir/a" - "/parent_a" == "/dir/a"
"/" + "/dir/a" == "/dir/a"

For mount 5:
"/parent_b/a" - "/parent_b" == "/a"
"/dir" + "/a" == "/dir/a"

So mounts 4 and 5 are a propagation group.

Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
---
 criu/include/mount.h |  1 +
 criu/mount.c         | 98 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 99 insertions(+)

Patch hide | download patch | download mbox

diff --git a/criu/include/mount.h b/criu/include/mount.h
index ed771ffac..84c62c8d0 100644
--- a/criu/include/mount.h
+++ b/criu/include/mount.h
@@ -65,6 +65,7 @@  struct mount_info {
 	struct list_head	mnt_slave_list;	/* list of slave mounts */
 	struct list_head	mnt_slave;	/* slave list entry */
 	struct mount_info	*mnt_master;	/* slave is on master->mnt_slave_list */
+	struct list_head	mnt_propagate;	/* circular list of mounts which propagate from each other */
 
 	struct list_head	postpone;
 
diff --git a/criu/mount.c b/criu/mount.c
index 637aebfd9..40d841984 100644
--- a/criu/mount.c
+++ b/criu/mount.c
@@ -931,6 +931,74 @@  static int resolve_external_mounts(struct mount_info *info)
 	return 0;
 }
 
+static int root_path_from_parent(struct mount_info *m, char *buf, int size)
+{
+	bool head_slash = false, tail_slash = false;
+	int p_len = strlen(m->parent->mountpoint),
+	    m_len = strlen(m->mountpoint),
+	    len;
+
+	if (!m->parent)
+		return -1;
+
+	len = snprintf(buf, size, "%s", m->parent->root);
+	if (len >= size)
+		return -1;
+
+	BUG_ON(len <= 0);
+	if (buf[len-1] == '/')
+		tail_slash = true;
+
+	size -= len;
+	buf += len;
+
+	len = m_len - p_len;
+	BUG_ON(len < 0);
+	if (len) {
+		if (m->mountpoint[p_len] == '/')
+			head_slash = true;
+
+		len = snprintf(buf, size, "%s%s",
+			       (!tail_slash && !head_slash) ? "/" : "",
+			       m->mountpoint + p_len + (tail_slash && head_slash));
+		if (len >= size)
+			return -1;
+	}
+
+	return 0;
+}
+
+static int same_propagation_group(struct mount_info *a, struct mount_info *b) {
+	char root_path_a[PATH_MAX], root_path_b[PATH_MAX];
+
+	/*
+	 * If mounts are in same propagation group:
+	 * 1) Their parents should be different
+	 * 2) Their parents should be together in same shared group
+	 */
+	if (!a->parent || !b->parent || a->parent == b->parent ||
+	    a->parent->shared_id != b->parent->shared_id)
+		return 0;
+
+	if (root_path_from_parent(a, root_path_a, PATH_MAX)) {
+		pr_err("Failed to get root path for mount %d\n", a->mnt_id);
+		return -1;
+	}
+
+	if (root_path_from_parent(b, root_path_b, PATH_MAX)) {
+		pr_err("Failed to get root path for mount %d\n", b->mnt_id);
+		return -1;
+	}
+
+	/*
+	 * 3) Their mountpoints relative to the root of the superblock of their
+	 * parent's share should be equal
+	 */
+	if (!strcmp(root_path_a, root_path_b))
+		return 1;
+	return 0;
+}
+
 static int resolve_shared_mounts(struct mount_info *info, int root_master_id)
 {
 	struct mount_info *m, *t;
@@ -1003,6 +1071,35 @@  static int resolve_shared_mounts(struct mount_info *info, int root_master_id)
 		}
 	}
 
+	/* Search propagation groups */
+	for (m = info; m; m = m->next) {
+		struct mount_info *sparent;
+
+		if (!list_empty(&m->mnt_propagate))
+			continue;
+
+		if (!m->parent || !m->parent->shared_id)
+			continue;
+
+		list_for_each_entry(sparent, &m->parent->mnt_share, mnt_share) {
+			struct mount_info *schild;
+
+			list_for_each_entry(schild, &sparent->children, siblings) {
+				int ret;
+
+				ret = same_propagation_group(m, schild);
+				if (ret < 0)
+					return -1;
+				else if (ret) {
+					BUG_ON(!mounts_equal(m, schild));
+					pr_debug("\tMount %3d is in same propagation group with %3d (@%s ~ @%s)\n",
+						 m->mnt_id, schild->mnt_id, m->mountpoint, schild->mountpoint);
+					list_add(&schild->mnt_propagate, &m->mnt_propagate);
+				}
+			}
+		}
+	}
+
 	return 0;
 }
 
@@ -2700,6 +2797,7 @@  struct mount_info *mnt_entry_alloc()
 		INIT_LIST_HEAD(&new->mnt_slave_list);
 		INIT_LIST_HEAD(&new->mnt_share);
 		INIT_LIST_HEAD(&new->mnt_bind);
+		INIT_LIST_HEAD(&new->mnt_propagate);
 		INIT_LIST_HEAD(&new->postpone);
 	}
 	return new;