.. | .. |
---|
126 | 126 | { |
---|
127 | 127 | struct rb_node *node; |
---|
128 | 128 | struct ext4_system_zone *entry; |
---|
| 129 | + struct ext4_system_blocks *system_blks; |
---|
129 | 130 | int first = 1; |
---|
130 | 131 | |
---|
131 | 132 | printk(KERN_INFO "System zones: "); |
---|
132 | | - node = rb_first(&sbi->system_blks->root); |
---|
| 133 | + rcu_read_lock(); |
---|
| 134 | + system_blks = rcu_dereference(sbi->s_system_blks); |
---|
| 135 | + node = rb_first(&system_blks->root); |
---|
133 | 136 | while (node) { |
---|
134 | 137 | entry = rb_entry(node, struct ext4_system_zone, node); |
---|
135 | 138 | printk(KERN_CONT "%s%llu-%llu", first ? "" : ", ", |
---|
.. | .. |
---|
137 | 140 | first = 0; |
---|
138 | 141 | node = rb_next(node); |
---|
139 | 142 | } |
---|
| 143 | + rcu_read_unlock(); |
---|
140 | 144 | printk(KERN_CONT "\n"); |
---|
141 | | -} |
---|
142 | | - |
---|
143 | | -/* |
---|
144 | | - * Returns 1 if the passed-in block region (start_blk, |
---|
145 | | - * start_blk+count) is valid; 0 if some part of the block region |
---|
146 | | - * overlaps with filesystem metadata blocks. |
---|
147 | | - */ |
---|
148 | | -static int ext4_data_block_valid_rcu(struct ext4_sb_info *sbi, |
---|
149 | | - struct ext4_system_blocks *system_blks, |
---|
150 | | - ext4_fsblk_t start_blk, |
---|
151 | | - unsigned int count, ino_t ino) |
---|
152 | | -{ |
---|
153 | | - struct ext4_system_zone *entry; |
---|
154 | | - struct rb_node *n; |
---|
155 | | - |
---|
156 | | - if ((start_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) || |
---|
157 | | - (start_blk + count < start_blk) || |
---|
158 | | - (start_blk + count > ext4_blocks_count(sbi->s_es))) { |
---|
159 | | - sbi->s_es->s_last_error_block = cpu_to_le64(start_blk); |
---|
160 | | - return 0; |
---|
161 | | - } |
---|
162 | | - |
---|
163 | | - if (system_blks == NULL) |
---|
164 | | - return 1; |
---|
165 | | - |
---|
166 | | - n = system_blks->root.rb_node; |
---|
167 | | - while (n) { |
---|
168 | | - entry = rb_entry(n, struct ext4_system_zone, node); |
---|
169 | | - if (start_blk + count - 1 < entry->start_blk) |
---|
170 | | - n = n->rb_left; |
---|
171 | | - else if (start_blk >= (entry->start_blk + entry->count)) |
---|
172 | | - n = n->rb_right; |
---|
173 | | - else { |
---|
174 | | - if (entry->ino == ino) |
---|
175 | | - return 1; |
---|
176 | | - sbi->s_es->s_last_error_block = cpu_to_le64(start_blk); |
---|
177 | | - return 0; |
---|
178 | | - } |
---|
179 | | - } |
---|
180 | | - return 1; |
---|
181 | 145 | } |
---|
182 | 146 | |
---|
183 | 147 | static int ext4_protect_reserved_inode(struct super_block *sb, |
---|
.. | .. |
---|
212 | 176 | err = add_system_zone(system_blks, map.m_pblk, n, ino); |
---|
213 | 177 | if (err < 0) { |
---|
214 | 178 | if (err == -EFSCORRUPTED) { |
---|
215 | | - ext4_error(sb, |
---|
216 | | - "blocks %llu-%llu from inode %u " |
---|
217 | | - "overlap system zone", map.m_pblk, |
---|
218 | | - map.m_pblk + map.m_len - 1, ino); |
---|
| 179 | + __ext4_error(sb, __func__, __LINE__, |
---|
| 180 | + -err, map.m_pblk, |
---|
| 181 | + "blocks %llu-%llu from inode %u overlap system zone", |
---|
| 182 | + map.m_pblk, |
---|
| 183 | + map.m_pblk + map.m_len - 1, |
---|
| 184 | + ino); |
---|
219 | 185 | } |
---|
220 | 186 | break; |
---|
221 | 187 | } |
---|
.. | .. |
---|
251 | 217 | struct ext4_system_blocks *system_blks; |
---|
252 | 218 | struct ext4_group_desc *gdp; |
---|
253 | 219 | ext4_group_t i; |
---|
254 | | - int flex_size = ext4_flex_bg_size(sbi); |
---|
255 | 220 | int ret; |
---|
256 | 221 | |
---|
257 | 222 | system_blks = kzalloc(sizeof(*system_blks), GFP_KERNEL); |
---|
.. | .. |
---|
259 | 224 | return -ENOMEM; |
---|
260 | 225 | |
---|
261 | 226 | for (i=0; i < ngroups; i++) { |
---|
262 | | - if (ext4_bg_has_super(sb, i) && |
---|
263 | | - ((i < 5) || ((i % flex_size) == 0))) |
---|
264 | | - add_system_zone(system_blks, |
---|
| 227 | + unsigned int meta_blks = ext4_num_base_meta_blocks(sb, i); |
---|
| 228 | + |
---|
| 229 | + cond_resched(); |
---|
| 230 | + if (meta_blks != 0) { |
---|
| 231 | + ret = add_system_zone(system_blks, |
---|
265 | 232 | ext4_group_first_block_no(sb, i), |
---|
266 | | - ext4_bg_num_gdb(sb, i) + 1, 0); |
---|
| 233 | + meta_blks, 0); |
---|
| 234 | + if (ret) |
---|
| 235 | + goto err; |
---|
| 236 | + } |
---|
267 | 237 | gdp = ext4_get_group_desc(sb, i, NULL); |
---|
268 | 238 | ret = add_system_zone(system_blks, |
---|
269 | 239 | ext4_block_bitmap(sb, gdp), 1, 0); |
---|
.. | .. |
---|
291 | 261 | * with ext4_data_block_valid() accessing the rbtree at the same |
---|
292 | 262 | * time. |
---|
293 | 263 | */ |
---|
294 | | - rcu_assign_pointer(sbi->system_blks, system_blks); |
---|
| 264 | + rcu_assign_pointer(sbi->s_system_blks, system_blks); |
---|
295 | 265 | |
---|
296 | 266 | if (test_opt(sb, DEBUG)) |
---|
297 | 267 | debug_print_tree(sbi); |
---|
.. | .. |
---|
316 | 286 | { |
---|
317 | 287 | struct ext4_system_blocks *system_blks; |
---|
318 | 288 | |
---|
319 | | - system_blks = rcu_dereference_protected(EXT4_SB(sb)->system_blks, |
---|
| 289 | + system_blks = rcu_dereference_protected(EXT4_SB(sb)->s_system_blks, |
---|
320 | 290 | lockdep_is_held(&sb->s_umount)); |
---|
321 | | - rcu_assign_pointer(EXT4_SB(sb)->system_blks, NULL); |
---|
| 291 | + rcu_assign_pointer(EXT4_SB(sb)->s_system_blks, NULL); |
---|
322 | 292 | |
---|
323 | 293 | if (system_blks) |
---|
324 | 294 | call_rcu(&system_blks->rcu, ext4_destroy_system_zone); |
---|
325 | 295 | } |
---|
326 | 296 | |
---|
327 | | -int ext4_inode_block_valid(struct inode *inode, ext4_fsblk_t start_blk, |
---|
328 | | - unsigned int count) |
---|
| 297 | +int ext4_sb_block_valid(struct super_block *sb, struct inode *inode, |
---|
| 298 | + ext4_fsblk_t start_blk, unsigned int count) |
---|
329 | 299 | { |
---|
| 300 | + struct ext4_sb_info *sbi = EXT4_SB(sb); |
---|
330 | 301 | struct ext4_system_blocks *system_blks; |
---|
331 | | - int ret; |
---|
| 302 | + struct ext4_system_zone *entry; |
---|
| 303 | + struct rb_node *n; |
---|
| 304 | + int ret = 1; |
---|
| 305 | + |
---|
| 306 | + if ((start_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) || |
---|
| 307 | + (start_blk + count < start_blk) || |
---|
| 308 | + (start_blk + count > ext4_blocks_count(sbi->s_es))) |
---|
| 309 | + return 0; |
---|
332 | 310 | |
---|
333 | 311 | /* |
---|
334 | 312 | * Lock the system zone to prevent it being released concurrently |
---|
.. | .. |
---|
336 | 314 | * mount option. |
---|
337 | 315 | */ |
---|
338 | 316 | rcu_read_lock(); |
---|
339 | | - system_blks = rcu_dereference(EXT4_SB(inode->i_sb)->system_blks); |
---|
340 | | - ret = ext4_data_block_valid_rcu(EXT4_SB(inode->i_sb), system_blks, |
---|
341 | | - start_blk, count, inode->i_ino); |
---|
| 317 | + system_blks = rcu_dereference(sbi->s_system_blks); |
---|
| 318 | + if (system_blks == NULL) |
---|
| 319 | + goto out_rcu; |
---|
| 320 | + |
---|
| 321 | + n = system_blks->root.rb_node; |
---|
| 322 | + while (n) { |
---|
| 323 | + entry = rb_entry(n, struct ext4_system_zone, node); |
---|
| 324 | + if (start_blk + count - 1 < entry->start_blk) |
---|
| 325 | + n = n->rb_left; |
---|
| 326 | + else if (start_blk >= (entry->start_blk + entry->count)) |
---|
| 327 | + n = n->rb_right; |
---|
| 328 | + else { |
---|
| 329 | + ret = 0; |
---|
| 330 | + if (inode) |
---|
| 331 | + ret = (entry->ino == inode->i_ino); |
---|
| 332 | + break; |
---|
| 333 | + } |
---|
| 334 | + } |
---|
| 335 | +out_rcu: |
---|
342 | 336 | rcu_read_unlock(); |
---|
343 | 337 | return ret; |
---|
| 338 | +} |
---|
| 339 | + |
---|
| 340 | +/* |
---|
| 341 | + * Returns 1 if the passed-in block region (start_blk, |
---|
| 342 | + * start_blk+count) is valid; 0 if some part of the block region |
---|
| 343 | + * overlaps with some other filesystem metadata blocks. |
---|
| 344 | + */ |
---|
| 345 | +int ext4_inode_block_valid(struct inode *inode, ext4_fsblk_t start_blk, |
---|
| 346 | + unsigned int count) |
---|
| 347 | +{ |
---|
| 348 | + return ext4_sb_block_valid(inode->i_sb, inode, start_blk, count); |
---|
344 | 349 | } |
---|
345 | 350 | |
---|
346 | 351 | int ext4_check_blockref(const char *function, unsigned int line, |
---|
347 | 352 | struct inode *inode, __le32 *p, unsigned int max) |
---|
348 | 353 | { |
---|
349 | | - struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es; |
---|
350 | 354 | __le32 *bref = p; |
---|
351 | 355 | unsigned int blk; |
---|
352 | 356 | |
---|
.. | .. |
---|
359 | 363 | blk = le32_to_cpu(*bref++); |
---|
360 | 364 | if (blk && |
---|
361 | 365 | unlikely(!ext4_inode_block_valid(inode, blk, 1))) { |
---|
362 | | - es->s_last_error_block = cpu_to_le64(blk); |
---|
363 | 366 | ext4_error_inode(inode, function, line, blk, |
---|
364 | 367 | "invalid block"); |
---|
365 | 368 | return -EFSCORRUPTED; |
---|