|
@@ -1593,7 +1593,8 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
|
|
|
|
|
|
cloned = file_in->f_op->remap_file_range(file_in, pos_in,
|
|
|
file_out, pos_out,
|
|
|
- min_t(loff_t, MAX_RW_COUNT, len), 0);
|
|
|
+ min_t(loff_t, MAX_RW_COUNT, len),
|
|
|
+ REMAP_FILE_CAN_SHORTEN);
|
|
|
if (cloned > 0) {
|
|
|
ret = cloned;
|
|
|
goto done;
|
|
@@ -1721,6 +1722,8 @@ static int remap_verify_area(struct file *file, loff_t pos, loff_t len,
|
|
|
* can't meaningfully compare post-EOF contents.
|
|
|
*
|
|
|
* For clone we only link a partial EOF block above the destination file's EOF.
|
|
|
+ *
|
|
|
+ * Shorten the request if possible.
|
|
|
*/
|
|
|
static int generic_remap_check_len(struct inode *inode_in,
|
|
|
struct inode *inode_out,
|
|
@@ -1729,16 +1732,24 @@ static int generic_remap_check_len(struct inode *inode_in,
|
|
|
unsigned int remap_flags)
|
|
|
{
|
|
|
u64 blkmask = i_blocksize(inode_in) - 1;
|
|
|
+ loff_t new_len = *len;
|
|
|
|
|
|
if ((*len & blkmask) == 0)
|
|
|
return 0;
|
|
|
|
|
|
- if (remap_flags & REMAP_FILE_DEDUP)
|
|
|
- *len &= ~blkmask;
|
|
|
- else if (pos_out + *len < i_size_read(inode_out))
|
|
|
- return -EINVAL;
|
|
|
+ if ((remap_flags & REMAP_FILE_DEDUP) ||
|
|
|
+ pos_out + *len < i_size_read(inode_out))
|
|
|
+ new_len &= ~blkmask;
|
|
|
|
|
|
- return 0;
|
|
|
+ if (new_len == *len)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (remap_flags & REMAP_FILE_CAN_SHORTEN) {
|
|
|
+ *len = new_len;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (remap_flags & REMAP_FILE_DEDUP) ? -EBADE : -EINVAL;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -2014,7 +2025,8 @@ loff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,
|
|
|
{
|
|
|
loff_t ret;
|
|
|
|
|
|
- WARN_ON_ONCE(remap_flags & ~(REMAP_FILE_DEDUP));
|
|
|
+ WARN_ON_ONCE(remap_flags & ~(REMAP_FILE_DEDUP |
|
|
|
+ REMAP_FILE_CAN_SHORTEN));
|
|
|
|
|
|
ret = mnt_want_write_file(dst_file);
|
|
|
if (ret)
|
|
@@ -2115,7 +2127,7 @@ int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same)
|
|
|
|
|
|
deduped = vfs_dedupe_file_range_one(file, off, dst_file,
|
|
|
info->dest_offset, len,
|
|
|
- 0);
|
|
|
+ REMAP_FILE_CAN_SHORTEN);
|
|
|
if (deduped == -EBADE)
|
|
|
info->status = FILE_DEDUPE_RANGE_DIFFERS;
|
|
|
else if (deduped < 0)
|