media: v4l2-core: split out data copy from video_usercopy

The copy-in/out portions of video_usercopy() are about to
get more complex, so turn then into separate functions as
a cleanup first.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
This commit is contained in:
Arnd Bergmann 2019-12-16 15:15:02 +01:00 committed by Mauro Carvalho Chehab
parent 4a873f3fa5
commit c8ef1a6076

View File

@ -3023,8 +3023,69 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
return ret;
}
static unsigned int video_translate_cmd(unsigned int cmd)
{
return cmd;
}
static int video_get_user(void __user *arg, void *parg, unsigned int cmd,
bool *always_copy)
{
unsigned int n = _IOC_SIZE(cmd);
if (!(_IOC_DIR(cmd) & _IOC_WRITE)) {
/* read-only ioctl */
memset(parg, 0, n);
return 0;
}
switch (cmd) {
default:
/*
* In some cases, only a few fields are used as input,
* i.e. when the app sets "index" and then the driver
* fills in the rest of the structure for the thing
* with that index. We only need to copy up the first
* non-input field.
*/
if (v4l2_is_known_ioctl(cmd)) {
u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags;
if (flags & INFO_FL_CLEAR_MASK)
n = (flags & INFO_FL_CLEAR_MASK) >> 16;
*always_copy = flags & INFO_FL_ALWAYS_COPY;
}
if (copy_from_user(parg, (void __user *)arg, n))
return -EFAULT;
/* zero out anything we don't copy from userspace */
if (n < _IOC_SIZE(cmd))
memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);
break;
}
return 0;
}
static int video_put_user(void __user *arg, void *parg, unsigned int cmd)
{
if (!(_IOC_DIR(cmd) & _IOC_READ))
return 0;
switch (cmd) {
default:
/* Copy results into user buffer */
if (copy_to_user(arg, parg, _IOC_SIZE(cmd)))
return -EFAULT;
break;
}
return 0;
}
long
video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg,
v4l2_kioctl func)
{
char sbuf[128];
@ -3036,6 +3097,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
size_t array_size = 0;
void __user *user_ptr = NULL;
void **kernel_ptr = NULL;
unsigned int cmd = video_translate_cmd(orig_cmd);
const size_t ioc_size = _IOC_SIZE(cmd);
/* Copy arguments into temp kernel buffer */
@ -3050,37 +3112,12 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
parg = mbuf;
}
err = -EFAULT;
if (_IOC_DIR(cmd) & _IOC_WRITE) {
unsigned int n = ioc_size;
/*
* In some cases, only a few fields are used as input,
* i.e. when the app sets "index" and then the driver
* fills in the rest of the structure for the thing
* with that index. We only need to copy up the first
* non-input field.
*/
if (v4l2_is_known_ioctl(cmd)) {
u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags;
if (flags & INFO_FL_CLEAR_MASK)
n = (flags & INFO_FL_CLEAR_MASK) >> 16;
always_copy = flags & INFO_FL_ALWAYS_COPY;
}
if (copy_from_user(parg, (void __user *)arg, n))
goto out;
/* zero out anything we don't copy from userspace */
if (n < ioc_size)
memset((u8 *)parg + n, 0, ioc_size - n);
} else {
/* read-only ioctl */
memset(parg, 0, ioc_size);
}
}
err = video_get_user((void __user *)arg, parg, orig_cmd, &always_copy);
if (err)
goto out;
err = check_array_args(cmd, parg, &array_size, &user_ptr, &kernel_ptr);
if (err < 0)
goto out;
@ -3131,15 +3168,8 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
goto out;
out_array_args:
/* Copy results into user buffer */
switch (_IOC_DIR(cmd)) {
case _IOC_READ:
case (_IOC_WRITE | _IOC_READ):
if (copy_to_user((void __user *)arg, parg, ioc_size))
err = -EFAULT;
break;
}
if (video_put_user((void __user *)arg, parg, orig_cmd))
err = -EFAULT;
out:
kvfree(mbuf);
return err;