Folks, could you check the following, on top of viro/vfs.git#fixes? do_move_mount(): split the checks in subtree-of-our-ns and entire-anon cases ... and fix the breakage in anon-to-anon case. There are two cases acceptable for do_move_mount() and mixing checks for those is making things hard to follow. One case is move of a subtree in caller's namespace. * source and destination must be in caller's namespace * source must be detachable from parent Another is moving the entire anon namespace elsewhere * source must be the root of anon namespace * target must either in caller's namespace or in a suitable anon namespace (see may_use_mount() for details). * target must not be in the same namespace as source. It's really easier to follow if tests are *not* mixed together... [[ This should be equivalent to what Christian has posted last night; please test on whatever tests you've got. ]] Signed-off-by: Al Viro <viro@xxxxxxxxxxxxxxxxxx> --- diff --git a/fs/namespace.c b/fs/namespace.c index 854099aafed5..59197109e6b9 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3656,37 +3656,29 @@ static int do_move_mount(struct path *old_path, ns = old->mnt_ns; err = -EINVAL; - if (!may_use_mount(p)) - goto out; - /* The thing moved must be mounted... */ if (!is_mounted(&old->mnt)) goto out; /* ... and either ours or the root of anon namespace */ - if (!(attached ? check_mnt(old) : is_anon_ns(ns))) - goto out; - - if (is_anon_ns(ns) && ns == p->mnt_ns) { - /* - * Ending up with two files referring to the root of the - * same anonymous mount namespace would cause an error - * as this would mean trying to move the same mount - * twice into the mount tree which would be rejected - * later. But be explicit about it right here. - */ - goto out; - } else if (is_anon_ns(p->mnt_ns)) { - /* - * Don't allow moving an attached mount tree to an - * anonymous mount tree. - */ - goto out; + if (check_mnt(old)) { + /* should be detachable from parent */ + if (!mnt_has_parent(old) || IS_MNT_LOCKED(old)) + goto out; + /* target should be ours */ + if (!check_mnt(p)) + goto out; + } else { + if (!is_anon_ns(ns) || mnt_has_parent(old)) + goto out; + /* not into the same anon ns - bail early in that case */ + if (ns == p->mnt_ns) + goto out; + /* target should be ours or in an acceptable anon ns */ + if (!may_use_mount(p)) + goto out; } - if (old->mnt.mnt_flags & MNT_LOCKED) - goto out; - if (!path_mounted(old_path)) goto out;