Skip to content

Commit

Permalink
compat_ioctl: scsi: move ioctl handling into drivers
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Arnd Bergmann committed Jan 3, 2020
1 parent c103d6e commit d320a95
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 204 deletions.
3 changes: 3 additions & 0 deletions drivers/block/virtio_blk.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
9 changes: 6 additions & 3 deletions drivers/scsi/ch.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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;
Expand All @@ -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));

}
}
Expand Down
50 changes: 19 additions & 31 deletions drivers/scsi/sd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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, "
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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

Expand Down
44 changes: 27 additions & 17 deletions drivers/scsi/sg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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

Expand Down
57 changes: 53 additions & 4 deletions drivers/scsi/sr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -598,6 +599,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_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)
{
Expand Down Expand Up @@ -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)
Expand Down
51 changes: 32 additions & 19 deletions drivers/scsi/st.c
Original file line number Diff line number Diff line change
Expand Up @@ -3501,15 +3501,14 @@ 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, bt;
int retval = 0;
unsigned int blk;
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;
Expand Down Expand Up @@ -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 ||
Expand All @@ -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

Expand Down
Loading

0 comments on commit d320a95

Please sign in to comment.