From: Artem Blagodarenko Rename ext4_dir_rec_len() to ext4_dirent_rec_len() to better reflect that it computes the record length for a directory entry based on the provided name length. Update the comment to clarify handling of dirdata-enabled directories and document the use of ext4_dir_entry_len() when dirdata is present. No functional changes. Signed-off-by: Artem Blagodarenko Reviewed-by: Andreas Dilger --- fs/ext4/dir.c | 11 +++++------ fs/ext4/ext4.h | 14 ++++++++++---- fs/ext4/inline.c | 14 +++++++------- fs/ext4/namei.c | 25 +++++++++++++------------ 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index 17edd678fa87..3294947f8555 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -89,16 +89,15 @@ int __ext4_check_dir_entry(const char *function, unsigned int line, bool fake = is_fake_dir_entry(de); bool has_csum = ext4_has_feature_metadata_csum(dir->i_sb); - if (unlikely(rlen < ext4_dir_rec_len(1, fake ? NULL : dir))) + if (unlikely(rlen < ext4_dirent_rec_len(1, fake ? NULL : dir))) error_msg = "rec_len is smaller than minimal"; else if (unlikely(rlen % 4 != 0)) error_msg = "rec_len % 4 != 0"; - else if (unlikely(rlen < ext4_dir_rec_len(de->name_len, - fake ? NULL : dir))) - error_msg = "rec_len is too small for name_len"; else if (unlikely(next_offset > size)) error_msg = "directory entry overrun"; - else if (unlikely(next_offset > size - ext4_dir_rec_len(1, + else if (unlikely(rlen < ext4_dir_entry_len(de, size, fake ? NULL : dir))) + error_msg = "rec_len is too small for name_len"; + else if (unlikely(next_offset > size - ext4_dirent_rec_len(1, has_csum ? NULL : dir) && next_offset != size)) error_msg = "directory entry too close to block end"; @@ -245,7 +244,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) * failure will be detected in the * dirent test below. */ if (ext4_rec_len_from_disk(de->rec_len, - sb->s_blocksize) < ext4_dir_rec_len(1, + sb->s_blocksize) < ext4_dirent_rec_len(1, inode)) break; i += ext4_rec_len_from_disk(de->rec_len, diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 4c3147387e47..02501363ea12 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2614,11 +2614,16 @@ struct ext4_dirent_hash { * casefolded and encrypted need to store the hash as well, so we add room for * ext4_extended_dir_entry_2. For all entries related to '.' or '..' you should * pass NULL for dir, as those entries do not use the extra fields. + * + * For directories with the dirdata feature, extra data may follow the filename. + * Use ext4_dir_entry_len() to compute the length of a directory entry + * including any dirdata, or ext4_dirent_rec_len() directly when the total + * name_len (including dirdata length) is already known. */ -static inline unsigned int ext4_dir_rec_len(__u8 name_len, +static inline unsigned int ext4_dirent_rec_len(unsigned int name_len, const struct inode *dir) { - int rec_len = (name_len + 8 + EXT4_DIR_ROUND); + unsigned int rec_len = (name_len + 8 + EXT4_DIR_ROUND); /* * Without dirdata, the casefold+fscrypt hash lives at a fixed position @@ -3034,7 +3039,8 @@ extern void ext4_htree_free_dir_info(struct dir_private_info *p); extern int ext4_find_dest_de(struct inode *dir, struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de); + struct ext4_dir_entry_2 **dest_de, + int dlen); void ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -4155,7 +4161,7 @@ static inline unsigned int ext4_dir_entry_len(struct ext4_dir_entry_2 *de, unsigned int rec_len = ext4_rec_len_from_disk(de->rec_len, blocksize); unsigned int dirdata = ext4_dirent_get_data_len(de, rec_len); - return ext4_dir_rec_len(de->name_len + dirdata, dir); + return ext4_dirent_rec_len(de->name_len + dirdata, dir); } extern const struct iomap_ops ext4_iomap_ops; diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 8045e4ff270c..5b3faacdf143 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -977,7 +977,7 @@ static int ext4_add_dirent_to_inline(handle_t *handle, struct ext4_dir_entry_2 *de; err = ext4_find_dest_de(dir, iloc->bh, inline_start, - inline_size, fname, &de); + inline_size, fname, &de, 0); if (err) return err; @@ -1055,7 +1055,7 @@ static int ext4_update_inline_dir(handle_t *handle, struct inode *dir, int old_size = EXT4_I(dir)->i_inline_size - EXT4_MIN_INLINE_DATA_SIZE; int new_size = get_max_inline_xattr_value_size(dir, iloc); - if (new_size - old_size <= ext4_dir_rec_len(1, NULL)) + if (new_size - old_size <= ext4_dirent_rec_len(1, NULL)) return -ENOSPC; ret = ext4_update_inline_data(handle, dir, @@ -1309,7 +1309,7 @@ int ext4_inlinedir_to_tree(struct file *dir_file, fake.name_len = 1; memcpy(fake.name, ".", 2); fake.rec_len = ext4_rec_len_to_disk( - ext4_dir_rec_len(fake.name_len, NULL), + ext4_dirent_rec_len(fake.name_len, NULL), inline_size); ext4_set_de_type(inode->i_sb, &fake, S_IFDIR); de = &fake; @@ -1319,7 +1319,7 @@ int ext4_inlinedir_to_tree(struct file *dir_file, fake.name_len = 2; memcpy(fake.name, "..", 3); fake.rec_len = ext4_rec_len_to_disk( - ext4_dir_rec_len(fake.name_len, NULL), + ext4_dirent_rec_len(fake.name_len, NULL), inline_size); ext4_set_de_type(inode->i_sb, &fake, S_IFDIR); de = &fake; @@ -1427,8 +1427,8 @@ int ext4_read_inline_dir(struct file *file, * So we will use extra_offset and extra_size to indicate them * during the inline dir iteration. */ - dotdot_offset = ext4_dir_rec_len(1, NULL); - dotdot_size = dotdot_offset + ext4_dir_rec_len(2, NULL); + dotdot_offset = ext4_dirent_rec_len(1, NULL); + dotdot_size = dotdot_offset + ext4_dirent_rec_len(2, NULL); extra_offset = dotdot_size - EXT4_INLINE_DOTDOT_SIZE; extra_size = extra_offset + inline_size; @@ -1463,7 +1463,7 @@ int ext4_read_inline_dir(struct file *file, * failure will be detected in the * dirent test below. */ if (ext4_rec_len_from_disk(de->rec_len, extra_size) - < ext4_dir_rec_len(1, NULL)) + < ext4_dirent_rec_len(1, NULL)) break; i += ext4_rec_len_from_disk(de->rec_len, extra_size); diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 44f5a83155b1..13d37b108a54 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1093,7 +1093,7 @@ static int htree_dirblock_to_tree(struct file *dir_file, /* csum entries are not larger in the casefolded encrypted case */ top = (struct ext4_dir_entry_2 *) ((char *) de + dir->i_sb->s_blocksize - - ext4_dir_rec_len(0, + ext4_dirent_rec_len(0, csum ? NULL : dir)); /* Check if the directory is encrypted */ if (IS_ENCRYPTED(dir)) { @@ -1887,7 +1887,7 @@ dx_move_dirents(struct inode *dir, char *from, char *to, while (count--) { struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *) (from + (map->offs<<2)); - rec_len = ext4_dir_rec_len(de->name_len, dir); + rec_len = ext4_dir_entry_len(de, blocksize, dir); memcpy (to, de, rec_len); ((struct ext4_dir_entry_2 *) to)->rec_len = @@ -1920,7 +1920,7 @@ static struct ext4_dir_entry_2 *dx_pack_dirents(struct inode *dir, char *base, while ((char*)de < base + blocksize) { next = ext4_next_entry(de, blocksize); if (de->inode && de->name_len) { - rec_len = ext4_dir_rec_len(de->name_len, dir); + rec_len = ext4_dir_entry_len(de, blocksize, dir); if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); @@ -2072,10 +2072,11 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, int ext4_find_dest_de(struct inode *dir, struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de) + struct ext4_dir_entry_2 **dest_de, + int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = ext4_dir_rec_len(fname_len(fname), dir); + unsigned short reclen = ext4_dirent_rec_len(fname_len(fname) + dlen, dir); int nlen, rlen; unsigned int offset = 0; char *top; @@ -2088,7 +2089,7 @@ int ext4_find_dest_de(struct inode *dir, struct buffer_head *bh, return -EFSCORRUPTED; if (ext4_match(dir, fname, de)) return -EEXIST; - nlen = ext4_dir_rec_len(de->name_len, dir); + nlen = ext4_dir_entry_len(de, buf_size, dir); rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; @@ -2111,7 +2112,7 @@ void ext4_insert_dentry(struct inode *dir, int nlen, rlen; - nlen = ext4_dir_rec_len(de->name_len, dir); + nlen = ext4_dir_entry_len(de, buf_size, dir); rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if (de->inode) { struct ext4_dir_entry_2 *de1 = @@ -2980,7 +2981,7 @@ int ext4_init_dirblock(handle_t *handle, struct inode *inode, de->inode = cpu_to_le32(inode->i_ino); de->name_len = 1; - de->rec_len = ext4_rec_len_to_disk(ext4_dir_rec_len(de->name_len, NULL), + de->rec_len = ext4_rec_len_to_disk(ext4_dirent_rec_len(de->name_len, NULL), blocksize); memcpy(de->name, ".", 2); ext4_set_de_type(inode->i_sb, de, S_IFDIR); @@ -2992,7 +2993,7 @@ int ext4_init_dirblock(handle_t *handle, struct inode *inode, ext4_set_de_type(inode->i_sb, de, S_IFDIR); if (inline_buf) { de->rec_len = ext4_rec_len_to_disk( - ext4_dir_rec_len(de->name_len, NULL), + ext4_dirent_rec_len(de->name_len, NULL), blocksize); de = ext4_next_entry(de, blocksize); header_size = (char *)de - bh->b_data; @@ -3001,7 +3002,7 @@ int ext4_init_dirblock(handle_t *handle, struct inode *inode, blocksize - csum_size); } else { de->rec_len = ext4_rec_len_to_disk(blocksize - - (csum_size + ext4_dir_rec_len(1, NULL)), + (csum_size + ext4_dirent_rec_len(1, NULL)), blocksize); } @@ -3124,8 +3125,8 @@ bool ext4_empty_dir(struct inode *inode) } sb = inode->i_sb; - if (inode->i_size < ext4_dir_rec_len(1, NULL) + - ext4_dir_rec_len(2, NULL)) { + if (inode->i_size < ext4_dirent_rec_len(1, NULL) + + ext4_dirent_rec_len(2, NULL)) { EXT4_ERROR_INODE(inode, "invalid size"); return false; } -- 2.43.7