diff options
Diffstat (limited to 'fs/namespace.c')
-rw-r--r-- | fs/namespace.c | 265 |
1 files changed, 233 insertions, 32 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 1bf302d0478b..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 |
@@ -1108,6 +1260,33 @@ void drop_collected_mounts(struct vfsmount *mnt) | |||
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,6 +1389,12 @@ 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 path *path) | 1400 | static int graft_tree(struct vfsmount *mnt, struct path *path) |
@@ -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 | /* |