Filipe Manana 1fc82cdd90 btrfs: update generation of hole file extent item when merging holes
commit e6e3dec6c3c288d556b991a85d5d8e3ee71e9046 upstream.

When punching a hole into a file range that is adjacent with a hole and we
are not using the no-holes feature, we expand the range of the adjacent
file extent item that represents a hole, to save metadata space.

However we don't update the generation of hole file extent item, which
means a full fsync will not log that file extent item if the fsync happens
in a later transaction (since commit 7f30c07288bb9e ("btrfs: stop copying
old file extents when doing a full fsync")).

For example, if we do this:

    $ mkfs.btrfs -f -O ^no-holes /dev/sdb
    $ mount /dev/sdb /mnt
    $ xfs_io -f -c "pwrite -S 0xab 2M 2M" /mnt/foobar
    $ sync

We end up with 2 file extent items in our file:

1) One that represents the hole for the file range [0, 2M), with a
   generation of 7;

2) Another one that represents an extent covering the range [2M, 4M).

After that if we do the following:

    $ xfs_io -c "fpunch 2M 2M" /mnt/foobar

We end up with a single file extent item in the file, which represents a
hole for the range [0, 4M) and with a generation of 7 - because we end
dropping the data extent for range [2M, 4M) and then update the file
extent item that represented the hole at [0, 2M), by increasing
length from 2M to 4M.

Then doing a full fsync and power failing:

    $ xfs_io -c "fsync" /mnt/foobar
    <power failure>

will result in the full fsync not logging the file extent item that
represents the hole for the range [0, 4M), because its generation is 7,
which is lower than the generation of the current transaction (8).
As a consequence, after mounting again the filesystem (after log replay),
the region [2M, 4M) does not have a hole, it still points to the
previous data extent.

So fix this by always updating the generation of existing file extent
items representing holes when we merge/expand them. This solves the
problem and it's the same approach as when we merge prealloc extents that
got written (at btrfs_mark_extent_written()). Setting the generation to
the current transaction's generation is also what we do when merging
the new hole extent map with the previous one or the next one.

A test case for fstests, covering both cases of hole file extent item
merging (to the left and to the right), will be sent soon.

Fixes: 7f30c07288bb9e ("btrfs: stop copying old file extents when doing a full fsync")
CC: stable@vger.kernel.org # 5.18+
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-08-31 17:18:13 +02:00
..
2022-06-17 06:03:30 +09:00
2022-05-09 16:21:44 -04:00
2022-05-09 16:21:45 -04:00
2022-05-09 16:21:45 -04:00
2022-05-02 11:23:49 -05:00
2022-06-03 10:34:34 -07:00
2022-05-24 19:55:07 -07:00
2022-05-09 16:21:45 -04:00
2022-05-24 19:55:07 -07:00
2022-08-31 17:18:02 +02:00
2022-05-09 23:12:53 -04:00
2022-05-09 16:21:46 -04:00
2022-05-23 20:24:12 -05:00
2022-05-24 19:55:07 -07:00
2022-05-24 19:55:07 -07:00
2022-05-24 19:55:07 -07:00
2022-03-28 17:29:53 -07:00
2022-05-27 15:49:30 -07:00
2022-05-09 18:20:49 -07:00
2022-06-04 18:52:00 -07:00
2022-04-01 19:35:56 -07:00
2022-08-17 15:14:20 +02:00
2022-04-19 10:19:02 -07:00
2022-05-22 21:03:01 +01:00
2022-04-17 19:49:59 -06:00