| .. | .. |
|---|
| 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; |
|---|