Re: [PATCH] ext4: do not accept rec_len 0 for block sizes below 64k

From: Baokun Li

Date: Wed Oct 07 2026 - 22:44:07 EST


On 2026/10/6 19:15, hengyul@xxxxxxxxxx wrote:
> From: Hengyu Liang <hengyul@xxxxxxxxxx>
>
> Commit afa6d5a16bf2 ("ext4: remove PAGE_SIZE checks for rec_len
> conversion") made ext4_rec_len_from_disk() read a rec_len of 0 as "this
> entry covers the whole block" for every block size.
>
> However, this special value only exists for block sizes of 64k and
> more. With smaller blocks, a rec_len of 0 means that the directory
> block is corrupted. As of now, a directory block that has been
> overwritten with zeroes is treated as an empty block. The kernel no
> longer reports the corruption and can store new entries in that block,
> while e2fsck still reports the block as corrupted.
>
> The issue can be reproduced on a file system without metadata_csum:
>
> mke2fs -q -t ext4 -b 1024 -O ^metadata_csum,^dir_index img 8M
> mount -o loop img /mnt
> mkdir /mnt/d
> for i in $(seq 100); do touch /mnt/d/file_with_a_long_name_$i; done
> umount /mnt
> dd if=/dev/zero of=img bs=1024 count=1 conv=notrunc \
> seek=$(debugfs -R "bmap d 1" img)
> mount -o loop img /mnt
> touch /mnt/d/new
>
> Before commit afa6d5a16bf2 ("ext4: remove PAGE_SIZE checks for rec_len
> conversion"), the last command fails with "Structure needs cleaning".
> After that commit, it succeeds.
>
> This patch makes ext4_rec_len_from_disk() return the on-disk value
> unchanged when the block size is below 64k, as e2fsprogs does.
>
> Fixes: afa6d5a16bf2 ("ext4: remove PAGE_SIZE checks for rec_len conversion")
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Hengyu Liang <hengyul@xxxxxxxxxx>

Looks good, thanks for the fix!

The extended rec_len encoding is only needed for block sizes >= 64k,
so this matches what e2fsprogs does: ext2fs_get_rec_len() has had the
same "blocksize < 65536" check since commit a4fdf09414e0 ("libext2fs:
Don't use the extended rec_len encoding for standard file systems").

Reviewed-by: Baokun Li <libaokun@xxxxxxxxxxxxxxxxx>


> ---
> fs/ext4/ext4.h | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 724a27e8be61..ab716a5ad6da 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -2611,6 +2611,13 @@ ext4_rec_len_from_disk(__le16 dlen, unsigned blocksize)
> {
> unsigned len = le16_to_cpu(dlen);
>
> + /*
> + * Only blocks of 64k and more need the special encoding of rec_len.
> + * For smaller blocks 0 and EXT4_MAX_REC_LEN are not valid lengths
> + * and must not be taken for an entry that spans the whole block.
> + */
> + if (blocksize < 65536)
> + return len;
> if (len == EXT4_MAX_REC_LEN || len == 0)
> return blocksize;
> return (len & 65532) | ((len & 3) << 16);