compat_ioctl: scsi: move ioctl handling into drivers
authorArnd Bergmann <arnd@arndb.de>
Fri, 15 Mar 2019 16:39:44 +0000 (17:39 +0100)
committerArnd Bergmann <arnd@arndb.de>
Fri, 3 Jan 2020 08:42:32 +0000 (09:42 +0100)
Each driver calling scsi_ioctl() gets an equivalent compat_ioctl()
handler that implements the same commands by calling scsi_compat_ioctl().

The scsi_cmd_ioctl() and scsi_cmd_blk_ioctl() functions are compatible
at this point, so any driver that calls those can do so for both native
and compat mode, with the argument passed through compat_ptr().

With this, we can remove the entries from fs/compat_ioctl.c.  The new
code is larger, but should be easier to maintain and keep updated with
newly added commands.

Reviewed-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
drivers/block/virtio_blk.c
drivers/scsi/ch.c
drivers/scsi/sd.c
drivers/scsi/sg.c
drivers/scsi/sr.c
drivers/scsi/st.c
fs/compat_ioctl.c

index 7ffd719..fbbf18a 100644 (file)
@@ -405,6 +405,9 @@ static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
 
 static const struct block_device_operations virtblk_fops = {
        .ioctl  = virtblk_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = blkdev_compat_ptr_ioctl,
+#endif
        .owner  = THIS_MODULE,
        .getgeo = virtblk_getgeo,
 };
index 76751d6..ed5f4a6 100644 (file)
@@ -872,6 +872,10 @@ static long ch_ioctl_compat(struct file * file,
                            unsigned int cmd, unsigned long arg)
 {
        scsi_changer *ch = file->private_data;
+       int retval = scsi_ioctl_block_when_processing_errors(ch->device, cmd,
+                                                       file->f_flags & O_NDELAY);
+       if (retval)
+               return retval;
 
        switch (cmd) {
        case CHIOGPARAMS:
@@ -883,7 +887,7 @@ static long ch_ioctl_compat(struct file * file,
        case CHIOINITELEM:
        case CHIOSVOLTAG:
                /* compatible */
-               return ch_ioctl(file, cmd, arg);
+               return ch_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
        case CHIOGSTATUS32:
        {
                struct changer_element_status32 ces32;
@@ -898,8 +902,7 @@ static long ch_ioctl_compat(struct file * file,
                return ch_gstatus(ch, ces32.ces_type, data);
        }
        default:
-               // return scsi_ioctl_compat(ch->device, cmd, (void*)arg);
-               return -ENOIOCTLCMD;
+               return scsi_compat_ioctl(ch->device, cmd, compat_ptr(arg));
 
        }
 }
index cea6259..5afb004 100644 (file)
@@ -1465,13 +1465,12 @@ static int sd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
  *     Note: most ioctls are forward onto the block subsystem or further
  *     down in the scsi subsystem.
  **/
-static int sd_ioctl(struct block_device *bdev, fmode_t mode,
-                   unsigned int cmd, unsigned long arg)
+static int sd_ioctl_common(struct block_device *bdev, fmode_t mode,
+                          unsigned int cmd, void __user *p)
 {
        struct gendisk *disk = bdev->bd_disk;
        struct scsi_disk *sdkp = scsi_disk(disk);
        struct scsi_device *sdp = sdkp->device;
-       void __user *p = (void __user *)arg;
        int error;
     
        SCSI_LOG_IOCTL(1, sd_printk(KERN_INFO, sdkp, "sd_ioctl: disk=%s, "
@@ -1507,9 +1506,6 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode,
                        break;
                default:
                        error = scsi_cmd_blk_ioctl(bdev, mode, cmd, p);
-                       if (error != -ENOTTY)
-                               break;
-                       error = scsi_ioctl(sdp, cmd, p);
                        break;
        }
 out:
@@ -1691,39 +1687,31 @@ static void sd_rescan(struct device *dev)
        revalidate_disk(sdkp->disk);
 }
 
+static int sd_ioctl(struct block_device *bdev, fmode_t mode,
+                   unsigned int cmd, unsigned long arg)
+{
+       void __user *p = (void __user *)arg;
+       int ret;
+
+       ret = sd_ioctl_common(bdev, mode, cmd, p);
+       if (ret != -ENOTTY)
+               return ret;
+
+       return scsi_ioctl(scsi_disk(bdev->bd_disk)->device, cmd, p);
+}
 
 #ifdef CONFIG_COMPAT
-/* 
- * This gets directly called from VFS. When the ioctl 
- * is not recognized we go back to the other translation paths. 
- */
 static int sd_compat_ioctl(struct block_device *bdev, fmode_t mode,
                           unsigned int cmd, unsigned long arg)
 {
-       struct gendisk *disk = bdev->bd_disk;
-       struct scsi_disk *sdkp = scsi_disk(disk);
-       struct scsi_device *sdev = sdkp->device;
        void __user *p = compat_ptr(arg);
-       int error;
-
-       error = scsi_verify_blk_ioctl(bdev, cmd);
-       if (error < 0)
-               return error;
+       int ret;
 
-       error = scsi_ioctl_block_when_processing_errors(sdev, cmd,
-                       (mode & FMODE_NDELAY) != 0);
-       if (error)
-               return error;
+       ret = sd_ioctl_common(bdev, mode, cmd, p);
+       if (ret != -ENOTTY)
+               return ret;
 
-       if (is_sed_ioctl(cmd))
-               return sed_ioctl(sdkp->opal_dev, cmd, p);
-              
-       /* 
-        * Let the static ioctl translation table take care of it.
-        */
-       if (!sdev->host->hostt->compat_ioctl)
-               return -ENOIOCTLCMD; 
-       return sdev->host->hostt->compat_ioctl(sdev, cmd, p);
+       return scsi_compat_ioctl(scsi_disk(bdev->bd_disk)->device, cmd, p);
 }
 #endif
 
index eace888..bafeaf7 100644 (file)
@@ -911,19 +911,14 @@ static int put_compat_request_table(struct compat_sg_req_info __user *o,
 #endif
 
 static long
-sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
+sg_ioctl_common(struct file *filp, Sg_device *sdp, Sg_fd *sfp,
+               unsigned int cmd_in, void __user *p)
 {
-       void __user *p = (void __user *)arg;
        int __user *ip = p;
        int result, val, read_only;
-       Sg_device *sdp;
-       Sg_fd *sfp;
        Sg_request *srp;
        unsigned long iflags;
 
-       if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
-               return -ENXIO;
-
        SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
                                   "sg_ioctl: cmd=0x%x\n", (int) cmd_in));
        read_only = (O_RDWR != (filp->f_flags & O_ACCMODE));
@@ -1146,29 +1141,44 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
                        cmd_in, filp->f_flags & O_NDELAY);
        if (result)
                return result;
+
+       return -ENOIOCTLCMD;
+}
+
+static long
+sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
+{
+       void __user *p = (void __user *)arg;
+       Sg_device *sdp;
+       Sg_fd *sfp;
+       int ret;
+
+       if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
+               return -ENXIO;
+
+       ret = sg_ioctl_common(filp, sdp, sfp, cmd_in, p);
+       if (ret != -ENOIOCTLCMD)
+               return ret;
+
        return scsi_ioctl(sdp->device, cmd_in, p);
 }
 
 #ifdef CONFIG_COMPAT
 static long sg_compat_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
 {
+       void __user *p = compat_ptr(arg);
        Sg_device *sdp;
        Sg_fd *sfp;
-       struct scsi_device *sdev;
+       int ret;
 
        if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
                return -ENXIO;
 
-       sdev = sdp->device;
-       if (sdev->host->hostt->compat_ioctl) { 
-               int ret;
-
-               ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg);
-
+       ret = sg_ioctl_common(filp, sdp, sfp, cmd_in, p);
+       if (ret != -ENOIOCTLCMD)
                return ret;
-       }
-       
-       return -ENOIOCTLCMD;
+
+       return scsi_compat_ioctl(sdp->device, cmd_in, p);
 }
 #endif
 
index 4664fdf..f1e7aab 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/bio.h>
+#include <linux/compat.h>
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/cdrom.h>
@@ -598,6 +599,55 @@ out:
        return ret;
 }
 
+#ifdef CONFIG_COMPAT
+static int sr_block_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
+                         unsigned long arg)
+{
+       struct scsi_cd *cd = scsi_cd(bdev->bd_disk);
+       struct scsi_device *sdev = cd->device;
+       void __user *argp = compat_ptr(arg);
+       int ret;
+
+       mutex_lock(&sr_mutex);
+
+       ret = scsi_ioctl_block_when_processing_errors(sdev, cmd,
+                       (mode & FMODE_NDELAY) != 0);
+       if (ret)
+               goto out;
+
+       scsi_autopm_get_device(sdev);
+
+       /*
+        * Send SCSI addressing ioctls directly to mid level, send other
+        * ioctls to cdrom/block level.
+        */
+       switch (cmd) {
+       case SCSI_IOCTL_GET_IDLUN:
+       case SCSI_IOCTL_GET_BUS_NUMBER:
+               ret = scsi_compat_ioctl(sdev, cmd, argp);
+               goto put;
+       }
+
+       /*
+        * CDROM ioctls are handled in the block layer, but
+        * do the scsi blk ioctls here.
+        */
+       ret = scsi_cmd_blk_ioctl(bdev, mode, cmd, argp);
+       if (ret != -ENOTTY)
+               goto put;
+
+       ret = scsi_compat_ioctl(sdev, cmd, argp);
+
+put:
+       scsi_autopm_put_device(sdev);
+
+out:
+       mutex_unlock(&sr_mutex);
+       return ret;
+
+}
+#endif
+
 static unsigned int sr_block_check_events(struct gendisk *disk,
                                          unsigned int clearing)
 {
@@ -641,12 +691,11 @@ static const struct block_device_operations sr_bdops =
        .open           = sr_block_open,
        .release        = sr_block_release,
        .ioctl          = sr_block_ioctl,
+#ifdef CONFIG_COMPAT
+       .ioctl          = sr_block_compat_ioctl,
+#endif
        .check_events   = sr_block_check_events,
        .revalidate_disk = sr_block_revalidate_disk,
-       /* 
-        * No compat_ioctl for now because sr_block_ioctl never
-        * seems to pass arbitrary ioctls down to host drivers.
-        */
 };
 
 static int sr_open(struct cdrom_device_info *cdi, int purpose)
index 9e3fff2..393f301 100644 (file)
@@ -3501,7 +3501,7 @@ out:
 
 
 /* The ioctl command */
-static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
+static long st_ioctl_common(struct file *file, unsigned int cmd_in, void __user *p)
 {
        int i, cmd_nr, cmd_type, bt;
        int retval = 0;
@@ -3509,7 +3509,6 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
        struct scsi_tape *STp = file->private_data;
        struct st_modedef *STm;
        struct st_partstat *STps;
-       void __user *p = (void __user *)arg;
 
        if (mutex_lock_interruptible(&STp->lock))
                return -ERESTARTSYS;
@@ -3824,9 +3823,19 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
        }
        mutex_unlock(&STp->lock);
        switch (cmd_in) {
+               case SCSI_IOCTL_STOP_UNIT:
+                       /* unload */
+                       retval = scsi_ioctl(STp->device, cmd_in, p);
+                       if (!retval) {
+                               STp->rew_at_close = 0;
+                               STp->ready = ST_NO_TAPE;
+                       }
+                       return retval;
+
                case SCSI_IOCTL_GET_IDLUN:
                case SCSI_IOCTL_GET_BUS_NUMBER:
                        break;
+
                default:
                        if ((cmd_in == SG_IO ||
                             cmd_in == SCSI_IOCTL_SEND_COMMAND ||
@@ -3840,42 +3849,46 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
                                return i;
                        break;
        }
-       retval = scsi_ioctl(STp->device, cmd_in, p);
-       if (!retval && cmd_in == SCSI_IOCTL_STOP_UNIT) { /* unload */
-               STp->rew_at_close = 0;
-               STp->ready = ST_NO_TAPE;
-       }
-       return retval;
+       return -ENOTTY;
 
  out:
        mutex_unlock(&STp->lock);
        return retval;
 }
 
+static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
+{
+       void __user *p = (void __user *)arg;
+       struct scsi_tape *STp = file->private_data;
+       int ret;
+
+       ret = st_ioctl_common(file, cmd_in, p);
+       if (ret != -ENOTTY)
+               return ret;
+
+       return scsi_ioctl(STp->device, cmd_in, p);
+}
+
 #ifdef CONFIG_COMPAT
 static long st_compat_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
 {
        void __user *p = compat_ptr(arg);
        struct scsi_tape *STp = file->private_data;
-       struct scsi_device *sdev = STp->device;
-       int ret = -ENOIOCTLCMD;
+       int ret;
 
        /* argument conversion is handled using put_user_mtpos/put_user_mtget */
        switch (cmd_in) {
-       case MTIOCTOP:
-               return st_ioctl(file, MTIOCTOP, (unsigned long)p);
        case MTIOCPOS32:
-               return st_ioctl(file, MTIOCPOS, (unsigned long)p);
+               return st_ioctl_common(file, MTIOCPOS, p);
        case MTIOCGET32:
-               return st_ioctl(file, MTIOCGET, (unsigned long)p);
+               return st_ioctl_common(file, MTIOCGET, p);
        }
 
-       if (sdev->host->hostt->compat_ioctl) { 
+       ret = st_ioctl_common(file, cmd_in, p);
+       if (ret != -ENOTTY)
+               return ret;
 
-               ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg);
-
-       }
-       return ret;
+       return scsi_compat_ioctl(STp->device, cmd_in, p);
 }
 #endif
 
index 358ea2e..ab4471f 100644 (file)
 
 #include "internal.h"
 
-#ifdef CONFIG_BLOCK
-#include <linux/cdrom.h>
-#include <linux/fd.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_ioctl.h>
-#include <scsi/sg.h>
-#endif
-
 #include <linux/uaccess.h>
 #include <linux/watchdog.h>
 
 #include <linux/hiddev.h>
 
-
-#include <linux/sort.h>
-
-/*
- * simple reversible transform to make our table more evenly
- * distributed after sorting.
- */
-#define XFORM(i) (((i) ^ ((i) << 27) ^ ((i) << 17)) & 0xffffffff)
-
-#define COMPATIBLE_IOCTL(cmd) XFORM((u32)cmd),
-static unsigned int ioctl_pointer[] = {
-#ifdef CONFIG_BLOCK
-/* Big S */
-COMPATIBLE_IOCTL(SCSI_IOCTL_GET_IDLUN)
-COMPATIBLE_IOCTL(SCSI_IOCTL_DOORLOCK)
-COMPATIBLE_IOCTL(SCSI_IOCTL_DOORUNLOCK)
-COMPATIBLE_IOCTL(SCSI_IOCTL_TEST_UNIT_READY)
-COMPATIBLE_IOCTL(SCSI_IOCTL_GET_BUS_NUMBER)
-COMPATIBLE_IOCTL(SCSI_IOCTL_SEND_COMMAND)
-COMPATIBLE_IOCTL(SCSI_IOCTL_PROBE_HOST)
-COMPATIBLE_IOCTL(SCSI_IOCTL_GET_PCI)
-#endif
-#ifdef CONFIG_BLOCK
-/* SG stuff */
-COMPATIBLE_IOCTL(SG_IO)
-COMPATIBLE_IOCTL(SG_GET_REQUEST_TABLE)
-COMPATIBLE_IOCTL(SG_SET_TIMEOUT)
-COMPATIBLE_IOCTL(SG_GET_TIMEOUT)
-COMPATIBLE_IOCTL(SG_EMULATED_HOST)
-COMPATIBLE_IOCTL(SG_GET_TRANSFORM)
-COMPATIBLE_IOCTL(SG_SET_RESERVED_SIZE)
-COMPATIBLE_IOCTL(SG_GET_RESERVED_SIZE)
-COMPATIBLE_IOCTL(SG_GET_SCSI_ID)
-COMPATIBLE_IOCTL(SG_SET_FORCE_LOW_DMA)
-COMPATIBLE_IOCTL(SG_GET_LOW_DMA)
-COMPATIBLE_IOCTL(SG_SET_FORCE_PACK_ID)
-COMPATIBLE_IOCTL(SG_GET_PACK_ID)
-COMPATIBLE_IOCTL(SG_GET_NUM_WAITING)
-COMPATIBLE_IOCTL(SG_SET_DEBUG)
-COMPATIBLE_IOCTL(SG_GET_SG_TABLESIZE)
-COMPATIBLE_IOCTL(SG_GET_COMMAND_Q)
-COMPATIBLE_IOCTL(SG_SET_COMMAND_Q)
-COMPATIBLE_IOCTL(SG_GET_VERSION_NUM)
-COMPATIBLE_IOCTL(SG_NEXT_CMD_LEN)
-COMPATIBLE_IOCTL(SG_SCSI_RESET)
-COMPATIBLE_IOCTL(SG_GET_REQUEST_TABLE)
-COMPATIBLE_IOCTL(SG_SET_KEEP_ORPHAN)
-COMPATIBLE_IOCTL(SG_GET_KEEP_ORPHAN)
-#endif
-};
-
-/*
- * Convert common ioctl arguments based on their command number
- *
- * Please do not add any code in here. Instead, implement
- * a compat_ioctl operation in the place that handleŃ• the
- * ioctl for the native case.
- */
-static long do_ioctl_trans(unsigned int cmd,
-                unsigned long arg, struct file *file)
-{
-       return -ENOIOCTLCMD;
-}
-
-static int compat_ioctl_check_table(unsigned int xcmd)
-{
-#ifdef CONFIG_BLOCK
-       int i;
-       const int max = ARRAY_SIZE(ioctl_pointer) - 1;
-
-       BUILD_BUG_ON(max >= (1 << 16));
-
-       /* guess initial offset into table, assuming a
-          normalized distribution */
-       i = ((xcmd >> 16) * max) >> 16;
-
-       /* do linear search up first, until greater or equal */
-       while (ioctl_pointer[i] < xcmd && i < max)
-               i++;
-
-       /* then do linear search down */
-       while (ioctl_pointer[i] > xcmd && i > 0)
-               i--;
-
-       return ioctl_pointer[i] == xcmd;
-#else
-       return 0;
-#endif
-}
-
 COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd,
                       compat_ulong_t, arg32)
 {
@@ -216,19 +118,9 @@ COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd,
                                goto out_fput;
                }
 
-               if (!f.file->f_op->unlocked_ioctl)
-                       goto do_ioctl;
-               break;
-       }
-
-       if (compat_ioctl_check_table(XFORM(cmd)))
-               goto found_handler;
-
-       error = do_ioctl_trans(cmd, arg, f.file);
-       if (error == -ENOIOCTLCMD)
                error = -ENOTTY;
-
-       goto out_fput;
+               goto out_fput;
+       }
 
  found_handler:
        arg = (unsigned long)compat_ptr(arg);
@@ -239,23 +131,3 @@ COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd,
  out:
        return error;
 }
-
-static int __init init_sys32_ioctl_cmp(const void *p, const void *q)
-{
-       unsigned int a, b;
-       a = *(unsigned int *)p;
-       b = *(unsigned int *)q;
-       if (a > b)
-               return 1;
-       if (a < b)
-               return -1;
-       return 0;
-}
-
-static int __init init_sys32_ioctl(void)
-{
-       sort(ioctl_pointer, ARRAY_SIZE(ioctl_pointer), sizeof(*ioctl_pointer),
-               init_sys32_ioctl_cmp, NULL);
-       return 0;
-}
-__initcall(init_sys32_ioctl);