diff options
| author | David Howells <dhowells@redhat.com> | 2018-11-05 12:40:31 -0500 |
|---|---|---|
| committer | Al Viro <viro@zeniv.linux.org.uk> | 2019-03-20 18:49:06 -0400 |
| commit | 44dfd84a6d54a675e35ab618d9fab47b36cb78cd (patch) | |
| tree | 6b5542f217686066119c9bdfed2182e2c2a6d4a9 | |
| parent | 2db154b3ea8e14b04fee23e3fdfd5e9d17fbc6ae (diff) | |
teach move_mount(2) to work with OPEN_TREE_CLONE
Allow a detached tree created by open_tree(..., OPEN_TREE_CLONE) to be
attached by move_mount(2).
If by the time of final fput() of OPEN_TREE_CLONE-opened file its tree is
not detached anymore, it won't be dissolved. move_mount(2) is adjusted
to handle detached source.
That gives us equivalents of mount --bind and mount --rbind.
Thanks also to Alan Jenkins <alan.christopher.jenkins@gmail.com> for
providing a whole bunch of ways to break things using this interface.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
| -rw-r--r-- | fs/namespace.c | 62 |
1 files changed, 55 insertions, 7 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index dc600f53de9d..1e72d19fa4f8 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
| @@ -1842,10 +1842,16 @@ void dissolve_on_fput(struct vfsmount *mnt) | |||
| 1842 | namespace_lock(); | 1842 | namespace_lock(); |
| 1843 | lock_mount_hash(); | 1843 | lock_mount_hash(); |
| 1844 | ns = real_mount(mnt)->mnt_ns; | 1844 | ns = real_mount(mnt)->mnt_ns; |
| 1845 | umount_tree(real_mount(mnt), UMOUNT_CONNECTED); | 1845 | if (ns) { |
| 1846 | if (is_anon_ns(ns)) | ||
| 1847 | umount_tree(real_mount(mnt), UMOUNT_CONNECTED); | ||
| 1848 | else | ||
| 1849 | ns = NULL; | ||
| 1850 | } | ||
| 1846 | unlock_mount_hash(); | 1851 | unlock_mount_hash(); |
| 1847 | namespace_unlock(); | 1852 | namespace_unlock(); |
| 1848 | free_mnt_ns(ns); | 1853 | if (ns) |
| 1854 | free_mnt_ns(ns); | ||
| 1849 | } | 1855 | } |
| 1850 | 1856 | ||
| 1851 | void drop_collected_mounts(struct vfsmount *mnt) | 1857 | void drop_collected_mounts(struct vfsmount *mnt) |
| @@ -2081,6 +2087,10 @@ static int attach_recursive_mnt(struct mount *source_mnt, | |||
| 2081 | attach_mnt(source_mnt, dest_mnt, dest_mp); | 2087 | attach_mnt(source_mnt, dest_mnt, dest_mp); |
| 2082 | touch_mnt_namespace(source_mnt->mnt_ns); | 2088 | touch_mnt_namespace(source_mnt->mnt_ns); |
| 2083 | } else { | 2089 | } else { |
| 2090 | if (source_mnt->mnt_ns) { | ||
| 2091 | /* move from anon - the caller will destroy */ | ||
| 2092 | list_del_init(&source_mnt->mnt_ns->list); | ||
| 2093 | } | ||
| 2084 | mnt_set_mountpoint(dest_mnt, dest_mp, source_mnt); | 2094 | mnt_set_mountpoint(dest_mnt, dest_mp, source_mnt); |
| 2085 | commit_tree(source_mnt); | 2095 | commit_tree(source_mnt); |
| 2086 | } | 2096 | } |
| @@ -2539,13 +2549,37 @@ static inline int tree_contains_unbindable(struct mount *mnt) | |||
| 2539 | return 0; | 2549 | return 0; |
| 2540 | } | 2550 | } |
| 2541 | 2551 | ||
| 2552 | /* | ||
| 2553 | * Check that there aren't references to earlier/same mount namespaces in the | ||
| 2554 | * specified subtree. Such references can act as pins for mount namespaces | ||
| 2555 | * that aren't checked by the mount-cycle checking code, thereby allowing | ||
| 2556 | * cycles to be made. | ||
| 2557 | */ | ||
| 2558 | static bool check_for_nsfs_mounts(struct mount *subtree) | ||
| 2559 | { | ||
| 2560 | struct mount *p; | ||
| 2561 | bool ret = false; | ||
| 2562 | |||
| 2563 | lock_mount_hash(); | ||
| 2564 | for (p = subtree; p; p = next_mnt(p, subtree)) | ||
| 2565 | if (mnt_ns_loop(p->mnt.mnt_root)) | ||
| 2566 | goto out; | ||
| 2567 | |||
| 2568 | ret = true; | ||
| 2569 | out: | ||
| 2570 | unlock_mount_hash(); | ||
| 2571 | return ret; | ||
| 2572 | } | ||
| 2573 | |||
| 2542 | static int do_move_mount(struct path *old_path, struct path *new_path) | 2574 | static int do_move_mount(struct path *old_path, struct path *new_path) |
| 2543 | { | 2575 | { |
| 2544 | struct path parent_path = {.mnt = NULL, .dentry = NULL}; | 2576 | struct path parent_path = {.mnt = NULL, .dentry = NULL}; |
| 2577 | struct mnt_namespace *ns; | ||
| 2545 | struct mount *p; | 2578 | struct mount *p; |
| 2546 | struct mount *old; | 2579 | struct mount *old; |
| 2547 | struct mountpoint *mp; | 2580 | struct mountpoint *mp; |
| 2548 | int err; | 2581 | int err; |
| 2582 | bool attached; | ||
| 2549 | 2583 | ||
| 2550 | mp = lock_mount(new_path); | 2584 | mp = lock_mount(new_path); |
| 2551 | if (IS_ERR(mp)) | 2585 | if (IS_ERR(mp)) |
| @@ -2553,12 +2587,19 @@ static int do_move_mount(struct path *old_path, struct path *new_path) | |||
| 2553 | 2587 | ||
| 2554 | old = real_mount(old_path->mnt); | 2588 | old = real_mount(old_path->mnt); |
| 2555 | p = real_mount(new_path->mnt); | 2589 | p = real_mount(new_path->mnt); |
| 2590 | attached = mnt_has_parent(old); | ||
| 2591 | ns = old->mnt_ns; | ||
| 2556 | 2592 | ||
| 2557 | err = -EINVAL; | 2593 | err = -EINVAL; |
| 2558 | if (!check_mnt(p) || !check_mnt(old)) | 2594 | /* The mountpoint must be in our namespace. */ |
| 2595 | if (!check_mnt(p)) | ||
| 2559 | goto out; | 2596 | goto out; |
| 2560 | 2597 | ||
| 2561 | if (!mnt_has_parent(old)) | 2598 | /* The thing moved should be either ours or completely unattached. */ |
| 2599 | if (attached && !check_mnt(old)) | ||
| 2600 | goto out; | ||
| 2601 | |||
| 2602 | if (!attached && !is_anon_ns(ns)) | ||
| 2562 | goto out; | 2603 | goto out; |
| 2563 | 2604 | ||
| 2564 | if (old->mnt.mnt_flags & MNT_LOCKED) | 2605 | if (old->mnt.mnt_flags & MNT_LOCKED) |
| @@ -2573,7 +2614,7 @@ static int do_move_mount(struct path *old_path, struct path *new_path) | |||
| 2573 | /* | 2614 | /* |
| 2574 | * Don't move a mount residing in a shared parent. | 2615 | * Don't move a mount residing in a shared parent. |
| 2575 | */ | 2616 | */ |
| 2576 | if (IS_MNT_SHARED(old->mnt_parent)) | 2617 | if (attached && IS_MNT_SHARED(old->mnt_parent)) |
| 2577 | goto out; | 2618 | goto out; |
| 2578 | /* | 2619 | /* |
| 2579 | * Don't move a mount tree containing unbindable mounts to a destination | 2620 | * Don't move a mount tree containing unbindable mounts to a destination |
| @@ -2582,12 +2623,14 @@ static int do_move_mount(struct path *old_path, struct path *new_path) | |||
| 2582 | if (IS_MNT_SHARED(p) && tree_contains_unbindable(old)) | 2623 | if (IS_MNT_SHARED(p) && tree_contains_unbindable(old)) |
| 2583 | goto out; | 2624 | goto out; |
| 2584 | err = -ELOOP; | 2625 | err = -ELOOP; |
| 2626 | if (!check_for_nsfs_mounts(old)) | ||
| 2627 | goto out; | ||
| 2585 | for (; mnt_has_parent(p); p = p->mnt_parent) | 2628 | for (; mnt_has_parent(p); p = p->mnt_parent) |
| 2586 | if (p == old) | 2629 | if (p == old) |
| 2587 | goto out; | 2630 | goto out; |
| 2588 | 2631 | ||
| 2589 | err = attach_recursive_mnt(old, real_mount(new_path->mnt), mp, | 2632 | err = attach_recursive_mnt(old, real_mount(new_path->mnt), mp, |
| 2590 | &parent_path); | 2633 | attached ? &parent_path : NULL); |
| 2591 | if (err) | 2634 | if (err) |
| 2592 | goto out; | 2635 | goto out; |
| 2593 | 2636 | ||
| @@ -2596,8 +2639,11 @@ static int do_move_mount(struct path *old_path, struct path *new_path) | |||
| 2596 | list_del_init(&old->mnt_expire); | 2639 | list_del_init(&old->mnt_expire); |
| 2597 | out: | 2640 | out: |
| 2598 | unlock_mount(mp); | 2641 | unlock_mount(mp); |
| 2599 | if (!err) | 2642 | if (!err) { |
| 2600 | path_put(&parent_path); | 2643 | path_put(&parent_path); |
| 2644 | if (!attached) | ||
| 2645 | free_mnt_ns(ns); | ||
| 2646 | } | ||
| 2601 | return err; | 2647 | return err; |
| 2602 | } | 2648 | } |
| 2603 | 2649 | ||
| @@ -3289,6 +3335,8 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, | |||
| 3289 | 3335 | ||
| 3290 | /* | 3336 | /* |
| 3291 | * Move a mount from one place to another. | 3337 | * Move a mount from one place to another. |
| 3338 | * In combination with open_tree(OPEN_TREE_CLONE [| AT_RECURSIVE]) it can be | ||
| 3339 | * used to copy a mount subtree. | ||
| 3292 | * | 3340 | * |
| 3293 | * Note the flags value is a combination of MOVE_MOUNT_* flags. | 3341 | * Note the flags value is a combination of MOVE_MOUNT_* flags. |
| 3294 | */ | 3342 | */ |
