[PATCH 1/2] vfs: fail dedupe requests that cannot make progress
From: Matthias Goergens
Date: Wed Aug 05 2026 - 03:14:29 EST
FIDEDUPERANGE allows the VFS to shorten each destination range. An
unaligned, non-EOF request shorter than the filesystem block size can
therefore be shortened to zero. vfs_dedupe_file_range_one() then returns
zero, but vfs_dedupe_file_range() reports the original length and success
even though it made no progress.
Reporting the actual return value would expose this as a successful
zero-byte operation. While diagnosing the over-reporting, Darrick Wong
pointed out that a caller such as duperemove, which advances only by
bytes_deduped and has no zero-progress guard, can retry that range forever.
Return per-destination -EINVAL when a nonzero request is shortened to zero.
Keep the historical result for an explicit zero-length request:
bytes_deduped remains zero with FILE_DEDUPE_RANGE_SAME. Other destinations
in the same ioctl continue to be processed.
Apply this guard before correcting bytes_deduped so that no intermediate
kernel exposes a successful zero-progress result.
With this guard and the following reporting correction, installed
duperemove rounded its match to 98304 bytes and exited with status 0.
Installed rmlint exercised the guard: it received 98304 bytes of progress,
then 0/-EINVAL for the 1696-byte remainder, and exited with status 1 rather
than retrying indefinitely.
Link: https://lore.kernel.org/linux-fsdevel/Y93BkIA4Nd3IJAk+@magnolia/
Signed-off-by: Matthias Goergens <matthias.goergens@xxxxxxxxx>
---
fs/remap_range.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/fs/remap_range.c b/fs/remap_range.c
index 26afbbbfb10c2..53330aa26b86f 100644
--- a/fs/remap_range.c
+++ b/fs/remap_range.c
@@ -555,6 +555,8 @@ int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same)
info->status = FILE_DEDUPE_RANGE_DIFFERS;
else if (deduped < 0)
info->status = deduped;
+ else if (!deduped && len)
+ info->status = -EINVAL;
else
info->bytes_deduped = len;
--
2.55.0