| From 1e9d62d252812575ded7c620d8fc67c32ff06c16 Mon Sep 17 00:00:00 2001 |
| From: Jun Nie <jun.nie@linaro.org> |
| Date: Tue, 3 Jan 2023 09:45:16 +0800 |
| Subject: ext4: optimize ea_inode block expansion |
| |
| From: Jun Nie <jun.nie@linaro.org> |
| |
| commit 1e9d62d252812575ded7c620d8fc67c32ff06c16 upstream. |
| |
| Copy ea data from inode entry when expanding ea block if possible. |
| Then remove the ea entry if expansion success. Thus memcpy to a |
| temporary buffer may be avoided. |
| |
| If the expansion fails, we do not need to recovery the removed ea |
| entry neither in this way. |
| |
| Reported-by: syzbot+2dacb8f015bf1420155f@syzkaller.appspotmail.com |
| Link: https://syzkaller.appspot.com/bug?id=3613786cb88c93aa1c6a279b1df6a7b201347d08 |
| Link: https://lore.kernel.org/r/20230103014517.495275-2-jun.nie@linaro.org |
| Cc: stable@kernel.org |
| Signed-off-by: Jun Nie <jun.nie@linaro.org> |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/ext4/xattr.c | 28 +++++++++++++++++----------- |
| 1 file changed, 17 insertions(+), 11 deletions(-) |
| |
| --- a/fs/ext4/xattr.c |
| +++ b/fs/ext4/xattr.c |
| @@ -2583,9 +2583,8 @@ static int ext4_xattr_move_to_block(hand |
| |
| is = kzalloc(sizeof(struct ext4_xattr_ibody_find), GFP_NOFS); |
| bs = kzalloc(sizeof(struct ext4_xattr_block_find), GFP_NOFS); |
| - buffer = kvmalloc(value_size, GFP_NOFS); |
| b_entry_name = kmalloc(entry->e_name_len + 1, GFP_NOFS); |
| - if (!is || !bs || !buffer || !b_entry_name) { |
| + if (!is || !bs || !b_entry_name) { |
| error = -ENOMEM; |
| goto out; |
| } |
| @@ -2597,12 +2596,18 @@ static int ext4_xattr_move_to_block(hand |
| |
| /* Save the entry name and the entry value */ |
| if (entry->e_value_inum) { |
| + buffer = kvmalloc(value_size, GFP_NOFS); |
| + if (!buffer) { |
| + error = -ENOMEM; |
| + goto out; |
| + } |
| + |
| error = ext4_xattr_inode_get(inode, entry, buffer, value_size); |
| if (error) |
| goto out; |
| } else { |
| size_t value_offs = le16_to_cpu(entry->e_value_offs); |
| - memcpy(buffer, (void *)IFIRST(header) + value_offs, value_size); |
| + buffer = (void *)IFIRST(header) + value_offs; |
| } |
| |
| memcpy(b_entry_name, entry->e_name, entry->e_name_len); |
| @@ -2617,25 +2622,26 @@ static int ext4_xattr_move_to_block(hand |
| if (error) |
| goto out; |
| |
| - /* Remove the chosen entry from the inode */ |
| - error = ext4_xattr_ibody_set(handle, inode, &i, is); |
| - if (error) |
| - goto out; |
| - |
| i.value = buffer; |
| i.value_len = value_size; |
| error = ext4_xattr_block_find(inode, &i, bs); |
| if (error) |
| goto out; |
| |
| - /* Add entry which was removed from the inode into the block */ |
| + /* Move ea entry from the inode into the block */ |
| error = ext4_xattr_block_set(handle, inode, &i, bs); |
| if (error) |
| goto out; |
| - error = 0; |
| + |
| + /* Remove the chosen entry from the inode */ |
| + i.value = NULL; |
| + i.value_len = 0; |
| + error = ext4_xattr_ibody_set(handle, inode, &i, is); |
| + |
| out: |
| kfree(b_entry_name); |
| - kvfree(buffer); |
| + if (entry->e_value_inum && buffer) |
| + kvfree(buffer); |
| if (is) |
| brelse(is->iloc.bh); |
| if (bs) |