|
@@ -660,7 +660,10 @@ Enomem:
|
|
|
|
|
|
/*
|
|
|
* @source_mnt : mount tree to be attached
|
|
|
- * @nd : place the mount tree @source_mnt is 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)
|
|
|
*
|
|
|
* 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.
|
|
@@ -685,6 +688,21 @@ Enomem:
|
|
|
* (+) the cloned mount is created under the destination mount and is marked
|
|
|
* as shared. The cloned mount is added to the peer group of the source
|
|
|
* mount.
|
|
|
+ * ---------------------------------------------
|
|
|
+ * | MOVE MOUNT OPERATION |
|
|
|
+ * |********************************************
|
|
|
+ * | source-->| shared | private |
|
|
|
+ * | dest | | |
|
|
|
+ * | | | | |
|
|
|
+ * | v | | |
|
|
|
+ * |********************************************
|
|
|
+ * | shared | shared (+) | shared (+) |
|
|
|
+ * | | | |
|
|
|
+ * |non-shared| shared (+*) | private |
|
|
|
+ * *********************************************
|
|
|
+ * (+) the mount is moved to the destination. And is then propagated to all
|
|
|
+ * the mounts in the propagation tree of the destination mount.
|
|
|
+ * (+*) the mount is moved to the destination.
|
|
|
*
|
|
|
* if the source mount is a tree, the operations explained above is
|
|
|
* applied to each mount in the tree.
|
|
@@ -692,7 +710,7 @@ Enomem:
|
|
|
* in allocations.
|
|
|
*/
|
|
|
static int attach_recursive_mnt(struct vfsmount *source_mnt,
|
|
|
- struct nameidata *nd)
|
|
|
+ struct nameidata *nd, struct nameidata *parent_nd)
|
|
|
{
|
|
|
LIST_HEAD(tree_list);
|
|
|
struct vfsmount *dest_mnt = nd->mnt;
|
|
@@ -708,8 +726,14 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt,
|
|
|
}
|
|
|
|
|
|
spin_lock(&vfsmount_lock);
|
|
|
- mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);
|
|
|
- commit_tree(source_mnt);
|
|
|
+ if (parent_nd) {
|
|
|
+ detach_mnt(source_mnt, parent_nd);
|
|
|
+ attach_mnt(source_mnt, nd);
|
|
|
+ touch_namespace(current->namespace);
|
|
|
+ } else {
|
|
|
+ mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);
|
|
|
+ commit_tree(source_mnt);
|
|
|
+ }
|
|
|
|
|
|
list_for_each_entry_safe(child, p, &tree_list, mnt_hash) {
|
|
|
list_del_init(&child->mnt_hash);
|
|
@@ -740,7 +764,7 @@ static int graft_tree(struct vfsmount *mnt, struct nameidata *nd)
|
|
|
|
|
|
err = -ENOENT;
|
|
|
if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry))
|
|
|
- err = attach_recursive_mnt(mnt, nd);
|
|
|
+ err = attach_recursive_mnt(mnt, nd, NULL);
|
|
|
out_unlock:
|
|
|
up(&nd->dentry->d_inode->i_sem);
|
|
|
if (!err)
|
|
@@ -869,35 +893,36 @@ static int do_move_mount(struct nameidata *nd, char *old_name)
|
|
|
if (IS_DEADDIR(nd->dentry->d_inode))
|
|
|
goto out1;
|
|
|
|
|
|
- spin_lock(&vfsmount_lock);
|
|
|
if (!IS_ROOT(nd->dentry) && d_unhashed(nd->dentry))
|
|
|
- goto out2;
|
|
|
+ goto out1;
|
|
|
|
|
|
err = -EINVAL;
|
|
|
if (old_nd.dentry != old_nd.mnt->mnt_root)
|
|
|
- goto out2;
|
|
|
+ goto out1;
|
|
|
|
|
|
if (old_nd.mnt == old_nd.mnt->mnt_parent)
|
|
|
- goto out2;
|
|
|
+ goto out1;
|
|
|
|
|
|
if (S_ISDIR(nd->dentry->d_inode->i_mode) !=
|
|
|
S_ISDIR(old_nd.dentry->d_inode->i_mode))
|
|
|
- goto out2;
|
|
|
-
|
|
|
+ goto out1;
|
|
|
+ /*
|
|
|
+ * Don't move a mount residing in a shared parent.
|
|
|
+ */
|
|
|
+ if (old_nd.mnt->mnt_parent && IS_MNT_SHARED(old_nd.mnt->mnt_parent))
|
|
|
+ goto out1;
|
|
|
err = -ELOOP;
|
|
|
for (p = nd->mnt; p->mnt_parent != p; p = p->mnt_parent)
|
|
|
if (p == old_nd.mnt)
|
|
|
- goto out2;
|
|
|
- err = 0;
|
|
|
+ goto out1;
|
|
|
|
|
|
- detach_mnt(old_nd.mnt, &parent_nd);
|
|
|
- attach_mnt(old_nd.mnt, nd);
|
|
|
- touch_namespace(current->namespace);
|
|
|
+ if ((err = attach_recursive_mnt(old_nd.mnt, nd, &parent_nd)))
|
|
|
+ goto out1;
|
|
|
|
|
|
+ spin_lock(&vfsmount_lock);
|
|
|
/* if the mount is moved, it should no longer be expire
|
|
|
* automatically */
|
|
|
list_del_init(&old_nd.mnt->mnt_expire);
|
|
|
-out2:
|
|
|
spin_unlock(&vfsmount_lock);
|
|
|
out1:
|
|
|
up(&nd->dentry->d_inode->i_sem);
|
|
@@ -1467,6 +1492,10 @@ asmlinkage long sys_pivot_root(const char __user * new_root,
|
|
|
down_write(&namespace_sem);
|
|
|
down(&old_nd.dentry->d_inode->i_sem);
|
|
|
error = -EINVAL;
|
|
|
+ if (IS_MNT_SHARED(old_nd.mnt) ||
|
|
|
+ IS_MNT_SHARED(new_nd.mnt->mnt_parent) ||
|
|
|
+ IS_MNT_SHARED(user_nd.mnt->mnt_parent))
|
|
|
+ goto out2;
|
|
|
if (!check_mnt(user_nd.mnt))
|
|
|
goto out2;
|
|
|
error = -ENOENT;
|