compat_ioctl: scsi: move ioctl handling into drivers Each driver calling scsi_cmd_blk_ioctl() and/or scsi_ioctl() gets an equivalent compat_ioctl() handler that implements the same commands by calling scsi_cmd_blk_compat_ioctl()/scsi_compat_ioctl(). 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. Signed-off-by: Arnd Bergmann <arnd@arndb.de>
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 2a7ca4a..45dc8da 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c
@@ -2,6 +2,7 @@ #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/blkdev.h> +#include <linux/compat.h> #include <linux/hdreg.h> #include <linux/module.h> #include <linux/mutex.h> @@ -138,6 +139,24 @@ static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, return scsi_cmd_blk_ioctl(bdev, mode, cmd, (void __user *)data); } + +#ifdef CONFIG_COMPAT +static int virtblk_compat_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long data) +{ + struct gendisk *disk = bdev->bd_disk; + struct virtio_blk *vblk = disk->private_data; + + /* + * Only allow the generic SCSI ioctls if the host can support it. + */ + if (!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_SCSI)) + return -ENOTTY; + + return scsi_cmd_blk_compat_ioctl(bdev, mode, cmd, + compat_ptr(data)); +} +#endif #else static inline int virtblk_add_req_scsi(struct virtqueue *vq, struct virtblk_req *vbr, struct scatterlist *data_sg, @@ -149,6 +168,7 @@ static inline void virtblk_scsi_request_done(struct request *req) { } #define virtblk_ioctl NULL +#define virtblk_compat_ioctl NULL #endif /* CONFIG_VIRTIO_BLK_SCSI */ static int virtblk_add_req(struct virtqueue *vq, struct virtblk_req *vbr, @@ -404,6 +424,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 = virtblk_compat_ioctl, +#endif .owner = THIS_MODULE, .getgeo = virtblk_getgeo, };
diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c index 1c5051b..0d2f003 100644 --- a/drivers/scsi/ch.c +++ b/drivers/scsi/ch.c
@@ -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)); } }
diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c index edfde6e..e71d470 100644 --- a/drivers/scsi/osst.c +++ b/drivers/scsi/osst.c
@@ -5281,7 +5281,7 @@ static long osst_compat_ioctl(struct file * file, unsigned int cmd_in, unsigned { struct osst_tape *STp = file->private_data; struct scsi_device *sdev = STp->device; - int ret = -ENOIOCTLCMD; + int retval = -ENOIOCTLCMD; switch (cmd_in) { case MTIOCTOP: @@ -5290,12 +5290,15 @@ static long osst_compat_ioctl(struct file * file, unsigned int cmd_in, unsigned return osst_ioctl(file, cmd_in, (unsigned long)compat_ptr(arg)); } - if (sdev->host->hostt->compat_ioctl) { - - ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg); - - } - return ret; + mutex_lock(&osst_int_mutex); + retval = scsi_ioctl_block_when_processing_errors(sdev, cmd_in, + file->f_flags & O_NDELAY); + if (retval) + goto out; + retval = scsi_compat_ioctl(sdev, cmd_in, compat_ptr(arg)); +out: + mutex_unlock(&osst_int_mutex); + return retval; } #endif
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 9596ee7..0532cd5 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c
@@ -1693,15 +1693,18 @@ static int sd_compat_ioctl(struct block_device *bdev, fmode_t mode, { struct gendisk *disk = bdev->bd_disk; struct scsi_disk *sdkp = scsi_disk(disk); - struct scsi_device *sdev = sdkp->device; + struct scsi_device *sdp = sdkp->device; void __user *p = compat_ptr(arg); int error; + SCSI_LOG_IOCTL(1, sd_printk(KERN_INFO, sdkp, "sd_ioctl: disk=%s, " + "cmd=0x%x\n", disk->disk_name, cmd)); + error = scsi_verify_blk_ioctl(bdev, cmd); if (error < 0) return error; - error = scsi_ioctl_block_when_processing_errors(sdev, cmd, + error = scsi_ioctl_block_when_processing_errors(sdp, cmd, (mode & FMODE_NDELAY) != 0); if (error) return error; @@ -1709,12 +1712,19 @@ static int sd_compat_ioctl(struct block_device *bdev, fmode_t mode, 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); + switch (cmd) { + case SCSI_IOCTL_GET_IDLUN: + case SCSI_IOCTL_GET_BUS_NUMBER: + error = scsi_compat_ioctl(sdp, cmd, p); + break; + default: + error = scsi_cmd_blk_compat_ioctl(bdev, mode, cmd, p); + if (error != -ENOTTY) + break; + error = scsi_compat_ioctl(sdp, cmd, p); + break; + } + return error; } #endif
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 7b0a533..487888a 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c
@@ -924,19 +924,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)); @@ -1170,29 +1165,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
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 039c27c2..a967c5c 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c
@@ -37,6 +37,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> @@ -597,6 +598,55 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, 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_compat_ioctl(bdev, mode, cmd, argp); + if (ret != -ENOIOCTLCMD) + return ret; + + 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) { @@ -640,12 +690,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)
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index f0cb359..ac8a589 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c
@@ -3501,7 +3501,7 @@ static int partition_tape(struct scsi_tape *STp, int size) /* 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, cmd_size, 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; @@ -3823,9 +3822,23 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) mt_pos.mt_blkno = blk; retval = put_user_mtpos(p, &mt_pos, cmd_size == sizeof(struct mtpos32)); - goto out; } + 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 i, retval; + + retval = st_ioctl_common(file, cmd_in, p); + + if (retval != -ENOIOCTLCMD) + return retval; + switch (cmd_in) { case SCSI_IOCTL_GET_IDLUN: case SCSI_IOCTL_GET_BUS_NUMBER: @@ -3849,32 +3862,43 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) STp->ready = ST_NO_TAPE; } return retval; - - out: - mutex_unlock(&STp->lock); - return retval; } #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 i, retval; + + retval = st_ioctl_common(file, cmd_in, p); + + if (retval != -ENOIOCTLCMD) + return retval; switch (cmd_in) { - case MTIOCTOP: - case MTIOCPOS32: - case MTIOCGET32: - return st_ioctl(file, cmd_in, (unsigned long)compat_ptr(arg)); + case SCSI_IOCTL_GET_IDLUN: + case SCSI_IOCTL_GET_BUS_NUMBER: + break; + default: + if ((cmd_in == SG_IO || + cmd_in == SCSI_IOCTL_SEND_COMMAND || + cmd_in == CDROM_SEND_PACKET) && + !capable(CAP_SYS_RAWIO)) + i = -EPERM; + else + i = scsi_cmd_compat_ioctl(STp->disk->queue, STp->disk, + file->f_mode, cmd_in, p); + if (i != -ENOTTY) + return i; + break; } - - if (sdev->host->hostt->compat_ioctl) { - - ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg); - + retval = scsi_compat_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 ret; + return retval; } #endif
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 5b45b1d..5fb2e17 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c
@@ -36,14 +36,6 @@ #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> @@ -132,45 +124,8 @@ COMPATIBLE_IOCTL(FIGETBSZ) COMPATIBLE_IOCTL(FIFREEZE) COMPATIBLE_IOCTL(FITHAW) COMPATIBLE_IOCTL(FITRIM) -#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 /* Socket level stuff */ COMPATIBLE_IOCTL(FIOQSIZE) -#ifdef CONFIG_BLOCK -/* SG stuff */ -COMPATIBLE_IOCTL(SG_IO) -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 /* PPP stuff */ COMPATIBLE_IOCTL(PPPIOCGUNIT) COMPATIBLE_IOCTL(PPPIOCGCHAN)