mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-22 07:27:12 +08:00
Merge tag 'pull-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull mount fixes from Al Viro: "A couple of races around legalize_mnt vs umount (both fairly old and hard to hit) plus two bugs in move_mount(2) - both around 'move detached subtree in place' logics" * tag 'pull-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: fix IS_MNT_PROPAGATING uses do_move_mount(): don't leak MNTNS_PROPAGATING on failures do_umount(): add missing barrier before refcount checks in sync case __legitimize_mnt(): check for MNT_SYNC_UMOUNT should be under mount_lock
This commit is contained in:
@@ -787,15 +787,11 @@ int __legitimize_mnt(struct vfsmount *bastard, unsigned seq)
|
||||
return 0;
|
||||
mnt = real_mount(bastard);
|
||||
mnt_add_count(mnt, 1);
|
||||
smp_mb(); // see mntput_no_expire()
|
||||
smp_mb(); // see mntput_no_expire() and do_umount()
|
||||
if (likely(!read_seqretry(&mount_lock, seq)))
|
||||
return 0;
|
||||
if (bastard->mnt_flags & MNT_SYNC_UMOUNT) {
|
||||
mnt_add_count(mnt, -1);
|
||||
return 1;
|
||||
}
|
||||
lock_mount_hash();
|
||||
if (unlikely(bastard->mnt_flags & MNT_DOOMED)) {
|
||||
if (unlikely(bastard->mnt_flags & (MNT_SYNC_UMOUNT | MNT_DOOMED))) {
|
||||
mnt_add_count(mnt, -1);
|
||||
unlock_mount_hash();
|
||||
return 1;
|
||||
@@ -2048,6 +2044,7 @@ static int do_umount(struct mount *mnt, int flags)
|
||||
umount_tree(mnt, UMOUNT_PROPAGATE);
|
||||
retval = 0;
|
||||
} else {
|
||||
smp_mb(); // paired with __legitimize_mnt()
|
||||
shrink_submounts(mnt);
|
||||
retval = -EBUSY;
|
||||
if (!propagate_mount_busy(mnt, 2)) {
|
||||
@@ -3560,7 +3557,8 @@ static int can_move_mount_beneath(const struct path *from,
|
||||
* @mnt_from itself. This defeats the whole purpose of mounting
|
||||
* @mnt_from beneath @mnt_to.
|
||||
*/
|
||||
if (propagation_would_overmount(parent_mnt_to, mnt_from, mp))
|
||||
if (check_mnt(mnt_from) &&
|
||||
propagation_would_overmount(parent_mnt_to, mnt_from, mp))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
@@ -3718,15 +3716,14 @@ static int do_move_mount(struct path *old_path,
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (is_anon_ns(ns))
|
||||
ns->mntns_flags &= ~MNTNS_PROPAGATING;
|
||||
|
||||
/* if the mount is moved, it should no longer be expire
|
||||
* automatically */
|
||||
list_del_init(&old->mnt_expire);
|
||||
if (attached)
|
||||
put_mountpoint(old_mp);
|
||||
out:
|
||||
if (is_anon_ns(ns))
|
||||
ns->mntns_flags &= ~MNTNS_PROPAGATING;
|
||||
unlock_mount(mp);
|
||||
if (!err) {
|
||||
if (attached) {
|
||||
|
||||
17
fs/pnode.c
17
fs/pnode.c
@@ -150,7 +150,7 @@ static struct mount *propagation_next(struct mount *m,
|
||||
struct mount *origin)
|
||||
{
|
||||
/* are there any slaves of this mount? */
|
||||
if (!IS_MNT_PROPAGATED(m) && !list_empty(&m->mnt_slave_list))
|
||||
if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list))
|
||||
return first_slave(m);
|
||||
|
||||
while (1) {
|
||||
@@ -174,7 +174,7 @@ static struct mount *skip_propagation_subtree(struct mount *m,
|
||||
* Advance m such that propagation_next will not return
|
||||
* the slaves of m.
|
||||
*/
|
||||
if (!IS_MNT_PROPAGATED(m) && !list_empty(&m->mnt_slave_list))
|
||||
if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list))
|
||||
m = last_slave(m);
|
||||
|
||||
return m;
|
||||
@@ -185,7 +185,7 @@ static struct mount *next_group(struct mount *m, struct mount *origin)
|
||||
while (1) {
|
||||
while (1) {
|
||||
struct mount *next;
|
||||
if (!IS_MNT_PROPAGATED(m) && !list_empty(&m->mnt_slave_list))
|
||||
if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list))
|
||||
return first_slave(m);
|
||||
next = next_peer(m);
|
||||
if (m->mnt_group_id == origin->mnt_group_id) {
|
||||
@@ -226,11 +226,15 @@ static int propagate_one(struct mount *m, struct mountpoint *dest_mp)
|
||||
struct mount *child;
|
||||
int type;
|
||||
/* skip ones added by this propagate_mnt() */
|
||||
if (IS_MNT_PROPAGATED(m))
|
||||
if (IS_MNT_NEW(m))
|
||||
return 0;
|
||||
/* skip if mountpoint isn't covered by it */
|
||||
/* skip if mountpoint isn't visible in m */
|
||||
if (!is_subdir(dest_mp->m_dentry, m->mnt.mnt_root))
|
||||
return 0;
|
||||
/* skip if m is in the anon_ns we are emptying */
|
||||
if (m->mnt_ns->mntns_flags & MNTNS_PROPAGATING)
|
||||
return 0;
|
||||
|
||||
if (peers(m, last_dest)) {
|
||||
type = CL_MAKE_SHARED;
|
||||
} else {
|
||||
@@ -380,9 +384,6 @@ bool propagation_would_overmount(const struct mount *from,
|
||||
if (!IS_MNT_SHARED(from))
|
||||
return false;
|
||||
|
||||
if (IS_MNT_PROPAGATED(to))
|
||||
return false;
|
||||
|
||||
if (to->mnt.mnt_root != mp->m_dentry)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#define IS_MNT_SHARED(m) ((m)->mnt.mnt_flags & MNT_SHARED)
|
||||
#define IS_MNT_SLAVE(m) ((m)->mnt_master)
|
||||
#define IS_MNT_PROPAGATED(m) (!(m)->mnt_ns || ((m)->mnt_ns->mntns_flags & MNTNS_PROPAGATING))
|
||||
#define IS_MNT_NEW(m) (!(m)->mnt_ns)
|
||||
#define CLEAR_MNT_SHARED(m) ((m)->mnt.mnt_flags &= ~MNT_SHARED)
|
||||
#define IS_MNT_UNBINDABLE(m) ((m)->mnt.mnt_flags & MNT_UNBINDABLE)
|
||||
#define IS_MNT_MARKED(m) ((m)->mnt.mnt_flags & MNT_MARKED)
|
||||
|
||||
Reference in New Issue
Block a user