ext4: optimize ea_inode block expansion
authorJun Nie <jun.nie@linaro.org>
Tue, 3 Jan 2023 01:45:16 +0000 (09:45 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 10 Mar 2023 08:40:05 +0000 (09:40 +0100)
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

index b92da41e9640945b74ea16fa7a9572e5e9452499..4a58667958fdeadf114d1276a3eb5a3bab983103 100644 (file)
@@ -2549,9 +2549,8 @@ static int ext4_xattr_move_to_block(handle_t *handle, struct inode *inode,
 
        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;
        }
@@ -2563,12 +2562,18 @@ static int ext4_xattr_move_to_block(handle_t *handle, struct inode *inode,
 
        /* 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);
@@ -2583,25 +2588,26 @@ static int ext4_xattr_move_to_block(handle_t *handle, struct inode *inode,
        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)