diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2011-06-12 10:24:33 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-06-12 17:45:34 -0400 |
commit | d251ed271d528afb407cc2ede30923e34cb209a5 (patch) | |
tree | b77e0133453057821ee50956c560013acb467c4c /fs/ubifs/super.c | |
parent | b1c27ab3f93daede979f804afc38b189c2f17c60 (diff) |
ubifs: fix sget races
* allocate ubifs_info in ->mount(), fill it enough for sb_test() and
set ->s_fs_info to it in set() callback passed to sget().
* do *not* free it in ->put_super(); do that in ->kill_sb() after we'd
done kill_anon_super().
* don't free it in ubifs_fill_super() either - deactivate_locked_super()
done by caller when ubifs_fill_super() returns an error will take care
of that sucker.
* get rid of kludge with passing ubi to ubifs_fill_super() in ->s_fs_info;
we only need it in alloc_ubifs_info(), so ubifs_fill_super() will need
only ubifs_info. Which it will find in ->s_fs_info just fine, no need to
reassign anything...
As the result, sb_test() becomes safe to apply to all superblocks that
can be found by sget() (and a kludge with temporary use of ->s_fs_info
to store a pointer to very different structure goes away).
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/ubifs/super.c')
-rw-r--r-- | fs/ubifs/super.c | 54 |
1 files changed, 30 insertions, 24 deletions
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index ddc3b02e8cf0..8c892c2d5300 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c | |||
@@ -1848,7 +1848,6 @@ static void ubifs_put_super(struct super_block *sb) | |||
1848 | bdi_destroy(&c->bdi); | 1848 | bdi_destroy(&c->bdi); |
1849 | ubi_close_volume(c->ubi); | 1849 | ubi_close_volume(c->ubi); |
1850 | mutex_unlock(&c->umount_mutex); | 1850 | mutex_unlock(&c->umount_mutex); |
1851 | kfree(c); | ||
1852 | } | 1851 | } |
1853 | 1852 | ||
1854 | static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data) | 1853 | static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data) |
@@ -2020,21 +2019,16 @@ static struct ubifs_info *alloc_ubifs_info(struct ubi_volume_desc *ubi) | |||
2020 | 2019 | ||
2021 | static int ubifs_fill_super(struct super_block *sb, void *data, int silent) | 2020 | static int ubifs_fill_super(struct super_block *sb, void *data, int silent) |
2022 | { | 2021 | { |
2023 | struct ubi_volume_desc *ubi = sb->s_fs_info; | 2022 | struct ubifs_info *c = sb->s_fs_info; |
2024 | struct ubifs_info *c; | ||
2025 | struct inode *root; | 2023 | struct inode *root; |
2026 | int err; | 2024 | int err; |
2027 | 2025 | ||
2028 | c = alloc_ubifs_info(ubi); | ||
2029 | if (!c) | ||
2030 | return -ENOMEM; | ||
2031 | |||
2032 | c->vfs_sb = sb; | 2026 | c->vfs_sb = sb; |
2033 | /* Re-open the UBI device in read-write mode */ | 2027 | /* Re-open the UBI device in read-write mode */ |
2034 | c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READWRITE); | 2028 | c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READWRITE); |
2035 | if (IS_ERR(c->ubi)) { | 2029 | if (IS_ERR(c->ubi)) { |
2036 | err = PTR_ERR(c->ubi); | 2030 | err = PTR_ERR(c->ubi); |
2037 | goto out_free; | 2031 | goto out; |
2038 | } | 2032 | } |
2039 | 2033 | ||
2040 | /* | 2034 | /* |
@@ -2100,24 +2094,29 @@ out_bdi: | |||
2100 | bdi_destroy(&c->bdi); | 2094 | bdi_destroy(&c->bdi); |
2101 | out_close: | 2095 | out_close: |
2102 | ubi_close_volume(c->ubi); | 2096 | ubi_close_volume(c->ubi); |
2103 | out_free: | 2097 | out: |
2104 | kfree(c); | ||
2105 | return err; | 2098 | return err; |
2106 | } | 2099 | } |
2107 | 2100 | ||
2108 | static int sb_test(struct super_block *sb, void *data) | 2101 | static int sb_test(struct super_block *sb, void *data) |
2109 | { | 2102 | { |
2110 | dev_t *dev = data; | 2103 | struct ubifs_info *c1 = data; |
2111 | struct ubifs_info *c = sb->s_fs_info; | 2104 | struct ubifs_info *c = sb->s_fs_info; |
2112 | 2105 | ||
2113 | return c->vi.cdev == *dev; | 2106 | return c->vi.cdev == c1->vi.cdev; |
2107 | } | ||
2108 | |||
2109 | static int sb_set(struct super_block *sb, void *data) | ||
2110 | { | ||
2111 | sb->s_fs_info = data; | ||
2112 | return set_anon_super(sb, NULL); | ||
2114 | } | 2113 | } |
2115 | 2114 | ||
2116 | static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags, | 2115 | static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags, |
2117 | const char *name, void *data) | 2116 | const char *name, void *data) |
2118 | { | 2117 | { |
2119 | struct ubi_volume_desc *ubi; | 2118 | struct ubi_volume_desc *ubi; |
2120 | struct ubi_volume_info vi; | 2119 | struct ubifs_info *c; |
2121 | struct super_block *sb; | 2120 | struct super_block *sb; |
2122 | int err; | 2121 | int err; |
2123 | 2122 | ||
@@ -2134,19 +2133,24 @@ static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags, | |||
2134 | name, (int)PTR_ERR(ubi)); | 2133 | name, (int)PTR_ERR(ubi)); |
2135 | return ERR_CAST(ubi); | 2134 | return ERR_CAST(ubi); |
2136 | } | 2135 | } |
2137 | ubi_get_volume_info(ubi, &vi); | ||
2138 | 2136 | ||
2139 | dbg_gen("opened ubi%d_%d", vi.ubi_num, vi.vol_id); | 2137 | c = alloc_ubifs_info(ubi); |
2138 | if (!c) { | ||
2139 | err = -ENOMEM; | ||
2140 | goto out_close; | ||
2141 | } | ||
2142 | |||
2143 | dbg_gen("opened ubi%d_%d", c->vi.ubi_num, c->vi.vol_id); | ||
2140 | 2144 | ||
2141 | sb = sget(fs_type, &sb_test, &set_anon_super, &vi.cdev); | 2145 | sb = sget(fs_type, sb_test, sb_set, c); |
2142 | if (IS_ERR(sb)) { | 2146 | if (IS_ERR(sb)) { |
2143 | err = PTR_ERR(sb); | 2147 | err = PTR_ERR(sb); |
2144 | goto out_close; | 2148 | kfree(c); |
2145 | } | 2149 | } |
2146 | 2150 | ||
2147 | if (sb->s_root) { | 2151 | if (sb->s_root) { |
2148 | struct ubifs_info *c1 = sb->s_fs_info; | 2152 | struct ubifs_info *c1 = sb->s_fs_info; |
2149 | 2153 | kfree(c); | |
2150 | /* A new mount point for already mounted UBIFS */ | 2154 | /* A new mount point for already mounted UBIFS */ |
2151 | dbg_gen("this ubi volume is already mounted"); | 2155 | dbg_gen("this ubi volume is already mounted"); |
2152 | if (!!(flags & MS_RDONLY) != c1->ro_mount) { | 2156 | if (!!(flags & MS_RDONLY) != c1->ro_mount) { |
@@ -2155,11 +2159,6 @@ static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags, | |||
2155 | } | 2159 | } |
2156 | } else { | 2160 | } else { |
2157 | sb->s_flags = flags; | 2161 | sb->s_flags = flags; |
2158 | /* | ||
2159 | * Pass 'ubi' to 'fill_super()' in sb->s_fs_info where it is | ||
2160 | * replaced by 'c'. | ||
2161 | */ | ||
2162 | sb->s_fs_info = ubi; | ||
2163 | err = ubifs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); | 2162 | err = ubifs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); |
2164 | if (err) | 2163 | if (err) |
2165 | goto out_deact; | 2164 | goto out_deact; |
@@ -2179,11 +2178,18 @@ out_close: | |||
2179 | return ERR_PTR(err); | 2178 | return ERR_PTR(err); |
2180 | } | 2179 | } |
2181 | 2180 | ||
2181 | static void kill_ubifs_super(struct super_block *s) | ||
2182 | { | ||
2183 | struct ubifs_info *c = s->s_fs_info; | ||
2184 | kill_anon_super(s); | ||
2185 | kfree(c); | ||
2186 | } | ||
2187 | |||
2182 | static struct file_system_type ubifs_fs_type = { | 2188 | static struct file_system_type ubifs_fs_type = { |
2183 | .name = "ubifs", | 2189 | .name = "ubifs", |
2184 | .owner = THIS_MODULE, | 2190 | .owner = THIS_MODULE, |
2185 | .mount = ubifs_mount, | 2191 | .mount = ubifs_mount, |
2186 | .kill_sb = kill_anon_super, | 2192 | .kill_sb = kill_ubifs_super, |
2187 | }; | 2193 | }; |
2188 | 2194 | ||
2189 | /* | 2195 | /* |