aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2017-12-10 18:03:54 -0800
committerDarrick J. Wong <darrick.wong@oracle.com>2017-12-14 09:20:11 -0800
commit5c989a0ee06eb77a44baffd1779a5dbb9a7e873f (patch)
tree6b727eabcb415cb66e64dc48ba4ae0887cddc2e7 /fs
parentxfs: move xfs_iext_insert tracepoint to report useful information (diff)
downloadlinux-dev-5c989a0ee06eb77a44baffd1779a5dbb9a7e873f.tar.xz
linux-dev-5c989a0ee06eb77a44baffd1779a5dbb9a7e873f.zip
xfs: remove dest file's post-eof preallocations before reflinking
If we try to reflink into a file with post-eof preallocations at an offset well past the preallocations, we increase i_size as one would expect. However, those allocations do not have page cache backing them, so they won't get cleaned out on their own. This leads to asserts in the collapse/insert range code and xfs_destroy_inode when they encounter delalloc extents they weren't expecting to find. Since there are plenty of other places where we dump those post-eof blocks, do the same to the reflink destination file before we start remapping extents. This was found by adding clonerange support to fsstress and running it in write-only mode. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'fs')
-rw-r--r--fs/xfs/xfs_reflink.c11
1 files changed, 11 insertions, 0 deletions
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index cf7c8f81bebb..e13f5ad57a03 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -1291,6 +1291,17 @@ xfs_reflink_remap_range(
trace_xfs_reflink_remap_range(src, pos_in, len, dest, pos_out);
+ /*
+ * Clear out post-eof preallocations because we don't have page cache
+ * backing the delayed allocations and they'll never get freed on
+ * their own.
+ */
+ if (xfs_can_free_eofblocks(dest, true)) {
+ ret = xfs_free_eofblocks(dest);
+ if (ret)
+ goto out_unlock;
+ }
+
/* Set flags and remap blocks. */
ret = xfs_reflink_set_inode_flag(src, dest);
if (ret)