diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/ioctl.c | 76 |
1 files changed, 45 insertions, 31 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index fba259ab9994..9d46f60cb943 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -2696,9 +2696,9 @@ out_unlock: | |||
2696 | static long btrfs_ioctl_file_extent_same(struct file *file, | 2696 | static long btrfs_ioctl_file_extent_same(struct file *file, |
2697 | void __user *argp) | 2697 | void __user *argp) |
2698 | { | 2698 | { |
2699 | struct btrfs_ioctl_same_args *args = argp; | 2699 | struct btrfs_ioctl_same_args tmp; |
2700 | struct btrfs_ioctl_same_args same; | 2700 | struct btrfs_ioctl_same_args *same; |
2701 | struct btrfs_ioctl_same_extent_info info; | 2701 | struct btrfs_ioctl_same_extent_info *info; |
2702 | struct inode *src = file->f_dentry->d_inode; | 2702 | struct inode *src = file->f_dentry->d_inode; |
2703 | struct file *dst_file = NULL; | 2703 | struct file *dst_file = NULL; |
2704 | struct inode *dst; | 2704 | struct inode *dst; |
@@ -2706,6 +2706,7 @@ static long btrfs_ioctl_file_extent_same(struct file *file, | |||
2706 | u64 len; | 2706 | u64 len; |
2707 | int i; | 2707 | int i; |
2708 | int ret; | 2708 | int ret; |
2709 | unsigned long size; | ||
2709 | u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize; | 2710 | u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize; |
2710 | bool is_admin = capable(CAP_SYS_ADMIN); | 2711 | bool is_admin = capable(CAP_SYS_ADMIN); |
2711 | 2712 | ||
@@ -2716,15 +2717,30 @@ static long btrfs_ioctl_file_extent_same(struct file *file, | |||
2716 | if (ret) | 2717 | if (ret) |
2717 | return ret; | 2718 | return ret; |
2718 | 2719 | ||
2719 | if (copy_from_user(&same, | 2720 | if (copy_from_user(&tmp, |
2720 | (struct btrfs_ioctl_same_args __user *)argp, | 2721 | (struct btrfs_ioctl_same_args __user *)argp, |
2721 | sizeof(same))) { | 2722 | sizeof(tmp))) { |
2722 | ret = -EFAULT; | 2723 | ret = -EFAULT; |
2723 | goto out; | 2724 | goto out; |
2724 | } | 2725 | } |
2725 | 2726 | ||
2726 | off = same.logical_offset; | 2727 | size = sizeof(tmp) + |
2727 | len = same.length; | 2728 | tmp.dest_count * sizeof(struct btrfs_ioctl_same_extent_info); |
2729 | |||
2730 | same = kmalloc(size, GFP_NOFS); | ||
2731 | if (!same) { | ||
2732 | ret = -EFAULT; | ||
2733 | goto out; | ||
2734 | } | ||
2735 | |||
2736 | if (copy_from_user(same, | ||
2737 | (struct btrfs_ioctl_same_args __user *)argp, size)) { | ||
2738 | ret = -EFAULT; | ||
2739 | goto out; | ||
2740 | } | ||
2741 | |||
2742 | off = same->logical_offset; | ||
2743 | len = same->length; | ||
2728 | 2744 | ||
2729 | /* | 2745 | /* |
2730 | * Limit the total length we will dedupe for each operation. | 2746 | * Limit the total length we will dedupe for each operation. |
@@ -2752,27 +2768,28 @@ static long btrfs_ioctl_file_extent_same(struct file *file, | |||
2752 | if (!S_ISREG(src->i_mode)) | 2768 | if (!S_ISREG(src->i_mode)) |
2753 | goto out; | 2769 | goto out; |
2754 | 2770 | ||
2755 | ret = 0; | 2771 | /* pre-format output fields to sane values */ |
2756 | for (i = 0; i < same.dest_count; i++) { | 2772 | for (i = 0; i < same->dest_count; i++) { |
2757 | if (copy_from_user(&info, &args->info[i], sizeof(info))) { | 2773 | same->info[i].bytes_deduped = 0ULL; |
2758 | ret = -EFAULT; | 2774 | same->info[i].status = 0; |
2759 | goto out; | 2775 | } |
2760 | } | ||
2761 | 2776 | ||
2762 | info.bytes_deduped = 0; | 2777 | ret = 0; |
2778 | for (i = 0; i < same->dest_count; i++) { | ||
2779 | info = &same->info[i]; | ||
2763 | 2780 | ||
2764 | dst_file = fget(info.fd); | 2781 | dst_file = fget(info->fd); |
2765 | if (!dst_file) { | 2782 | if (!dst_file) { |
2766 | info.status = -EBADF; | 2783 | info->status = -EBADF; |
2767 | goto next; | 2784 | goto next; |
2768 | } | 2785 | } |
2769 | 2786 | ||
2770 | if (!(is_admin || (dst_file->f_mode & FMODE_WRITE))) { | 2787 | if (!(is_admin || (dst_file->f_mode & FMODE_WRITE))) { |
2771 | info.status = -EINVAL; | 2788 | info->status = -EINVAL; |
2772 | goto next; | 2789 | goto next; |
2773 | } | 2790 | } |
2774 | 2791 | ||
2775 | info.status = -EXDEV; | 2792 | info->status = -EXDEV; |
2776 | if (file->f_path.mnt != dst_file->f_path.mnt) | 2793 | if (file->f_path.mnt != dst_file->f_path.mnt) |
2777 | goto next; | 2794 | goto next; |
2778 | 2795 | ||
@@ -2781,32 +2798,29 @@ static long btrfs_ioctl_file_extent_same(struct file *file, | |||
2781 | goto next; | 2798 | goto next; |
2782 | 2799 | ||
2783 | if (S_ISDIR(dst->i_mode)) { | 2800 | if (S_ISDIR(dst->i_mode)) { |
2784 | info.status = -EISDIR; | 2801 | info->status = -EISDIR; |
2785 | goto next; | 2802 | goto next; |
2786 | } | 2803 | } |
2787 | 2804 | ||
2788 | if (!S_ISREG(dst->i_mode)) { | 2805 | if (!S_ISREG(dst->i_mode)) { |
2789 | info.status = -EACCES; | 2806 | info->status = -EACCES; |
2790 | goto next; | 2807 | goto next; |
2791 | } | 2808 | } |
2792 | 2809 | ||
2793 | info.status = btrfs_extent_same(src, off, len, dst, | 2810 | info->status = btrfs_extent_same(src, off, len, dst, |
2794 | info.logical_offset); | 2811 | info->logical_offset); |
2795 | if (info.status == 0) | 2812 | if (info->status == 0) |
2796 | info.bytes_deduped += len; | 2813 | info->bytes_deduped += len; |
2797 | 2814 | ||
2798 | next: | 2815 | next: |
2799 | if (dst_file) | 2816 | if (dst_file) |
2800 | fput(dst_file); | 2817 | fput(dst_file); |
2801 | |||
2802 | if (__put_user_unaligned(info.status, &args->info[i].status) || | ||
2803 | __put_user_unaligned(info.bytes_deduped, | ||
2804 | &args->info[i].bytes_deduped)) { | ||
2805 | ret = -EFAULT; | ||
2806 | goto out; | ||
2807 | } | ||
2808 | } | 2818 | } |
2809 | 2819 | ||
2820 | ret = copy_to_user(argp, same, size); | ||
2821 | if (ret) | ||
2822 | ret = -EFAULT; | ||
2823 | |||
2810 | out: | 2824 | out: |
2811 | mnt_drop_write_file(file); | 2825 | mnt_drop_write_file(file); |
2812 | return ret; | 2826 | return ret; |