From: Artem Blagodarenko ext4_dir_entry_len() (added in the previous patch, where dx_get_dx_info() first needs it) computes the required rec_len for a directory entry, taking into account dirdata and casefold+fscrypt hash space. Update dx_root_limit() to use ext4_dir_entry_len() instead of open-coded ext4_dir_rec_len() for '.' and '..' entries. Signed-off-by: Artem Blagodarenko Assisted-by: Claude:claude-opus-5-5 --- fs/ext4/ext4.h | 8 +++++--- fs/ext4/namei.c | 28 +++++++++++++++++++--------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index cebbfac05fa5..fe6b8fe4634e 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -3085,12 +3085,14 @@ static const unsigned char ext4_filetype_table[] = { DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK }; -static inline unsigned char get_dtype(struct super_block *sb, int filetype) +static inline unsigned char get_dtype(struct super_block *sb, int filetype) { - if (!ext4_has_feature_filetype(sb) || filetype >= EXT4_FT_MAX) + unsigned char fl_index = filetype & EXT4_FT_MASK; + + if (!ext4_has_feature_filetype(sb) || fl_index >= EXT4_FT_MAX) return DT_UNKNOWN; - return ext4_filetype_table[filetype]; + return ext4_filetype_table[fl_index]; } extern int ext4_check_all_de(struct inode *dir, struct buffer_head *bh, void *buf, int buf_size); diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index c221bcd562cc..9953583ad6c3 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -611,14 +611,21 @@ static inline void dx_set_limit(struct dx_entry *entries, unsigned value) ((struct dx_countlimit *) entries)->limit = cpu_to_le16(value); } -static inline unsigned dx_root_limit(struct inode *dir, unsigned infosize) +static inline unsigned dx_root_limit(struct inode *dir, + struct ext4_dir_entry_2 *dot_de) { - unsigned int entry_space = dir->i_sb->s_blocksize - - ext4_dir_rec_len(1, NULL) - - ext4_dir_rec_len(2, NULL) - infosize; + struct dx_root_info *info; + unsigned int entry_space, used; + info = dx_get_dx_info(dir, dot_de); + if (IS_ERR(info)) + return 0; + used = (char *)info - (char *)dot_de + info->info_length; if (ext4_has_feature_metadata_csum(dir->i_sb)) - entry_space -= sizeof(struct dx_tail); + used += sizeof(struct dx_tail); + if (used > dir->i_sb->s_blocksize) + return 0; + entry_space = dir->i_sb->s_blocksize - used; return entry_space / sizeof(struct dx_entry); } @@ -919,10 +926,13 @@ dx_probe(struct ext4_filename *fname, struct inode *dir, entries = (struct dx_entry *)(((char *)info) + info->info_length); - if (dx_get_limit(entries) != dx_root_limit(dir, info->info_length)) { + if (dx_get_limit(entries) != + dx_root_limit(dir, (struct ext4_dir_entry_2 *)frame->bh->b_data)) { ext4_warning_inode(dir, "dx entry: limit %u != root limit %u", dx_get_limit(entries), - dx_root_limit(dir, info->info_length)); + dx_root_limit(dir, + (struct ext4_dir_entry_2 *)frame->bh->b_data + )); goto fail; } @@ -2372,10 +2382,10 @@ static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname, dx_info->hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version; - entries = (void *)dx_info + sizeof(*dx_info); + entries = (void *)dx_info + dx_info->info_length; dx_set_block(entries, 1); dx_set_count(entries, 1); - dx_set_limit(entries, dx_root_limit(dir, sizeof(*dx_info))); + dx_set_limit(entries, dx_root_limit(dir, dot_de)); /* Initialize as for dx_probe */ fname->hinfo.hash_version = dx_info->hash_version; -- 2.52.0