[CRIU] [PATCH 6/9] mntns: rework validation to support non-root shared bind-mounts
Andrew Vagin
avagin at parallels.com
Wed Nov 5 11:34:56 PST 2014
On Wed, Nov 05, 2014 at 08:52:49PM +0400, Pavel Emelyanov wrote:
> On 10/31/2014 04:12 PM, 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
> > / /a/x
> > / /a/x/y
> > / /a/z
> > /x /b
> > / /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.
> >
> > Signed-off-by: Andrey Vagin <avagin at openvz.org>
> > ---
> > mount.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
> > 1 file changed, 95 insertions(+), 12 deletions(-)
> >
> > diff --git a/mount.c b/mount.c
> > index edc69f9..d982e75 100644
> > --- a/mount.c
> > +++ b/mount.c
> > @@ -372,28 +372,111 @@ 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.
> > + */
> > +
> > + /*
> > + * 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 (strncmp(t->root, m->root, strlen(t->root)) == 0)
>
> What if t->root is "/foo/abc" and m->root is "/foo/abcd"? This strncmp
> would return 0, but probably t should not even be considered as m's wider
> mount, shouldn't it?
No, it should not. Thanks.
>
> > + break;
> > + }
> >
> > - if (!list_empty(&m->parent->mnt_share))
> > + /*
> > + * 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->mountpoint[tpm - 1] == '/')
> > + tpm--;
> > + if (m->mountpoint[mpm - 1] == '/')
> > + mpm--;
> > +
> > + /*
> > + * ct: | t->root | /reletive mount point |
> > + * cm: | m->root | /reletive mount point |
> > + * | A | B |
> > + * \ct->mountpoint + tpm
> > + * \cm->root + strlen(t->root)
> > + */
> > 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 */
> > + 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);
>
> OK, so for each mount in t (widest) we try to find a mount in m (the one being
> validated now), that is a) equal and b) has path visible in t, right?
>
> If so, two questions:
>
> a) why the logic is inverted? Shouldn't we scan for m's mounts and find the
> respective image in t?
Look at the answer on the second question. We want to get an empty
children list at the end.
>
> b) why tossing the m's children list? Why not just find the respective mount
> and leave it in?
We need to compare two unsorted lists. We enumirate all elements from
one of them, then find a proper element in a second list and remove it
from there.
At the end we check that a second list is empty. Otherwise it contains
elements, which don't exist in a first list.
>
> > 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)) {
> > + list_splice(&children, &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);
> > return -1;
> > }
> > -
> > + list_splice(&children, &m->children);
> > return 0;
> > }
> >
> > @@ -406,7 +489,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