Re: [PATCH] xfs: fix exchange-range reflink flag clearing issue with INO1_WRITTEN

"Darrick J. Wong" <[email protected]> Mon, 27 Jul 2026 09:21:30 -0700
Newsgroups org.kernel.vger.linux-xfs,org.kernel.vger.stable
Message-ID <20260727162130.GD2901224@frogsfrogsfrogs>
On Mon, Jul 27, 2026 at 08:52:03PM +0800, Lin Jiapeng wrote:
> When exchanging two full-file ranges, xmi_can_exchange_reflink_flags()
> can move the reflink inode flag from the file that currently has it to
> the other file, as long as exactly one side is marked.  This assumes
> that the file contents, and therefore all shared extents, are exchanged.
> 
> That assumption is not true when XFS_EXCHMAPS_INO1_WRITTEN is set.
> xfs_exchmaps_can_skip_mapping() can skip hole and unwritten mappings
> from file1, so an exchange can complete without moving every mapping
> that the earlier flag-swap decision accounted for.  In that case the
> post-operation cleanup can clear the reflink flag from an inode that
> still owns shared written extents.  Later writes then take the
> non-reflink write path and may update blocks that should still have
> been protected by CoW, which shows up as data corruption between
> reflink-related files.
> 
> Fix this by disabling the reflink flag exchange whenever
> XFS_EXCHMAPS_INO1_WRITTEN is requested.  The contents exchange can still
> proceed; the conservative outcome is that both inodes keep the reflink
> flag.  The regular reflink flag cleanup path can drop the extra flag
> later once the inode no longer has shared extents.
> 
> Reported-by: Lin Jiapeng(TencentOS Red Team) <[email protected]>
> Fixes: 966ceafc7a43 ("xfs: create deferred log items for file mapping exchanges")
> Signed-off-by: Lin Jiapeng <[email protected]>

Good catch!  Please add:

Cc: <[email protected]> # v6.10
Reviewed-by: "Darrick J. Wong" <[email protected]>

--D

> ---
>  fs/xfs/libxfs/xfs_exchmaps.c | 10 ++++++++++
>  1 file changed, 10 insertions(+)
> 
> diff --git a/fs/xfs/libxfs/xfs_exchmaps.c b/fs/xfs/libxfs/xfs_exchmaps.c
> index dcd0bd0b13b4..3efed37cb98a 100644
> --- a/fs/xfs/libxfs/xfs_exchmaps.c
> +++ b/fs/xfs/libxfs/xfs_exchmaps.c
> @@ -959,6 +959,16 @@ xmi_can_exchange_reflink_flags(
>  {
>  	struct xfs_mount		*mp = req->ip1->i_mount;
>  
> +	/*
> +	 * The INO1_WRITTEN optimization can skip exchanging hole and
> +	 * unwritten mappings, which means we cannot guarantee that all
> +	 * shared extents actually moved to the other file.  Clearing the
> +	 * reflink flag of an inode that still holds shared extents breaks
> +	 * the CoW write path, so refuse to exchange the flags in that case.
> +	 */
> +	if (req->flags & XFS_EXCHMAPS_INO1_WRITTEN)
> +		return false;
> +
>  	if (hweight32(reflink_state) != 1)
>  		return false;
>  	if (req->startoff1 != 0 || req->startoff2 != 0)
> -- 
> 2.50.1 (Apple Git-155)
> 
>