[CRIU] [PATCH 1/4] mntns: rework validation to support non-root shared bind-mounts (v2)
Pavel Emelyanov
xemul at parallels.com
Tue Nov 11 04:29:55 PST 2014
On 11/11/2014 02:24 AM, Andrey Vagin wrote:
> 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 "/".
I've fixed the issubpath() routine, now issubpath("/foo/bar", "/") is true :)
Would that help to simplify the patch?
> 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;
>
> /*
>
More information about the CRIU
mailing list