Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1b4f42a1 authored by Miklos Szeredi's avatar Miklos Szeredi
Browse files

vfs: dedupe: extract helper for a single dedup



Extract vfs_dedupe_file_range_one() helper to deal with a single dedup
request.

Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 87eb5eb2
Loading
Loading
Loading
Loading
+49 −40
Original line number Diff line number Diff line
@@ -1964,6 +1964,44 @@ int vfs_dedupe_file_range_compare(struct inode *src, loff_t srcoff,
}
EXPORT_SYMBOL(vfs_dedupe_file_range_compare);

static int vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,
				     struct file *dst_file, loff_t dst_pos,
				     u64 len)
{
	s64 ret;

	ret = mnt_want_write_file(dst_file);
	if (ret)
		return ret;

	ret = clone_verify_area(dst_file, dst_pos, len, true);
	if (ret < 0)
		goto out_drop_write;

	ret = -EINVAL;
	if (!(capable(CAP_SYS_ADMIN) || (dst_file->f_mode & FMODE_WRITE)))
		goto out_drop_write;

	ret = -EXDEV;
	if (src_file->f_path.mnt != dst_file->f_path.mnt)
		goto out_drop_write;

	ret = -EISDIR;
	if (S_ISDIR(file_inode(dst_file)->i_mode))
		goto out_drop_write;

	ret = -EINVAL;
	if (!dst_file->f_op->dedupe_file_range)
		goto out_drop_write;

	ret = dst_file->f_op->dedupe_file_range(src_file, src_pos,
						dst_file, dst_pos, len);
out_drop_write:
	mnt_drop_write_file(dst_file);

	return ret;
}

int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same)
{
	struct file_dedupe_range_info *info;
@@ -1972,10 +2010,7 @@ int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same)
	u64 len;
	int i;
	int ret;
	bool is_admin = capable(CAP_SYS_ADMIN);
	u16 count = same->dest_count;
	struct file *dst_file;
	loff_t dst_off;
	int deduped;

	if (!(file->f_mode & FMODE_READ))
@@ -2013,54 +2048,28 @@ int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same)
	}

	for (i = 0, info = same->info; i < count; i++, info++) {
		struct inode *dst;
		struct fd dst_fd = fdget(info->dest_fd);
		struct file *dst_file = dst_fd.file;

		dst_file = dst_fd.file;
		if (!dst_file) {
			info->status = -EBADF;
			goto next_loop;
		}
		dst = file_inode(dst_file);

		ret = mnt_want_write_file(dst_file);
		if (ret) {
			info->status = ret;
		if (info->reserved) {
			info->status = -EINVAL;
			goto next_fdput;
		}

		dst_off = info->dest_offset;
		ret = clone_verify_area(dst_file, dst_off, len, true);
		if (ret < 0) {
			info->status = ret;
			goto next_file;
		}
		ret = 0;

		if (info->reserved) {
			info->status = -EINVAL;
		} else if (!(is_admin || (dst_file->f_mode & FMODE_WRITE))) {
			info->status = -EINVAL;
		} else if (file->f_path.mnt != dst_file->f_path.mnt) {
			info->status = -EXDEV;
		} else if (S_ISDIR(dst->i_mode)) {
			info->status = -EISDIR;
		} else if (dst_file->f_op->dedupe_file_range == NULL) {
			info->status = -EINVAL;
		} else {
			deduped = dst_file->f_op->dedupe_file_range(file, off,
							dst_file,
		deduped = vfs_dedupe_file_range_one(file, off, dst_file,
						    info->dest_offset, len);
		if (deduped == -EBADE)
			info->status = FILE_DEDUPE_RANGE_DIFFERS;
		else if (deduped < 0)
			info->status = deduped;
		else
				info->bytes_deduped += len;
		}
			info->bytes_deduped = len;

next_file:
		mnt_drop_write_file(dst_file);
next_fdput:
		fdput(dst_fd);
next_loop: