]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
btrfs: mark file extent range dirty after converting prealloc extents
authorRobbie Ko <robbieko@synology.com>
Fri, 8 May 2026 13:42:11 +0000 (21:42 +0800)
committerDavid Sterba <dsterba@suse.com>
Sat, 16 May 2026 01:06:37 +0000 (03:06 +0200)
When writing into a preallocated extent, ordered extent completion calls
btrfs_mark_extent_written() to convert the file extent item from the
BTRFS_FILE_EXTENT_PREALLOC type to the BTRFS_FILE_EXTENT_REG type.

If the preallocated extent was created beyond i_size with fallocate
keep-size, and the inode is evicted and loaded again before the write,
the inode's file_extent_tree is initialized only up to i_size.

The beyond i_size prealloc extent is therefore not tracked there.

After a write into that extent extends i_size, btrfs_mark_extent_written()
updates the file extent item, but the corresponding range is not marked
dirty in the inode's file_extent_tree.

This can leave disk_i_size stale when the filesystem does not use the
no-holes feature, so after remount the file size can go back to the old
value.

The following reproducer triggers the problem:

  $ cat test.sh
  #!/bin/bash

  DEV=/dev/sdi
  MNT=/mnt/sdi

  mkfs.btrfs -f -O ^no-holes $DEV
  mount $DEV $MNT

  touch $MNT/file
  fallocate -n -l 2M $MNT/file

  umount $MNT
  mount $DEV $MNT

  dd if=/dev/zero of=$MNT/file bs=1M count=1 conv=notrunc
  ls -lh $MNT/file

  umount $MNT
  mount $DEV $MNT

  ls -lh $MNT/file
  umount $MNT

Running the reproducer gives the following result:

  $ ./test.sh
  (...)
  1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.000596024 s, 1.8 GB/s
  -rw-rw-r-- 1 root root 1.0M May  8 16:34 /mnt/sdi/file
  -rw-rw-r-- 1 root root 0 May  8 16:34 /mnt/sdi/file

Fix this by marking the written range dirty in the inode's
file_extent_tree after successfully converting the prealloc extent to a
regular extent.

Fixes: 9ddc959e802b ("btrfs: use the file extent tree infrastructure")
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Robbie Ko <robbieko@synology.com>
[ Minor change log updates ]
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/file.c

index cf1cb5c4db75796436ea122d4437511695f1ca97..8c171ed07008b6a244e9caebae721a697e5f90df 100644 (file)
@@ -633,7 +633,7 @@ again:
                                                         trans->transid);
                        btrfs_set_file_extent_num_bytes(leaf, fi,
                                                        end - other_start);
-                       return 0;
+                       goto mark_dirty;
                }
        }
 
@@ -661,7 +661,7 @@ again:
                                                        other_end - start);
                        btrfs_set_file_extent_offset(leaf, fi,
                                                     start - orig_offset);
-                       return 0;
+                       goto mark_dirty;
                }
        }
 
@@ -788,7 +788,12 @@ again:
                }
        }
 
-       return 0;
+mark_dirty:
+       ret = btrfs_inode_set_file_extent_range(inode, start, end - start);
+       if (ret)
+               btrfs_abort_transaction(trans, ret);
+
+       return ret;
 }
 
 /*