diff options
Diffstat (limited to 'fs/namespace.c')
-rw-r--r-- | fs/namespace.c | 63 |
1 files changed, 46 insertions, 17 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 9f5a084b239f..1487982dbc24 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -660,7 +660,10 @@ Enomem: | |||
660 | 660 | ||
661 | /* | 661 | /* |
662 | * @source_mnt : mount tree to be attached | 662 | * @source_mnt : mount tree to be attached |
663 | * @nd : place the mount tree @source_mnt is attached | 663 | * @nd : place the mount tree @source_mnt is attached |
664 | * @parent_nd : if non-null, detach the source_mnt from its parent and | ||
665 | * store the parent mount and mountpoint dentry. | ||
666 | * (done when source_mnt is moved) | ||
664 | * | 667 | * |
665 | * NOTE: in the table below explains the semantics when a source mount | 668 | * NOTE: in the table below explains the semantics when a source mount |
666 | * of a given type is attached to a destination mount of a given type. | 669 | * of a given type is attached to a destination mount of a given type. |
@@ -685,6 +688,21 @@ Enomem: | |||
685 | * (+) the cloned mount is created under the destination mount and is marked | 688 | * (+) the cloned mount is created under the destination mount and is marked |
686 | * as shared. The cloned mount is added to the peer group of the source | 689 | * as shared. The cloned mount is added to the peer group of the source |
687 | * mount. | 690 | * mount. |
691 | * --------------------------------------------- | ||
692 | * | MOVE MOUNT OPERATION | | ||
693 | * |******************************************** | ||
694 | * | source-->| shared | private | | ||
695 | * | dest | | | | ||
696 | * | | | | | | ||
697 | * | v | | | | ||
698 | * |******************************************** | ||
699 | * | shared | shared (+) | shared (+) | | ||
700 | * | | | | | ||
701 | * |non-shared| shared (+*) | private | | ||
702 | * ********************************************* | ||
703 | * (+) the mount is moved to the destination. And is then propagated to all | ||
704 | * the mounts in the propagation tree of the destination mount. | ||
705 | * (+*) the mount is moved to the destination. | ||
688 | * | 706 | * |
689 | * if the source mount is a tree, the operations explained above is | 707 | * if the source mount is a tree, the operations explained above is |
690 | * applied to each mount in the tree. | 708 | * applied to each mount in the tree. |
@@ -692,7 +710,7 @@ Enomem: | |||
692 | * in allocations. | 710 | * in allocations. |
693 | */ | 711 | */ |
694 | static int attach_recursive_mnt(struct vfsmount *source_mnt, | 712 | static int attach_recursive_mnt(struct vfsmount *source_mnt, |
695 | struct nameidata *nd) | 713 | struct nameidata *nd, struct nameidata *parent_nd) |
696 | { | 714 | { |
697 | LIST_HEAD(tree_list); | 715 | LIST_HEAD(tree_list); |
698 | struct vfsmount *dest_mnt = nd->mnt; | 716 | struct vfsmount *dest_mnt = nd->mnt; |
@@ -708,8 +726,14 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt, | |||
708 | } | 726 | } |
709 | 727 | ||
710 | spin_lock(&vfsmount_lock); | 728 | spin_lock(&vfsmount_lock); |
711 | mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt); | 729 | if (parent_nd) { |
712 | commit_tree(source_mnt); | 730 | detach_mnt(source_mnt, parent_nd); |
731 | attach_mnt(source_mnt, nd); | ||
732 | touch_namespace(current->namespace); | ||
733 | } else { | ||
734 | mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt); | ||
735 | commit_tree(source_mnt); | ||
736 | } | ||
713 | 737 | ||
714 | list_for_each_entry_safe(child, p, &tree_list, mnt_hash) { | 738 | list_for_each_entry_safe(child, p, &tree_list, mnt_hash) { |
715 | list_del_init(&child->mnt_hash); | 739 | list_del_init(&child->mnt_hash); |
@@ -740,7 +764,7 @@ static int graft_tree(struct vfsmount *mnt, struct nameidata *nd) | |||
740 | 764 | ||
741 | err = -ENOENT; | 765 | err = -ENOENT; |
742 | if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry)) | 766 | if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry)) |
743 | err = attach_recursive_mnt(mnt, nd); | 767 | err = attach_recursive_mnt(mnt, nd, NULL); |
744 | out_unlock: | 768 | out_unlock: |
745 | up(&nd->dentry->d_inode->i_sem); | 769 | up(&nd->dentry->d_inode->i_sem); |
746 | if (!err) | 770 | if (!err) |
@@ -869,35 +893,36 @@ static int do_move_mount(struct nameidata *nd, char *old_name) | |||
869 | if (IS_DEADDIR(nd->dentry->d_inode)) | 893 | if (IS_DEADDIR(nd->dentry->d_inode)) |
870 | goto out1; | 894 | goto out1; |
871 | 895 | ||
872 | spin_lock(&vfsmount_lock); | ||
873 | if (!IS_ROOT(nd->dentry) && d_unhashed(nd->dentry)) | 896 | if (!IS_ROOT(nd->dentry) && d_unhashed(nd->dentry)) |
874 | goto out2; | 897 | goto out1; |
875 | 898 | ||
876 | err = -EINVAL; | 899 | err = -EINVAL; |
877 | if (old_nd.dentry != old_nd.mnt->mnt_root) | 900 | if (old_nd.dentry != old_nd.mnt->mnt_root) |
878 | goto out2; | 901 | goto out1; |
879 | 902 | ||
880 | if (old_nd.mnt == old_nd.mnt->mnt_parent) | 903 | if (old_nd.mnt == old_nd.mnt->mnt_parent) |
881 | goto out2; | 904 | goto out1; |
882 | 905 | ||
883 | if (S_ISDIR(nd->dentry->d_inode->i_mode) != | 906 | if (S_ISDIR(nd->dentry->d_inode->i_mode) != |
884 | S_ISDIR(old_nd.dentry->d_inode->i_mode)) | 907 | S_ISDIR(old_nd.dentry->d_inode->i_mode)) |
885 | goto out2; | 908 | goto out1; |
886 | 909 | /* | |
910 | * Don't move a mount residing in a shared parent. | ||
911 | */ | ||
912 | if (old_nd.mnt->mnt_parent && IS_MNT_SHARED(old_nd.mnt->mnt_parent)) | ||
913 | goto out1; | ||
887 | err = -ELOOP; | 914 | err = -ELOOP; |
888 | for (p = nd->mnt; p->mnt_parent != p; p = p->mnt_parent) | 915 | for (p = nd->mnt; p->mnt_parent != p; p = p->mnt_parent) |
889 | if (p == old_nd.mnt) | 916 | if (p == old_nd.mnt) |
890 | goto out2; | 917 | goto out1; |
891 | err = 0; | ||
892 | 918 | ||
893 | detach_mnt(old_nd.mnt, &parent_nd); | 919 | if ((err = attach_recursive_mnt(old_nd.mnt, nd, &parent_nd))) |
894 | attach_mnt(old_nd.mnt, nd); | 920 | goto out1; |
895 | touch_namespace(current->namespace); | ||
896 | 921 | ||
922 | spin_lock(&vfsmount_lock); | ||
897 | /* if the mount is moved, it should no longer be expire | 923 | /* if the mount is moved, it should no longer be expire |
898 | * automatically */ | 924 | * automatically */ |
899 | list_del_init(&old_nd.mnt->mnt_expire); | 925 | list_del_init(&old_nd.mnt->mnt_expire); |
900 | out2: | ||
901 | spin_unlock(&vfsmount_lock); | 926 | spin_unlock(&vfsmount_lock); |
902 | out1: | 927 | out1: |
903 | up(&nd->dentry->d_inode->i_sem); | 928 | up(&nd->dentry->d_inode->i_sem); |
@@ -1467,6 +1492,10 @@ asmlinkage long sys_pivot_root(const char __user * new_root, | |||
1467 | down_write(&namespace_sem); | 1492 | down_write(&namespace_sem); |
1468 | down(&old_nd.dentry->d_inode->i_sem); | 1493 | down(&old_nd.dentry->d_inode->i_sem); |
1469 | error = -EINVAL; | 1494 | error = -EINVAL; |
1495 | if (IS_MNT_SHARED(old_nd.mnt) || | ||
1496 | IS_MNT_SHARED(new_nd.mnt->mnt_parent) || | ||
1497 | IS_MNT_SHARED(user_nd.mnt->mnt_parent)) | ||
1498 | goto out2; | ||
1470 | if (!check_mnt(user_nd.mnt)) | 1499 | if (!check_mnt(user_nd.mnt)) |
1471 | goto out2; | 1500 | goto out2; |
1472 | error = -ENOENT; | 1501 | error = -ENOENT; |