diff options
Diffstat (limited to 'fs/sysfs/mount.c')
| -rw-r--r-- | fs/sysfs/mount.c | 95 |
1 files changed, 92 insertions, 3 deletions
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 776137828dca..281c0c9bc39f 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c | |||
| @@ -35,7 +35,7 @@ static const struct super_operations sysfs_ops = { | |||
| 35 | struct sysfs_dirent sysfs_root = { | 35 | struct sysfs_dirent sysfs_root = { |
| 36 | .s_name = "", | 36 | .s_name = "", |
| 37 | .s_count = ATOMIC_INIT(1), | 37 | .s_count = ATOMIC_INIT(1), |
| 38 | .s_flags = SYSFS_DIR, | 38 | .s_flags = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT), |
| 39 | .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, | 39 | .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, |
| 40 | .s_ino = 1, | 40 | .s_ino = 1, |
| 41 | }; | 41 | }; |
| @@ -72,18 +72,107 @@ static int sysfs_fill_super(struct super_block *sb, void *data, int silent) | |||
| 72 | return 0; | 72 | return 0; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | static int sysfs_test_super(struct super_block *sb, void *data) | ||
| 76 | { | ||
| 77 | struct sysfs_super_info *sb_info = sysfs_info(sb); | ||
| 78 | struct sysfs_super_info *info = data; | ||
| 79 | enum kobj_ns_type type; | ||
| 80 | int found = 1; | ||
| 81 | |||
| 82 | for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) { | ||
| 83 | if (sb_info->ns[type] != info->ns[type]) | ||
| 84 | found = 0; | ||
| 85 | } | ||
| 86 | return found; | ||
| 87 | } | ||
| 88 | |||
| 89 | static int sysfs_set_super(struct super_block *sb, void *data) | ||
| 90 | { | ||
| 91 | int error; | ||
| 92 | error = set_anon_super(sb, data); | ||
| 93 | if (!error) | ||
| 94 | sb->s_fs_info = data; | ||
| 95 | return error; | ||
| 96 | } | ||
| 97 | |||
| 75 | static int sysfs_get_sb(struct file_system_type *fs_type, | 98 | static int sysfs_get_sb(struct file_system_type *fs_type, |
| 76 | int flags, const char *dev_name, void *data, struct vfsmount *mnt) | 99 | int flags, const char *dev_name, void *data, struct vfsmount *mnt) |
| 77 | { | 100 | { |
| 78 | return get_sb_single(fs_type, flags, data, sysfs_fill_super, mnt); | 101 | struct sysfs_super_info *info; |
| 102 | enum kobj_ns_type type; | ||
| 103 | struct super_block *sb; | ||
| 104 | int error; | ||
| 105 | |||
| 106 | error = -ENOMEM; | ||
| 107 | info = kzalloc(sizeof(*info), GFP_KERNEL); | ||
| 108 | if (!info) | ||
| 109 | goto out; | ||
| 110 | |||
| 111 | for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) | ||
| 112 | info->ns[type] = kobj_ns_current(type); | ||
| 113 | |||
| 114 | sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info); | ||
| 115 | if (IS_ERR(sb) || sb->s_fs_info != info) | ||
| 116 | kfree(info); | ||
| 117 | if (IS_ERR(sb)) { | ||
| 118 | error = PTR_ERR(sb); | ||
| 119 | goto out; | ||
| 120 | } | ||
| 121 | if (!sb->s_root) { | ||
| 122 | sb->s_flags = flags; | ||
| 123 | error = sysfs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); | ||
| 124 | if (error) { | ||
| 125 | deactivate_locked_super(sb); | ||
| 126 | goto out; | ||
| 127 | } | ||
| 128 | sb->s_flags |= MS_ACTIVE; | ||
| 129 | } | ||
| 130 | |||
| 131 | simple_set_mnt(mnt, sb); | ||
| 132 | error = 0; | ||
| 133 | out: | ||
| 134 | return error; | ||
| 135 | } | ||
| 136 | |||
| 137 | static void sysfs_kill_sb(struct super_block *sb) | ||
| 138 | { | ||
| 139 | struct sysfs_super_info *info = sysfs_info(sb); | ||
| 140 | |||
| 141 | /* Remove the superblock from fs_supers/s_instances | ||
| 142 | * so we can't find it, before freeing sysfs_super_info. | ||
| 143 | */ | ||
| 144 | kill_anon_super(sb); | ||
| 145 | kfree(info); | ||
| 79 | } | 146 | } |
| 80 | 147 | ||
| 81 | static struct file_system_type sysfs_fs_type = { | 148 | static struct file_system_type sysfs_fs_type = { |
| 82 | .name = "sysfs", | 149 | .name = "sysfs", |
| 83 | .get_sb = sysfs_get_sb, | 150 | .get_sb = sysfs_get_sb, |
| 84 | .kill_sb = kill_anon_super, | 151 | .kill_sb = sysfs_kill_sb, |
| 85 | }; | 152 | }; |
| 86 | 153 | ||
| 154 | void sysfs_exit_ns(enum kobj_ns_type type, const void *ns) | ||
| 155 | { | ||
| 156 | struct super_block *sb; | ||
| 157 | |||
| 158 | mutex_lock(&sysfs_mutex); | ||
| 159 | spin_lock(&sb_lock); | ||
| 160 | list_for_each_entry(sb, &sysfs_fs_type.fs_supers, s_instances) { | ||
| 161 | struct sysfs_super_info *info = sysfs_info(sb); | ||
| 162 | /* | ||
| 163 | * If we see a superblock on the fs_supers/s_instances | ||
| 164 | * list the unmount has not completed and sb->s_fs_info | ||
| 165 | * points to a valid struct sysfs_super_info. | ||
| 166 | */ | ||
| 167 | /* Ignore superblocks with the wrong ns */ | ||
| 168 | if (info->ns[type] != ns) | ||
| 169 | continue; | ||
| 170 | info->ns[type] = NULL; | ||
| 171 | } | ||
| 172 | spin_unlock(&sb_lock); | ||
| 173 | mutex_unlock(&sysfs_mutex); | ||
| 174 | } | ||
| 175 | |||
| 87 | int __init sysfs_init(void) | 176 | int __init sysfs_init(void) |
| 88 | { | 177 | { |
| 89 | int err = -ENOMEM; | 178 | int err = -ENOMEM; |
