On Monday 12 July 2010 17:25:46 Laurent Pinchart wrote: > The two functions are mostly identical. They handle the copy_from_user > and copy_to_user operations related with V4L2 ioctls and call the real > ioctl handler. > > Create a __video_usercopy function that implements the core of > video_usercopy and video_ioctl2, and call that function from both. Acked-by: Hans Verkuil <hverkuil@xxxxxxxxx> Two notes: 1) This change will clash with the multiplane patches. 2) Perhaps it is time that we remove the __OLD_VIDIOC_ support? Regards, Hans > > Signed-off-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx> > --- > drivers/media/video/v4l2-ioctl.c | 218 ++++++++++++------------------------- > 1 files changed, 71 insertions(+), 147 deletions(-) > > diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c > index 0eeceae..486eaba 100644 > --- a/drivers/media/video/v4l2-ioctl.c > +++ b/drivers/media/video/v4l2-ioctl.c > @@ -373,35 +373,62 @@ video_fix_command(unsigned int cmd) > } > #endif > > -/* > - * Obsolete usercopy function - Should be removed soon > - */ > -long > -video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, > +/* 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. */ > +static unsigned long cmd_input_size(unsigned int cmd) > +{ > + /* Size of structure up to and including 'field' */ > +#define CMDINSIZE(cmd, type, field) \ > + case VIDIOC_##cmd: \ > + return offsetof(struct v4l2_##type, field) + \ > + sizeof(((struct v4l2_##type *)0)->field); > + > + switch (cmd) { > + CMDINSIZE(ENUM_FMT, fmtdesc, type); > + CMDINSIZE(G_FMT, format, type); > + CMDINSIZE(QUERYBUF, buffer, type); > + CMDINSIZE(G_PARM, streamparm, type); > + CMDINSIZE(ENUMSTD, standard, index); > + CMDINSIZE(ENUMINPUT, input, index); > + CMDINSIZE(G_CTRL, control, id); > + CMDINSIZE(G_TUNER, tuner, index); > + CMDINSIZE(QUERYCTRL, queryctrl, id); > + CMDINSIZE(QUERYMENU, querymenu, index); > + CMDINSIZE(ENUMOUTPUT, output, index); > + CMDINSIZE(G_MODULATOR, modulator, index); > + CMDINSIZE(G_FREQUENCY, frequency, tuner); > + CMDINSIZE(CROPCAP, cropcap, type); > + CMDINSIZE(G_CROP, crop, type); > + CMDINSIZE(ENUMAUDIO, audio, index); > + CMDINSIZE(ENUMAUDOUT, audioout, index); > + CMDINSIZE(ENCODER_CMD, encoder_cmd, flags); > + CMDINSIZE(TRY_ENCODER_CMD, encoder_cmd, flags); > + CMDINSIZE(G_SLICED_VBI_CAP, sliced_vbi_cap, type); > + CMDINSIZE(ENUM_FRAMESIZES, frmsizeenum, pixel_format); > + CMDINSIZE(ENUM_FRAMEINTERVALS, frmivalenum, height); > + default: > + return _IOC_SIZE(cmd); > + } > +} > + > +static long > +__video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, > v4l2_kioctl func) > { > char sbuf[128]; > void *mbuf = NULL; > - void *parg = NULL; > + void *parg = (void *)arg; > long err = -EINVAL; > int is_ext_ctrl; > size_t ctrls_size = 0; > void __user *user_ptr = NULL; > > -#ifdef __OLD_VIDIOC_ > - cmd = video_fix_command(cmd); > -#endif > is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || > cmd == VIDIOC_TRY_EXT_CTRLS); > > /* Copy arguments into temp kernel buffer */ > - switch (_IOC_DIR(cmd)) { > - case _IOC_NONE: > - parg = NULL; > - break; > - case _IOC_READ: > - case _IOC_WRITE: > - case (_IOC_WRITE | _IOC_READ): > + if (_IOC_DIR(cmd) != _IOC_NONE) { > if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { > parg = sbuf; > } else { > @@ -413,11 +440,21 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, > } > > err = -EFAULT; > - if (_IOC_DIR(cmd) & _IOC_WRITE) > - if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) > + if (_IOC_DIR(cmd) & _IOC_WRITE) { > + unsigned long n = cmd_input_size(cmd); > + > + if (copy_from_user(parg, (void __user *)arg, n)) > goto out; > - break; > + > + /* zero out anything we don't copy from userspace */ > + if (n < _IOC_SIZE(cmd)) > + memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n); > + } else { > + /* read-only ioctl */ > + memset(parg, 0, _IOC_SIZE(cmd)); > + } > } > + > if (is_ext_ctrl) { > struct v4l2_ext_controls *p = parg; > > @@ -439,7 +476,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, > } > } > > - /* call driver */ > + /* Handles IOCTL */ > err = func(file, cmd, parg); > if (err == -ENOIOCTLCMD) > err = -EINVAL; > @@ -468,6 +505,19 @@ out: > kfree(mbuf); > return err; > } > + > +/* > + * Obsolete usercopy function - Should be removed soon > + */ > +long > +video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, > + v4l2_kioctl func) > +{ > +#ifdef __OLD_VIDIOC_ > + cmd = video_fix_command(cmd); > +#endif > + return __video_usercopy(file, cmd, arg, func); > +} > EXPORT_SYMBOL(video_usercopy); > > static void dbgbuf(unsigned int cmd, struct video_device *vfd, > @@ -2021,138 +2071,12 @@ static long __video_do_ioctl(struct file *file, > return ret; > } > > -/* 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. */ > -static unsigned long cmd_input_size(unsigned int cmd) > -{ > - /* Size of structure up to and including 'field' */ > -#define CMDINSIZE(cmd, type, field) \ > - case VIDIOC_##cmd: \ > - return offsetof(struct v4l2_##type, field) + \ > - sizeof(((struct v4l2_##type *)0)->field); > - > - switch (cmd) { > - CMDINSIZE(ENUM_FMT, fmtdesc, type); > - CMDINSIZE(G_FMT, format, type); > - CMDINSIZE(QUERYBUF, buffer, type); > - CMDINSIZE(G_PARM, streamparm, type); > - CMDINSIZE(ENUMSTD, standard, index); > - CMDINSIZE(ENUMINPUT, input, index); > - CMDINSIZE(G_CTRL, control, id); > - CMDINSIZE(G_TUNER, tuner, index); > - CMDINSIZE(QUERYCTRL, queryctrl, id); > - CMDINSIZE(QUERYMENU, querymenu, index); > - CMDINSIZE(ENUMOUTPUT, output, index); > - CMDINSIZE(G_MODULATOR, modulator, index); > - CMDINSIZE(G_FREQUENCY, frequency, tuner); > - CMDINSIZE(CROPCAP, cropcap, type); > - CMDINSIZE(G_CROP, crop, type); > - CMDINSIZE(ENUMAUDIO, audio, index); > - CMDINSIZE(ENUMAUDOUT, audioout, index); > - CMDINSIZE(ENCODER_CMD, encoder_cmd, flags); > - CMDINSIZE(TRY_ENCODER_CMD, encoder_cmd, flags); > - CMDINSIZE(G_SLICED_VBI_CAP, sliced_vbi_cap, type); > - CMDINSIZE(ENUM_FRAMESIZES, frmsizeenum, pixel_format); > - CMDINSIZE(ENUM_FRAMEINTERVALS, frmivalenum, height); > - default: > - return _IOC_SIZE(cmd); > - } > -} > - > long video_ioctl2(struct file *file, > unsigned int cmd, unsigned long arg) > { > - char sbuf[128]; > - void *mbuf = NULL; > - void *parg = (void *)arg; > - long err = -EINVAL; > - int is_ext_ctrl; > - size_t ctrls_size = 0; > - void __user *user_ptr = NULL; > - > #ifdef __OLD_VIDIOC_ > cmd = video_fix_command(cmd); > #endif > - is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || > - cmd == VIDIOC_TRY_EXT_CTRLS); > - > - /* Copy arguments into temp kernel buffer */ > - if (_IOC_DIR(cmd) != _IOC_NONE) { > - if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { > - parg = sbuf; > - } else { > - /* too big to allocate from stack */ > - mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); > - if (NULL == mbuf) > - return -ENOMEM; > - parg = mbuf; > - } > - > - err = -EFAULT; > - if (_IOC_DIR(cmd) & _IOC_WRITE) { > - unsigned long n = cmd_input_size(cmd); > - > - if (copy_from_user(parg, (void __user *)arg, n)) > - goto out; > - > - /* zero out anything we don't copy from userspace */ > - if (n < _IOC_SIZE(cmd)) > - memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n); > - } else { > - /* read-only ioctl */ > - memset(parg, 0, _IOC_SIZE(cmd)); > - } > - } > - > - if (is_ext_ctrl) { > - struct v4l2_ext_controls *p = parg; > - > - /* In case of an error, tell the caller that it wasn't > - a specific control that caused it. */ > - p->error_idx = p->count; > - user_ptr = (void __user *)p->controls; > - if (p->count) { > - ctrls_size = sizeof(struct v4l2_ext_control) * p->count; > - /* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */ > - mbuf = kmalloc(ctrls_size, GFP_KERNEL); > - err = -ENOMEM; > - if (NULL == mbuf) > - goto out_ext_ctrl; > - err = -EFAULT; > - if (copy_from_user(mbuf, user_ptr, ctrls_size)) > - goto out_ext_ctrl; > - p->controls = mbuf; > - } > - } > - > - /* Handles IOCTL */ > - err = __video_do_ioctl(file, cmd, parg); > - if (err == -ENOIOCTLCMD) > - err = -EINVAL; > - if (is_ext_ctrl) { > - struct v4l2_ext_controls *p = parg; > - > - p->controls = (void *)user_ptr; > - if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size)) > - err = -EFAULT; > - goto out_ext_ctrl; > - } > - if (err < 0) > - goto out; > - > -out_ext_ctrl: > - /* 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(cmd))) > - err = -EFAULT; > - break; > - } > - > -out: > - kfree(mbuf); > - return err; > + return __video_usercopy(file, cmd, arg, __video_do_ioctl); > } > EXPORT_SYMBOL(video_ioctl2); > -- Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html