| From c154989d1a83c28b39ddf8004fcbee8e4cd33196 Mon Sep 17 00:00:00 2001 |
| From: Calvin Owens <calvinowens@fb.com> |
| Date: Mon, 3 Apr 2017 12:22:29 -0700 |
| Subject: [PATCH] xfs: Honor FALLOC_FL_KEEP_SIZE when punching ends of files |
| |
| commit 3dd09d5a8589c640abb49cfcf92b4ed669eafad1 upstream. |
| |
| When punching past EOF on XFS, fallocate(mode=PUNCH_HOLE|KEEP_SIZE) will |
| round the file size up to the nearest multiple of PAGE_SIZE: |
| |
| calvinow@vm-disks/generic-xfs-1 ~$ dd if=/dev/urandom of=test bs=2048 count=1 |
| calvinow@vm-disks/generic-xfs-1 ~$ stat test |
| Size: 2048 Blocks: 8 IO Block: 4096 regular file |
| calvinow@vm-disks/generic-xfs-1 ~$ fallocate -n -l 2048 -o 2048 -p test |
| calvinow@vm-disks/generic-xfs-1 ~$ stat test |
| Size: 4096 Blocks: 8 IO Block: 4096 regular file |
| |
| Commit 3c2bdc912a1cc050 ("xfs: kill xfs_zero_remaining_bytes") replaced |
| xfs_zero_remaining_bytes() with calls to iomap helpers. The new helpers |
| don't enforce that [pos,offset) lies strictly on [0,i_size) when being |
| called from xfs_free_file_space(), so by "leaking" these ranges into |
| xfs_zero_range() we get this buggy behavior. |
| |
| Fix this by reintroducing the checks xfs_zero_remaining_bytes() did |
| against i_size at the bottom of xfs_free_file_space(). |
| |
| Reported-by: Aaron Gao <gzh@fb.com> |
| Fixes: 3c2bdc912a1cc050 ("xfs: kill xfs_zero_remaining_bytes") |
| Cc: Christoph Hellwig <hch@lst.de> |
| Cc: Brian Foster <bfoster@redhat.com> |
| Cc: <stable@vger.kernel.org> # 4.8+ |
| Signed-off-by: Calvin Owens <calvinowens@fb.com> |
| Reviewed-by: Christoph Hellwig <hch@lst.de> |
| Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c |
| index da1677dd1e65..0299c7dbfcde 100644 |
| --- a/fs/xfs/xfs_bmap_util.c |
| +++ b/fs/xfs/xfs_bmap_util.c |
| @@ -1179,8 +1179,16 @@ xfs_free_file_space( |
| /* |
| * Now that we've unmap all full blocks we'll have to zero out any |
| * partial block at the beginning and/or end. xfs_zero_range is |
| - * smart enough to skip any holes, including those we just created. |
| + * smart enough to skip any holes, including those we just created, |
| + * but we must take care not to zero beyond EOF and enlarge i_size. |
| */ |
| + |
| + if (offset >= XFS_ISIZE(ip)) |
| + return 0; |
| + |
| + if (offset + len > XFS_ISIZE(ip)) |
| + len = XFS_ISIZE(ip) - offset; |
| + |
| return xfs_zero_range(ip, offset, len, NULL); |
| } |
| |
| -- |
| 2.12.0 |
| |