Commit eca3654e authored by Darrick J. Wong's avatar Darrick J. Wong Committed by Dave Chinner
Browse files

vfs: enable remap callers that can handle short operations



Plumb in a remap flag that enables the filesystem remap handler to
shorten remapping requests for callers that can handle it.  Now
copy_file_range can report partial success (in case we run up against
alignment problems, resource limits, etc.).

We also enable CAN_SHORTEN for fideduperange to maintain existing
userspace-visible behavior where xfs/btrfs shorten the dedupe range to
avoid stale post-eof data exposure.

Signed-off-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarDave Chinner <david@fromorbit.com>
parent df365836
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -970,7 +970,9 @@ otherwise noted.
	negative error code if errors occurred before any bytes were remapped.
	The remap_flags parameter accepts REMAP_FILE_* flags.  If
	REMAP_FILE_DEDUP is set then the implementation must only remap if the
	requested file ranges have identical contents.
	requested file ranges have identical contents.  If REMAP_CAN_SHORTEN is
	set, the caller is ok with the implementation shortening the request
	length to satisfy alignment or EOF requirements (or any other reason).

  fadvise: possibly called by the fadvise64() system call.

+20 −8
Original line number Diff line number Diff line
@@ -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,18 +1732,26 @@ 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;

	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;
}

/*
 * Check that the two inodes are eligible for cloning, the ranges make
 * sense, and then flush all dirty data.  Caller must ensure that the
@@ -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)
+3 −2
Original line number Diff line number Diff line
@@ -1727,8 +1727,10 @@ struct block_device_operations;
 * See Documentation/filesystems/vfs.txt for more details about this call.
 *
 * REMAP_FILE_DEDUP: only remap if contents identical (i.e. deduplicate)
 * REMAP_FILE_CAN_SHORTEN: caller can handle a shortened request
 */
#define REMAP_FILE_DEDUP		(1 << 0)
#define REMAP_FILE_CAN_SHORTEN		(1 << 1)

/*
 * These flags signal that the caller is ok with altering various aspects of
@@ -1736,9 +1738,8 @@ struct block_device_operations;
 * implementation; the vfs remap helper functions can take advantage of them.
 * Flags in this category exist to preserve the quirky behavior of the hoisted
 * btrfs clone/dedupe ioctls.
 * There are no flags yet, but subsequent commits will add some.
 */
#define REMAP_FILE_ADVISORY		(0)
#define REMAP_FILE_ADVISORY		(REMAP_FILE_CAN_SHORTEN)

struct iov_iter;

+7 −4
Original line number Diff line number Diff line
@@ -3045,8 +3045,7 @@ int generic_remap_checks(struct file *file_in, loff_t pos_in,
		bcount = ALIGN(size_in, bs) - pos_in;
	} else {
		if (!IS_ALIGNED(count, bs))
			return -EINVAL;

			count = ALIGN_DOWN(count, bs);
		bcount = count;
	}

@@ -3056,10 +3055,14 @@ int generic_remap_checks(struct file *file_in, loff_t pos_in,
	    pos_out < pos_in + bcount)
		return -EINVAL;

	/* For now we don't support changing the length. */
	if (*req_count != count)
	/*
	 * We shortened the request but the caller can't deal with that, so
	 * bounce the request back to userspace.
	 */
	if (*req_count != count && !(remap_flags & REMAP_FILE_CAN_SHORTEN))
		return -EINVAL;

	*req_count = count;
	return 0;
}