Merge tag 'wireless-2023-09-27' of git://git.kernel.org/pub/scm/linux/kernel/git...
[platform/kernel/linux-rpi.git] / fs / namespace.c
index 54847db..e157efc 100644 (file)
@@ -309,9 +309,16 @@ static unsigned int mnt_get_writers(struct mount *mnt)
 
 static int mnt_is_readonly(struct vfsmount *mnt)
 {
-       if (mnt->mnt_sb->s_readonly_remount)
+       if (READ_ONCE(mnt->mnt_sb->s_readonly_remount))
                return 1;
-       /* Order wrt setting s_flags/s_readonly_remount in do_remount() */
+       /*
+        * The barrier pairs with the barrier in sb_start_ro_state_change()
+        * making sure if we don't see s_readonly_remount set yet, we also will
+        * not see any superblock / mount flag changes done by remount.
+        * It also pairs with the barrier in sb_end_ro_state_change()
+        * assuring that if we see s_readonly_remount already cleared, we will
+        * see the values of superblock / mount flags updated by remount.
+        */
        smp_rmb();
        return __mnt_is_readonly(mnt);
 }
@@ -364,9 +371,11 @@ int __mnt_want_write(struct vfsmount *m)
                }
        }
        /*
-        * After the slowpath clears MNT_WRITE_HOLD, mnt_is_readonly will
-        * be set to match its requirements. So we must not load that until
-        * MNT_WRITE_HOLD is cleared.
+        * The barrier pairs with the barrier sb_start_ro_state_change() making
+        * sure that if we see MNT_WRITE_HOLD cleared, we will also see
+        * s_readonly_remount set (or even SB_RDONLY / MNT_READONLY flags) in
+        * mnt_is_readonly() and bail in case we are racing with remount
+        * read-only.
         */
        smp_rmb();
        if (mnt_is_readonly(m)) {
@@ -588,10 +597,8 @@ int sb_prepare_remount_readonly(struct super_block *sb)
        if (!err && atomic_long_read(&sb->s_remove_count))
                err = -EBUSY;
 
-       if (!err) {
-               sb->s_readonly_remount = 1;
-               smp_wmb();
-       }
+       if (!err)
+               sb_start_ro_state_change(sb);
        list_for_each_entry(mnt, &sb->s_mounts, mnt_instance) {
                if (mnt->mnt.mnt_flags & MNT_WRITE_HOLD)
                        mnt->mnt.mnt_flags &= ~MNT_WRITE_HOLD;
@@ -658,9 +665,25 @@ static bool legitimize_mnt(struct vfsmount *bastard, unsigned seq)
        return false;
 }
 
-/*
- * find the first mount at @dentry on vfsmount @mnt.
- * call under rcu_read_lock()
+/**
+ * __lookup_mnt - find first child mount
+ * @mnt:       parent mount
+ * @dentry:    mountpoint
+ *
+ * If @mnt has a child mount @c mounted @dentry find and return it.
+ *
+ * Note that the child mount @c need not be unique. There are cases
+ * where shadow mounts are created. For example, during mount
+ * propagation when a source mount @mnt whose root got overmounted by a
+ * mount @o after path lookup but before @namespace_sem could be
+ * acquired gets copied and propagated. So @mnt gets copied including
+ * @o. When @mnt is propagated to a destination mount @d that already
+ * has another mount @n mounted at the same mountpoint then the source
+ * mount @mnt will be tucked beneath @n, i.e., @n will be mounted on
+ * @mnt and @mnt mounted on @d. Now both @n and @o are mounted at @mnt
+ * on @dentry.
+ *
+ * Return: The first child of @mnt mounted @dentry or NULL.
  */
 struct mount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry)
 {
@@ -910,6 +933,33 @@ void mnt_set_mountpoint(struct mount *mnt,
        hlist_add_head(&child_mnt->mnt_mp_list, &mp->m_list);
 }
 
+/**
+ * mnt_set_mountpoint_beneath - mount a mount beneath another one
+ *
+ * @new_parent: the source mount
+ * @top_mnt:    the mount beneath which @new_parent is mounted
+ * @new_mp:     the new mountpoint of @top_mnt on @new_parent
+ *
+ * Remove @top_mnt from its current mountpoint @top_mnt->mnt_mp and
+ * parent @top_mnt->mnt_parent and mount it on top of @new_parent at
+ * @new_mp. And mount @new_parent on the old parent and old
+ * mountpoint of @top_mnt.
+ *
+ * Context: This function expects namespace_lock() and lock_mount_hash()
+ *          to have been acquired in that order.
+ */
+static void mnt_set_mountpoint_beneath(struct mount *new_parent,
+                                      struct mount *top_mnt,
+                                      struct mountpoint *new_mp)
+{
+       struct mount *old_top_parent = top_mnt->mnt_parent;
+       struct mountpoint *old_top_mp = top_mnt->mnt_mp;
+
+       mnt_set_mountpoint(old_top_parent, old_top_mp, new_parent);
+       mnt_change_mountpoint(new_parent, new_mp, top_mnt);
+}
+
+
 static void __attach_mnt(struct mount *mnt, struct mount *parent)
 {
        hlist_add_head_rcu(&mnt->mnt_hash,
@@ -917,15 +967,42 @@ static void __attach_mnt(struct mount *mnt, struct mount *parent)
        list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
 }
 
-/*
- * vfsmount lock must be held for write
+/**
+ * attach_mnt - mount a mount, attach to @mount_hashtable and parent's
+ *              list of child mounts
+ * @parent:  the parent
+ * @mnt:     the new mount
+ * @mp:      the new mountpoint
+ * @beneath: whether to mount @mnt beneath or on top of @parent
+ *
+ * If @beneath is false, mount @mnt at @mp on @parent. Then attach @mnt
+ * to @parent's child mount list and to @mount_hashtable.
+ *
+ * If @beneath is true, remove @mnt from its current parent and
+ * mountpoint and mount it on @mp on @parent, and mount @parent on the
+ * old parent and old mountpoint of @mnt. Finally, attach @parent to
+ * @mnt_hashtable and @parent->mnt_parent->mnt_mounts.
+ *
+ * Note, when __attach_mnt() is called @mnt->mnt_parent already points
+ * to the correct parent.
+ *
+ * Context: This function expects namespace_lock() and lock_mount_hash()
+ *          to have been acquired in that order.
  */
-static void attach_mnt(struct mount *mnt,
-                       struct mount *parent,
-                       struct mountpoint *mp)
+static void attach_mnt(struct mount *mnt, struct mount *parent,
+                      struct mountpoint *mp, bool beneath)
 {
-       mnt_set_mountpoint(parent, mp, mnt);
-       __attach_mnt(mnt, parent);
+       if (beneath)
+               mnt_set_mountpoint_beneath(mnt, parent, mp);
+       else
+               mnt_set_mountpoint(parent, mp, mnt);
+       /*
+        * Note, @mnt->mnt_parent has to be used. If @mnt was mounted
+        * beneath @parent then @mnt will need to be attached to
+        * @parent's old parent, not @parent. IOW, @mnt->mnt_parent
+        * isn't the same mount as @parent.
+        */
+       __attach_mnt(mnt, mnt->mnt_parent);
 }
 
 void mnt_change_mountpoint(struct mount *parent, struct mountpoint *mp, struct mount *mnt)
@@ -937,7 +1014,7 @@ void mnt_change_mountpoint(struct mount *parent, struct mountpoint *mp, struct m
        hlist_del_init(&mnt->mnt_mp_list);
        hlist_del_init_rcu(&mnt->mnt_hash);
 
-       attach_mnt(mnt, parent, mp);
+       attach_mnt(mnt, parent, mp, false);
 
        put_mountpoint(old_mp);
        mnt_add_count(old_parent, -1);
@@ -1767,6 +1844,19 @@ bool may_mount(void)
        return ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN);
 }
 
+/**
+ * path_mounted - check whether path is mounted
+ * @path: path to check
+ *
+ * Determine whether @path refers to the root of a mount.
+ *
+ * Return: true if @path is the root of a mount, false if not.
+ */
+static inline bool path_mounted(const struct path *path)
+{
+       return path->mnt->mnt_root == path->dentry;
+}
+
 static void warn_mandlock(void)
 {
        pr_warn_once("=======================================================\n"
@@ -1782,7 +1872,7 @@ static int can_umount(const struct path *path, int flags)
 
        if (!may_mount())
                return -EPERM;
-       if (path->dentry != path->mnt->mnt_root)
+       if (!path_mounted(path))
                return -EINVAL;
        if (!check_mnt(mnt))
                return -EINVAL;
@@ -1925,7 +2015,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
                                goto out;
                        lock_mount_hash();
                        list_add_tail(&q->mnt_list, &res->mnt_list);
-                       attach_mnt(q, parent, p->mnt_mp);
+                       attach_mnt(q, parent, p->mnt_mp, false);
                        unlock_mount_hash();
                }
        }
@@ -2134,12 +2224,17 @@ int count_mounts(struct mnt_namespace *ns, struct mount *mnt)
        return 0;
 }
 
-/*
- *  @source_mnt : mount tree to be attached
- *  @nd         : place the mount tree @source_mnt is attached
- *  @parent_nd  : if non-null, detach the source_mnt from its parent and
- *                store the parent mount and mountpoint dentry.
- *                (done when source_mnt is moved)
+enum mnt_tree_flags_t {
+       MNT_TREE_MOVE = BIT(0),
+       MNT_TREE_BENEATH = BIT(1),
+};
+
+/**
+ * attach_recursive_mnt - attach a source mount tree
+ * @source_mnt: mount tree to be attached
+ * @top_mnt:    mount that @source_mnt will be mounted on or mounted beneath
+ * @dest_mp:    the mountpoint @source_mnt will be mounted at
+ * @flags:      modify how @source_mnt is supposed to be attached
  *
  *  NOTE: in the table below explains the semantics when a source mount
  *  of a given type is attached to a destination mount of a given type.
@@ -2196,22 +2291,28 @@ int count_mounts(struct mnt_namespace *ns, struct mount *mnt)
  * applied to each mount in the tree.
  * Must be called without spinlocks held, since this function can sleep
  * in allocations.
+ *
+ * Context: The function expects namespace_lock() to be held.
+ * Return: If @source_mnt was successfully attached 0 is returned.
+ *         Otherwise a negative error code is returned.
  */
 static int attach_recursive_mnt(struct mount *source_mnt,
-                       struct mount *dest_mnt,
-                       struct mountpoint *dest_mp,
-                       bool moving)
+                               struct mount *top_mnt,
+                               struct mountpoint *dest_mp,
+                               enum mnt_tree_flags_t flags)
 {
        struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns;
        HLIST_HEAD(tree_list);
-       struct mnt_namespace *ns = dest_mnt->mnt_ns;
+       struct mnt_namespace *ns = top_mnt->mnt_ns;
        struct mountpoint *smp;
-       struct mount *child, *p;
+       struct mount *child, *dest_mnt, *p;
        struct hlist_node *n;
-       int err;
+       int err = 0;
+       bool moving = flags & MNT_TREE_MOVE, beneath = flags & MNT_TREE_BENEATH;
 
-       /* Preallocate a mountpoint in case the new mounts need
-        * to be tucked under other mounts.
+       /*
+        * Preallocate a mountpoint in case the new mounts need to be
+        * mounted beneath mounts on the same mountpoint.
         */
        smp = get_mountpoint(source_mnt->mnt.mnt_root);
        if (IS_ERR(smp))
@@ -2224,29 +2325,41 @@ static int attach_recursive_mnt(struct mount *source_mnt,
                        goto out;
        }
 
+       if (beneath)
+               dest_mnt = top_mnt->mnt_parent;
+       else
+               dest_mnt = top_mnt;
+
        if (IS_MNT_SHARED(dest_mnt)) {
                err = invent_group_ids(source_mnt, true);
                if (err)
                        goto out;
                err = propagate_mnt(dest_mnt, dest_mp, source_mnt, &tree_list);
-               lock_mount_hash();
-               if (err)
-                       goto out_cleanup_ids;
+       }
+       lock_mount_hash();
+       if (err)
+               goto out_cleanup_ids;
+
+       if (IS_MNT_SHARED(dest_mnt)) {
                for (p = source_mnt; p; p = next_mnt(p, source_mnt))
                        set_mnt_shared(p);
-       } else {
-               lock_mount_hash();
        }
+
        if (moving) {
+               if (beneath)
+                       dest_mp = smp;
                unhash_mnt(source_mnt);
-               attach_mnt(source_mnt, dest_mnt, dest_mp);
+               attach_mnt(source_mnt, top_mnt, dest_mp, beneath);
                touch_mnt_namespace(source_mnt->mnt_ns);
        } else {
                if (source_mnt->mnt_ns) {
                        /* move from anon - the caller will destroy */
                        list_del_init(&source_mnt->mnt_ns->list);
                }
-               mnt_set_mountpoint(dest_mnt, dest_mp, source_mnt);
+               if (beneath)
+                       mnt_set_mountpoint_beneath(source_mnt, top_mnt, smp);
+               else
+                       mnt_set_mountpoint(dest_mnt, dest_mp, source_mnt);
                commit_tree(source_mnt);
        }
 
@@ -2286,33 +2399,101 @@ static int attach_recursive_mnt(struct mount *source_mnt,
        return err;
 }
 
-static struct mountpoint *lock_mount(struct path *path)
+/**
+ * do_lock_mount - lock mount and mountpoint
+ * @path:    target path
+ * @beneath: whether the intention is to mount beneath @path
+ *
+ * Follow the mount stack on @path until the top mount @mnt is found. If
+ * the initial @path->{mnt,dentry} is a mountpoint lookup the first
+ * mount stacked on top of it. Then simply follow @{mnt,mnt->mnt_root}
+ * until nothing is stacked on top of it anymore.
+ *
+ * Acquire the inode_lock() on the top mount's ->mnt_root to protect
+ * against concurrent removal of the new mountpoint from another mount
+ * namespace.
+ *
+ * If @beneath is requested, acquire inode_lock() on @mnt's mountpoint
+ * @mp on @mnt->mnt_parent must be acquired. This protects against a
+ * concurrent unlink of @mp->mnt_dentry from another mount namespace
+ * where @mnt doesn't have a child mount mounted @mp. A concurrent
+ * removal of @mnt->mnt_root doesn't matter as nothing will be mounted
+ * on top of it for @beneath.
+ *
+ * In addition, @beneath needs to make sure that @mnt hasn't been
+ * unmounted or moved from its current mountpoint in between dropping
+ * @mount_lock and acquiring @namespace_sem. For the !@beneath case @mnt
+ * being unmounted would be detected later by e.g., calling
+ * check_mnt(mnt) in the function it's called from. For the @beneath
+ * case however, it's useful to detect it directly in do_lock_mount().
+ * If @mnt hasn't been unmounted then @mnt->mnt_mountpoint still points
+ * to @mnt->mnt_mp->m_dentry. But if @mnt has been unmounted it will
+ * point to @mnt->mnt_root and @mnt->mnt_mp will be NULL.
+ *
+ * Return: Either the target mountpoint on the top mount or the top
+ *         mount's mountpoint.
+ */
+static struct mountpoint *do_lock_mount(struct path *path, bool beneath)
 {
-       struct vfsmount *mnt;
-       struct dentry *dentry = path->dentry;
-retry:
-       inode_lock(dentry->d_inode);
-       if (unlikely(cant_mount(dentry))) {
-               inode_unlock(dentry->d_inode);
-               return ERR_PTR(-ENOENT);
-       }
-       namespace_lock();
-       mnt = lookup_mnt(path);
-       if (likely(!mnt)) {
-               struct mountpoint *mp = get_mountpoint(dentry);
-               if (IS_ERR(mp)) {
+       struct vfsmount *mnt = path->mnt;
+       struct dentry *dentry;
+       struct mountpoint *mp = ERR_PTR(-ENOENT);
+
+       for (;;) {
+               struct mount *m;
+
+               if (beneath) {
+                       m = real_mount(mnt);
+                       read_seqlock_excl(&mount_lock);
+                       dentry = dget(m->mnt_mountpoint);
+                       read_sequnlock_excl(&mount_lock);
+               } else {
+                       dentry = path->dentry;
+               }
+
+               inode_lock(dentry->d_inode);
+               if (unlikely(cant_mount(dentry))) {
+                       inode_unlock(dentry->d_inode);
+                       goto out;
+               }
+
+               namespace_lock();
+
+               if (beneath && (!is_mounted(mnt) || m->mnt_mountpoint != dentry)) {
                        namespace_unlock();
                        inode_unlock(dentry->d_inode);
-                       return mp;
+                       goto out;
                }
-               return mp;
+
+               mnt = lookup_mnt(path);
+               if (likely(!mnt))
+                       break;
+
+               namespace_unlock();
+               inode_unlock(dentry->d_inode);
+               if (beneath)
+                       dput(dentry);
+               path_put(path);
+               path->mnt = mnt;
+               path->dentry = dget(mnt->mnt_root);
        }
-       namespace_unlock();
-       inode_unlock(path->dentry->d_inode);
-       path_put(path);
-       path->mnt = mnt;
-       dentry = path->dentry = dget(mnt->mnt_root);
-       goto retry;
+
+       mp = get_mountpoint(dentry);
+       if (IS_ERR(mp)) {
+               namespace_unlock();
+               inode_unlock(dentry->d_inode);
+       }
+
+out:
+       if (beneath)
+               dput(dentry);
+
+       return mp;
+}
+
+static inline struct mountpoint *lock_mount(struct path *path)
+{
+       return do_lock_mount(path, false);
 }
 
 static void unlock_mount(struct mountpoint *where)
@@ -2336,7 +2517,7 @@ static int graft_tree(struct mount *mnt, struct mount *p, struct mountpoint *mp)
              d_is_dir(mnt->mnt.mnt_root))
                return -ENOTDIR;
 
-       return attach_recursive_mnt(mnt, p, mp, false);
+       return attach_recursive_mnt(mnt, p, mp, 0);
 }
 
 /*
@@ -2367,7 +2548,7 @@ static int do_change_type(struct path *path, int ms_flags)
        int type;
        int err = 0;
 
-       if (path->dentry != path->mnt->mnt_root)
+       if (!path_mounted(path))
                return -EINVAL;
 
        type = flags_to_propagation_type(ms_flags);
@@ -2643,7 +2824,7 @@ static int do_reconfigure_mnt(struct path *path, unsigned int mnt_flags)
        if (!check_mnt(mnt))
                return -EINVAL;
 
-       if (path->dentry != mnt->mnt.mnt_root)
+       if (!path_mounted(path))
                return -EINVAL;
 
        if (!can_change_locked_flags(mnt, mnt_flags))
@@ -2682,7 +2863,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
        if (!check_mnt(mnt))
                return -EINVAL;
 
-       if (path->dentry != path->mnt->mnt_root)
+       if (!path_mounted(path))
                return -EINVAL;
 
        if (!can_change_locked_flags(mnt, mnt_flags))
@@ -2772,9 +2953,9 @@ static int do_set_group(struct path *from_path, struct path *to_path)
 
        err = -EINVAL;
        /* To and From paths should be mount roots */
-       if (from_path->dentry != from_path->mnt->mnt_root)
+       if (!path_mounted(from_path))
                goto out;
-       if (to_path->dentry != to_path->mnt->mnt_root)
+       if (!path_mounted(to_path))
                goto out;
 
        /* Setting sharing groups is only allowed across same superblock */
@@ -2818,7 +2999,110 @@ out:
        return err;
 }
 
-static int do_move_mount(struct path *old_path, struct path *new_path)
+/**
+ * path_overmounted - check if path is overmounted
+ * @path: path to check
+ *
+ * Check if path is overmounted, i.e., if there's a mount on top of
+ * @path->mnt with @path->dentry as mountpoint.
+ *
+ * Context: This function expects namespace_lock() to be held.
+ * Return: If path is overmounted true is returned, false if not.
+ */
+static inline bool path_overmounted(const struct path *path)
+{
+       rcu_read_lock();
+       if (unlikely(__lookup_mnt(path->mnt, path->dentry))) {
+               rcu_read_unlock();
+               return true;
+       }
+       rcu_read_unlock();
+       return false;
+}
+
+/**
+ * can_move_mount_beneath - check that we can mount beneath the top mount
+ * @from: mount to mount beneath
+ * @to:   mount under which to mount
+ *
+ * - Make sure that @to->dentry is actually the root of a mount under
+ *   which we can mount another mount.
+ * - Make sure that nothing can be mounted beneath the caller's current
+ *   root or the rootfs of the namespace.
+ * - Make sure that the caller can unmount the topmost mount ensuring
+ *   that the caller could reveal the underlying mountpoint.
+ * - Ensure that nothing has been mounted on top of @from before we
+ *   grabbed @namespace_sem to avoid creating pointless shadow mounts.
+ * - Prevent mounting beneath a mount if the propagation relationship
+ *   between the source mount, parent mount, and top mount would lead to
+ *   nonsensical mount trees.
+ *
+ * Context: This function expects namespace_lock() to be held.
+ * Return: On success 0, and on error a negative error code is returned.
+ */
+static int can_move_mount_beneath(const struct path *from,
+                                 const struct path *to,
+                                 const struct mountpoint *mp)
+{
+       struct mount *mnt_from = real_mount(from->mnt),
+                    *mnt_to = real_mount(to->mnt),
+                    *parent_mnt_to = mnt_to->mnt_parent;
+
+       if (!mnt_has_parent(mnt_to))
+               return -EINVAL;
+
+       if (!path_mounted(to))
+               return -EINVAL;
+
+       if (IS_MNT_LOCKED(mnt_to))
+               return -EINVAL;
+
+       /* Avoid creating shadow mounts during mount propagation. */
+       if (path_overmounted(from))
+               return -EINVAL;
+
+       /*
+        * Mounting beneath the rootfs only makes sense when the
+        * semantics of pivot_root(".", ".") are used.
+        */
+       if (&mnt_to->mnt == current->fs->root.mnt)
+               return -EINVAL;
+       if (parent_mnt_to == current->nsproxy->mnt_ns->root)
+               return -EINVAL;
+
+       for (struct mount *p = mnt_from; mnt_has_parent(p); p = p->mnt_parent)
+               if (p == mnt_to)
+                       return -EINVAL;
+
+       /*
+        * If the parent mount propagates to the child mount this would
+        * mean mounting @mnt_from on @mnt_to->mnt_parent and then
+        * propagating a copy @c of @mnt_from on top of @mnt_to. This
+        * defeats the whole purpose of mounting beneath another mount.
+        */
+       if (propagation_would_overmount(parent_mnt_to, mnt_to, mp))
+               return -EINVAL;
+
+       /*
+        * If @mnt_to->mnt_parent propagates to @mnt_from this would
+        * mean propagating a copy @c of @mnt_from on top of @mnt_from.
+        * Afterwards @mnt_from would be mounted on top of
+        * @mnt_to->mnt_parent and @mnt_to would be unmounted from
+        * @mnt->mnt_parent and remounted on @mnt_from. But since @c is
+        * already mounted on @mnt_from, @mnt_to would ultimately be
+        * remounted on top of @c. Afterwards, @mnt_from would be
+        * covered by a copy @c of @mnt_from and @c would be covered by
+        * @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))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int do_move_mount(struct path *old_path, struct path *new_path,
+                        bool beneath)
 {
        struct mnt_namespace *ns;
        struct mount *p;
@@ -2827,8 +3111,9 @@ static int do_move_mount(struct path *old_path, struct path *new_path)
        struct mountpoint *mp, *old_mp;
        int err;
        bool attached;
+       enum mnt_tree_flags_t flags = 0;
 
-       mp = lock_mount(new_path);
+       mp = do_lock_mount(new_path, beneath);
        if (IS_ERR(mp))
                return PTR_ERR(mp);
 
@@ -2836,6 +3121,8 @@ static int do_move_mount(struct path *old_path, struct path *new_path)
        p = real_mount(new_path->mnt);
        parent = old->mnt_parent;
        attached = mnt_has_parent(old);
+       if (attached)
+               flags |= MNT_TREE_MOVE;
        old_mp = old->mnt_mp;
        ns = old->mnt_ns;
 
@@ -2855,7 +3142,7 @@ static int do_move_mount(struct path *old_path, struct path *new_path)
        if (old->mnt.mnt_flags & MNT_LOCKED)
                goto out;
 
-       if (old_path->dentry != old_path->mnt->mnt_root)
+       if (!path_mounted(old_path))
                goto out;
 
        if (d_is_dir(new_path->dentry) !=
@@ -2866,6 +3153,17 @@ static int do_move_mount(struct path *old_path, struct path *new_path)
         */
        if (attached && IS_MNT_SHARED(parent))
                goto out;
+
+       if (beneath) {
+               err = can_move_mount_beneath(old_path, new_path, mp);
+               if (err)
+                       goto out;
+
+               err = -EINVAL;
+               p = p->mnt_parent;
+               flags |= MNT_TREE_BENEATH;
+       }
+
        /*
         * Don't move a mount tree containing unbindable mounts to a destination
         * mount which is shared.
@@ -2879,8 +3177,7 @@ static int do_move_mount(struct path *old_path, struct path *new_path)
                if (p == old)
                        goto out;
 
-       err = attach_recursive_mnt(old, real_mount(new_path->mnt), mp,
-                                  attached);
+       err = attach_recursive_mnt(old, real_mount(new_path->mnt), mp, flags);
        if (err)
                goto out;
 
@@ -2912,7 +3209,7 @@ static int do_move_mount_old(struct path *path, const char *old_name)
        if (err)
                return err;
 
-       err = do_move_mount(&old_path, path);
+       err = do_move_mount(&old_path, path, false);
        path_put(&old_path);
        return err;
 }
@@ -2937,8 +3234,7 @@ static int do_add_mount(struct mount *newmnt, struct mountpoint *mp,
        }
 
        /* Refuse the same filesystem on the same mount point */
-       if (path->mnt->mnt_sb == newmnt->mnt.mnt_sb &&
-           path->mnt->mnt_root == path->dentry)
+       if (path->mnt->mnt_sb == newmnt->mnt.mnt_sb && path_mounted(path))
                return -EBUSY;
 
        if (d_is_symlink(newmnt->mnt.mnt_root))
@@ -3079,13 +3375,10 @@ int finish_automount(struct vfsmount *m, const struct path *path)
                err = -ENOENT;
                goto discard_locked;
        }
-       rcu_read_lock();
-       if (unlikely(__lookup_mnt(path->mnt, dentry))) {
-               rcu_read_unlock();
+       if (path_overmounted(path)) {
                err = 0;
                goto discard_locked;
        }
-       rcu_read_unlock();
        mp = get_mountpoint(dentry);
        if (IS_ERR(mp)) {
                err = PTR_ERR(mp);
@@ -3777,6 +4070,10 @@ SYSCALL_DEFINE5(move_mount,
        if (flags & ~MOVE_MOUNT__MASK)
                return -EINVAL;
 
+       if ((flags & (MOVE_MOUNT_BENEATH | MOVE_MOUNT_SET_GROUP)) ==
+           (MOVE_MOUNT_BENEATH | MOVE_MOUNT_SET_GROUP))
+               return -EINVAL;
+
        /* If someone gives a pathname, they aren't permitted to move
         * from an fd that requires unmount as we can't get at the flag
         * to clear it afterwards.
@@ -3806,7 +4103,8 @@ SYSCALL_DEFINE5(move_mount,
        if (flags & MOVE_MOUNT_SET_GROUP)
                ret = do_set_group(&from_path, &to_path);
        else
-               ret = do_move_mount(&from_path, &to_path);
+               ret = do_move_mount(&from_path, &to_path,
+                                   (flags & MOVE_MOUNT_BENEATH));
 
 out_to:
        path_put(&to_path);
@@ -3917,11 +4215,11 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
        if (new_mnt == root_mnt || old_mnt == root_mnt)
                goto out4; /* loop, on the same file system  */
        error = -EINVAL;
-       if (root.mnt->mnt_root != root.dentry)
+       if (!path_mounted(&root))
                goto out4; /* not a mountpoint */
        if (!mnt_has_parent(root_mnt))
                goto out4; /* not attached */
-       if (new.mnt->mnt_root != new.dentry)
+       if (!path_mounted(&new))
                goto out4; /* not a mountpoint */
        if (!mnt_has_parent(new_mnt))
                goto out4; /* not attached */
@@ -3939,9 +4237,9 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
                root_mnt->mnt.mnt_flags &= ~MNT_LOCKED;
        }
        /* mount old root on put_old */
-       attach_mnt(root_mnt, old_mnt, old_mp);
+       attach_mnt(root_mnt, old_mnt, old_mp, false);
        /* mount new_root on / */
-       attach_mnt(new_mnt, root_parent, root_mp);
+       attach_mnt(new_mnt, root_parent, root_mp, false);
        mnt_add_count(root_parent, -1);
        touch_mnt_namespace(current->nsproxy->mnt_ns);
        /* A moved mount should not expire automatically */
@@ -4124,7 +4422,7 @@ static int do_mount_setattr(struct path *path, struct mount_kattr *kattr)
        struct mount *mnt = real_mount(path->mnt);
        int err = 0;
 
-       if (path->dentry != mnt->mnt.mnt_root)
+       if (!path_mounted(path))
                return -EINVAL;
 
        if (kattr->mnt_userns) {