summaryrefslogtreecommitdiff
path: root/fs/xfs
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2021-09-01 11:19:50 -0700
committerDarrick J. Wong <djwong@kernel.org>2021-10-22 16:41:12 -0700
commitac5be15a06fe9f73d92fabbd7bef32131d666094 (patch)
tree4ec4ea2a5c0206c00653747941b8699fecffc556 /fs/xfs
parent1111f3c7992ffef84fdec231734676e3bca4d83d (diff)
xfs: forcibly convert unwritten blocks within an rt extent before sharing
As noted in the previous patch, XFS can only unmap and map full rt extents. This means that we cannot stop mid-extent for any reason, including stepping around unwritten/written extents. Second, the reflink and CoW mechanisms were not designed to handle shared unwritten extents, so we have to do something to get rid of them. If the user asks us to remap two files, we must scan both ranges beforehand to convert any unwritten extents that are not aligned to rt extent boundaries into zeroed written extents before sharing. Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Diffstat (limited to 'fs/xfs')
-rw-r--r--fs/xfs/xfs_reflink.c19
1 files changed, 19 insertions, 0 deletions
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index 2edbe9494aa0..b93faa819894 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -1504,6 +1504,25 @@ xfs_reflink_remap_prep(
goto out_unlock;
/*
+ * Now that we've marked both inodes for reflink, make sure that all
+ * possible rt extents in both files' ranges are either wholly written,
+ * wholly unwritten, or holes. The bmap code requires that we align
+ * all unmap and remap requests to a rt extent boundary. We've already
+ * flushed the page cache and finished directio for the range that's
+ * being remapped, so we can convert the extents directly.
+ */
+ if (xfs_inode_has_bigrtextents(src)) {
+ ret = xfs_rtfile_convert_unwritten(src, pos_in, *len);
+ if (ret)
+ goto out_unlock;
+ }
+ if (xfs_inode_has_bigrtextents(dest)) {
+ ret = xfs_rtfile_convert_unwritten(dest, pos_out, *len);
+ if (ret)
+ goto out_unlock;
+ }
+
+ /*
* If pos_out > EOF, we may have dirtied blocks between EOF and
* pos_out. In that case, we need to extend the flush and unmap to cover
* from EOF to the end of the copy length.