Skip to content

Commit ce9f24c

Browse files
jankaratytso
authored andcommitted
ext4: check journal inode extents more carefully
Currently, system zones just track ranges of block, that are "important" fs metadata (bitmaps, group descriptors, journal blocks, etc.). This however complicates how extent tree (or indirect blocks) can be checked for inodes that actually track such metadata - currently the journal inode but arguably we should be treating quota files or resize inode similarly. We cannot run __ext4_ext_check() on such metadata inodes when loading their extents as that would immediately trigger the validity checks and so we just hack around that and special-case the journal inode. This however leads to a situation that a journal inode which has extent tree of depth at least one can have invalid extent tree that gets unnoticed until ext4_cache_extents() crashes. To overcome this limitation, track inode number each system zone belongs to (0 is used for zones not belonging to any inode). We can then verify inode number matches the expected one when verifying extent tree and thus avoid the false errors. With this there's no need to to special-case journal inode during extent tree checking anymore so remove it. Fixes: 0a944e8 ("ext4: don't perform block validity checks on the journal inode") Reported-by: Wolfgang Frisch <[email protected]> Reviewed-by: Lukas Czerner <[email protected]> Signed-off-by: Jan Kara <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Theodore Ts'o <[email protected]>
1 parent bf9a379 commit ce9f24c

File tree

6 files changed

+41
-47
lines changed

6 files changed

+41
-47
lines changed

fs/ext4/block_validity.c

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ struct ext4_system_zone {
2424
struct rb_node node;
2525
ext4_fsblk_t start_blk;
2626
unsigned int count;
27+
u32 ino;
2728
};
2829

2930
static struct kmem_cache *ext4_system_zone_cachep;
@@ -45,7 +46,8 @@ void ext4_exit_system_zone(void)
4546
static inline int can_merge(struct ext4_system_zone *entry1,
4647
struct ext4_system_zone *entry2)
4748
{
48-
if ((entry1->start_blk + entry1->count) == entry2->start_blk)
49+
if ((entry1->start_blk + entry1->count) == entry2->start_blk &&
50+
entry1->ino == entry2->ino)
4951
return 1;
5052
return 0;
5153
}
@@ -66,7 +68,7 @@ static void release_system_zone(struct ext4_system_blocks *system_blks)
6668
*/
6769
static int add_system_zone(struct ext4_system_blocks *system_blks,
6870
ext4_fsblk_t start_blk,
69-
unsigned int count)
71+
unsigned int count, u32 ino)
7072
{
7173
struct ext4_system_zone *new_entry, *entry;
7274
struct rb_node **n = &system_blks->root.rb_node, *node;
@@ -89,6 +91,7 @@ static int add_system_zone(struct ext4_system_blocks *system_blks,
8991
return -ENOMEM;
9092
new_entry->start_blk = start_blk;
9193
new_entry->count = count;
94+
new_entry->ino = ino;
9295
new_node = &new_entry->node;
9396

9497
rb_link_node(new_node, parent, n);
@@ -149,7 +152,7 @@ static void debug_print_tree(struct ext4_sb_info *sbi)
149152
static int ext4_data_block_valid_rcu(struct ext4_sb_info *sbi,
150153
struct ext4_system_blocks *system_blks,
151154
ext4_fsblk_t start_blk,
152-
unsigned int count)
155+
unsigned int count, ino_t ino)
153156
{
154157
struct ext4_system_zone *entry;
155158
struct rb_node *n;
@@ -170,7 +173,7 @@ static int ext4_data_block_valid_rcu(struct ext4_sb_info *sbi,
170173
else if (start_blk >= (entry->start_blk + entry->count))
171174
n = n->rb_right;
172175
else
173-
return 0;
176+
return entry->ino == ino;
174177
}
175178
return 1;
176179
}
@@ -204,19 +207,18 @@ static int ext4_protect_reserved_inode(struct super_block *sb,
204207
if (n == 0) {
205208
i++;
206209
} else {
207-
if (!ext4_data_block_valid_rcu(sbi, system_blks,
208-
map.m_pblk, n)) {
209-
err = -EFSCORRUPTED;
210-
__ext4_error(sb, __func__, __LINE__, -err,
211-
map.m_pblk, "blocks %llu-%llu "
212-
"from inode %u overlap system zone",
213-
map.m_pblk,
214-
map.m_pblk + map.m_len - 1, ino);
210+
err = add_system_zone(system_blks, map.m_pblk, n, ino);
211+
if (err < 0) {
212+
if (err == -EFSCORRUPTED) {
213+
__ext4_error(sb, __func__, __LINE__,
214+
-err, map.m_pblk,
215+
"blocks %llu-%llu from inode %u overlap system zone",
216+
map.m_pblk,
217+
map.m_pblk + map.m_len - 1,
218+
ino);
219+
}
215220
break;
216221
}
217-
err = add_system_zone(system_blks, map.m_pblk, n);
218-
if (err < 0)
219-
break;
220222
i += n;
221223
}
222224
}
@@ -270,19 +272,19 @@ int ext4_setup_system_zone(struct super_block *sb)
270272
((i < 5) || ((i % flex_size) == 0)))
271273
add_system_zone(system_blks,
272274
ext4_group_first_block_no(sb, i),
273-
ext4_bg_num_gdb(sb, i) + 1);
275+
ext4_bg_num_gdb(sb, i) + 1, 0);
274276
gdp = ext4_get_group_desc(sb, i, NULL);
275277
ret = add_system_zone(system_blks,
276-
ext4_block_bitmap(sb, gdp), 1);
278+
ext4_block_bitmap(sb, gdp), 1, 0);
277279
if (ret)
278280
goto err;
279281
ret = add_system_zone(system_blks,
280-
ext4_inode_bitmap(sb, gdp), 1);
282+
ext4_inode_bitmap(sb, gdp), 1, 0);
281283
if (ret)
282284
goto err;
283285
ret = add_system_zone(system_blks,
284286
ext4_inode_table(sb, gdp),
285-
sbi->s_itb_per_group);
287+
sbi->s_itb_per_group, 0);
286288
if (ret)
287289
goto err;
288290
}
@@ -331,7 +333,7 @@ void ext4_release_system_zone(struct super_block *sb)
331333
call_rcu(&system_blks->rcu, ext4_destroy_system_zone);
332334
}
333335

334-
int ext4_data_block_valid(struct ext4_sb_info *sbi, ext4_fsblk_t start_blk,
336+
int ext4_inode_block_valid(struct inode *inode, ext4_fsblk_t start_blk,
335337
unsigned int count)
336338
{
337339
struct ext4_system_blocks *system_blks;
@@ -343,9 +345,9 @@ int ext4_data_block_valid(struct ext4_sb_info *sbi, ext4_fsblk_t start_blk,
343345
* mount option.
344346
*/
345347
rcu_read_lock();
346-
system_blks = rcu_dereference(sbi->system_blks);
347-
ret = ext4_data_block_valid_rcu(sbi, system_blks, start_blk,
348-
count);
348+
system_blks = rcu_dereference(EXT4_SB(inode->i_sb)->system_blks);
349+
ret = ext4_data_block_valid_rcu(EXT4_SB(inode->i_sb), system_blks,
350+
start_blk, count, inode->i_ino);
349351
rcu_read_unlock();
350352
return ret;
351353
}
@@ -364,8 +366,7 @@ int ext4_check_blockref(const char *function, unsigned int line,
364366
while (bref < p+max) {
365367
blk = le32_to_cpu(*bref++);
366368
if (blk &&
367-
unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb),
368-
blk, 1))) {
369+
unlikely(!ext4_inode_block_valid(inode, blk, 1))) {
369370
ext4_error_inode(inode, function, line, blk,
370371
"invalid block");
371372
return -EFSCORRUPTED;

fs/ext4/ext4.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3397,9 +3397,9 @@ extern void ext4_release_system_zone(struct super_block *sb);
33973397
extern int ext4_setup_system_zone(struct super_block *sb);
33983398
extern int __init ext4_init_system_zone(void);
33993399
extern void ext4_exit_system_zone(void);
3400-
extern int ext4_data_block_valid(struct ext4_sb_info *sbi,
3401-
ext4_fsblk_t start_blk,
3402-
unsigned int count);
3400+
extern int ext4_inode_block_valid(struct inode *inode,
3401+
ext4_fsblk_t start_blk,
3402+
unsigned int count);
34033403
extern int ext4_check_blockref(const char *, unsigned int,
34043404
struct inode *, __le32 *, unsigned int);
34053405

fs/ext4/extents.c

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -340,15 +340,15 @@ static int ext4_valid_extent(struct inode *inode, struct ext4_extent *ext)
340340
*/
341341
if (lblock + len <= lblock)
342342
return 0;
343-
return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, len);
343+
return ext4_inode_block_valid(inode, block, len);
344344
}
345345

346346
static int ext4_valid_extent_idx(struct inode *inode,
347347
struct ext4_extent_idx *ext_idx)
348348
{
349349
ext4_fsblk_t block = ext4_idx_pblock(ext_idx);
350350

351-
return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, 1);
351+
return ext4_inode_block_valid(inode, block, 1);
352352
}
353353

354354
static int ext4_valid_extent_entries(struct inode *inode,
@@ -507,14 +507,10 @@ __read_extent_tree_block(const char *function, unsigned int line,
507507
}
508508
if (buffer_verified(bh) && !(flags & EXT4_EX_FORCE_CACHE))
509509
return bh;
510-
if (!ext4_has_feature_journal(inode->i_sb) ||
511-
(inode->i_ino !=
512-
le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) {
513-
err = __ext4_ext_check(function, line, inode,
514-
ext_block_hdr(bh), depth, pblk);
515-
if (err)
516-
goto errout;
517-
}
510+
err = __ext4_ext_check(function, line, inode,
511+
ext_block_hdr(bh), depth, pblk);
512+
if (err)
513+
goto errout;
518514
set_buffer_verified(bh);
519515
/*
520516
* If this is a leaf block, cache all of its entries

fs/ext4/indirect.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -858,8 +858,7 @@ static int ext4_clear_blocks(handle_t *handle, struct inode *inode,
858858
else if (ext4_should_journal_data(inode))
859859
flags |= EXT4_FREE_BLOCKS_FORGET;
860860

861-
if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), block_to_free,
862-
count)) {
861+
if (!ext4_inode_block_valid(inode, block_to_free, count)) {
863862
EXT4_ERROR_INODE(inode, "attempt to clear invalid "
864863
"blocks %llu len %lu",
865864
(unsigned long long) block_to_free, count);
@@ -1004,8 +1003,7 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode,
10041003
if (!nr)
10051004
continue; /* A hole */
10061005

1007-
if (!ext4_data_block_valid(EXT4_SB(inode->i_sb),
1008-
nr, 1)) {
1006+
if (!ext4_inode_block_valid(inode, nr, 1)) {
10091007
EXT4_ERROR_INODE(inode,
10101008
"invalid indirect mapped "
10111009
"block %lu (level %d)",

fs/ext4/inode.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -394,8 +394,7 @@ static int __check_block_validity(struct inode *inode, const char *func,
394394
(inode->i_ino ==
395395
le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum)))
396396
return 0;
397-
if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), map->m_pblk,
398-
map->m_len)) {
397+
if (!ext4_inode_block_valid(inode, map->m_pblk, map->m_len)) {
399398
ext4_error_inode(inode, func, line, map->m_pblk,
400399
"lblock %lu mapped to illegal pblock %llu "
401400
"(length %d)", (unsigned long) map->m_lblk,
@@ -4761,7 +4760,7 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
47614760

47624761
ret = 0;
47634762
if (ei->i_file_acl &&
4764-
!ext4_data_block_valid(EXT4_SB(sb), ei->i_file_acl, 1)) {
4763+
!ext4_inode_block_valid(inode, ei->i_file_acl, 1)) {
47654764
ext4_error_inode(inode, function, line, 0,
47664765
"iget: bad extended attribute block %llu",
47674766
ei->i_file_acl);

fs/ext4/mballoc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3233,7 +3233,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
32333233
block = ext4_grp_offs_to_block(sb, &ac->ac_b_ex);
32343234

32353235
len = EXT4_C2B(sbi, ac->ac_b_ex.fe_len);
3236-
if (!ext4_data_block_valid(sbi, block, len)) {
3236+
if (!ext4_inode_block_valid(ac->ac_inode, block, len)) {
32373237
ext4_error(sb, "Allocating blocks %llu-%llu which overlap "
32383238
"fs metadata", block, block+len);
32393239
/* File system mounted not to panic on error
@@ -5058,7 +5058,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
50585058

50595059
sbi = EXT4_SB(sb);
50605060
if (!(flags & EXT4_FREE_BLOCKS_VALIDATED) &&
5061-
!ext4_data_block_valid(sbi, block, count)) {
5061+
!ext4_inode_block_valid(inode, block, count)) {
50625062
ext4_error(sb, "Freeing blocks not in datazone - "
50635063
"block = %llu, count = %lu", block, count);
50645064
goto error_return;

0 commit comments

Comments
 (0)