From patchwork Thu Nov 25 02:21:46 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 355242 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id oAP2MDTh021881 for ; Thu, 25 Nov 2010 02:22:14 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756042Ab0KYCVq (ORCPT ); Wed, 24 Nov 2010 21:21:46 -0500 Received: from perceval.ideasonboard.com ([95.142.166.194]:35107 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755749Ab0KYCVp (ORCPT ); Wed, 24 Nov 2010 21:21:45 -0500 Received: from localhost.localdomain (unknown [91.178.68.191]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1848D35CA6; Thu, 25 Nov 2010 02:21:42 +0000 (UTC) From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: sakari.ailus@maxwell.research.nokia.com Subject: [RFC/PATCH v4 1/7] v4l: Share code between video_usercopy and video_ioctl2 Date: Thu, 25 Nov 2010 03:21:46 +0100 Message-Id: <1290651712-14940-2-git-send-email-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 1.7.2.2 In-Reply-To: <1290651712-14940-1-git-send-email-laurent.pinchart@ideasonboard.com> References: <1290651712-14940-1-git-send-email-laurent.pinchart@ideasonboard.com> Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Thu, 25 Nov 2010 02:22:14 +0000 (UTC) diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index dd9283f..1e01554 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -374,35 +374,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 { @@ -414,11 +441,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; @@ -440,7 +477,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; @@ -469,6 +506,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, @@ -2041,138 +2091,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);