Blob Blame History Raw
From 579cdbc0567451ed2df89584a267c13ba7365b89 Mon Sep 17 00:00:00 2001
From: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Date: Fri, 20 Dec 2019 17:26:07 +0300
Subject: [PATCH 161/245] mount: setup mnt_bind list before using it in
 mnt_is_external

Before this patch mnt_is_external() used non-populated mnt_bind list
when called from resolve_shared_mounts(), thus it could work not as
intended.

Let's add separate helper search_bindmounts() for populating mnt_bind
list, and add mnt_bind_is_populated to differentiate between
non-populated list and just empty populated list. This way we can add a
BUG_ON to mnt_is_external to catch such order problems in future.

Cherry-picked from Virtuozzo criu:
https://src.openvz.org/projects/OVZ/repos/criu/commits/e464c1c6d
Cherry-picked from Virtuozzo criu:
https://src.openvz.org/projects/OVZ/repos/criu/commits/8b22b30d5
Cherry-picked one hunk from Virtuozzo criu:
https://src.openvz.org/projects/OVZ/repos/criu/commits/ca9de41e3

Changes: simplify commit message, merge fixups: search bindmounts
earlier so that we have bindmounts info as early as possible, rename
mnt_no_bind to mnt_bind_is_populated and simplify it's logic a bit.

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

diff --git a/criu/include/mount.h b/criu/include/mount.h
index b49b55f3f..03959e1b0 100644
--- a/criu/include/mount.h
+++ b/criu/include/mount.h
@@ -73,6 +73,7 @@ struct mount_info {
 	struct list_head siblings;
 
 	struct list_head mnt_bind;	 /* circular list of derivatives of one real mount */
+	bool mnt_bind_is_populated;	 /* indicate that mnt_bind list is ready to use */
 	struct list_head mnt_share;	 /* circular list of shared mounts */
 	struct list_head mnt_slave_list; /* list of slave mounts */
 	struct list_head mnt_slave;	 /* slave list entry */
diff --git a/criu/mount.c b/criu/mount.c
index 9d560a0b2..de6722e69 100644
--- a/criu/mount.c
+++ b/criu/mount.c
@@ -571,6 +571,11 @@ static bool mnt_is_external(struct mount_info *m)
 				if (t->external)
 					return 1;
 
+		/*
+		 * Shouldn't use mnt_bind list before it was populated in search_bindmounts
+		 */
+		BUG_ON(!m->mnt_bind_is_populated);
+
 		if (m->master_id <= 0 && !list_empty(&m->mnt_bind))
 			list_for_each_entry(t, &m->mnt_bind, mnt_bind)
 				if (issubpath(m->root, t->root) && t->external)
@@ -892,6 +897,43 @@ static int same_propagation_group(struct mount_info *a, struct mount_info *b)
 	return 0;
 }
 
+/*
+ * Note: Only valid if called consequently on all mounts in mntinfo list.
+ *
+ * Note: We may want to iterate over all bindmounts of some mount, and we would
+ * use ->mnt_bind list for this, but iterating over ->mnt_bind list is
+ * obviously meaningless before search_bindmounts had actually put bindmounts
+ * in it. That's why we have ->mnt_bind_is_populated to protect from misuse of
+ * ->mnt_bind. (As ->mnt_bind list can validly be empty when mount has no
+ *  bindmounts we need separate field to indicate population.)
+ */
+static void __search_bindmounts(struct mount_info *mi)
+{
+	struct mount_info *t;
+
+	if (mi->mnt_bind_is_populated)
+		return;
+
+	for (t = mi->next; t; t = t->next) {
+		if (mounts_sb_equal(mi, t)) {
+			list_add(&t->mnt_bind, &mi->mnt_bind);
+			t->mnt_bind_is_populated = true;
+			pr_debug("\tThe mount %3d is bind for %3d (@%s -> @%s)\n", t->mnt_id, mi->mnt_id, t->mountpoint,
+				 mi->mountpoint);
+		}
+	}
+
+	mi->mnt_bind_is_populated = true;
+}
+
+static void search_bindmounts(void)
+{
+	struct mount_info *mi;
+
+	for (mi = mntinfo; mi; mi = mi->next)
+		__search_bindmounts(mi);
+}
+
 static int resolve_shared_mounts(struct mount_info *info, int root_master_id)
 {
 	struct mount_info *m, *t;
@@ -944,21 +986,6 @@ static int resolve_shared_mounts(struct mount_info *info, int root_master_id)
 			       m->mnt_id, m->mountpoint, m->master_id, m->shared_id);
 			return -1;
 		}
-
-		/* Search bind-mounts */
-		if (list_empty(&m->mnt_bind)) {
-			/*
-			 * A first mounted point will be set up as a source point
-			 * for others. Look at propagate_mount()
-			 */
-			for (t = m->next; t; t = t->next) {
-				if (mounts_sb_equal(m, t)) {
-					list_add(&t->mnt_bind, &m->mnt_bind);
-					pr_debug("\tThe mount %3d is bind for %3d (@%s -> @%s)\n", t->mnt_id, m->mnt_id,
-						 t->mountpoint, m->mountpoint);
-				}
-			}
-		}
 	}
 
 	/* Search propagation groups */
@@ -1539,6 +1566,7 @@ static __maybe_unused int add_cr_time_mount(struct mount_info *root, char *fsnam
 			break;
 	}
 
+	mi->mnt_bind_is_populated = true;
 	mi->nsid = parent->nsid;
 	mi->parent = parent;
 	mi->parent_mnt_id = parent->mnt_id;
@@ -3143,6 +3171,9 @@ int read_mnt_ns_img(void)
 	}
 
 	mntinfo = pms;
+
+	search_bindmounts();
+
 	return 0;
 }
 
@@ -3320,6 +3351,7 @@ static int populate_mnt_ns(void)
 
 	root_yard_mp->mountpoint = mnt_roots;
 	root_yard_mp->mounted = true;
+	root_yard_mp->mnt_bind_is_populated = true;
 
 	if (merge_mount_trees(root_yard_mp))
 		return -1;
@@ -3756,6 +3788,8 @@ int collect_mnt_namespaces(bool for_dump)
 	if (ret)
 		goto err;
 
+	search_bindmounts();
+
 #ifdef CONFIG_BINFMT_MISC_VIRTUALIZED
 	if (for_dump && !opts.has_binfmt_misc) {
 		unsigned int s_dev = 0;
-- 
2.35.1