Skip to content

Commit 7fda67e

Browse files
Shida Zhangtytso
authored andcommitted
ext4: fix rec_len verify error
With the configuration PAGE_SIZE 64k and filesystem blocksize 64k, a problem occurred when more than 13 million files were directly created under a directory: EXT4-fs error (device xx): ext4_dx_csum_set:492: inode #xxxx: comm xxxxx: dir seems corrupt? Run e2fsck -D. EXT4-fs error (device xx): ext4_dx_csum_verify:463: inode #xxxx: comm xxxxx: dir seems corrupt? Run e2fsck -D. EXT4-fs error (device xx): dx_probe:856: inode #xxxx: block 8188: comm xxxxx: Directory index failed checksum When enough files are created, the fake_dirent->reclen will be 0xffff. it doesn't equal to the blocksize 65536, i.e. 0x10000. But it is not the same condition when blocksize equals to 4k. when enough files are created, the fake_dirent->reclen will be 0x1000. it equals to the blocksize 4k, i.e. 0x1000. The problem seems to be related to the limitation of the 16-bit field when the blocksize is set to 64k. To address this, helpers like ext4_rec_len_{from,to}_disk has already been introduced to complete the conversion between the encoded and the plain form of rec_len. So fix this one by using the helper, and all the other in this file too. Cc: stable@kernel.org Fixes: dbe8944 ("ext4: Calculate and verify checksums for htree nodes") Suggested-by: Andreas Dilger <adilger@dilger.ca> Suggested-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Shida Zhang <zhangshida@kylinos.cn> Reviewed-by: Andreas Dilger <adilger@dilger.ca> Reviewed-by: Darrick J. Wong <djwong@kernel.org> Link: https://lore.kernel.org/r/20230803060938.1929759-1-zhangshida@kylinos.cn Signed-off-by: Theodore Ts'o <tytso@mit.edu>
1 parent 5229a65 commit 7fda67e

File tree

1 file changed

+15
-11
lines changed

1 file changed

+15
-11
lines changed

fs/ext4/namei.c

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -343,17 +343,17 @@ static struct ext4_dir_entry_tail *get_dirent_tail(struct inode *inode,
343343
struct buffer_head *bh)
344344
{
345345
struct ext4_dir_entry_tail *t;
346+
int blocksize = EXT4_BLOCK_SIZE(inode->i_sb);
346347

347348
#ifdef PARANOID
348349
struct ext4_dir_entry *d, *top;
349350

350351
d = (struct ext4_dir_entry *)bh->b_data;
351352
top = (struct ext4_dir_entry *)(bh->b_data +
352-
(EXT4_BLOCK_SIZE(inode->i_sb) -
353-
sizeof(struct ext4_dir_entry_tail)));
354-
while (d < top && d->rec_len)
353+
(blocksize - sizeof(struct ext4_dir_entry_tail)));
354+
while (d < top && ext4_rec_len_from_disk(d->rec_len, blocksize))
355355
d = (struct ext4_dir_entry *)(((void *)d) +
356-
le16_to_cpu(d->rec_len));
356+
ext4_rec_len_from_disk(d->rec_len, blocksize));
357357

358358
if (d != top)
359359
return NULL;
@@ -364,7 +364,8 @@ static struct ext4_dir_entry_tail *get_dirent_tail(struct inode *inode,
364364
#endif
365365

366366
if (t->det_reserved_zero1 ||
367-
le16_to_cpu(t->det_rec_len) != sizeof(struct ext4_dir_entry_tail) ||
367+
(ext4_rec_len_from_disk(t->det_rec_len, blocksize) !=
368+
sizeof(struct ext4_dir_entry_tail)) ||
368369
t->det_reserved_zero2 ||
369370
t->det_reserved_ft != EXT4_FT_DIR_CSUM)
370371
return NULL;
@@ -445,13 +446,14 @@ static struct dx_countlimit *get_dx_countlimit(struct inode *inode,
445446
struct ext4_dir_entry *dp;
446447
struct dx_root_info *root;
447448
int count_offset;
449+
int blocksize = EXT4_BLOCK_SIZE(inode->i_sb);
450+
unsigned int rlen = ext4_rec_len_from_disk(dirent->rec_len, blocksize);
448451

449-
if (le16_to_cpu(dirent->rec_len) == EXT4_BLOCK_SIZE(inode->i_sb))
452+
if (rlen == blocksize)
450453
count_offset = 8;
451-
else if (le16_to_cpu(dirent->rec_len) == 12) {
454+
else if (rlen == 12) {
452455
dp = (struct ext4_dir_entry *)(((void *)dirent) + 12);
453-
if (le16_to_cpu(dp->rec_len) !=
454-
EXT4_BLOCK_SIZE(inode->i_sb) - 12)
456+
if (ext4_rec_len_from_disk(dp->rec_len, blocksize) != blocksize - 12)
455457
return NULL;
456458
root = (struct dx_root_info *)(((void *)dp + 12));
457459
if (root->reserved_zero ||
@@ -1315,6 +1317,7 @@ static int dx_make_map(struct inode *dir, struct buffer_head *bh,
13151317
unsigned int buflen = bh->b_size;
13161318
char *base = bh->b_data;
13171319
struct dx_hash_info h = *hinfo;
1320+
int blocksize = EXT4_BLOCK_SIZE(dir->i_sb);
13181321

13191322
if (ext4_has_metadata_csum(dir->i_sb))
13201323
buflen -= sizeof(struct ext4_dir_entry_tail);
@@ -1335,11 +1338,12 @@ static int dx_make_map(struct inode *dir, struct buffer_head *bh,
13351338
map_tail--;
13361339
map_tail->hash = h.hash;
13371340
map_tail->offs = ((char *) de - base)>>2;
1338-
map_tail->size = le16_to_cpu(de->rec_len);
1341+
map_tail->size = ext4_rec_len_from_disk(de->rec_len,
1342+
blocksize);
13391343
count++;
13401344
cond_resched();
13411345
}
1342-
de = ext4_next_entry(de, dir->i_sb->s_blocksize);
1346+
de = ext4_next_entry(de, blocksize);
13431347
}
13441348
return count;
13451349
}

0 commit comments

Comments
 (0)