aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r--fs/btrfs/ioctl.c70
1 files changed, 24 insertions, 46 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 21da5762b0b1..ad27dcea319c 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2686,14 +2686,11 @@ out_unlock:
2686#define BTRFS_MAX_DEDUPE_LEN (16 * 1024 * 1024) 2686#define BTRFS_MAX_DEDUPE_LEN (16 * 1024 * 1024)
2687 2687
2688static long btrfs_ioctl_file_extent_same(struct file *file, 2688static long btrfs_ioctl_file_extent_same(struct file *file,
2689 void __user *argp) 2689 struct btrfs_ioctl_same_args __user *argp)
2690{ 2690{
2691 struct btrfs_ioctl_same_args tmp;
2692 struct btrfs_ioctl_same_args *same; 2691 struct btrfs_ioctl_same_args *same;
2693 struct btrfs_ioctl_same_extent_info *info; 2692 struct btrfs_ioctl_same_extent_info *info;
2694 struct inode *src = file->f_dentry->d_inode; 2693 struct inode *src = file_inode(file);
2695 struct file *dst_file = NULL;
2696 struct inode *dst;
2697 u64 off; 2694 u64 off;
2698 u64 len; 2695 u64 len;
2699 int i; 2696 int i;
@@ -2701,6 +2698,7 @@ static long btrfs_ioctl_file_extent_same(struct file *file,
2701 unsigned long size; 2698 unsigned long size;
2702 u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize; 2699 u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize;
2703 bool is_admin = capable(CAP_SYS_ADMIN); 2700 bool is_admin = capable(CAP_SYS_ADMIN);
2701 u16 count;
2704 2702
2705 if (!(file->f_mode & FMODE_READ)) 2703 if (!(file->f_mode & FMODE_READ))
2706 return -EINVAL; 2704 return -EINVAL;
@@ -2709,17 +2707,14 @@ static long btrfs_ioctl_file_extent_same(struct file *file,
2709 if (ret) 2707 if (ret)
2710 return ret; 2708 return ret;
2711 2709
2712 if (copy_from_user(&tmp, 2710 if (get_user(count, &argp->dest_count)) {
2713 (struct btrfs_ioctl_same_args __user *)argp,
2714 sizeof(tmp))) {
2715 ret = -EFAULT; 2711 ret = -EFAULT;
2716 goto out; 2712 goto out;
2717 } 2713 }
2718 2714
2719 size = sizeof(tmp) + 2715 size = offsetof(struct btrfs_ioctl_same_args __user, info[count]);
2720 tmp.dest_count * sizeof(struct btrfs_ioctl_same_extent_info);
2721 2716
2722 same = memdup_user((struct btrfs_ioctl_same_args __user *)argp, size); 2717 same = memdup_user(argp, size);
2723 2718
2724 if (IS_ERR(same)) { 2719 if (IS_ERR(same)) {
2725 ret = PTR_ERR(same); 2720 ret = PTR_ERR(same);
@@ -2756,52 +2751,35 @@ static long btrfs_ioctl_file_extent_same(struct file *file,
2756 goto out; 2751 goto out;
2757 2752
2758 /* pre-format output fields to sane values */ 2753 /* pre-format output fields to sane values */
2759 for (i = 0; i < same->dest_count; i++) { 2754 for (i = 0; i < count; i++) {
2760 same->info[i].bytes_deduped = 0ULL; 2755 same->info[i].bytes_deduped = 0ULL;
2761 same->info[i].status = 0; 2756 same->info[i].status = 0;
2762 } 2757 }
2763 2758
2764 ret = 0; 2759 for (i = 0, info = same->info; i < count; i++, info++) {
2765 for (i = 0; i < same->dest_count; i++) { 2760 struct inode *dst;
2766 info = &same->info[i]; 2761 struct fd dst_file = fdget(info->fd);
2767 2762 if (!dst_file.file) {
2768 dst_file = fget(info->fd);
2769 if (!dst_file) {
2770 info->status = -EBADF; 2763 info->status = -EBADF;
2771 goto next; 2764 continue;
2772 } 2765 }
2766 dst = file_inode(dst_file.file);
2773 2767
2774 if (!(is_admin || (dst_file->f_mode & FMODE_WRITE))) { 2768 if (!(is_admin || (dst_file.file->f_mode & FMODE_WRITE))) {
2775 info->status = -EINVAL; 2769 info->status = -EINVAL;
2776 goto next; 2770 } else if (file->f_path.mnt != dst_file.file->f_path.mnt) {
2777 } 2771 info->status = -EXDEV;
2778 2772 } else if (S_ISDIR(dst->i_mode)) {
2779 info->status = -EXDEV;
2780 if (file->f_path.mnt != dst_file->f_path.mnt)
2781 goto next;
2782
2783 dst = dst_file->f_dentry->d_inode;
2784 if (src->i_sb != dst->i_sb)
2785 goto next;
2786
2787 if (S_ISDIR(dst->i_mode)) {
2788 info->status = -EISDIR; 2773 info->status = -EISDIR;
2789 goto next; 2774 } else if (!S_ISREG(dst->i_mode)) {
2790 }
2791
2792 if (!S_ISREG(dst->i_mode)) {
2793 info->status = -EACCES; 2775 info->status = -EACCES;
2794 goto next; 2776 } else {
2777 info->status = btrfs_extent_same(src, off, len, dst,
2778 info->logical_offset);
2779 if (info->status == 0)
2780 info->bytes_deduped += len;
2795 } 2781 }
2796 2782 fdput(dst_file);
2797 info->status = btrfs_extent_same(src, off, len, dst,
2798 info->logical_offset);
2799 if (info->status == 0)
2800 info->bytes_deduped += len;
2801
2802next:
2803 if (dst_file)
2804 fput(dst_file);
2805 } 2783 }
2806 2784
2807 ret = copy_to_user(argp, same, size); 2785 ret = copy_to_user(argp, same, size);