diff options
| -rw-r--r-- | fs/btrfs/ctree.h | 2 | ||||
| -rw-r--r-- | fs/btrfs/file.c | 1 | ||||
| -rw-r--r-- | fs/btrfs/ioctl.c | 110 |
3 files changed, 16 insertions, 97 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index dd4733fa882c..b7e4e344e8e0 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
| @@ -4024,6 +4024,8 @@ void btrfs_get_block_group_info(struct list_head *groups_list, | |||
| 4024 | struct btrfs_ioctl_space_info *space); | 4024 | struct btrfs_ioctl_space_info *space); |
| 4025 | void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock, | 4025 | void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock, |
| 4026 | struct btrfs_ioctl_balance_args *bargs); | 4026 | struct btrfs_ioctl_balance_args *bargs); |
| 4027 | ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen, | ||
| 4028 | struct file *dst_file, u64 dst_loff); | ||
| 4027 | 4029 | ||
| 4028 | /* file.c */ | 4030 | /* file.c */ |
| 4029 | int btrfs_auto_defrag_init(void); | 4031 | int btrfs_auto_defrag_init(void); |
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 232e300a6c93..d012e0a96ec3 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c | |||
| @@ -2926,6 +2926,7 @@ const struct file_operations btrfs_file_operations = { | |||
| 2926 | #endif | 2926 | #endif |
| 2927 | .copy_file_range = btrfs_copy_file_range, | 2927 | .copy_file_range = btrfs_copy_file_range, |
| 2928 | .clone_file_range = btrfs_clone_file_range, | 2928 | .clone_file_range = btrfs_clone_file_range, |
| 2929 | .dedupe_file_range = btrfs_dedupe_file_range, | ||
| 2929 | }; | 2930 | }; |
| 2930 | 2931 | ||
| 2931 | void btrfs_auto_defrag_exit(void) | 2932 | void btrfs_auto_defrag_exit(void) |
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 85b1caeeec85..e21997385d14 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
| @@ -2962,7 +2962,7 @@ static int btrfs_cmp_data(struct inode *src, u64 loff, struct inode *dst, | |||
| 2962 | flush_dcache_page(dst_page); | 2962 | flush_dcache_page(dst_page); |
| 2963 | 2963 | ||
| 2964 | if (memcmp(addr, dst_addr, cmp_len)) | 2964 | if (memcmp(addr, dst_addr, cmp_len)) |
| 2965 | ret = BTRFS_SAME_DATA_DIFFERS; | 2965 | ret = -EBADE; |
| 2966 | 2966 | ||
| 2967 | kunmap_atomic(addr); | 2967 | kunmap_atomic(addr); |
| 2968 | kunmap_atomic(dst_addr); | 2968 | kunmap_atomic(dst_addr); |
| @@ -3098,53 +3098,16 @@ out_unlock: | |||
| 3098 | 3098 | ||
| 3099 | #define BTRFS_MAX_DEDUPE_LEN (16 * 1024 * 1024) | 3099 | #define BTRFS_MAX_DEDUPE_LEN (16 * 1024 * 1024) |
| 3100 | 3100 | ||
| 3101 | static long btrfs_ioctl_file_extent_same(struct file *file, | 3101 | ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen, |
| 3102 | struct btrfs_ioctl_same_args __user *argp) | 3102 | struct file *dst_file, u64 dst_loff) |
| 3103 | { | 3103 | { |
| 3104 | struct btrfs_ioctl_same_args *same = NULL; | 3104 | struct inode *src = file_inode(src_file); |
| 3105 | struct btrfs_ioctl_same_extent_info *info; | 3105 | struct inode *dst = file_inode(dst_file); |
| 3106 | struct inode *src = file_inode(file); | ||
| 3107 | u64 off; | ||
| 3108 | u64 len; | ||
| 3109 | int i; | ||
| 3110 | int ret; | ||
| 3111 | unsigned long size; | ||
| 3112 | u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize; | 3106 | u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize; |
| 3113 | bool is_admin = capable(CAP_SYS_ADMIN); | 3107 | ssize_t res; |
| 3114 | u16 count; | ||
| 3115 | |||
| 3116 | if (!(file->f_mode & FMODE_READ)) | ||
| 3117 | return -EINVAL; | ||
| 3118 | 3108 | ||
| 3119 | ret = mnt_want_write_file(file); | 3109 | if (olen > BTRFS_MAX_DEDUPE_LEN) |
| 3120 | if (ret) | 3110 | olen = BTRFS_MAX_DEDUPE_LEN; |
| 3121 | return ret; | ||
| 3122 | |||
| 3123 | if (get_user(count, &argp->dest_count)) { | ||
| 3124 | ret = -EFAULT; | ||
| 3125 | goto out; | ||
| 3126 | } | ||
| 3127 | |||
| 3128 | size = offsetof(struct btrfs_ioctl_same_args __user, info[count]); | ||
| 3129 | |||
| 3130 | same = memdup_user(argp, size); | ||
| 3131 | |||
| 3132 | if (IS_ERR(same)) { | ||
| 3133 | ret = PTR_ERR(same); | ||
| 3134 | same = NULL; | ||
| 3135 | goto out; | ||
| 3136 | } | ||
| 3137 | |||
| 3138 | off = same->logical_offset; | ||
| 3139 | len = same->length; | ||
| 3140 | |||
| 3141 | /* | ||
| 3142 | * Limit the total length we will dedupe for each operation. | ||
| 3143 | * This is intended to bound the total time spent in this | ||
| 3144 | * ioctl to something sane. | ||
| 3145 | */ | ||
| 3146 | if (len > BTRFS_MAX_DEDUPE_LEN) | ||
| 3147 | len = BTRFS_MAX_DEDUPE_LEN; | ||
| 3148 | 3111 | ||
| 3149 | if (WARN_ON_ONCE(bs < PAGE_CACHE_SIZE)) { | 3112 | if (WARN_ON_ONCE(bs < PAGE_CACHE_SIZE)) { |
| 3150 | /* | 3113 | /* |
| @@ -3152,58 +3115,13 @@ static long btrfs_ioctl_file_extent_same(struct file *file, | |||
| 3152 | * result, btrfs_cmp_data() won't correctly handle | 3115 | * result, btrfs_cmp_data() won't correctly handle |
| 3153 | * this situation without an update. | 3116 | * this situation without an update. |
| 3154 | */ | 3117 | */ |
| 3155 | ret = -EINVAL; | 3118 | return -EINVAL; |
| 3156 | goto out; | ||
| 3157 | } | ||
| 3158 | |||
| 3159 | ret = -EISDIR; | ||
| 3160 | if (S_ISDIR(src->i_mode)) | ||
| 3161 | goto out; | ||
| 3162 | |||
| 3163 | ret = -EACCES; | ||
| 3164 | if (!S_ISREG(src->i_mode)) | ||
| 3165 | goto out; | ||
| 3166 | |||
| 3167 | /* pre-format output fields to sane values */ | ||
| 3168 | for (i = 0; i < count; i++) { | ||
| 3169 | same->info[i].bytes_deduped = 0ULL; | ||
| 3170 | same->info[i].status = 0; | ||
| 3171 | } | ||
| 3172 | |||
| 3173 | for (i = 0, info = same->info; i < count; i++, info++) { | ||
| 3174 | struct inode *dst; | ||
| 3175 | struct fd dst_file = fdget(info->fd); | ||
| 3176 | if (!dst_file.file) { | ||
| 3177 | info->status = -EBADF; | ||
| 3178 | continue; | ||
| 3179 | } | ||
| 3180 | dst = file_inode(dst_file.file); | ||
| 3181 | |||
| 3182 | if (!(is_admin || (dst_file.file->f_mode & FMODE_WRITE))) { | ||
| 3183 | info->status = -EINVAL; | ||
| 3184 | } else if (file->f_path.mnt != dst_file.file->f_path.mnt) { | ||
| 3185 | info->status = -EXDEV; | ||
| 3186 | } else if (S_ISDIR(dst->i_mode)) { | ||
| 3187 | info->status = -EISDIR; | ||
| 3188 | } else if (!S_ISREG(dst->i_mode)) { | ||
| 3189 | info->status = -EACCES; | ||
| 3190 | } else { | ||
| 3191 | info->status = btrfs_extent_same(src, off, len, dst, | ||
| 3192 | info->logical_offset); | ||
| 3193 | if (info->status == 0) | ||
| 3194 | info->bytes_deduped += len; | ||
| 3195 | } | ||
| 3196 | fdput(dst_file); | ||
| 3197 | } | 3119 | } |
| 3198 | 3120 | ||
| 3199 | ret = copy_to_user(argp, same, size); | 3121 | res = btrfs_extent_same(src, loff, olen, dst, dst_loff); |
| 3200 | if (ret) | 3122 | if (res) |
| 3201 | ret = -EFAULT; | 3123 | return res; |
| 3202 | 3124 | return olen; | |
| 3203 | out: | ||
| 3204 | mnt_drop_write_file(file); | ||
| 3205 | kfree(same); | ||
| 3206 | return ret; | ||
| 3207 | } | 3125 | } |
| 3208 | 3126 | ||
| 3209 | static int clone_finish_inode_update(struct btrfs_trans_handle *trans, | 3127 | static int clone_finish_inode_update(struct btrfs_trans_handle *trans, |
| @@ -5536,8 +5454,6 @@ long btrfs_ioctl(struct file *file, unsigned int | |||
| 5536 | return btrfs_ioctl_get_fslabel(file, argp); | 5454 | return btrfs_ioctl_get_fslabel(file, argp); |
| 5537 | case BTRFS_IOC_SET_FSLABEL: | 5455 | case BTRFS_IOC_SET_FSLABEL: |
| 5538 | return btrfs_ioctl_set_fslabel(file, argp); | 5456 | return btrfs_ioctl_set_fslabel(file, argp); |
| 5539 | case BTRFS_IOC_FILE_EXTENT_SAME: | ||
| 5540 | return btrfs_ioctl_file_extent_same(file, argp); | ||
| 5541 | case BTRFS_IOC_GET_SUPPORTED_FEATURES: | 5457 | case BTRFS_IOC_GET_SUPPORTED_FEATURES: |
| 5542 | return btrfs_ioctl_get_supported_features(file, argp); | 5458 | return btrfs_ioctl_get_supported_features(file, argp); |
| 5543 | case BTRFS_IOC_GET_FEATURES: | 5459 | case BTRFS_IOC_GET_FEATURES: |
