add patch dont-check-quota-format-when-there-are-no-quota-files
[ext4-patch-queue.git] / fix-zero_range-hidden-bug
blob4ad6e25e235efe24154df5ee80ed38a4aefdc526
1 ext4: fix ZERO_RANGE bug hidden by flag aliasing
3 We accidently aliased EXT4_EX_NOCACHE and EXT4_GET_CONVERT_UNWRITTEN
4 falgs, which apparently was hiding a bug that was unmasked when this
5 flag aliasing issue was addressed (see the subsequent commit).  The
6 reproduction case was:
8    fsx -N 10000 -l 500000 -r 4096 -t 4096 -w 4096 -Z -R -W /vdb/junk
10 ... which would cause fsx to report corruption in the data file.
12 The fix we have is a bit of an overkill, but I'd much rather be
13 conservative for now, and we can optimize ZERO_RANGE_FL handling
14 later.  The fact that we need to zap the extent_status cache for the
15 inode is unfortunate, but correctness is far more important than
16 performance.
18 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
19 Cc: Namjae Jeon <namjae.jeon@samsung.com>
20 ---
21  fs/ext4/extents.c | 21 ++++++++++++++-------
22  1 file changed, 14 insertions(+), 7 deletions(-)
24 diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
25 index bc3b49f..4571b5d 100644
26 --- a/fs/ext4/extents.c
27 +++ b/fs/ext4/extents.c
28 @@ -4808,7 +4808,8 @@ static long ext4_zero_range(struct file *file, loff_t offset,
29                 max_blocks -= lblk;
31         flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT |
32 -               EXT4_GET_BLOCKS_CONVERT_UNWRITTEN;
33 +               EXT4_GET_BLOCKS_CONVERT_UNWRITTEN |
34 +               EXT4_EX_NOCACHE;
35         if (mode & FALLOC_FL_KEEP_SIZE)
36                 flags |= EXT4_GET_BLOCKS_KEEP_SIZE;
38 @@ -4846,15 +4847,21 @@ static long ext4_zero_range(struct file *file, loff_t offset,
39                 ext4_inode_block_unlocked_dio(inode);
40                 inode_dio_wait(inode);
42 +               ret = ext4_alloc_file_blocks(file, lblk, max_blocks, new_size,
43 +                                            flags, mode);
44 +               if (ret)
45 +                       goto out_dio;
46                 /*
47                  * Remove entire range from the extent status tree.
48 +                *
49 +                * ext4_es_remove_extent(inode, lblk, max_blocks) is
50 +                * NOT sufficient.  I'm not sure why this is the case,
51 +                * but let's be conservative and remove the extent
52 +                * status tree for the entire inode.  There should be
53 +                * no outstanding delalloc extents thanks to the
54 +                * filemap_write_and_wait_range() call above.
55                  */
56 -               ret = ext4_es_remove_extent(inode, lblk, max_blocks);
57 -               if (ret)
58 -                       goto out_dio;
60 -               ret = ext4_alloc_file_blocks(file, lblk, max_blocks, new_size,
61 -                                            flags, mode);
62 +               ret = ext4_es_remove_extent(inode, 0, EXT_MAX_BLOCKS);
63                 if (ret)
64                         goto out_dio;
65         }