media: dvb_frontend: Add commands implementation for compat ioct
authorJaedon Shin <jaedon.shin@gmail.com>
Fri, 1 Dec 2017 12:31:30 +0000 (07:31 -0500)
committerMauro Carvalho Chehab <mchehab@s-opensource.com>
Wed, 13 Dec 2017 13:58:18 +0000 (08:58 -0500)
The dtv_properties structure and the dtv_property structure are
different sizes in 32-bit and 64-bit system. This patch provides
FE_SET_PROPERTY and FE_GET_PROPERTY ioctl commands implementation for
32-bit user space applications.

Signed-off-by: Jaedon Shin <jaedon.shin@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
drivers/media/dvb-core/dvb_frontend.c

index 130e9d2..ee3ea4d 100644 (file)
@@ -1979,9 +1979,140 @@ static long dvb_frontend_ioctl(struct file *file, unsigned int cmd,
 }
 
 #ifdef CONFIG_COMPAT
+struct compat_dtv_property {
+       __u32 cmd;
+       __u32 reserved[3];
+       union {
+               __u32 data;
+               struct dtv_fe_stats st;
+               struct {
+                       __u8 data[32];
+                       __u32 len;
+                       __u32 reserved1[3];
+                       compat_uptr_t reserved2;
+               } buffer;
+       } u;
+       int result;
+} __attribute__ ((packed));
+
+struct compat_dtv_properties {
+       __u32 num;
+       compat_uptr_t props;
+};
+
+#define COMPAT_FE_SET_PROPERTY    _IOW('o', 82, struct compat_dtv_properties)
+#define COMPAT_FE_GET_PROPERTY    _IOR('o', 83, struct compat_dtv_properties)
+
+static int dvb_frontend_handle_compat_ioctl(struct file *file, unsigned int cmd,
+                                           unsigned long arg)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct dvb_frontend *fe = dvbdev->priv;
+       struct dvb_frontend_private *fepriv = fe->frontend_priv;
+       int i, err = 0;
+
+       if (cmd == COMPAT_FE_SET_PROPERTY) {
+               struct compat_dtv_properties prop, *tvps = NULL;
+               struct compat_dtv_property *tvp = NULL;
+
+               if (copy_from_user(&prop, compat_ptr(arg), sizeof(prop)))
+                       return -EFAULT;
+
+               tvps = &prop;
+
+               /*
+                * Put an arbitrary limit on the number of messages that can
+                * be sent at once
+                */
+               if (!tvps->num || (tvps->num > DTV_IOCTL_MAX_MSGS))
+                       return -EINVAL;
+
+               tvp = memdup_user(compat_ptr(tvps->props), tvps->num * sizeof(*tvp));
+               if (IS_ERR(tvp))
+                       return PTR_ERR(tvp);
+
+               for (i = 0; i < tvps->num; i++) {
+                       err = dtv_property_process_set(fe, file,
+                                                       (tvp + i)->cmd,
+                                                       (tvp + i)->u.data);
+                       if (err < 0) {
+                               kfree(tvp);
+                               return err;
+                       }
+               }
+               kfree(tvp);
+       } else if (cmd == COMPAT_FE_GET_PROPERTY) {
+               struct compat_dtv_properties prop, *tvps = NULL;
+               struct compat_dtv_property *tvp = NULL;
+               struct dtv_frontend_properties getp = fe->dtv_property_cache;
+
+               if (copy_from_user(&prop, compat_ptr(arg), sizeof(prop)))
+                       return -EFAULT;
+
+               tvps = &prop;
+
+               /*
+                * Put an arbitrary limit on the number of messages that can
+                * be sent at once
+                */
+               if (!tvps->num || (tvps->num > DTV_IOCTL_MAX_MSGS))
+                       return -EINVAL;
+
+               tvp = memdup_user(compat_ptr(tvps->props), tvps->num * sizeof(*tvp));
+               if (IS_ERR(tvp))
+                       return PTR_ERR(tvp);
+
+               /*
+                * Let's use our own copy of property cache, in order to
+                * avoid mangling with DTV zigzag logic, as drivers might
+                * return crap, if they don't check if the data is available
+                * before updating the properties cache.
+                */
+               if (fepriv->state != FESTATE_IDLE) {
+                       err = dtv_get_frontend(fe, &getp, NULL);
+                       if (err < 0) {
+                               kfree(tvp);
+                               return err;
+                       }
+               }
+               for (i = 0; i < tvps->num; i++) {
+                       err = dtv_property_process_get(
+                           fe, &getp, (struct dtv_property *)tvp + i, file);
+                       if (err < 0) {
+                               kfree(tvp);
+                               return err;
+                       }
+               }
+
+               if (copy_to_user((void __user *)compat_ptr(tvps->props), tvp,
+                                tvps->num * sizeof(struct compat_dtv_property))) {
+                       kfree(tvp);
+                       return -EFAULT;
+               }
+               kfree(tvp);
+       }
+
+       return err;
+}
+
 static long dvb_frontend_compat_ioctl(struct file *file, unsigned int cmd,
                                      unsigned long arg)
 {
+       struct dvb_device *dvbdev = file->private_data;
+       struct dvb_frontend *fe = dvbdev->priv;
+       struct dvb_frontend_private *fepriv = fe->frontend_priv;
+       int err;
+
+       if (cmd == COMPAT_FE_SET_PROPERTY || cmd == COMPAT_FE_GET_PROPERTY) {
+               if (down_interruptible(&fepriv->sem))
+                       return -ERESTARTSYS;
+
+               err = dvb_frontend_handle_compat_ioctl(file, cmd, arg);
+
+               up(&fepriv->sem);
+               return err;
+       }
+
        return dvb_frontend_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
 }
 #endif