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: Miklos Szeredi <mszeredi@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
Miklos Szeredi 2018-07-06 23:57:03 +02:00
parent 87eb5eb242
commit 1b4f42a1e3
1 changed files with 49 additions and 40 deletions

View File

@ -1964,6 +1964,44 @@ out_error:
}
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;
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,
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;
goto next_fdput;
}
next_file:
mnt_drop_write_file(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;
next_fdput:
fdput(dst_fd);
next_loop: