forked from luck/tmp_suning_uos_patched
btrfs: sanitize BTRFS_IOC_FILE_EXTENT_SAME
* don't assume that ->dest_count won't change between copy_from_user() and memdup_user() * use fdget instead of fget * don't bother comparing superblocks when we'd already compared vfsmounts * get rid of excessive goto * use file_inode() instead of open-coding the sucker Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
208adb6403
commit
1c1c8747cd
|
@ -2686,14 +2686,11 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 len,
|
|||
#define BTRFS_MAX_DEDUPE_LEN (16 * 1024 * 1024)
|
||||
|
||||
static long btrfs_ioctl_file_extent_same(struct file *file,
|
||||
void __user *argp)
|
||||
struct btrfs_ioctl_same_args __user *argp)
|
||||
{
|
||||
struct btrfs_ioctl_same_args tmp;
|
||||
struct btrfs_ioctl_same_args *same;
|
||||
struct btrfs_ioctl_same_extent_info *info;
|
||||
struct inode *src = file->f_dentry->d_inode;
|
||||
struct file *dst_file = NULL;
|
||||
struct inode *dst;
|
||||
struct inode *src = file_inode(file);
|
||||
u64 off;
|
||||
u64 len;
|
||||
int i;
|
||||
|
@ -2701,6 +2698,7 @@ static long btrfs_ioctl_file_extent_same(struct file *file,
|
|||
unsigned long size;
|
||||
u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize;
|
||||
bool is_admin = capable(CAP_SYS_ADMIN);
|
||||
u16 count;
|
||||
|
||||
if (!(file->f_mode & FMODE_READ))
|
||||
return -EINVAL;
|
||||
|
@ -2709,17 +2707,14 @@ static long btrfs_ioctl_file_extent_same(struct file *file,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (copy_from_user(&tmp,
|
||||
(struct btrfs_ioctl_same_args __user *)argp,
|
||||
sizeof(tmp))) {
|
||||
if (get_user(count, &argp->dest_count)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size = sizeof(tmp) +
|
||||
tmp.dest_count * sizeof(struct btrfs_ioctl_same_extent_info);
|
||||
size = offsetof(struct btrfs_ioctl_same_args __user, info[count]);
|
||||
|
||||
same = memdup_user((struct btrfs_ioctl_same_args __user *)argp, size);
|
||||
same = memdup_user(argp, size);
|
||||
|
||||
if (IS_ERR(same)) {
|
||||
ret = PTR_ERR(same);
|
||||
|
@ -2756,52 +2751,35 @@ static long btrfs_ioctl_file_extent_same(struct file *file,
|
|||
goto out;
|
||||
|
||||
/* pre-format output fields to sane values */
|
||||
for (i = 0; i < same->dest_count; i++) {
|
||||
for (i = 0; i < count; i++) {
|
||||
same->info[i].bytes_deduped = 0ULL;
|
||||
same->info[i].status = 0;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
for (i = 0; i < same->dest_count; i++) {
|
||||
info = &same->info[i];
|
||||
|
||||
dst_file = fget(info->fd);
|
||||
if (!dst_file) {
|
||||
for (i = 0, info = same->info; i < count; i++, info++) {
|
||||
struct inode *dst;
|
||||
struct fd dst_file = fdget(info->fd);
|
||||
if (!dst_file.file) {
|
||||
info->status = -EBADF;
|
||||
goto next;
|
||||
continue;
|
||||
}
|
||||
dst = file_inode(dst_file.file);
|
||||
|
||||
if (!(is_admin || (dst_file->f_mode & FMODE_WRITE))) {
|
||||
if (!(is_admin || (dst_file.file->f_mode & FMODE_WRITE))) {
|
||||
info->status = -EINVAL;
|
||||
goto next;
|
||||
}
|
||||
|
||||
info->status = -EXDEV;
|
||||
if (file->f_path.mnt != dst_file->f_path.mnt)
|
||||
goto next;
|
||||
|
||||
dst = dst_file->f_dentry->d_inode;
|
||||
if (src->i_sb != dst->i_sb)
|
||||
goto next;
|
||||
|
||||
if (S_ISDIR(dst->i_mode)) {
|
||||
} else if (file->f_path.mnt != dst_file.file->f_path.mnt) {
|
||||
info->status = -EXDEV;
|
||||
} else if (S_ISDIR(dst->i_mode)) {
|
||||
info->status = -EISDIR;
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (!S_ISREG(dst->i_mode)) {
|
||||
} else if (!S_ISREG(dst->i_mode)) {
|
||||
info->status = -EACCES;
|
||||
goto next;
|
||||
} else {
|
||||
info->status = btrfs_extent_same(src, off, len, dst,
|
||||
info->logical_offset);
|
||||
if (info->status == 0)
|
||||
info->bytes_deduped += len;
|
||||
}
|
||||
|
||||
info->status = btrfs_extent_same(src, off, len, dst,
|
||||
info->logical_offset);
|
||||
if (info->status == 0)
|
||||
info->bytes_deduped += len;
|
||||
|
||||
next:
|
||||
if (dst_file)
|
||||
fput(dst_file);
|
||||
fdput(dst_file);
|
||||
}
|
||||
|
||||
ret = copy_to_user(argp, same, size);
|
||||
|
|
Loading…
Reference in New Issue
Block a user