diff options
Diffstat (limited to 'fs/namespace.c')
| -rw-r--r-- | fs/namespace.c | 331 |
1 files changed, 265 insertions, 66 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 678f7ce060f2..0505fb61aa74 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include <linux/mount.h> | 27 | #include <linux/mount.h> |
| 28 | #include <linux/ramfs.h> | 28 | #include <linux/ramfs.h> |
| 29 | #include <linux/log2.h> | 29 | #include <linux/log2.h> |
| 30 | #include <linux/idr.h> | ||
| 30 | #include <asm/uaccess.h> | 31 | #include <asm/uaccess.h> |
| 31 | #include <asm/unistd.h> | 32 | #include <asm/unistd.h> |
| 32 | #include "pnode.h" | 33 | #include "pnode.h" |
| @@ -39,6 +40,8 @@ | |||
| 39 | __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); | 40 | __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); |
| 40 | 41 | ||
| 41 | static int event; | 42 | static int event; |
| 43 | static DEFINE_IDA(mnt_id_ida); | ||
| 44 | static DEFINE_IDA(mnt_group_ida); | ||
| 42 | 45 | ||
| 43 | static struct list_head *mount_hashtable __read_mostly; | 46 | static struct list_head *mount_hashtable __read_mostly; |
| 44 | static struct kmem_cache *mnt_cache __read_mostly; | 47 | static struct kmem_cache *mnt_cache __read_mostly; |
| @@ -58,10 +61,63 @@ static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry) | |||
| 58 | 61 | ||
| 59 | #define MNT_WRITER_UNDERFLOW_LIMIT -(1<<16) | 62 | #define MNT_WRITER_UNDERFLOW_LIMIT -(1<<16) |
| 60 | 63 | ||
| 64 | /* allocation is serialized by namespace_sem */ | ||
| 65 | static int mnt_alloc_id(struct vfsmount *mnt) | ||
| 66 | { | ||
| 67 | int res; | ||
| 68 | |||
| 69 | retry: | ||
| 70 | ida_pre_get(&mnt_id_ida, GFP_KERNEL); | ||
| 71 | spin_lock(&vfsmount_lock); | ||
| 72 | res = ida_get_new(&mnt_id_ida, &mnt->mnt_id); | ||
| 73 | spin_unlock(&vfsmount_lock); | ||
| 74 | if (res == -EAGAIN) | ||
| 75 | goto retry; | ||
| 76 | |||
| 77 | return res; | ||
| 78 | } | ||
| 79 | |||
| 80 | static void mnt_free_id(struct vfsmount *mnt) | ||
| 81 | { | ||
| 82 | spin_lock(&vfsmount_lock); | ||
| 83 | ida_remove(&mnt_id_ida, mnt->mnt_id); | ||
| 84 | spin_unlock(&vfsmount_lock); | ||
| 85 | } | ||
| 86 | |||
| 87 | /* | ||
| 88 | * Allocate a new peer group ID | ||
| 89 | * | ||
| 90 | * mnt_group_ida is protected by namespace_sem | ||
| 91 | */ | ||
| 92 | static int mnt_alloc_group_id(struct vfsmount *mnt) | ||
| 93 | { | ||
| 94 | if (!ida_pre_get(&mnt_group_ida, GFP_KERNEL)) | ||
| 95 | return -ENOMEM; | ||
| 96 | |||
| 97 | return ida_get_new_above(&mnt_group_ida, 1, &mnt->mnt_group_id); | ||
| 98 | } | ||
| 99 | |||
| 100 | /* | ||
| 101 | * Release a peer group ID | ||
| 102 | */ | ||
| 103 | void mnt_release_group_id(struct vfsmount *mnt) | ||
| 104 | { | ||
| 105 | ida_remove(&mnt_group_ida, mnt->mnt_group_id); | ||
| 106 | mnt->mnt_group_id = 0; | ||
| 107 | } | ||
| 108 | |||
| 61 | struct vfsmount *alloc_vfsmnt(const char *name) | 109 | struct vfsmount *alloc_vfsmnt(const char *name) |
| 62 | { | 110 | { |
| 63 | struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); | 111 | struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); |
| 64 | if (mnt) { | 112 | if (mnt) { |
| 113 | int err; | ||
| 114 | |||
| 115 | err = mnt_alloc_id(mnt); | ||
| 116 | if (err) { | ||
| 117 | kmem_cache_free(mnt_cache, mnt); | ||
| 118 | return NULL; | ||
| 119 | } | ||
| 120 | |||
| 65 | atomic_set(&mnt->mnt_count, 1); | 121 | atomic_set(&mnt->mnt_count, 1); |
| 66 | INIT_LIST_HEAD(&mnt->mnt_hash); | 122 | INIT_LIST_HEAD(&mnt->mnt_hash); |
| 67 | INIT_LIST_HEAD(&mnt->mnt_child); | 123 | INIT_LIST_HEAD(&mnt->mnt_child); |
| @@ -353,6 +409,7 @@ EXPORT_SYMBOL(simple_set_mnt); | |||
| 353 | void free_vfsmnt(struct vfsmount *mnt) | 409 | void free_vfsmnt(struct vfsmount *mnt) |
| 354 | { | 410 | { |
| 355 | kfree(mnt->mnt_devname); | 411 | kfree(mnt->mnt_devname); |
| 412 | mnt_free_id(mnt); | ||
| 356 | kmem_cache_free(mnt_cache, mnt); | 413 | kmem_cache_free(mnt_cache, mnt); |
| 357 | } | 414 | } |
| 358 | 415 | ||
| @@ -499,6 +556,17 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, | |||
| 499 | struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname); | 556 | struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname); |
| 500 | 557 | ||
| 501 | if (mnt) { | 558 | if (mnt) { |
| 559 | if (flag & (CL_SLAVE | CL_PRIVATE)) | ||
| 560 | mnt->mnt_group_id = 0; /* not a peer of original */ | ||
| 561 | else | ||
| 562 | mnt->mnt_group_id = old->mnt_group_id; | ||
| 563 | |||
| 564 | if ((flag & CL_MAKE_SHARED) && !mnt->mnt_group_id) { | ||
| 565 | int err = mnt_alloc_group_id(mnt); | ||
| 566 | if (err) | ||
| 567 | goto out_free; | ||
| 568 | } | ||
| 569 | |||
| 502 | mnt->mnt_flags = old->mnt_flags; | 570 | mnt->mnt_flags = old->mnt_flags; |
| 503 | atomic_inc(&sb->s_active); | 571 | atomic_inc(&sb->s_active); |
| 504 | mnt->mnt_sb = sb; | 572 | mnt->mnt_sb = sb; |
| @@ -528,6 +596,10 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, | |||
| 528 | } | 596 | } |
| 529 | } | 597 | } |
| 530 | return mnt; | 598 | return mnt; |
| 599 | |||
| 600 | out_free: | ||
| 601 | free_vfsmnt(mnt); | ||
| 602 | return NULL; | ||
| 531 | } | 603 | } |
| 532 | 604 | ||
| 533 | static inline void __mntput(struct vfsmount *mnt) | 605 | static inline void __mntput(struct vfsmount *mnt) |
| @@ -652,20 +724,21 @@ void save_mount_options(struct super_block *sb, char *options) | |||
| 652 | } | 724 | } |
| 653 | EXPORT_SYMBOL(save_mount_options); | 725 | EXPORT_SYMBOL(save_mount_options); |
| 654 | 726 | ||
| 727 | #ifdef CONFIG_PROC_FS | ||
| 655 | /* iterator */ | 728 | /* iterator */ |
| 656 | static void *m_start(struct seq_file *m, loff_t *pos) | 729 | static void *m_start(struct seq_file *m, loff_t *pos) |
| 657 | { | 730 | { |
| 658 | struct mnt_namespace *n = m->private; | 731 | struct proc_mounts *p = m->private; |
| 659 | 732 | ||
| 660 | down_read(&namespace_sem); | 733 | down_read(&namespace_sem); |
| 661 | return seq_list_start(&n->list, *pos); | 734 | return seq_list_start(&p->ns->list, *pos); |
| 662 | } | 735 | } |
| 663 | 736 | ||
| 664 | static void *m_next(struct seq_file *m, void *v, loff_t *pos) | 737 | static void *m_next(struct seq_file *m, void *v, loff_t *pos) |
| 665 | { | 738 | { |
| 666 | struct mnt_namespace *n = m->private; | 739 | struct proc_mounts *p = m->private; |
| 667 | 740 | ||
| 668 | return seq_list_next(v, &n->list, pos); | 741 | return seq_list_next(v, &p->ns->list, pos); |
| 669 | } | 742 | } |
| 670 | 743 | ||
| 671 | static void m_stop(struct seq_file *m, void *v) | 744 | static void m_stop(struct seq_file *m, void *v) |
| @@ -673,20 +746,30 @@ static void m_stop(struct seq_file *m, void *v) | |||
| 673 | up_read(&namespace_sem); | 746 | up_read(&namespace_sem); |
| 674 | } | 747 | } |
| 675 | 748 | ||
| 676 | static int show_vfsmnt(struct seq_file *m, void *v) | 749 | struct proc_fs_info { |
| 750 | int flag; | ||
| 751 | const char *str; | ||
| 752 | }; | ||
| 753 | |||
| 754 | static void show_sb_opts(struct seq_file *m, struct super_block *sb) | ||
| 677 | { | 755 | { |
| 678 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | 756 | static const struct proc_fs_info fs_info[] = { |
| 679 | int err = 0; | ||
| 680 | static struct proc_fs_info { | ||
| 681 | int flag; | ||
| 682 | char *str; | ||
| 683 | } fs_info[] = { | ||
| 684 | { MS_SYNCHRONOUS, ",sync" }, | 757 | { MS_SYNCHRONOUS, ",sync" }, |
| 685 | { MS_DIRSYNC, ",dirsync" }, | 758 | { MS_DIRSYNC, ",dirsync" }, |
| 686 | { MS_MANDLOCK, ",mand" }, | 759 | { MS_MANDLOCK, ",mand" }, |
| 687 | { 0, NULL } | 760 | { 0, NULL } |
| 688 | }; | 761 | }; |
| 689 | static struct proc_fs_info mnt_info[] = { | 762 | const struct proc_fs_info *fs_infop; |
| 763 | |||
| 764 | for (fs_infop = fs_info; fs_infop->flag; fs_infop++) { | ||
| 765 | if (sb->s_flags & fs_infop->flag) | ||
| 766 | seq_puts(m, fs_infop->str); | ||
| 767 | } | ||
| 768 | } | ||
| 769 | |||
| 770 | static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt) | ||
| 771 | { | ||
| 772 | static const struct proc_fs_info mnt_info[] = { | ||
| 690 | { MNT_NOSUID, ",nosuid" }, | 773 | { MNT_NOSUID, ",nosuid" }, |
| 691 | { MNT_NODEV, ",nodev" }, | 774 | { MNT_NODEV, ",nodev" }, |
| 692 | { MNT_NOEXEC, ",noexec" }, | 775 | { MNT_NOEXEC, ",noexec" }, |
| @@ -695,40 +778,108 @@ static int show_vfsmnt(struct seq_file *m, void *v) | |||
| 695 | { MNT_RELATIME, ",relatime" }, | 778 | { MNT_RELATIME, ",relatime" }, |
| 696 | { 0, NULL } | 779 | { 0, NULL } |
| 697 | }; | 780 | }; |
| 698 | struct proc_fs_info *fs_infop; | 781 | const struct proc_fs_info *fs_infop; |
| 782 | |||
| 783 | for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) { | ||
| 784 | if (mnt->mnt_flags & fs_infop->flag) | ||
| 785 | seq_puts(m, fs_infop->str); | ||
| 786 | } | ||
| 787 | } | ||
| 788 | |||
| 789 | static void show_type(struct seq_file *m, struct super_block *sb) | ||
| 790 | { | ||
| 791 | mangle(m, sb->s_type->name); | ||
| 792 | if (sb->s_subtype && sb->s_subtype[0]) { | ||
| 793 | seq_putc(m, '.'); | ||
| 794 | mangle(m, sb->s_subtype); | ||
| 795 | } | ||
| 796 | } | ||
| 797 | |||
| 798 | static int show_vfsmnt(struct seq_file *m, void *v) | ||
| 799 | { | ||
| 800 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | ||
| 801 | int err = 0; | ||
| 699 | struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; | 802 | struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; |
| 700 | 803 | ||
| 701 | mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); | 804 | mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); |
| 702 | seq_putc(m, ' '); | 805 | seq_putc(m, ' '); |
| 703 | seq_path(m, &mnt_path, " \t\n\\"); | 806 | seq_path(m, &mnt_path, " \t\n\\"); |
| 704 | seq_putc(m, ' '); | 807 | seq_putc(m, ' '); |
| 705 | mangle(m, mnt->mnt_sb->s_type->name); | 808 | show_type(m, mnt->mnt_sb); |
| 706 | if (mnt->mnt_sb->s_subtype && mnt->mnt_sb->s_subtype[0]) { | ||
| 707 | seq_putc(m, '.'); | ||
| 708 | mangle(m, mnt->mnt_sb->s_subtype); | ||
| 709 | } | ||
| 710 | seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw"); | 809 | seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw"); |
| 711 | for (fs_infop = fs_info; fs_infop->flag; fs_infop++) { | 810 | show_sb_opts(m, mnt->mnt_sb); |
| 712 | if (mnt->mnt_sb->s_flags & fs_infop->flag) | 811 | show_mnt_opts(m, mnt); |
| 713 | seq_puts(m, fs_infop->str); | ||
| 714 | } | ||
| 715 | for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) { | ||
| 716 | if (mnt->mnt_flags & fs_infop->flag) | ||
| 717 | seq_puts(m, fs_infop->str); | ||
| 718 | } | ||
| 719 | if (mnt->mnt_sb->s_op->show_options) | 812 | if (mnt->mnt_sb->s_op->show_options) |
| 720 | err = mnt->mnt_sb->s_op->show_options(m, mnt); | 813 | err = mnt->mnt_sb->s_op->show_options(m, mnt); |
| 721 | seq_puts(m, " 0 0\n"); | 814 | seq_puts(m, " 0 0\n"); |
| 722 | return err; | 815 | return err; |
| 723 | } | 816 | } |
| 724 | 817 | ||
| 725 | struct seq_operations mounts_op = { | 818 | const struct seq_operations mounts_op = { |
| 726 | .start = m_start, | 819 | .start = m_start, |
| 727 | .next = m_next, | 820 | .next = m_next, |
| 728 | .stop = m_stop, | 821 | .stop = m_stop, |
| 729 | .show = show_vfsmnt | 822 | .show = show_vfsmnt |
| 730 | }; | 823 | }; |
| 731 | 824 | ||
| 825 | static int show_mountinfo(struct seq_file *m, void *v) | ||
| 826 | { | ||
| 827 | struct proc_mounts *p = m->private; | ||
| 828 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | ||
| 829 | struct super_block *sb = mnt->mnt_sb; | ||
| 830 | struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; | ||
| 831 | struct path root = p->root; | ||
| 832 | int err = 0; | ||
| 833 | |||
| 834 | seq_printf(m, "%i %i %u:%u ", mnt->mnt_id, mnt->mnt_parent->mnt_id, | ||
| 835 | MAJOR(sb->s_dev), MINOR(sb->s_dev)); | ||
| 836 | seq_dentry(m, mnt->mnt_root, " \t\n\\"); | ||
| 837 | seq_putc(m, ' '); | ||
| 838 | seq_path_root(m, &mnt_path, &root, " \t\n\\"); | ||
| 839 | if (root.mnt != p->root.mnt || root.dentry != p->root.dentry) { | ||
| 840 | /* | ||
| 841 | * Mountpoint is outside root, discard that one. Ugly, | ||
| 842 | * but less so than trying to do that in iterator in a | ||
| 843 | * race-free way (due to renames). | ||
| 844 | */ | ||
| 845 | return SEQ_SKIP; | ||
| 846 | } | ||
| 847 | seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw"); | ||
| 848 | show_mnt_opts(m, mnt); | ||
| 849 | |||
| 850 | /* Tagged fields ("foo:X" or "bar") */ | ||
| 851 | if (IS_MNT_SHARED(mnt)) | ||
| 852 | seq_printf(m, " shared:%i", mnt->mnt_group_id); | ||
| 853 | if (IS_MNT_SLAVE(mnt)) { | ||
| 854 | int master = mnt->mnt_master->mnt_group_id; | ||
| 855 | int dom = get_dominating_id(mnt, &p->root); | ||
| 856 | seq_printf(m, " master:%i", master); | ||
| 857 | if (dom && dom != master) | ||
| 858 | seq_printf(m, " propagate_from:%i", dom); | ||
| 859 | } | ||
| 860 | if (IS_MNT_UNBINDABLE(mnt)) | ||
| 861 | seq_puts(m, " unbindable"); | ||
| 862 | |||
| 863 | /* Filesystem specific data */ | ||
| 864 | seq_puts(m, " - "); | ||
| 865 | show_type(m, sb); | ||
| 866 | seq_putc(m, ' '); | ||
| 867 | mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); | ||
| 868 | seq_puts(m, sb->s_flags & MS_RDONLY ? " ro" : " rw"); | ||
| 869 | show_sb_opts(m, sb); | ||
| 870 | if (sb->s_op->show_options) | ||
| 871 | err = sb->s_op->show_options(m, mnt); | ||
| 872 | seq_putc(m, '\n'); | ||
| 873 | return err; | ||
| 874 | } | ||
| 875 | |||
| 876 | const struct seq_operations mountinfo_op = { | ||
| 877 | .start = m_start, | ||
| 878 | .next = m_next, | ||
| 879 | .stop = m_stop, | ||
| 880 | .show = show_mountinfo, | ||
| 881 | }; | ||
| 882 | |||
| 732 | static int show_vfsstat(struct seq_file *m, void *v) | 883 | static int show_vfsstat(struct seq_file *m, void *v) |
| 733 | { | 884 | { |
| 734 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | 885 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); |
| @@ -749,7 +900,7 @@ static int show_vfsstat(struct seq_file *m, void *v) | |||
| 749 | 900 | ||
| 750 | /* file system type */ | 901 | /* file system type */ |
| 751 | seq_puts(m, "with fstype "); | 902 | seq_puts(m, "with fstype "); |
| 752 | mangle(m, mnt->mnt_sb->s_type->name); | 903 | show_type(m, mnt->mnt_sb); |
| 753 | 904 | ||
| 754 | /* optional statistics */ | 905 | /* optional statistics */ |
| 755 | if (mnt->mnt_sb->s_op->show_stats) { | 906 | if (mnt->mnt_sb->s_op->show_stats) { |
| @@ -761,12 +912,13 @@ static int show_vfsstat(struct seq_file *m, void *v) | |||
| 761 | return err; | 912 | return err; |
| 762 | } | 913 | } |
| 763 | 914 | ||
| 764 | struct seq_operations mountstats_op = { | 915 | const struct seq_operations mountstats_op = { |
| 765 | .start = m_start, | 916 | .start = m_start, |
| 766 | .next = m_next, | 917 | .next = m_next, |
| 767 | .stop = m_stop, | 918 | .stop = m_stop, |
| 768 | .show = show_vfsstat, | 919 | .show = show_vfsstat, |
| 769 | }; | 920 | }; |
| 921 | #endif /* CONFIG_PROC_FS */ | ||
| 770 | 922 | ||
| 771 | /** | 923 | /** |
| 772 | * may_umount_tree - check if a mount tree is busy | 924 | * may_umount_tree - check if a mount tree is busy |
| @@ -1091,23 +1243,50 @@ Enomem: | |||
| 1091 | struct vfsmount *collect_mounts(struct vfsmount *mnt, struct dentry *dentry) | 1243 | struct vfsmount *collect_mounts(struct vfsmount *mnt, struct dentry *dentry) |
| 1092 | { | 1244 | { |
| 1093 | struct vfsmount *tree; | 1245 | struct vfsmount *tree; |
| 1094 | down_read(&namespace_sem); | 1246 | down_write(&namespace_sem); |
| 1095 | tree = copy_tree(mnt, dentry, CL_COPY_ALL | CL_PRIVATE); | 1247 | tree = copy_tree(mnt, dentry, CL_COPY_ALL | CL_PRIVATE); |
| 1096 | up_read(&namespace_sem); | 1248 | up_write(&namespace_sem); |
| 1097 | return tree; | 1249 | return tree; |
| 1098 | } | 1250 | } |
| 1099 | 1251 | ||
| 1100 | void drop_collected_mounts(struct vfsmount *mnt) | 1252 | void drop_collected_mounts(struct vfsmount *mnt) |
| 1101 | { | 1253 | { |
| 1102 | LIST_HEAD(umount_list); | 1254 | LIST_HEAD(umount_list); |
| 1103 | down_read(&namespace_sem); | 1255 | down_write(&namespace_sem); |
| 1104 | spin_lock(&vfsmount_lock); | 1256 | spin_lock(&vfsmount_lock); |
| 1105 | umount_tree(mnt, 0, &umount_list); | 1257 | umount_tree(mnt, 0, &umount_list); |
| 1106 | spin_unlock(&vfsmount_lock); | 1258 | spin_unlock(&vfsmount_lock); |
| 1107 | up_read(&namespace_sem); | 1259 | up_write(&namespace_sem); |
| 1108 | release_mounts(&umount_list); | 1260 | release_mounts(&umount_list); |
| 1109 | } | 1261 | } |
| 1110 | 1262 | ||
| 1263 | static void cleanup_group_ids(struct vfsmount *mnt, struct vfsmount *end) | ||
| 1264 | { | ||
| 1265 | struct vfsmount *p; | ||
| 1266 | |||
| 1267 | for (p = mnt; p != end; p = next_mnt(p, mnt)) { | ||
| 1268 | if (p->mnt_group_id && !IS_MNT_SHARED(p)) | ||
| 1269 | mnt_release_group_id(p); | ||
| 1270 | } | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | static int invent_group_ids(struct vfsmount *mnt, bool recurse) | ||
| 1274 | { | ||
| 1275 | struct vfsmount *p; | ||
| 1276 | |||
| 1277 | for (p = mnt; p; p = recurse ? next_mnt(p, mnt) : NULL) { | ||
| 1278 | if (!p->mnt_group_id && !IS_MNT_SHARED(p)) { | ||
| 1279 | int err = mnt_alloc_group_id(p); | ||
| 1280 | if (err) { | ||
| 1281 | cleanup_group_ids(mnt, p); | ||
| 1282 | return err; | ||
| 1283 | } | ||
| 1284 | } | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | return 0; | ||
| 1288 | } | ||
| 1289 | |||
| 1111 | /* | 1290 | /* |
| 1112 | * @source_mnt : mount tree to be attached | 1291 | * @source_mnt : mount tree to be attached |
| 1113 | * @nd : place the mount tree @source_mnt is attached | 1292 | * @nd : place the mount tree @source_mnt is attached |
| @@ -1178,9 +1357,16 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt, | |||
| 1178 | struct vfsmount *dest_mnt = path->mnt; | 1357 | struct vfsmount *dest_mnt = path->mnt; |
| 1179 | struct dentry *dest_dentry = path->dentry; | 1358 | struct dentry *dest_dentry = path->dentry; |
| 1180 | struct vfsmount *child, *p; | 1359 | struct vfsmount *child, *p; |
| 1360 | int err; | ||
| 1181 | 1361 | ||
| 1182 | if (propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list)) | 1362 | if (IS_MNT_SHARED(dest_mnt)) { |
| 1183 | return -EINVAL; | 1363 | err = invent_group_ids(source_mnt, true); |
| 1364 | if (err) | ||
| 1365 | goto out; | ||
| 1366 | } | ||
| 1367 | err = propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list); | ||
| 1368 | if (err) | ||
| 1369 | goto out_cleanup_ids; | ||
| 1184 | 1370 | ||
| 1185 | if (IS_MNT_SHARED(dest_mnt)) { | 1371 | if (IS_MNT_SHARED(dest_mnt)) { |
| 1186 | for (p = source_mnt; p; p = next_mnt(p, source_mnt)) | 1372 | for (p = source_mnt; p; p = next_mnt(p, source_mnt)) |
| @@ -1203,34 +1389,40 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt, | |||
| 1203 | } | 1389 | } |
| 1204 | spin_unlock(&vfsmount_lock); | 1390 | spin_unlock(&vfsmount_lock); |
| 1205 | return 0; | 1391 | return 0; |
| 1392 | |||
| 1393 | out_cleanup_ids: | ||
| 1394 | if (IS_MNT_SHARED(dest_mnt)) | ||
| 1395 | cleanup_group_ids(source_mnt, NULL); | ||
| 1396 | out: | ||
| 1397 | return err; | ||
| 1206 | } | 1398 | } |
| 1207 | 1399 | ||
| 1208 | static int graft_tree(struct vfsmount *mnt, struct nameidata *nd) | 1400 | static int graft_tree(struct vfsmount *mnt, struct path *path) |
| 1209 | { | 1401 | { |
| 1210 | int err; | 1402 | int err; |
| 1211 | if (mnt->mnt_sb->s_flags & MS_NOUSER) | 1403 | if (mnt->mnt_sb->s_flags & MS_NOUSER) |
| 1212 | return -EINVAL; | 1404 | return -EINVAL; |
| 1213 | 1405 | ||
| 1214 | if (S_ISDIR(nd->path.dentry->d_inode->i_mode) != | 1406 | if (S_ISDIR(path->dentry->d_inode->i_mode) != |
| 1215 | S_ISDIR(mnt->mnt_root->d_inode->i_mode)) | 1407 | S_ISDIR(mnt->mnt_root->d_inode->i_mode)) |
| 1216 | return -ENOTDIR; | 1408 | return -ENOTDIR; |
| 1217 | 1409 | ||
| 1218 | err = -ENOENT; | 1410 | err = -ENOENT; |
| 1219 | mutex_lock(&nd->path.dentry->d_inode->i_mutex); | 1411 | mutex_lock(&path->dentry->d_inode->i_mutex); |
| 1220 | if (IS_DEADDIR(nd->path.dentry->d_inode)) | 1412 | if (IS_DEADDIR(path->dentry->d_inode)) |
| 1221 | goto out_unlock; | 1413 | goto out_unlock; |
| 1222 | 1414 | ||
| 1223 | err = security_sb_check_sb(mnt, nd); | 1415 | err = security_sb_check_sb(mnt, path); |
| 1224 | if (err) | 1416 | if (err) |
| 1225 | goto out_unlock; | 1417 | goto out_unlock; |
| 1226 | 1418 | ||
| 1227 | err = -ENOENT; | 1419 | err = -ENOENT; |
| 1228 | if (IS_ROOT(nd->path.dentry) || !d_unhashed(nd->path.dentry)) | 1420 | if (IS_ROOT(path->dentry) || !d_unhashed(path->dentry)) |
| 1229 | err = attach_recursive_mnt(mnt, &nd->path, NULL); | 1421 | err = attach_recursive_mnt(mnt, path, NULL); |
| 1230 | out_unlock: | 1422 | out_unlock: |
| 1231 | mutex_unlock(&nd->path.dentry->d_inode->i_mutex); | 1423 | mutex_unlock(&path->dentry->d_inode->i_mutex); |
| 1232 | if (!err) | 1424 | if (!err) |
| 1233 | security_sb_post_addmount(mnt, nd); | 1425 | security_sb_post_addmount(mnt, path); |
| 1234 | return err; | 1426 | return err; |
| 1235 | } | 1427 | } |
| 1236 | 1428 | ||
| @@ -1243,6 +1435,7 @@ static noinline int do_change_type(struct nameidata *nd, int flag) | |||
| 1243 | struct vfsmount *m, *mnt = nd->path.mnt; | 1435 | struct vfsmount *m, *mnt = nd->path.mnt; |
| 1244 | int recurse = flag & MS_REC; | 1436 | int recurse = flag & MS_REC; |
| 1245 | int type = flag & ~MS_REC; | 1437 | int type = flag & ~MS_REC; |
| 1438 | int err = 0; | ||
| 1246 | 1439 | ||
| 1247 | if (!capable(CAP_SYS_ADMIN)) | 1440 | if (!capable(CAP_SYS_ADMIN)) |
| 1248 | return -EPERM; | 1441 | return -EPERM; |
| @@ -1251,12 +1444,20 @@ static noinline int do_change_type(struct nameidata *nd, int flag) | |||
| 1251 | return -EINVAL; | 1444 | return -EINVAL; |
| 1252 | 1445 | ||
| 1253 | down_write(&namespace_sem); | 1446 | down_write(&namespace_sem); |
| 1447 | if (type == MS_SHARED) { | ||
| 1448 | err = invent_group_ids(mnt, recurse); | ||
| 1449 | if (err) | ||
| 1450 | goto out_unlock; | ||
| 1451 | } | ||
| 1452 | |||
| 1254 | spin_lock(&vfsmount_lock); | 1453 | spin_lock(&vfsmount_lock); |
| 1255 | for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL)) | 1454 | for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL)) |
| 1256 | change_mnt_propagation(m, type); | 1455 | change_mnt_propagation(m, type); |
| 1257 | spin_unlock(&vfsmount_lock); | 1456 | spin_unlock(&vfsmount_lock); |
| 1457 | |||
| 1458 | out_unlock: | ||
| 1258 | up_write(&namespace_sem); | 1459 | up_write(&namespace_sem); |
| 1259 | return 0; | 1460 | return err; |
| 1260 | } | 1461 | } |
| 1261 | 1462 | ||
| 1262 | /* | 1463 | /* |
| @@ -1294,7 +1495,7 @@ static noinline int do_loopback(struct nameidata *nd, char *old_name, | |||
| 1294 | if (!mnt) | 1495 | if (!mnt) |
| 1295 | goto out; | 1496 | goto out; |
| 1296 | 1497 | ||
| 1297 | err = graft_tree(mnt, nd); | 1498 | err = graft_tree(mnt, &nd->path); |
| 1298 | if (err) { | 1499 | if (err) { |
| 1299 | LIST_HEAD(umount_list); | 1500 | LIST_HEAD(umount_list); |
| 1300 | spin_lock(&vfsmount_lock); | 1501 | spin_lock(&vfsmount_lock); |
| @@ -1501,7 +1702,7 @@ int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd, | |||
| 1501 | goto unlock; | 1702 | goto unlock; |
| 1502 | 1703 | ||
| 1503 | newmnt->mnt_flags = mnt_flags; | 1704 | newmnt->mnt_flags = mnt_flags; |
| 1504 | if ((err = graft_tree(newmnt, nd))) | 1705 | if ((err = graft_tree(newmnt, &nd->path))) |
| 1505 | goto unlock; | 1706 | goto unlock; |
| 1506 | 1707 | ||
| 1507 | if (fslist) /* add to the specified expiration list */ | 1708 | if (fslist) /* add to the specified expiration list */ |
| @@ -1746,7 +1947,8 @@ long do_mount(char *dev_name, char *dir_name, char *type_page, | |||
| 1746 | if (retval) | 1947 | if (retval) |
| 1747 | return retval; | 1948 | return retval; |
| 1748 | 1949 | ||
| 1749 | retval = security_sb_mount(dev_name, &nd, type_page, flags, data_page); | 1950 | retval = security_sb_mount(dev_name, &nd.path, |
| 1951 | type_page, flags, data_page); | ||
| 1750 | if (retval) | 1952 | if (retval) |
| 1751 | goto dput_out; | 1953 | goto dput_out; |
| 1752 | 1954 | ||
| @@ -1986,15 +2188,13 @@ asmlinkage long sys_pivot_root(const char __user * new_root, | |||
| 1986 | const char __user * put_old) | 2188 | const char __user * put_old) |
| 1987 | { | 2189 | { |
| 1988 | struct vfsmount *tmp; | 2190 | struct vfsmount *tmp; |
| 1989 | struct nameidata new_nd, old_nd, user_nd; | 2191 | struct nameidata new_nd, old_nd; |
| 1990 | struct path parent_path, root_parent; | 2192 | struct path parent_path, root_parent, root; |
| 1991 | int error; | 2193 | int error; |
| 1992 | 2194 | ||
| 1993 | if (!capable(CAP_SYS_ADMIN)) | 2195 | if (!capable(CAP_SYS_ADMIN)) |
| 1994 | return -EPERM; | 2196 | return -EPERM; |
| 1995 | 2197 | ||
| 1996 | lock_kernel(); | ||
| 1997 | |||
| 1998 | error = __user_walk(new_root, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, | 2198 | error = __user_walk(new_root, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, |
| 1999 | &new_nd); | 2199 | &new_nd); |
| 2000 | if (error) | 2200 | if (error) |
| @@ -2007,14 +2207,14 @@ asmlinkage long sys_pivot_root(const char __user * new_root, | |||
| 2007 | if (error) | 2207 | if (error) |
| 2008 | goto out1; | 2208 | goto out1; |
| 2009 | 2209 | ||
| 2010 | error = security_sb_pivotroot(&old_nd, &new_nd); | 2210 | error = security_sb_pivotroot(&old_nd.path, &new_nd.path); |
| 2011 | if (error) { | 2211 | if (error) { |
| 2012 | path_put(&old_nd.path); | 2212 | path_put(&old_nd.path); |
| 2013 | goto out1; | 2213 | goto out1; |
| 2014 | } | 2214 | } |
| 2015 | 2215 | ||
| 2016 | read_lock(¤t->fs->lock); | 2216 | read_lock(¤t->fs->lock); |
| 2017 | user_nd.path = current->fs->root; | 2217 | root = current->fs->root; |
| 2018 | path_get(¤t->fs->root); | 2218 | path_get(¤t->fs->root); |
| 2019 | read_unlock(¤t->fs->lock); | 2219 | read_unlock(¤t->fs->lock); |
| 2020 | down_write(&namespace_sem); | 2220 | down_write(&namespace_sem); |
| @@ -2022,9 +2222,9 @@ asmlinkage long sys_pivot_root(const char __user * new_root, | |||
| 2022 | error = -EINVAL; | 2222 | error = -EINVAL; |
| 2023 | if (IS_MNT_SHARED(old_nd.path.mnt) || | 2223 | if (IS_MNT_SHARED(old_nd.path.mnt) || |
| 2024 | IS_MNT_SHARED(new_nd.path.mnt->mnt_parent) || | 2224 | IS_MNT_SHARED(new_nd.path.mnt->mnt_parent) || |
| 2025 | IS_MNT_SHARED(user_nd.path.mnt->mnt_parent)) | 2225 | IS_MNT_SHARED(root.mnt->mnt_parent)) |
| 2026 | goto out2; | 2226 | goto out2; |
| 2027 | if (!check_mnt(user_nd.path.mnt)) | 2227 | if (!check_mnt(root.mnt)) |
| 2028 | goto out2; | 2228 | goto out2; |
| 2029 | error = -ENOENT; | 2229 | error = -ENOENT; |
| 2030 | if (IS_DEADDIR(new_nd.path.dentry->d_inode)) | 2230 | if (IS_DEADDIR(new_nd.path.dentry->d_inode)) |
| @@ -2034,13 +2234,13 @@ asmlinkage long sys_pivot_root(const char __user * new_root, | |||
| 2034 | if (d_unhashed(old_nd.path.dentry) && !IS_ROOT(old_nd.path.dentry)) | 2234 | if (d_unhashed(old_nd.path.dentry) && !IS_ROOT(old_nd.path.dentry)) |
| 2035 | goto out2; | 2235 | goto out2; |
| 2036 | error = -EBUSY; | 2236 | error = -EBUSY; |
| 2037 | if (new_nd.path.mnt == user_nd.path.mnt || | 2237 | if (new_nd.path.mnt == root.mnt || |
| 2038 | old_nd.path.mnt == user_nd.path.mnt) | 2238 | old_nd.path.mnt == root.mnt) |
| 2039 | goto out2; /* loop, on the same file system */ | 2239 | goto out2; /* loop, on the same file system */ |
| 2040 | error = -EINVAL; | 2240 | error = -EINVAL; |
| 2041 | if (user_nd.path.mnt->mnt_root != user_nd.path.dentry) | 2241 | if (root.mnt->mnt_root != root.dentry) |
| 2042 | goto out2; /* not a mountpoint */ | 2242 | goto out2; /* not a mountpoint */ |
| 2043 | if (user_nd.path.mnt->mnt_parent == user_nd.path.mnt) | 2243 | if (root.mnt->mnt_parent == root.mnt) |
| 2044 | goto out2; /* not attached */ | 2244 | goto out2; /* not attached */ |
| 2045 | if (new_nd.path.mnt->mnt_root != new_nd.path.dentry) | 2245 | if (new_nd.path.mnt->mnt_root != new_nd.path.dentry) |
| 2046 | goto out2; /* not a mountpoint */ | 2246 | goto out2; /* not a mountpoint */ |
| @@ -2062,27 +2262,26 @@ asmlinkage long sys_pivot_root(const char __user * new_root, | |||
| 2062 | } else if (!is_subdir(old_nd.path.dentry, new_nd.path.dentry)) | 2262 | } else if (!is_subdir(old_nd.path.dentry, new_nd.path.dentry)) |
| 2063 | goto out3; | 2263 | goto out3; |
| 2064 | detach_mnt(new_nd.path.mnt, &parent_path); | 2264 | detach_mnt(new_nd.path.mnt, &parent_path); |
| 2065 | detach_mnt(user_nd.path.mnt, &root_parent); | 2265 | detach_mnt(root.mnt, &root_parent); |
| 2066 | /* mount old root on put_old */ | 2266 | /* mount old root on put_old */ |
| 2067 | attach_mnt(user_nd.path.mnt, &old_nd.path); | 2267 | attach_mnt(root.mnt, &old_nd.path); |
| 2068 | /* mount new_root on / */ | 2268 | /* mount new_root on / */ |
| 2069 | attach_mnt(new_nd.path.mnt, &root_parent); | 2269 | attach_mnt(new_nd.path.mnt, &root_parent); |
| 2070 | touch_mnt_namespace(current->nsproxy->mnt_ns); | 2270 | touch_mnt_namespace(current->nsproxy->mnt_ns); |
| 2071 | spin_unlock(&vfsmount_lock); | 2271 | spin_unlock(&vfsmount_lock); |
| 2072 | chroot_fs_refs(&user_nd.path, &new_nd.path); | 2272 | chroot_fs_refs(&root, &new_nd.path); |
| 2073 | security_sb_post_pivotroot(&user_nd, &new_nd); | 2273 | security_sb_post_pivotroot(&root, &new_nd.path); |
| 2074 | error = 0; | 2274 | error = 0; |
| 2075 | path_put(&root_parent); | 2275 | path_put(&root_parent); |
| 2076 | path_put(&parent_path); | 2276 | path_put(&parent_path); |
| 2077 | out2: | 2277 | out2: |
| 2078 | mutex_unlock(&old_nd.path.dentry->d_inode->i_mutex); | 2278 | mutex_unlock(&old_nd.path.dentry->d_inode->i_mutex); |
| 2079 | up_write(&namespace_sem); | 2279 | up_write(&namespace_sem); |
| 2080 | path_put(&user_nd.path); | 2280 | path_put(&root); |
| 2081 | path_put(&old_nd.path); | 2281 | path_put(&old_nd.path); |
| 2082 | out1: | 2282 | out1: |
| 2083 | path_put(&new_nd.path); | 2283 | path_put(&new_nd.path); |
| 2084 | out0: | 2284 | out0: |
| 2085 | unlock_kernel(); | ||
| 2086 | return error; | 2285 | return error; |
| 2087 | out3: | 2286 | out3: |
| 2088 | spin_unlock(&vfsmount_lock); | 2287 | spin_unlock(&vfsmount_lock); |
