mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-29 09:12:07 +00:00
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:
parent
87eb5eb242
commit
1b4f42a1e3
@ -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;
|
||||
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:
|
||||
|
Loading…
Reference in New Issue
Block a user