[CRIU] [PATCH 1/4] mntns: rework validation to support non-root shared bind-mounts (v2)
Andrey Vagin
avagin at openvz.org
Mon Nov 10 14:24:53 PST 2014
A problem which is solved in this path is that some children can be
unaccessiable (unvisiable) for non-root bind-mounts
root mount point
-------------------
/ /a (shared:1)
/ /a/x
/ /a/x/y
/ /a/z
/x /b (shared:1)
/ /b/y
/b is a non-root bind-mount of /a
/y is visiable to both mounts
/z is vidiable only for /a
Before this patch we checked that the set of children is the same for
all mount in a shared group. Now we check that a visiable set of mounts
is the same for all mounts in a shared group.
Now we take the next mount in the shared group, which is wider or equal
to current and compare children between them.
Before this patch validate_shared(m) validates the m->parent mount.
Now it validates the "m" mount. So you can find following lines in the
patch:
- if (m->parent->shared_id && validate_shared(m))
+ if (m->shared_id && validate_shared(m))
We doesn't support shared mounts with different set of children.
Here is an example of such case can be created:
mount tmpfs a /a
mount --make-shared /a
mkdir /a/b
mount tmpfs b /a/b
mount --bind /a /c
In this case /c doesn't have the /b child. To support such cases,
we need to sort all shared mounts accoding with a set of children.
v2: If root is equal to "/", its len should be zero. We expect that the
last symbol in a path is not "/".
Signed-off-by: Andrey Vagin <avagin at openvz.org>
---
mount.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 117 insertions(+), 12 deletions(-)
diff --git a/mount.c b/mount.c
index 086b640..7751d84 100644
--- a/mount.c
+++ b/mount.c
@@ -372,28 +372,133 @@ static int try_resolve_ext_mount(struct mount_info *info)
info->external = em;
return 0;
}
+
static int validate_shared(struct mount_info *m)
{
- struct mount_info *ct, *t;
+ struct mount_info *t, *ct, *cm, *tmp;
+ int t_root_len, m_root_len, tpm, mpm;
+ LIST_HEAD(children);
+
+ /*
+ * Check that all mounts in one shared group has the same set of
+ * children. Only visible children are accounted. A non-root bind-mount
+ * doesn't see children out of its root and it's excpected case.
+ *
+ * Here is a few conditions:
+ * 1. t is wider than m
+ * 2. We search a wider mount in the same direction, so when we
+ * enumirate all mounts, we can't be sure that all of them
+ * has the same set of children.
+ */
- if (list_empty(&m->parent->mnt_share))
+ /*
+ * Try to find a mount, which is wider or equal.
+ * A is wider than B, if A->root is a substring of B->root.
+ */
+ list_for_each_entry(t, &m->mnt_share, mnt_share)
+ if (issubpath(m->root, t->root))
+ break;
+
+ /*
+ * The current mount is widest one in its shared group,
+ * all others should be compared with it.
+ */
+ if (&t->mnt_share == &m->mnt_share)
return 0;
- t = list_first_entry(&m->parent->mnt_share, struct mount_info, mnt_share);
+ /* A set of childrent which ar visiable for both should be the same */
+
+ t_root_len = strlen(t->root);
+ m_root_len = strlen(m->root);
+ tpm = strlen(t->mountpoint);
+ mpm = strlen(m->mountpoint);
+
+ if (t->root[t_root_len - 1] == '/')
+ t_root_len--;
+ if (m->root[m_root_len - 1] == '/')
+ m_root_len--;
+ if (t->mountpoint[tpm - 1] == '/')
+ tpm--;
+ if (m->mountpoint[mpm - 1] == '/')
+ mpm--;
+
+ /* For example:
+ * t->root = / t->mp = ./zdtm/live/static/mntns_root_bind.test
+ * m->root = /test m->mp = ./zdtm/live/static/mntns_root_bind.test/test.bind
+ * t_root_len = 0 tpm = 39
+ * m_root_len = 5 mpm = 49
+ * ct->root = / ct->mp = ./zdtm/live/static/mntns_root_bind.test/test/sub
+ * tp = /test/sub mp = /test len=5
+ */
+
+ /*
+ * ct: | t->root | child mount point |
+ * cm: | m->root | child mount point |
+ * ct: | | /test/sub |
+ * cm: | /test | /sub |
+ * | A | B |
+ * | ct->mountpoint + tpm
+ * | m->root + strlen(t->root)
+ */
+ /* Search a child, which is visiable in both mounts. */
list_for_each_entry(ct, &t->children, siblings) {
- if (mounts_equal(m, ct, false))
+ char *tp, *mp;
+ int len;
+
+ if (ct->is_ns_root)
+ continue;
+
+ tp = ct->mountpoint + tpm;
+ mp = m->root + t_root_len;
+ len = m_root_len - t_root_len;
+
+ /* A */
+
+ /* issubpath() can't be used here, because tp should not be
+ * equal to mp. Otherwise ct will be eqaul to m or its brother.
+ */
+ if (strncmp(tp, mp, len))
+ continue;
+
+ if (tp[len] != '/')
+ continue;
+
+ list_for_each_entry_safe(cm, tmp, &m->children, siblings) {
+ /* B */
+ if (strcmp(ct->mountpoint + tpm + len, cm->mountpoint + mpm))
+ continue;
+
+ if (!mounts_equal(cm, ct, false)) {
+ pr_err("Two shared mounts on same spaces have different mounts\n");
+ pr_err("%d:%s\n", cm->mnt_id, cm->mountpoint);
+ pr_err("%d:%s\n", ct->mnt_id, ct->mountpoint);
+ return -1;
+ }
+
+ list_move(&cm->siblings, &children);
break;
+ }
+ if (&cm->siblings == &m->children) {
+ list_splice(&children, &m->children);
+
+ pr_err("%d:%s and %d:%s have different set of mounts\n",
+ m->mnt_id, m->mountpoint, t->mnt_id, t->mountpoint);
+ pr_err("For example %d:%s\n", ct->mnt_id, ct->mountpoint);
+ return -1;
+ }
}
- if (&ct->siblings == &t->children) {
- pr_err("Two shared mounts %d, %d have different sets of children\n",
- m->parent->mnt_id, t->mnt_id);
- pr_err("%d:%s doesn't have a proper point for %d:%s\n",
- t->mnt_id, t->mountpoint,
- m->mnt_id, m->mountpoint);
+
+ if (!list_empty(&m->children)) {
+
+ cm = list_first_entry(&m->children, struct mount_info, siblings);
+ pr_err("%d:%s and %d:%s have different set of mounts\n",
+ m->mnt_id, m->mountpoint, t->mnt_id, t->mountpoint);
+ pr_err("For example %d:%s\n", cm->mnt_id, cm->mountpoint);
+ list_splice(&children, &m->children);
return -1;
}
-
+ list_splice(&children, &m->children);
return 0;
}
@@ -426,7 +531,7 @@ static int validate_mounts(struct mount_info *info, bool for_dump)
/* root mount can be any */
continue;
- if (m->parent->shared_id && validate_shared(m))
+ if (m->shared_id && validate_shared(m))
return -1;
/*
--
1.9.3
More information about the CRIU
mailing list