Commit 51e6ce82 authored by Miklos Szeredi's avatar Miklos Szeredi

Merge branch 'dedupe-cleanup' into overlayfs-next

Following series for stacking overlay files depends on this mini series.
parents 9951934d 1b4f42a1
...@@ -3247,8 +3247,9 @@ void btrfs_get_block_group_info(struct list_head *groups_list, ...@@ -3247,8 +3247,9 @@ void btrfs_get_block_group_info(struct list_head *groups_list,
struct btrfs_ioctl_space_info *space); struct btrfs_ioctl_space_info *space);
void btrfs_update_ioctl_balance_args(struct btrfs_fs_info *fs_info, void btrfs_update_ioctl_balance_args(struct btrfs_fs_info *fs_info,
struct btrfs_ioctl_balance_args *bargs); struct btrfs_ioctl_balance_args *bargs);
ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen, int btrfs_dedupe_file_range(struct file *src_file, loff_t src_loff,
struct file *dst_file, u64 dst_loff); struct file *dst_file, loff_t dst_loff,
u64 olen);
/* file.c */ /* file.c */
int __init btrfs_auto_defrag_init(void); int __init btrfs_auto_defrag_init(void);
......
...@@ -3600,13 +3600,13 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, ...@@ -3600,13 +3600,13 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen,
return ret; return ret;
} }
ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen, int btrfs_dedupe_file_range(struct file *src_file, loff_t src_loff,
struct file *dst_file, u64 dst_loff) struct file *dst_file, loff_t dst_loff,
u64 olen)
{ {
struct inode *src = file_inode(src_file); struct inode *src = file_inode(src_file);
struct inode *dst = file_inode(dst_file); struct inode *dst = file_inode(dst_file);
u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize; u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize;
ssize_t res;
if (WARN_ON_ONCE(bs < PAGE_SIZE)) { if (WARN_ON_ONCE(bs < PAGE_SIZE)) {
/* /*
...@@ -3617,10 +3617,7 @@ ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen, ...@@ -3617,10 +3617,7 @@ ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen,
return -EINVAL; return -EINVAL;
} }
res = btrfs_extent_same(src, loff, olen, dst, dst_loff); return btrfs_extent_same(src, src_loff, olen, dst, dst_loff);
if (res)
return res;
return olen;
} }
static int clone_finish_inode_update(struct btrfs_trans_handle *trans, static int clone_finish_inode_update(struct btrfs_trans_handle *trans,
......
...@@ -2537,19 +2537,14 @@ static int ocfs2_file_clone_range(struct file *file_in, ...@@ -2537,19 +2537,14 @@ static int ocfs2_file_clone_range(struct file *file_in,
len, false); len, false);
} }
static ssize_t ocfs2_file_dedupe_range(struct file *src_file, static int ocfs2_file_dedupe_range(struct file *file_in,
u64 loff, loff_t pos_in,
u64 len, struct file *file_out,
struct file *dst_file, loff_t pos_out,
u64 dst_loff) u64 len)
{ {
int error; return ocfs2_reflink_remap_range(file_in, pos_in, file_out, pos_out,
error = ocfs2_reflink_remap_range(src_file, loff, dst_file, dst_loff,
len, true); len, true);
if (error)
return error;
return len;
} }
const struct inode_operations ocfs2_file_iops = { const struct inode_operations ocfs2_file_iops = {
......
...@@ -1964,6 +1964,44 @@ int vfs_dedupe_file_range_compare(struct inode *src, loff_t srcoff, ...@@ -1964,6 +1964,44 @@ int vfs_dedupe_file_range_compare(struct inode *src, loff_t srcoff,
} }
EXPORT_SYMBOL(vfs_dedupe_file_range_compare); 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) int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same)
{ {
struct file_dedupe_range_info *info; struct file_dedupe_range_info *info;
...@@ -1972,11 +2010,8 @@ int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same) ...@@ -1972,11 +2010,8 @@ int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same)
u64 len; u64 len;
int i; int i;
int ret; int ret;
bool is_admin = capable(CAP_SYS_ADMIN);
u16 count = same->dest_count; u16 count = same->dest_count;
struct file *dst_file; int deduped;
loff_t dst_off;
ssize_t deduped;
if (!(file->f_mode & FMODE_READ)) if (!(file->f_mode & FMODE_READ))
return -EINVAL; return -EINVAL;
...@@ -2003,6 +2038,9 @@ int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same) ...@@ -2003,6 +2038,9 @@ int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same)
if (off + len > i_size_read(src)) if (off + len > i_size_read(src))
return -EINVAL; return -EINVAL;
/* Arbitrary 1G limit on a single dedupe request, can be raised. */
len = min_t(u64, len, 1 << 30);
/* pre-format output fields to sane values */ /* pre-format output fields to sane values */
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
same->info[i].bytes_deduped = 0ULL; same->info[i].bytes_deduped = 0ULL;
...@@ -2010,54 +2048,28 @@ int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same) ...@@ -2010,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++) { for (i = 0, info = same->info; i < count; i++, info++) {
struct inode *dst;
struct fd dst_fd = fdget(info->dest_fd); struct fd dst_fd = fdget(info->dest_fd);
struct file *dst_file = dst_fd.file;
dst_file = dst_fd.file;
if (!dst_file) { if (!dst_file) {
info->status = -EBADF; info->status = -EBADF;
goto next_loop; goto next_loop;
} }
dst = file_inode(dst_file);
ret = mnt_want_write_file(dst_file); if (info->reserved) {
if (ret) { info->status = -EINVAL;
info->status = ret;
goto next_fdput; goto next_fdput;
} }
dst_off = info->dest_offset; deduped = vfs_dedupe_file_range_one(file, off, dst_file,
ret = clone_verify_area(dst_file, dst_off, len, true); info->dest_offset, len);
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,
len, dst_file,
info->dest_offset);
if (deduped == -EBADE) if (deduped == -EBADE)
info->status = FILE_DEDUPE_RANGE_DIFFERS; info->status = FILE_DEDUPE_RANGE_DIFFERS;
else if (deduped < 0) else if (deduped < 0)
info->status = deduped; info->status = deduped;
else else
info->bytes_deduped += deduped; info->bytes_deduped = len;
}
next_file:
mnt_drop_write_file(dst_file);
next_fdput: next_fdput:
fdput(dst_fd); fdput(dst_fd);
next_loop: next_loop:
......
...@@ -933,31 +933,16 @@ xfs_file_clone_range( ...@@ -933,31 +933,16 @@ xfs_file_clone_range(
len, false); len, false);
} }
STATIC ssize_t STATIC int
xfs_file_dedupe_range( xfs_file_dedupe_range(
struct file *src_file, struct file *file_in,
u64 loff, loff_t pos_in,
u64 len, struct file *file_out,
struct file *dst_file, loff_t pos_out,
u64 dst_loff) u64 len)
{ {
struct inode *srci = file_inode(src_file); return xfs_reflink_remap_range(file_in, pos_in, file_out, pos_out,
u64 max_dedupe;
int error;
/*
* Since we have to read all these pages in to compare them, cut
* it off at MAX_RW_COUNT/2 rounded down to the nearest block.
* That means we won't do more than MAX_RW_COUNT IO per request.
*/
max_dedupe = (MAX_RW_COUNT >> 1) & ~(i_blocksize(srci) - 1);
if (len > max_dedupe)
len = max_dedupe;
error = xfs_reflink_remap_range(src_file, loff, dst_file, dst_loff,
len, true); len, true);
if (error)
return error;
return len;
} }
STATIC int STATIC int
......
...@@ -1751,7 +1751,7 @@ struct file_operations { ...@@ -1751,7 +1751,7 @@ struct file_operations {
loff_t, size_t, unsigned int); loff_t, size_t, unsigned int);
int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t, int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
u64); u64);
ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *, int (*dedupe_file_range)(struct file *, loff_t, struct file *, loff_t,
u64); u64);
} __randomize_layout; } __randomize_layout;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment