diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r-- | fs/btrfs/ioctl.c | 70 |
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 | ||
2688 | static long btrfs_ioctl_file_extent_same(struct file *file, | 2688 | static 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 | |||
2802 | next: | ||
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); |