xfs: remove dest file's post-eof preallocations before reflinking
authorDarrick J. Wong <darrick.wong@oracle.com>
Mon, 11 Dec 2017 02:03:54 +0000 (18:03 -0800)
committerDarrick J. Wong <darrick.wong@oracle.com>
Thu, 14 Dec 2017 17:20:11 +0000 (09:20 -0800)
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>
fs/xfs/xfs_reflink.c

index cf7c8f81bebb566a486f0f732a0fe6a1040022c4..e13f5ad57a03eb9727059b18341e5b8472a24265 100644 (file)
@@ -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)