Skip to content
  • Filipe Manana's avatar
    Btrfs: fix shrinking truncate when the no_holes feature is enabled · c1aa4575
    Filipe Manana authored
    
    
    If the no_holes feature is enabled, we attempt to shrink a file to a size
    that ends up in the middle of a hole and we don't have any file extent
    items in the fs/subvol tree that go beyond the new file size (or any
    ordered extents that will insert such file extent items), we end up not
    updating the inode's disk_i_size, we only update the inode's i_size.
    
    This means that after unmounting and mounting the filesystem, or after
    the inode is evicted and reloaded, its i_size ends up being incorrect
    (an inode's i_size is set to the disk_i_size field when an inode is
    loaded). This happens when btrfs_truncate_inode_items() doesn't find
    any file extent items to drop - in this case it never makes a call to
    btrfs_ordered_update_i_size() in order to update the inode's disk_i_size.
    
    Example reproducer:
    
      $ mkfs.btrfs -O no-holes -f /dev/sdd
      $ mount /dev/sdd /mnt
    
      # Create our test file with some data and durably persist it.
      $ xfs_io -f -c "pwrite -S 0xaa 0 128K" /mnt/foo
      $ sync
    
      # Append some data to the file, increasing its size, and leave a hole
      # between the old size and the start offset if the following write. So
      # our file gets a hole in the range [128Kb, 256Kb[.
      $ xfs_io -c "truncate 160K" /mnt/foo
    
      # We expect to see our file with a size of 160Kb, with the first 128Kb
      # of data all having the value 0xaa and the remaining 32Kb of data all
      # having the value 0x00.
      $ od -t x1 /mnt/foo
      0000000 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
      *
      0400000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      *
      0500000
    
      # Now cleanly unmount and mount again the filesystem.
      $ umount /mnt
      $ mount /dev/sdd /mnt
    
      # We expect to get the same result as before, a file with a size of
      # 160Kb, with the first 128Kb of data all having the value 0xaa and the
      # remaining 32Kb of data all having the value 0x00.
      $ od -t x1 /mnt/foo
      0000000 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
      *
      0400000
    
    In the example above the file size/data do not match what they were before
    the remount.
    
    Fix this by always calling btrfs_ordered_update_i_size() with a size
    matching the size the file was truncated to if btrfs_truncate_inode_items()
    is not called for a log tree and no file extent items were dropped. This
    ensures the same behaviour as when the no_holes feature is not enabled.
    
    A test case for fstests follows soon.
    
    Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
    c1aa4575