Loading fs/btrfs/ctree.h +3 −2 Original line number Original line Diff line number Diff line Loading @@ -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); Loading fs/btrfs/ioctl.c +4 −7 Original line number Original line Diff line number Diff line Loading @@ -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)) { /* /* Loading @@ -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, Loading fs/ocfs2/file.c +6 −11 Original line number Original line Diff line number Diff line Loading @@ -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 = { Loading fs/read_write.c +53 −41 Original line number Original line Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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: Loading fs/xfs/xfs_file.c +7 −22 Original line number Original line Diff line number Diff line Loading @@ -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 Loading Loading
fs/btrfs/ctree.h +3 −2 Original line number Original line Diff line number Diff line Loading @@ -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); Loading
fs/btrfs/ioctl.c +4 −7 Original line number Original line Diff line number Diff line Loading @@ -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)) { /* /* Loading @@ -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, Loading
fs/ocfs2/file.c +6 −11 Original line number Original line Diff line number Diff line Loading @@ -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 = { Loading
fs/read_write.c +53 −41 Original line number Original line Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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: Loading
fs/xfs/xfs_file.c +7 −22 Original line number Original line Diff line number Diff line Loading @@ -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 Loading