From: Artem Blagodarenko Since commit 471fbbea7ff7 ("ext4: handle casefolding with encryption"), dx_node_limit() passes the directory to ext4_dir_rec_len(), which for a casefolded+encrypted directory reserves 8 bytes for a hash in the fake dirent at the start of an index node. That dirent never holds a name or a hash, and e2fsck computes the limit without it. e2fsck also only treats a block as an index node when its limit has that value. As a result, on a casefolded+encrypted directory deep enough to have index nodes, e2fsck reports the index nodes the kernel wrote as corrupt leaf blocks ("directory corrupted"), and the kernel refuses the index nodes that e2fsck -D wrote ("dx entry: limit 510 != node limit 509", 4k blocks with metadata_csum). Compute the limit without the hash, as e2fsck does, and keep accepting the old value so that directories written by older kernels can still be read. Older kernels already reject index nodes written by e2fsck -D; they will reject those written by this kernel in the same way. Fixes: 471fbbea7ff7 ("ext4: handle casefolding with encryption") Signed-off-by: Artem Blagodarenko Assisted-by: Claude:claude-opus-5-5 --- fs/ext4/namei.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 76b9aa3fb03c..3d69b6739cfe 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -593,16 +593,37 @@ static inline unsigned dx_root_limit(struct inode *dir, unsigned infosize) return entry_space / sizeof(struct dx_entry); } -static inline unsigned dx_node_limit(struct inode *dir) +static inline unsigned int __dx_node_limit(struct inode *dir, + const struct inode *hash_dir) { unsigned int entry_space = dir->i_sb->s_blocksize - - ext4_dir_rec_len(0, dir); + ext4_dir_rec_len(0, hash_dir); if (ext4_has_feature_metadata_csum(dir->i_sb)) entry_space -= sizeof(struct dx_tail); return entry_space / sizeof(struct dx_entry); } +/* + * The fake dirent at the start of an index node never holds a name, so no + * room is reserved for a casefold+fscrypt hash in it. This is the limit + * e2fsck computes and expects. + */ +static inline unsigned dx_node_limit(struct inode *dir) +{ + return __dx_node_limit(dir, NULL); +} + +/* + * Older kernels did reserve the hash in casefolded+encrypted directories, + * so their index nodes have a limit one smaller. Accept those too. + */ +static inline bool dx_node_limit_ok(struct inode *dir, unsigned int limit) +{ + return limit == dx_node_limit(dir) || + limit == __dx_node_limit(dir, dir); +} + /* * Debug */ @@ -924,7 +945,7 @@ dx_probe(struct ext4_filename *fname, struct inode *dir, entries = ((struct dx_node *) frame->bh->b_data)->entries; - if (dx_get_limit(entries) != dx_node_limit(dir)) { + if (!dx_node_limit_ok(dir, dx_get_limit(entries))) { ext4_warning_inode(dir, "dx entry: limit %u != node limit %u", dx_get_limit(entries), dx_node_limit(dir)); -- 2.52.0