USB: gadget: Implement COMPAT_IOCTL for ioctls
32-bit userspace calling into 64-bit kernel cause different ioctl codes to get generated. The different ioctl code gets generated because sizeof is used on mtp and accessory structures which is different for 32/64 compilation. Because of this, 64-bit kernel can never execute the right ioctl command. Implement compat_ioctl to handle such execution environment. Change-Id: I26cc10986e28a28eab6f3c65f28f4d2b808112d9 Signed-off-by: Sujeet Kumar <ksujeet@codeaurora.org> Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
This commit is contained in:
parent
78ece44dc6
commit
ff1259b38e
3 changed files with 196 additions and 77 deletions
|
@ -818,6 +818,9 @@ static const struct file_operations acc_fops = {
|
||||||
.read = acc_read,
|
.read = acc_read,
|
||||||
.write = acc_write,
|
.write = acc_write,
|
||||||
.unlocked_ioctl = acc_ioctl,
|
.unlocked_ioctl = acc_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl = acc_ioctl,
|
||||||
|
#endif
|
||||||
.open = acc_open,
|
.open = acc_open,
|
||||||
.release = acc_release,
|
.release = acc_release,
|
||||||
};
|
};
|
||||||
|
|
|
@ -999,26 +999,20 @@ static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
|
static long mtp_send_receive_ioctl(struct file *fp, unsigned code,
|
||||||
|
struct mtp_file_range *mfr)
|
||||||
{
|
{
|
||||||
struct mtp_dev *dev = fp->private_data;
|
struct mtp_dev *dev = fp->private_data;
|
||||||
struct file *filp = NULL;
|
struct file *filp = NULL;
|
||||||
|
struct work_struct *work;
|
||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
|
|
||||||
if (mtp_lock(&dev->ioctl_excl))
|
if (mtp_lock(&dev->ioctl_excl))
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
switch (code) {
|
|
||||||
case MTP_SEND_FILE:
|
|
||||||
case MTP_RECEIVE_FILE:
|
|
||||||
case MTP_SEND_FILE_WITH_HEADER:
|
|
||||||
{
|
|
||||||
struct mtp_file_range mfr;
|
|
||||||
struct work_struct *work;
|
|
||||||
|
|
||||||
spin_lock_irq(&dev->lock);
|
spin_lock_irq(&dev->lock);
|
||||||
if (dev->state == STATE_CANCELED) {
|
if (dev->state == STATE_CANCELED) {
|
||||||
/* report cancelation to userspace */
|
/* report cancellation to userspace */
|
||||||
dev->state = STATE_READY;
|
dev->state = STATE_READY;
|
||||||
spin_unlock_irq(&dev->lock);
|
spin_unlock_irq(&dev->lock);
|
||||||
ret = -ECANCELED;
|
ret = -ECANCELED;
|
||||||
|
@ -1032,12 +1026,8 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
|
||||||
dev->state = STATE_BUSY;
|
dev->state = STATE_BUSY;
|
||||||
spin_unlock_irq(&dev->lock);
|
spin_unlock_irq(&dev->lock);
|
||||||
|
|
||||||
if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) {
|
|
||||||
ret = -EFAULT;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
/* hold a reference to the file while we are working with it */
|
/* hold a reference to the file while we are working with it */
|
||||||
filp = fget(mfr.fd);
|
filp = fget(mfr->fd);
|
||||||
if (!filp) {
|
if (!filp) {
|
||||||
ret = -EBADF;
|
ret = -EBADF;
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -1045,15 +1035,16 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
|
||||||
|
|
||||||
/* write the parameters */
|
/* write the parameters */
|
||||||
dev->xfer_file = filp;
|
dev->xfer_file = filp;
|
||||||
dev->xfer_file_offset = mfr.offset;
|
dev->xfer_file_offset = mfr->offset;
|
||||||
dev->xfer_file_length = mfr.length;
|
dev->xfer_file_length = mfr->length;
|
||||||
|
/* make sure write is done before parameters are read */
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
|
|
||||||
if (code == MTP_SEND_FILE_WITH_HEADER) {
|
if (code == MTP_SEND_FILE_WITH_HEADER) {
|
||||||
work = &dev->send_file_work;
|
work = &dev->send_file_work;
|
||||||
dev->xfer_send_header = 1;
|
dev->xfer_send_header = 1;
|
||||||
dev->xfer_command = mfr.command;
|
dev->xfer_command = mfr->command;
|
||||||
dev->xfer_transaction_id = mfr.transaction_id;
|
dev->xfer_transaction_id = mfr->transaction_id;
|
||||||
} else if (code == MTP_SEND_FILE) {
|
} else if (code == MTP_SEND_FILE) {
|
||||||
work = &dev->send_file_work;
|
work = &dev->send_file_work;
|
||||||
dev->xfer_send_header = 0;
|
dev->xfer_send_header = 0;
|
||||||
|
@ -1073,21 +1064,6 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
|
||||||
/* read the result */
|
/* read the result */
|
||||||
smp_rmb();
|
smp_rmb();
|
||||||
ret = dev->xfer_result;
|
ret = dev->xfer_result;
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MTP_SEND_EVENT:
|
|
||||||
{
|
|
||||||
struct mtp_event event;
|
|
||||||
/* return here so we don't change dev->state below,
|
|
||||||
* which would interfere with bulk transfer state.
|
|
||||||
*/
|
|
||||||
if (copy_from_user(&event, (void __user *)value, sizeof(event)))
|
|
||||||
ret = -EFAULT;
|
|
||||||
else
|
|
||||||
ret = mtp_send_event(dev, &event);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
spin_lock_irq(&dev->lock);
|
spin_lock_irq(&dev->lock);
|
||||||
|
@ -1102,6 +1078,113 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
|
||||||
|
{
|
||||||
|
struct mtp_dev *dev = fp->private_data;
|
||||||
|
struct mtp_file_range mfr;
|
||||||
|
struct mtp_event event;
|
||||||
|
int ret = -EINVAL;
|
||||||
|
|
||||||
|
switch (code) {
|
||||||
|
case MTP_SEND_FILE:
|
||||||
|
case MTP_RECEIVE_FILE:
|
||||||
|
case MTP_SEND_FILE_WITH_HEADER:
|
||||||
|
if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) {
|
||||||
|
ret = -EFAULT;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
ret = mtp_send_receive_ioctl(fp, code, &mfr);
|
||||||
|
break;
|
||||||
|
case MTP_SEND_EVENT:
|
||||||
|
if (mtp_lock(&dev->ioctl_excl))
|
||||||
|
return -EBUSY;
|
||||||
|
/* return here so we don't change dev->state below,
|
||||||
|
* which would interfere with bulk transfer state.
|
||||||
|
*/
|
||||||
|
if (copy_from_user(&event, (void __user *)value, sizeof(event)))
|
||||||
|
ret = -EFAULT;
|
||||||
|
else
|
||||||
|
ret = mtp_send_event(dev, &event);
|
||||||
|
mtp_unlock(&dev->ioctl_excl);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DBG(dev->cdev, "unknown ioctl code: %d\n", code);
|
||||||
|
}
|
||||||
|
fail:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 32 bit userspace calling into 64 bit kernel. handle ioctl code
|
||||||
|
* and userspace pointer
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static long compat_mtp_ioctl(struct file *fp, unsigned code,
|
||||||
|
unsigned long value)
|
||||||
|
{
|
||||||
|
struct mtp_dev *dev = fp->private_data;
|
||||||
|
struct mtp_file_range mfr;
|
||||||
|
struct __compat_mtp_file_range cmfr;
|
||||||
|
struct mtp_event event;
|
||||||
|
struct __compat_mtp_event cevent;
|
||||||
|
unsigned cmd;
|
||||||
|
bool send_file = false;
|
||||||
|
int ret = -EINVAL;
|
||||||
|
|
||||||
|
switch (code) {
|
||||||
|
case COMPAT_MTP_SEND_FILE:
|
||||||
|
cmd = MTP_SEND_FILE;
|
||||||
|
send_file = true;
|
||||||
|
break;
|
||||||
|
case COMPAT_MTP_RECEIVE_FILE:
|
||||||
|
cmd = MTP_RECEIVE_FILE;
|
||||||
|
send_file = true;
|
||||||
|
break;
|
||||||
|
case COMPAT_MTP_SEND_FILE_WITH_HEADER:
|
||||||
|
cmd = MTP_SEND_FILE_WITH_HEADER;
|
||||||
|
send_file = true;
|
||||||
|
break;
|
||||||
|
case COMPAT_MTP_SEND_EVENT:
|
||||||
|
cmd = MTP_SEND_EVENT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DBG(dev->cdev, "unknown compat_ioctl code: %d\n", code);
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (send_file) {
|
||||||
|
if (copy_from_user(&cmfr, (void __user *)value, sizeof(cmfr))) {
|
||||||
|
ret = -EFAULT;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
mfr.fd = cmfr.fd;
|
||||||
|
mfr.offset = cmfr.offset;
|
||||||
|
mfr.length = cmfr.length;
|
||||||
|
mfr.command = cmfr.command;
|
||||||
|
mfr.transaction_id = cmfr.transaction_id;
|
||||||
|
ret = mtp_send_receive_ioctl(fp, cmd, &mfr);
|
||||||
|
} else {
|
||||||
|
if (mtp_lock(&dev->ioctl_excl))
|
||||||
|
return -EBUSY;
|
||||||
|
/* return here so we don't change dev->state below,
|
||||||
|
* which would interfere with bulk transfer state.
|
||||||
|
*/
|
||||||
|
if (copy_from_user(&cevent, (void __user *)value,
|
||||||
|
sizeof(cevent))) {
|
||||||
|
ret = -EFAULT;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
event.length = cevent.length;
|
||||||
|
event.data = compat_ptr(cevent.data);
|
||||||
|
ret = mtp_send_event(dev, &event);
|
||||||
|
mtp_unlock(&dev->ioctl_excl);
|
||||||
|
}
|
||||||
|
fail:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int mtp_open(struct inode *ip, struct file *fp)
|
static int mtp_open(struct inode *ip, struct file *fp)
|
||||||
{
|
{
|
||||||
printk(KERN_INFO "mtp_open\n");
|
printk(KERN_INFO "mtp_open\n");
|
||||||
|
@ -1130,6 +1213,9 @@ static const struct file_operations mtp_fops = {
|
||||||
.read = mtp_read,
|
.read = mtp_read,
|
||||||
.write = mtp_write,
|
.write = mtp_write,
|
||||||
.unlocked_ioctl = mtp_ioctl,
|
.unlocked_ioctl = mtp_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl = compat_mtp_ioctl,
|
||||||
|
#endif
|
||||||
.open = mtp_open,
|
.open = mtp_open,
|
||||||
.release = mtp_release,
|
.release = mtp_release,
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,5 +19,35 @@
|
||||||
#define __LINUX_USB_F_MTP_H
|
#define __LINUX_USB_F_MTP_H
|
||||||
|
|
||||||
#include <uapi/linux/usb/f_mtp.h>
|
#include <uapi/linux/usb/f_mtp.h>
|
||||||
|
#include <linux/ioctl.h>
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
#include <linux/compat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
struct __compat_mtp_file_range {
|
||||||
|
compat_int_t fd;
|
||||||
|
compat_loff_t offset;
|
||||||
|
int64_t length;
|
||||||
|
uint16_t command;
|
||||||
|
uint32_t transaction_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct __compat_mtp_event {
|
||||||
|
compat_size_t length;
|
||||||
|
compat_caddr_t data;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define COMPAT_MTP_SEND_FILE _IOW('M', 0, \
|
||||||
|
struct __compat_mtp_file_range)
|
||||||
|
#define COMPAT_MTP_RECEIVE_FILE _IOW('M', 1, \
|
||||||
|
struct __compat_mtp_file_range)
|
||||||
|
#define COMPAT_MTP_SEND_EVENT _IOW('M', 3, \
|
||||||
|
struct __compat_mtp_event)
|
||||||
|
#define COMPAT_MTP_SEND_FILE_WITH_HEADER _IOW('M', 4, \
|
||||||
|
struct __compat_mtp_file_range)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
#endif /* __LINUX_USB_F_MTP_H */
|
#endif /* __LINUX_USB_F_MTP_H */
|
||||||
|
|
Loading…
Add table
Reference in a new issue