]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ext4: fix i_version handling in ext4
authorJeff Layton <jlayton@kernel.org>
Thu, 8 Sep 2022 17:24:42 +0000 (13:24 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 24 Oct 2022 07:56:55 +0000 (09:56 +0200)
commit a642c2c0827f5604a93f9fa1e5701eecdce4ae22 upstream.

ext4 currently updates the i_version counter when the atime is updated
during a read. This is less than ideal as it can cause unnecessary cache
invalidations with NFSv4 and unnecessary remeasurements for IMA.

The increment in ext4_mark_iloc_dirty is also problematic since it can
corrupt the i_version counter for ea_inodes. We aren't bumping the file
times in ext4_mark_iloc_dirty, so changing the i_version there seems
wrong, and is the cause of both problems.

Remove that callsite and add increments to the setattr, setxattr and
ioctl codepaths, at the same times that we update the ctime. The
i_version bump that already happens during timestamp updates should take
care of the rest.

In ext4_move_extents, increment the i_version on both inodes, and also
add in missing ctime updates.

[ Some minor updates since we've already enabled the i_version counter
  unconditionally already via another patch series. -- TYT ]

Cc: stable@kernel.org
Cc: Lukas Czerner <lczerner@redhat.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Reviewed-by: Christian Brauner (Microsoft) <brauner@kernel.org>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Link: https://lore.kernel.org/r/20220908172448.208585-3-jlayton@kernel.org
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/ext4/inode.c
fs/ext4/ioctl.c
fs/ext4/xattr.c

index d6d2a4cd192b3904de60215aa509443f2abfaae2..7e5e8457026a114945b0c169851ee287d8839f32 100644 (file)
@@ -5347,6 +5347,7 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
        int error, rc = 0;
        int orphan = 0;
        const unsigned int ia_valid = attr->ia_valid;
+       bool inc_ivers = true;
 
        if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
                return -EIO;
@@ -5432,8 +5433,8 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
                        return -EINVAL;
                }
 
-               if (attr->ia_size != inode->i_size)
-                       inode_inc_iversion(inode);
+               if (attr->ia_size == inode->i_size)
+                       inc_ivers = false;
 
                if (shrink) {
                        if (ext4_should_order_data(inode)) {
@@ -5535,6 +5536,8 @@ out_mmap_sem:
        }
 
        if (!error) {
+               if (inc_ivers)
+                       inode_inc_iversion(inode);
                setattr_copy(mnt_userns, inode, attr);
                mark_inode_dirty(inode);
        }
@@ -5738,13 +5741,6 @@ int ext4_mark_iloc_dirty(handle_t *handle,
        }
        ext4_fc_track_inode(handle, inode);
 
-       /*
-        * ea_inodes are using i_version for storing reference count, don't
-        * mess with it
-        */
-       if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL))
-               inode_inc_iversion(inode);
-
        /* the do_update_inode consumes one bh->b_count */
        get_bh(iloc->bh);
 
index cb01c1da0f9d5136da6a11281fad7587c4d340a7..b6f694d195997d722b49651efd9d854e635f0544 100644 (file)
@@ -442,6 +442,7 @@ static long swap_inode_boot_loader(struct super_block *sb,
        swap_inode_data(inode, inode_bl);
 
        inode->i_ctime = inode_bl->i_ctime = current_time(inode);
+       inode_inc_iversion(inode);
 
        inode->i_generation = prandom_u32();
        inode_bl->i_generation = prandom_u32();
@@ -655,6 +656,7 @@ static int ext4_ioctl_setflags(struct inode *inode,
        ext4_set_inode_flags(inode, false);
 
        inode->i_ctime = current_time(inode);
+       inode_inc_iversion(inode);
 
        err = ext4_mark_iloc_dirty(handle, inode, &iloc);
 flags_err:
@@ -765,6 +767,7 @@ static int ext4_ioctl_setproject(struct inode *inode, __u32 projid)
 
        EXT4_I(inode)->i_projid = kprojid;
        inode->i_ctime = current_time(inode);
+       inode_inc_iversion(inode);
 out_dirty:
        rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
        if (!err)
@@ -1178,6 +1181,7 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                err = ext4_reserve_inode_write(handle, inode, &iloc);
                if (err == 0) {
                        inode->i_ctime = current_time(inode);
+                       inode_inc_iversion(inode);
                        inode->i_generation = generation;
                        err = ext4_mark_iloc_dirty(handle, inode, &iloc);
                }
index 533216e80fa2b023a5aeb5060961706a5b23b4d7..36d6ba7190b6db6d650ad2ac85c6313aae2b3ae1 100644 (file)
@@ -2412,6 +2412,7 @@ retry_inode:
        if (!error) {
                ext4_xattr_update_super_block(handle, inode->i_sb);
                inode->i_ctime = current_time(inode);
+               inode_inc_iversion(inode);
                if (!value)
                        no_expand = 0;
                error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);