diff options
-rw-r--r-- | fs/namespace.c | 34 | ||||
-rw-r--r-- | include/linux/mount.h | 1 |
2 files changed, 35 insertions, 0 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 7b1ca9ba0b0a..7e16a730559c 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -831,6 +831,10 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, | |||
831 | if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY)) | 831 | if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY)) |
832 | mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; | 832 | mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; |
833 | 833 | ||
834 | /* Don't allow unprivileged users to reveal what is under a mount */ | ||
835 | if ((flag & CL_UNPRIVILEGED) && list_empty(&old->mnt_expire)) | ||
836 | mnt->mnt.mnt_flags |= MNT_LOCKED; | ||
837 | |||
834 | atomic_inc(&sb->s_active); | 838 | atomic_inc(&sb->s_active); |
835 | mnt->mnt.mnt_sb = sb; | 839 | mnt->mnt.mnt_sb = sb; |
836 | mnt->mnt.mnt_root = dget(root); | 840 | mnt->mnt.mnt_root = dget(root); |
@@ -1327,6 +1331,8 @@ SYSCALL_DEFINE2(umount, char __user *, name, int, flags) | |||
1327 | goto dput_and_out; | 1331 | goto dput_and_out; |
1328 | if (!check_mnt(mnt)) | 1332 | if (!check_mnt(mnt)) |
1329 | goto dput_and_out; | 1333 | goto dput_and_out; |
1334 | if (mnt->mnt.mnt_flags & MNT_LOCKED) | ||
1335 | goto dput_and_out; | ||
1330 | 1336 | ||
1331 | retval = do_umount(mnt, flags); | 1337 | retval = do_umount(mnt, flags); |
1332 | dput_and_out: | 1338 | dput_and_out: |
@@ -1381,6 +1387,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry, | |||
1381 | if (IS_ERR(q)) | 1387 | if (IS_ERR(q)) |
1382 | return q; | 1388 | return q; |
1383 | 1389 | ||
1390 | q->mnt.mnt_flags &= ~MNT_LOCKED; | ||
1384 | q->mnt_mountpoint = mnt->mnt_mountpoint; | 1391 | q->mnt_mountpoint = mnt->mnt_mountpoint; |
1385 | 1392 | ||
1386 | p = mnt; | 1393 | p = mnt; |
@@ -1696,6 +1703,19 @@ static int do_change_type(struct path *path, int flag) | |||
1696 | return err; | 1703 | return err; |
1697 | } | 1704 | } |
1698 | 1705 | ||
1706 | static bool has_locked_children(struct mount *mnt, struct dentry *dentry) | ||
1707 | { | ||
1708 | struct mount *child; | ||
1709 | list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) { | ||
1710 | if (!is_subdir(child->mnt_mountpoint, dentry)) | ||
1711 | continue; | ||
1712 | |||
1713 | if (child->mnt.mnt_flags & MNT_LOCKED) | ||
1714 | return true; | ||
1715 | } | ||
1716 | return false; | ||
1717 | } | ||
1718 | |||
1699 | /* | 1719 | /* |
1700 | * do loopback mount. | 1720 | * do loopback mount. |
1701 | */ | 1721 | */ |
@@ -1731,6 +1751,9 @@ static int do_loopback(struct path *path, const char *old_name, | |||
1731 | if (!check_mnt(parent) || !check_mnt(old)) | 1751 | if (!check_mnt(parent) || !check_mnt(old)) |
1732 | goto out2; | 1752 | goto out2; |
1733 | 1753 | ||
1754 | if (!recurse && has_locked_children(old, old_path.dentry)) | ||
1755 | goto out2; | ||
1756 | |||
1734 | if (recurse) | 1757 | if (recurse) |
1735 | mnt = copy_tree(old, old_path.dentry, 0); | 1758 | mnt = copy_tree(old, old_path.dentry, 0); |
1736 | else | 1759 | else |
@@ -1741,6 +1764,8 @@ static int do_loopback(struct path *path, const char *old_name, | |||
1741 | goto out2; | 1764 | goto out2; |
1742 | } | 1765 | } |
1743 | 1766 | ||
1767 | mnt->mnt.mnt_flags &= ~MNT_LOCKED; | ||
1768 | |||
1744 | err = graft_tree(mnt, parent, mp); | 1769 | err = graft_tree(mnt, parent, mp); |
1745 | if (err) { | 1770 | if (err) { |
1746 | br_write_lock(&vfsmount_lock); | 1771 | br_write_lock(&vfsmount_lock); |
@@ -1853,6 +1878,9 @@ static int do_move_mount(struct path *path, const char *old_name) | |||
1853 | if (!check_mnt(p) || !check_mnt(old)) | 1878 | if (!check_mnt(p) || !check_mnt(old)) |
1854 | goto out1; | 1879 | goto out1; |
1855 | 1880 | ||
1881 | if (old->mnt.mnt_flags & MNT_LOCKED) | ||
1882 | goto out1; | ||
1883 | |||
1856 | err = -EINVAL; | 1884 | err = -EINVAL; |
1857 | if (old_path.dentry != old_path.mnt->mnt_root) | 1885 | if (old_path.dentry != old_path.mnt->mnt_root) |
1858 | goto out1; | 1886 | goto out1; |
@@ -2630,6 +2658,8 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, | |||
2630 | goto out4; | 2658 | goto out4; |
2631 | if (!check_mnt(root_mnt) || !check_mnt(new_mnt)) | 2659 | if (!check_mnt(root_mnt) || !check_mnt(new_mnt)) |
2632 | goto out4; | 2660 | goto out4; |
2661 | if (new_mnt->mnt.mnt_flags & MNT_LOCKED) | ||
2662 | goto out4; | ||
2633 | error = -ENOENT; | 2663 | error = -ENOENT; |
2634 | if (d_unlinked(new.dentry)) | 2664 | if (d_unlinked(new.dentry)) |
2635 | goto out4; | 2665 | goto out4; |
@@ -2653,6 +2683,10 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, | |||
2653 | br_write_lock(&vfsmount_lock); | 2683 | br_write_lock(&vfsmount_lock); |
2654 | detach_mnt(new_mnt, &parent_path); | 2684 | detach_mnt(new_mnt, &parent_path); |
2655 | detach_mnt(root_mnt, &root_parent); | 2685 | detach_mnt(root_mnt, &root_parent); |
2686 | if (root_mnt->mnt.mnt_flags & MNT_LOCKED) { | ||
2687 | new_mnt->mnt.mnt_flags |= MNT_LOCKED; | ||
2688 | root_mnt->mnt.mnt_flags &= ~MNT_LOCKED; | ||
2689 | } | ||
2656 | /* mount old root on put_old */ | 2690 | /* mount old root on put_old */ |
2657 | attach_mnt(root_mnt, old_mnt, old_mp); | 2691 | attach_mnt(root_mnt, old_mnt, old_mp); |
2658 | /* mount new_root on / */ | 2692 | /* mount new_root on / */ |
diff --git a/include/linux/mount.h b/include/linux/mount.h index 73005f9957ea..38cd98f112a0 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h | |||
@@ -48,6 +48,7 @@ struct mnt_namespace; | |||
48 | #define MNT_INTERNAL 0x4000 | 48 | #define MNT_INTERNAL 0x4000 |
49 | 49 | ||
50 | #define MNT_LOCK_READONLY 0x400000 | 50 | #define MNT_LOCK_READONLY 0x400000 |
51 | #define MNT_LOCKED 0x800000 | ||
51 | 52 | ||
52 | struct vfsmount { | 53 | struct vfsmount { |
53 | struct dentry *mnt_root; /* root of the mounted tree */ | 54 | struct dentry *mnt_root; /* root of the mounted tree */ |