2019-04-30 21:42:39 +03:00
// SPDX-License-Identifier: GPL-2.0
2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 2001 Jens Axboe < axboe @ suse . de >
*/
2019-03-14 19:45:18 +03:00
# include <linux/compat.h>
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/module.h>
# include <linux/blkdev.h>
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/completion.h>
# include <linux/cdrom.h>
2012-01-12 19:01:28 +04:00
# include <linux/ratelimit.h>
2005-04-17 02:20:36 +04:00
# include <linux/slab.h>
# include <linux/times.h>
2013-05-08 03:19:08 +04:00
# include <linux/uio.h>
2016-12-24 22:46:01 +03:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
# include <scsi/scsi.h>
# include <scsi/scsi_ioctl.h>
# include <scsi/scsi_cmnd.h>
2019-12-04 11:35:00 +03:00
# include <scsi/sg.h>
2005-04-17 02:20:36 +04:00
2009-06-26 18:27:10 +04:00
struct blk_cmd_filter {
unsigned long read_ok [ BLK_SCSI_CMD_PER_LONG ] ;
unsigned long write_ok [ BLK_SCSI_CMD_PER_LONG ] ;
2009-11-04 11:10:33 +03:00
} ;
static struct blk_cmd_filter blk_default_cmd_filter ;
2009-06-26 18:27:10 +04:00
2005-04-17 02:20:36 +04:00
/* Command group 3 is reserved and should never be used. */
2008-04-30 12:27:26 +04:00
const unsigned char scsi_command_size_tbl [ 8 ] =
2005-04-17 02:20:36 +04:00
{
6 , 10 , 10 , 12 ,
16 , 12 , 10 , 10
} ;
2008-04-30 12:27:26 +04:00
EXPORT_SYMBOL ( scsi_command_size_tbl ) ;
2005-04-17 02:20:36 +04:00
static int sg_get_version ( int __user * p )
{
2006-01-06 11:46:02 +03:00
static const int sg_version_num = 30527 ;
2005-04-17 02:20:36 +04:00
return put_user ( sg_version_num , p ) ;
}
2007-07-24 11:28:11 +04:00
static int scsi_get_idlun ( struct request_queue * q , int __user * p )
2005-04-17 02:20:36 +04:00
{
return put_user ( 0 , p ) ;
}
2007-07-24 11:28:11 +04:00
static int scsi_get_bus ( struct request_queue * q , int __user * p )
2005-04-17 02:20:36 +04:00
{
return put_user ( 0 , p ) ;
}
2007-07-24 11:28:11 +04:00
static int sg_get_timeout ( struct request_queue * q )
2005-04-17 02:20:36 +04:00
{
2008-11-17 15:10:34 +03:00
return jiffies_to_clock_t ( q - > sg_timeout ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-24 11:28:11 +04:00
static int sg_set_timeout ( struct request_queue * q , int __user * p )
2005-04-17 02:20:36 +04:00
{
int timeout , err = get_user ( timeout , p ) ;
if ( ! err )
2008-11-17 15:10:34 +03:00
q - > sg_timeout = clock_t_to_jiffies ( timeout ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2014-05-25 16:43:34 +04:00
static int max_sectors_bytes ( struct request_queue * q )
{
unsigned int max_sectors = queue_max_sectors ( q ) ;
max_sectors = min_t ( unsigned int , max_sectors , INT_MAX > > 9 ) ;
return max_sectors < < 9 ;
}
2007-07-24 11:28:11 +04:00
static int sg_get_reserved_size ( struct request_queue * q , int __user * p )
2005-04-17 02:20:36 +04:00
{
2014-05-25 16:43:34 +04:00
int val = min_t ( int , q - > sg_reserved_size , max_sectors_bytes ( q ) ) ;
2007-02-20 19:01:57 +03:00
return put_user ( val , p ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-24 11:28:11 +04:00
static int sg_set_reserved_size ( struct request_queue * q , int __user * p )
2005-04-17 02:20:36 +04:00
{
int size , err = get_user ( size , p ) ;
if ( err )
return err ;
if ( size < 0 )
return - EINVAL ;
2014-05-25 16:43:34 +04:00
q - > sg_reserved_size = min ( size , max_sectors_bytes ( q ) ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* will always return that we are ATAPI even for a real SCSI drive , I ' m not
* so sure this is worth doing anything about ( why would you care ? ? )
*/
2007-07-24 11:28:11 +04:00
static int sg_emulated_host ( struct request_queue * q , int __user * p )
2005-04-17 02:20:36 +04:00
{
return put_user ( 1 , p ) ;
}
2009-06-26 18:27:10 +04:00
static void blk_set_cmd_filter_defaults ( struct blk_cmd_filter * filter )
2008-08-16 09:10:05 +04:00
{
/* Basic read-only commands */
__set_bit ( TEST_UNIT_READY , filter - > read_ok ) ;
__set_bit ( REQUEST_SENSE , filter - > read_ok ) ;
__set_bit ( READ_6 , filter - > read_ok ) ;
__set_bit ( READ_10 , filter - > read_ok ) ;
__set_bit ( READ_12 , filter - > read_ok ) ;
__set_bit ( READ_16 , filter - > read_ok ) ;
__set_bit ( READ_BUFFER , filter - > read_ok ) ;
__set_bit ( READ_DEFECT_DATA , filter - > read_ok ) ;
__set_bit ( READ_CAPACITY , filter - > read_ok ) ;
__set_bit ( READ_LONG , filter - > read_ok ) ;
__set_bit ( INQUIRY , filter - > read_ok ) ;
__set_bit ( MODE_SENSE , filter - > read_ok ) ;
__set_bit ( MODE_SENSE_10 , filter - > read_ok ) ;
__set_bit ( LOG_SENSE , filter - > read_ok ) ;
__set_bit ( START_STOP , filter - > read_ok ) ;
__set_bit ( GPCMD_VERIFY_10 , filter - > read_ok ) ;
__set_bit ( VERIFY_16 , filter - > read_ok ) ;
__set_bit ( REPORT_LUNS , filter - > read_ok ) ;
2014-11-17 16:25:19 +03:00
__set_bit ( SERVICE_ACTION_IN_16 , filter - > read_ok ) ;
2008-08-16 09:10:05 +04:00
__set_bit ( RECEIVE_DIAGNOSTIC , filter - > read_ok ) ;
__set_bit ( MAINTENANCE_IN , filter - > read_ok ) ;
__set_bit ( GPCMD_READ_BUFFER_CAPACITY , filter - > read_ok ) ;
/* Audio CD commands */
__set_bit ( GPCMD_PLAY_CD , filter - > read_ok ) ;
__set_bit ( GPCMD_PLAY_AUDIO_10 , filter - > read_ok ) ;
__set_bit ( GPCMD_PLAY_AUDIO_MSF , filter - > read_ok ) ;
__set_bit ( GPCMD_PLAY_AUDIO_TI , filter - > read_ok ) ;
__set_bit ( GPCMD_PAUSE_RESUME , filter - > read_ok ) ;
/* CD/DVD data reading */
__set_bit ( GPCMD_READ_CD , filter - > read_ok ) ;
__set_bit ( GPCMD_READ_CD_MSF , filter - > read_ok ) ;
__set_bit ( GPCMD_READ_DISC_INFO , filter - > read_ok ) ;
__set_bit ( GPCMD_READ_CDVD_CAPACITY , filter - > read_ok ) ;
__set_bit ( GPCMD_READ_DVD_STRUCTURE , filter - > read_ok ) ;
__set_bit ( GPCMD_READ_HEADER , filter - > read_ok ) ;
__set_bit ( GPCMD_READ_TRACK_RZONE_INFO , filter - > read_ok ) ;
__set_bit ( GPCMD_READ_SUBCHANNEL , filter - > read_ok ) ;
__set_bit ( GPCMD_READ_TOC_PMA_ATIP , filter - > read_ok ) ;
__set_bit ( GPCMD_REPORT_KEY , filter - > read_ok ) ;
__set_bit ( GPCMD_SCAN , filter - > read_ok ) ;
__set_bit ( GPCMD_GET_CONFIGURATION , filter - > read_ok ) ;
__set_bit ( GPCMD_READ_FORMAT_CAPACITIES , filter - > read_ok ) ;
__set_bit ( GPCMD_GET_EVENT_STATUS_NOTIFICATION , filter - > read_ok ) ;
__set_bit ( GPCMD_GET_PERFORMANCE , filter - > read_ok ) ;
__set_bit ( GPCMD_SEEK , filter - > read_ok ) ;
__set_bit ( GPCMD_STOP_PLAY_SCAN , filter - > read_ok ) ;
/* Basic writing commands */
__set_bit ( WRITE_6 , filter - > write_ok ) ;
__set_bit ( WRITE_10 , filter - > write_ok ) ;
__set_bit ( WRITE_VERIFY , filter - > write_ok ) ;
__set_bit ( WRITE_12 , filter - > write_ok ) ;
__set_bit ( WRITE_VERIFY_12 , filter - > write_ok ) ;
__set_bit ( WRITE_16 , filter - > write_ok ) ;
__set_bit ( WRITE_LONG , filter - > write_ok ) ;
__set_bit ( WRITE_LONG_2 , filter - > write_ok ) ;
block: allow WRITE_SAME commands with the SG_IO ioctl
The WRITE_SAME commands are not present in the blk_default_cmd_filter
write_ok list, and thus are failed with -EPERM when the SG_IO ioctl()
is executed without CAP_SYS_RAWIO capability (e.g., unprivileged users).
[ sg_io() -> blk_fill_sghdr_rq() > blk_verify_command() -> -EPERM ]
The problem can be reproduced with the sg_write_same command
# sg_write_same --num 1 --xferlen 512 /dev/sda
#
# capsh --drop=cap_sys_rawio -- -c \
'sg_write_same --num 1 --xferlen 512 /dev/sda'
Write same: pass through os error: Operation not permitted
#
For comparison, the WRITE_VERIFY command does not observe this problem,
since it is in that list:
# capsh --drop=cap_sys_rawio -- -c \
'sg_write_verify --num 1 --ilen 512 --lba 0 /dev/sda'
#
So, this patch adds the WRITE_SAME commands to the list, in order
for the SG_IO ioctl to finish successfully:
# capsh --drop=cap_sys_rawio -- -c \
'sg_write_same --num 1 --xferlen 512 /dev/sda'
#
That case happens to be exercised by QEMU KVM guests with 'scsi-block' devices
(qemu "-device scsi-block" [1], libvirt "<disk type='block' device='lun'>" [2]),
which employs the SG_IO ioctl() and runs as an unprivileged user (libvirt-qemu).
In that scenario, when a filesystem (e.g., ext4) performs its zero-out calls,
which are translated to write-same calls in the guest kernel, and then into
SG_IO ioctls to the host kernel, SCSI I/O errors may be observed in the guest:
[...] sd 0:0:0:0: [sda] tag#0 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
[...] sd 0:0:0:0: [sda] tag#0 Sense Key : Aborted Command [current]
[...] sd 0:0:0:0: [sda] tag#0 Add. Sense: I/O process terminated
[...] sd 0:0:0:0: [sda] tag#0 CDB: Write Same(10) 41 00 01 04 e0 78 00 00 08 00
[...] blk_update_request: I/O error, dev sda, sector 17096824
Links:
[1] http://git.qemu.org/?p=qemu.git;a=commit;h=336a6915bc7089fb20fea4ba99972ad9a97c5f52
[2] https://libvirt.org/formatdomain.html#elementsDisks (see 'disk' -> 'device')
Signed-off-by: Mauricio Faria de Oliveira <mauricfo@linux.vnet.ibm.com>
Signed-off-by: Brahadambal Srinivasan <latha@linux.vnet.ibm.com>
Reported-by: Manjunatha H R <manjuhr1@in.ibm.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <axboe@fb.com>
2016-12-15 20:48:18 +03:00
__set_bit ( WRITE_SAME , filter - > write_ok ) ;
__set_bit ( WRITE_SAME_16 , filter - > write_ok ) ;
__set_bit ( WRITE_SAME_32 , filter - > write_ok ) ;
2008-08-16 09:10:05 +04:00
__set_bit ( ERASE , filter - > write_ok ) ;
__set_bit ( GPCMD_MODE_SELECT_10 , filter - > write_ok ) ;
__set_bit ( MODE_SELECT , filter - > write_ok ) ;
__set_bit ( LOG_SELECT , filter - > write_ok ) ;
__set_bit ( GPCMD_BLANK , filter - > write_ok ) ;
__set_bit ( GPCMD_CLOSE_TRACK , filter - > write_ok ) ;
__set_bit ( GPCMD_FLUSH_CACHE , filter - > write_ok ) ;
__set_bit ( GPCMD_FORMAT_UNIT , filter - > write_ok ) ;
__set_bit ( GPCMD_REPAIR_RZONE_TRACK , filter - > write_ok ) ;
__set_bit ( GPCMD_RESERVE_RZONE_TRACK , filter - > write_ok ) ;
__set_bit ( GPCMD_SEND_DVD_STRUCTURE , filter - > write_ok ) ;
__set_bit ( GPCMD_SEND_EVENT , filter - > write_ok ) ;
__set_bit ( GPCMD_SEND_KEY , filter - > write_ok ) ;
__set_bit ( GPCMD_SEND_OPC , filter - > write_ok ) ;
__set_bit ( GPCMD_SEND_CUE_SHEET , filter - > write_ok ) ;
__set_bit ( GPCMD_SET_SPEED , filter - > write_ok ) ;
__set_bit ( GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL , filter - > write_ok ) ;
__set_bit ( GPCMD_LOAD_UNLOAD , filter - > write_ok ) ;
__set_bit ( GPCMD_SET_STREAMING , filter - > write_ok ) ;
2008-08-22 13:12:21 +04:00
__set_bit ( GPCMD_SET_READ_AHEAD , filter - > write_ok ) ;
2020-02-26 20:05:19 +03:00
/* ZBC Commands */
__set_bit ( ZBC_OUT , filter - > write_ok ) ;
__set_bit ( ZBC_IN , filter - > read_ok ) ;
2008-08-16 09:10:05 +04:00
}
2009-06-26 18:27:10 +04:00
2017-11-05 10:36:31 +03:00
int blk_verify_command ( unsigned char * cmd , fmode_t mode )
2009-06-26 18:27:10 +04:00
{
struct blk_cmd_filter * filter = & blk_default_cmd_filter ;
/* root can do any command. */
if ( capable ( CAP_SYS_RAWIO ) )
return 0 ;
/* Anybody who can open the device can do a read-safe command */
if ( test_bit ( cmd [ 0 ] , filter - > read_ok ) )
return 0 ;
/* Write-safe commands require a writable open */
2017-11-05 10:36:31 +03:00
if ( test_bit ( cmd [ 0 ] , filter - > write_ok ) & & ( mode & FMODE_WRITE ) )
2009-06-26 18:27:10 +04:00
return 0 ;
return - EPERM ;
}
EXPORT_SYMBOL ( blk_verify_command ) ;
2008-08-16 09:10:05 +04:00
2007-07-24 11:28:11 +04:00
static int blk_fill_sghdr_rq ( struct request_queue * q , struct request * rq ,
2008-09-03 00:35:55 +04:00
struct sg_io_hdr * hdr , fmode_t mode )
2007-07-09 14:38:05 +04:00
{
2017-01-27 11:46:29 +03:00
struct scsi_request * req = scsi_req ( rq ) ;
if ( copy_from_user ( req - > cmd , hdr - > cmdp , hdr - > cmd_len ) )
2007-07-09 14:38:05 +04:00
return - EFAULT ;
2017-11-05 10:36:31 +03:00
if ( blk_verify_command ( req - > cmd , mode ) )
2007-07-09 14:38:05 +04:00
return - EPERM ;
/*
* fill in request structure
*/
2017-01-27 11:46:29 +03:00
req - > cmd_len = hdr - > cmd_len ;
2007-07-09 14:38:05 +04:00
2007-12-05 23:28:24 +03:00
rq - > timeout = msecs_to_jiffies ( hdr - > timeout ) ;
2007-07-09 14:38:05 +04:00
if ( ! rq - > timeout )
rq - > timeout = q - > sg_timeout ;
if ( ! rq - > timeout )
rq - > timeout = BLK_DEFAULT_SG_TIMEOUT ;
2008-12-06 01:49:18 +03:00
if ( rq - > timeout < BLK_MIN_SG_TIMEOUT )
rq - > timeout = BLK_MIN_SG_TIMEOUT ;
2007-07-09 14:38:05 +04:00
return 0 ;
}
2007-07-22 05:06:50 +04:00
static int blk_complete_sghdr_rq ( struct request * rq , struct sg_io_hdr * hdr ,
struct bio * bio )
2007-07-09 14:38:05 +04:00
{
2017-01-27 11:46:29 +03:00
struct scsi_request * req = scsi_req ( rq ) ;
2009-04-13 22:03:10 +04:00
int r , ret = 0 ;
2007-07-09 14:38:05 +04:00
/*
* fill in all the output members
*/
2017-04-20 17:03:01 +03:00
hdr - > status = req - > result & 0xff ;
hdr - > masked_status = status_byte ( req - > result ) ;
hdr - > msg_status = msg_byte ( req - > result ) ;
hdr - > host_status = host_byte ( req - > result ) ;
hdr - > driver_status = driver_byte ( req - > result ) ;
2007-07-09 14:38:05 +04:00
hdr - > info = 0 ;
if ( hdr - > masked_status | | hdr - > host_status | | hdr - > driver_status )
hdr - > info | = SG_INFO_CHECK ;
2017-01-27 11:46:29 +03:00
hdr - > resid = req - > resid_len ;
2007-07-09 14:38:05 +04:00
hdr - > sb_len_wr = 0 ;
2017-01-27 11:46:29 +03:00
if ( req - > sense_len & & hdr - > sbp ) {
int len = min ( ( unsigned int ) hdr - > mx_sb_len , req - > sense_len ) ;
2007-07-09 14:38:05 +04:00
2017-01-27 11:46:29 +03:00
if ( ! copy_to_user ( hdr - > sbp , req - > sense , len ) )
2007-07-09 14:38:05 +04:00
hdr - > sb_len_wr = len ;
else
ret = - EFAULT ;
}
2009-04-13 22:03:10 +04:00
r = blk_rq_unmap_user ( bio ) ;
if ( ! ret )
ret = r ;
2007-07-09 14:38:05 +04:00
2009-03-24 14:35:07 +03:00
return ret ;
2007-07-09 14:38:05 +04:00
}
2008-09-03 00:35:55 +04:00
static int sg_io ( struct request_queue * q , struct gendisk * bd_disk ,
struct sg_io_hdr * hdr , fmode_t mode )
2005-04-17 02:20:36 +04:00
{
2007-07-09 14:38:05 +04:00
unsigned long start_time ;
2013-08-08 01:20:17 +04:00
ssize_t ret = 0 ;
int writing = 0 ;
2014-07-01 20:48:05 +04:00
int at_head = 0 ;
2005-04-17 02:20:36 +04:00
struct request * rq ;
2017-01-27 11:46:29 +03:00
struct scsi_request * req ;
2006-12-11 12:01:34 +03:00
struct bio * bio ;
2005-04-17 02:20:36 +04:00
if ( hdr - > interface_id ! = ' S ' )
return - EINVAL ;
2009-05-23 01:17:50 +04:00
if ( hdr - > dxfer_len > ( queue_max_hw_sectors ( q ) < < 9 ) )
2005-04-17 02:20:36 +04:00
return - EIO ;
2005-06-20 16:06:52 +04:00
if ( hdr - > dxfer_len )
2005-04-17 02:20:36 +04:00
switch ( hdr - > dxfer_direction ) {
default :
return - EINVAL ;
case SG_DXFER_TO_DEV :
writing = 1 ;
break ;
2006-11-13 20:04:59 +03:00
case SG_DXFER_TO_FROM_DEV :
2005-04-17 02:20:36 +04:00
case SG_DXFER_FROM_DEV :
break ;
}
2014-07-01 20:48:05 +04:00
if ( hdr - > flags & SG_FLAG_Q_AT_HEAD )
at_head = 1 ;
2005-04-17 02:20:36 +04:00
2014-08-22 05:39:53 +04:00
ret = - ENOMEM ;
2018-05-09 10:54:05 +03:00
rq = blk_get_request ( q , writing ? REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN , 0 ) ;
2014-08-28 18:15:21 +04:00
if ( IS_ERR ( rq ) )
return PTR_ERR ( rq ) ;
2017-01-27 11:46:29 +03:00
req = scsi_req ( rq ) ;
2005-04-17 02:20:36 +04:00
2014-08-22 05:39:53 +04:00
if ( hdr - > cmd_len > BLK_MAX_CDB ) {
2017-01-27 11:46:29 +03:00
req - > cmd = kzalloc ( hdr - > cmd_len , GFP_KERNEL ) ;
if ( ! req - > cmd )
2014-08-22 05:39:53 +04:00
goto out_put_request ;
2007-07-09 14:38:05 +04:00
}
2005-04-17 02:20:36 +04:00
2015-06-26 12:44:46 +03:00
ret = blk_fill_sghdr_rq ( q , rq , hdr , mode ) ;
if ( ret < 0 )
2014-08-22 05:39:53 +04:00
goto out_free_cdb ;
2005-04-17 02:20:36 +04:00
2014-08-26 18:14:02 +04:00
ret = 0 ;
2006-12-01 12:40:55 +03:00
if ( hdr - > iovec_count ) {
2015-01-18 18:16:31 +03:00
struct iov_iter i ;
2014-01-19 05:08:49 +04:00
struct iovec * iov = NULL ;
2006-12-01 12:40:55 +03:00
2020-09-25 07:51:41 +03:00
ret = import_iovec ( rq_data_dir ( rq ) , hdr - > dxferp ,
hdr - > iovec_count , 0 , & iov , & i ) ;
2015-03-22 03:02:55 +03:00
if ( ret < 0 )
2014-08-22 05:39:53 +04:00
goto out_free_cdb ;
2006-12-01 12:40:55 +03:00
2009-04-15 17:10:24 +04:00
/* SG_IO howto says that the shorter of the two wins */
2015-03-22 03:02:55 +03:00
iov_iter_truncate ( & i , hdr - > dxfer_len ) ;
2009-04-15 17:10:24 +04:00
2015-01-18 18:16:31 +03:00
ret = blk_rq_map_user_iov ( q , rq , NULL , & i , GFP_KERNEL ) ;
2013-08-08 01:20:17 +04:00
kfree ( iov ) ;
2006-12-01 12:40:55 +03:00
} else if ( hdr - > dxfer_len )
2008-08-28 11:17:06 +04:00
ret = blk_rq_map_user ( q , rq , NULL , hdr - > dxferp , hdr - > dxfer_len ,
2008-08-28 11:17:05 +04:00
GFP_KERNEL ) ;
2006-12-01 12:40:55 +03:00
if ( ret )
2014-08-22 05:39:53 +04:00
goto out_free_cdb ;
2006-12-01 12:40:55 +03:00
2006-12-11 12:01:34 +03:00
bio = rq - > bio ;
2017-04-05 20:18:12 +03:00
req - > retries = 0 ;
2006-02-03 10:37:08 +03:00
2005-04-17 02:20:36 +04:00
start_time = jiffies ;
2021-01-25 07:49:58 +03:00
blk_execute_rq ( bd_disk , rq , at_head ) ;
2005-04-17 02:20:36 +04:00
2007-12-05 23:28:24 +03:00
hdr - > duration = jiffies_to_msecs ( jiffies - start_time ) ;
2005-04-17 02:20:36 +04:00
2014-08-22 05:38:27 +04:00
ret = blk_complete_sghdr_rq ( rq , hdr , bio ) ;
2014-08-22 05:39:53 +04:00
out_free_cdb :
2017-01-27 11:46:29 +03:00
scsi_req_free_cmd ( req ) ;
2014-08-22 05:39:53 +04:00
out_put_request :
2005-06-20 16:06:01 +04:00
blk_put_request ( rq ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2006-03-22 19:52:04 +03:00
/**
* sg_scsi_ioctl - - handle deprecated SCSI_IOCTL_SEND_COMMAND ioctl
* @ q : request queue to send scsi commands down
* @ disk : gendisk to operate on ( option )
2018-01-09 21:11:00 +03:00
* @ mode : mode used to open the file through which the ioctl has been
* submitted
2006-03-22 19:52:04 +03:00
* @ sic : userspace structure describing the command to perform
*
* Send down the scsi command described by @ sic to the device below
* the request queue @ q . If @ file is non - NULL it ' s used to perform
* fine - grained permission checks that allow users to send down
* non - destructive SCSI commands . If the caller has a struct gendisk
* available it should be passed in as @ disk to allow the low level
* driver to use the information contained in it . A non - NULL @ disk
* is only allowed if the caller knows that the low level driver doesn ' t
* need it ( e . g . in the scsi subsystem ) .
*
* Notes :
* - This interface is deprecated - users should use the SG_IO
* interface instead , as this is a more flexible approach to
* performing SCSI commands on a device .
* - The SCSI command length is determined by examining the 1 st byte
* of the given command . There is no way to override this .
* - Data transfers are limited to PAGE_SIZE
* - The length ( x + y ) must be at least OMAX_SB_LEN bytes long to
* accommodate the sense buffer when an error occurs .
* The sense buffer is truncated to OMAX_SB_LEN ( 16 ) bytes so that
* old code will not be surprised .
* - If a Unix error occurs ( e . g . ENOMEM ) then the user will receive
* a negative return and the Unix error code in ' errno ' .
* If the SCSI command succeeds then 0 is returned .
* Positive numbers returned are the compacted SCSI error codes ( 4
* bytes in one int ) where the lowest byte is the SCSI status .
*/
2008-09-03 01:16:41 +04:00
int sg_scsi_ioctl ( struct request_queue * q , struct gendisk * disk , fmode_t mode ,
struct scsi_ioctl_command __user * sic )
2005-04-17 02:20:36 +04:00
{
2018-01-09 21:11:00 +03:00
enum { OMAX_SB_LEN = 16 } ; /* For backward compatibility */
2005-04-17 02:20:36 +04:00
struct request * rq ;
2017-01-27 11:46:29 +03:00
struct scsi_request * req ;
2008-09-02 23:28:45 +04:00
int err ;
2005-04-17 02:20:36 +04:00
unsigned int in_len , out_len , bytes , opcode , cmdlen ;
2017-01-27 11:46:29 +03:00
char * buffer = NULL ;
2005-04-17 02:20:36 +04:00
2006-03-22 19:52:04 +03:00
if ( ! sic )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
/*
* get in an out lengths , verify they don ' t exceed a page worth of data
*/
if ( get_user ( in_len , & sic - > inlen ) )
return - EFAULT ;
if ( get_user ( out_len , & sic - > outlen ) )
return - EFAULT ;
if ( in_len > PAGE_SIZE | | out_len > PAGE_SIZE )
return - EINVAL ;
if ( get_user ( opcode , sic - > data ) )
return - EFAULT ;
bytes = max ( in_len , out_len ) ;
if ( bytes ) {
2021-03-31 10:29:59 +03:00
buffer = kzalloc ( bytes , GFP_NOIO | GFP_USER | __GFP_NOWARN ) ;
2005-04-17 02:20:36 +04:00
if ( ! buffer )
return - ENOMEM ;
}
2018-05-09 10:54:05 +03:00
rq = blk_get_request ( q , in_len ? REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN , 0 ) ;
2014-08-28 18:15:21 +04:00
if ( IS_ERR ( rq ) ) {
err = PTR_ERR ( rq ) ;
2014-11-11 01:40:02 +03:00
goto error_free_buffer ;
2014-07-02 23:35:16 +04:00
}
2017-01-27 11:46:29 +03:00
req = scsi_req ( rq ) ;
2005-04-17 02:20:36 +04:00
cmdlen = COMMAND_SIZE ( opcode ) ;
/*
* get command and data to send to device , if any
*/
err = - EFAULT ;
2017-01-27 11:46:29 +03:00
req - > cmd_len = cmdlen ;
if ( copy_from_user ( req - > cmd , sic - > data , cmdlen ) )
2005-04-17 02:20:36 +04:00
goto error ;
2006-03-22 19:52:04 +03:00
if ( in_len & & copy_from_user ( buffer , sic - > data + cmdlen , in_len ) )
2005-04-17 02:20:36 +04:00
goto error ;
2017-11-05 10:36:31 +03:00
err = blk_verify_command ( req - > cmd , mode ) ;
2005-04-17 02:20:36 +04:00
if ( err )
goto error ;
2006-03-22 19:52:04 +03:00
/* default. possible overriden later */
2017-04-05 20:18:12 +03:00
req - > retries = 5 ;
2006-03-22 19:52:04 +03:00
2005-04-17 02:20:36 +04:00
switch ( opcode ) {
2006-03-22 19:52:04 +03:00
case SEND_DIAGNOSTIC :
case FORMAT_UNIT :
rq - > timeout = FORMAT_UNIT_TIMEOUT ;
2017-04-05 20:18:12 +03:00
req - > retries = 1 ;
2006-03-22 19:52:04 +03:00
break ;
case START_STOP :
rq - > timeout = START_STOP_TIMEOUT ;
break ;
case MOVE_MEDIUM :
rq - > timeout = MOVE_MEDIUM_TIMEOUT ;
break ;
case READ_ELEMENT_STATUS :
rq - > timeout = READ_ELEMENT_STATUS_TIMEOUT ;
break ;
case READ_DEFECT_DATA :
rq - > timeout = READ_DEFECT_DATA_TIMEOUT ;
2017-04-05 20:18:12 +03:00
req - > retries = 1 ;
2006-03-22 19:52:04 +03:00
break ;
default :
2007-07-09 14:38:05 +04:00
rq - > timeout = BLK_DEFAULT_SG_TIMEOUT ;
2006-03-22 19:52:04 +03:00
break ;
}
2018-05-09 10:54:08 +03:00
if ( bytes & & blk_rq_map_kern ( q , rq , buffer , bytes , GFP_NOIO ) ) {
2006-03-22 19:52:04 +03:00
err = DRIVER_ERROR < < 24 ;
2014-10-23 06:13:39 +04:00
goto error ;
2005-04-17 02:20:36 +04:00
}
2021-01-25 07:49:58 +03:00
blk_execute_rq ( disk , rq , 0 ) ;
2006-03-22 19:52:04 +03:00
2017-04-20 17:03:01 +03:00
err = req - > result & 0xff ; /* only 8 bit SCSI status */
2005-04-17 02:20:36 +04:00
if ( err ) {
2017-01-27 11:46:29 +03:00
if ( req - > sense_len & & req - > sense ) {
bytes = ( OMAX_SB_LEN > req - > sense_len ) ?
req - > sense_len : OMAX_SB_LEN ;
if ( copy_to_user ( sic - > data , req - > sense , bytes ) )
2005-04-17 02:20:36 +04:00
err = - EFAULT ;
}
} else {
if ( copy_to_user ( sic - > data , buffer , out_len ) )
err = - EFAULT ;
}
error :
2014-11-11 01:40:02 +03:00
blk_put_request ( rq ) ;
error_free_buffer :
2014-07-02 23:35:16 +04:00
kfree ( buffer ) ;
2014-11-11 01:40:02 +03:00
2005-04-17 02:20:36 +04:00
return err ;
}
2006-03-22 19:52:04 +03:00
EXPORT_SYMBOL_GPL ( sg_scsi_ioctl ) ;
2005-12-19 22:49:24 +03:00
/* Send basic block requests */
2007-07-24 11:28:11 +04:00
static int __blk_send_generic ( struct request_queue * q , struct gendisk * bd_disk ,
int cmd , int data )
2005-12-19 22:49:24 +03:00
{
struct request * rq ;
int err ;
2018-05-09 10:54:05 +03:00
rq = blk_get_request ( q , REQ_OP_SCSI_OUT , 0 ) ;
2014-08-28 18:15:21 +04:00
if ( IS_ERR ( rq ) )
return PTR_ERR ( rq ) ;
2007-07-09 14:38:05 +04:00
rq - > timeout = BLK_DEFAULT_SG_TIMEOUT ;
2017-01-27 11:46:29 +03:00
scsi_req ( rq ) - > cmd [ 0 ] = cmd ;
scsi_req ( rq ) - > cmd [ 4 ] = data ;
scsi_req ( rq ) - > cmd_len = 6 ;
2021-01-25 07:49:58 +03:00
blk_execute_rq ( bd_disk , rq , 0 ) ;
2017-04-20 17:03:01 +03:00
err = scsi_req ( rq ) - > result ? - EIO : 0 ;
2005-12-19 22:49:24 +03:00
blk_put_request ( rq ) ;
return err ;
}
2007-07-24 11:28:11 +04:00
static inline int blk_send_start_stop ( struct request_queue * q ,
struct gendisk * bd_disk , int data )
2005-12-19 22:49:24 +03:00
{
return __blk_send_generic ( q , bd_disk , GPCMD_START_STOP_UNIT , data ) ;
}
2019-03-14 19:45:18 +03:00
int put_sg_io_hdr ( const struct sg_io_hdr * hdr , void __user * argp )
{
# ifdef CONFIG_COMPAT
if ( in_compat_syscall ( ) ) {
struct compat_sg_io_hdr hdr32 = {
. interface_id = hdr - > interface_id ,
. dxfer_direction = hdr - > dxfer_direction ,
. cmd_len = hdr - > cmd_len ,
. mx_sb_len = hdr - > mx_sb_len ,
. iovec_count = hdr - > iovec_count ,
. dxfer_len = hdr - > dxfer_len ,
. dxferp = ( uintptr_t ) hdr - > dxferp ,
. cmdp = ( uintptr_t ) hdr - > cmdp ,
. sbp = ( uintptr_t ) hdr - > sbp ,
. timeout = hdr - > timeout ,
. flags = hdr - > flags ,
. pack_id = hdr - > pack_id ,
. usr_ptr = ( uintptr_t ) hdr - > usr_ptr ,
. status = hdr - > status ,
. masked_status = hdr - > masked_status ,
. msg_status = hdr - > msg_status ,
. sb_len_wr = hdr - > sb_len_wr ,
. host_status = hdr - > host_status ,
. driver_status = hdr - > driver_status ,
. resid = hdr - > resid ,
. duration = hdr - > duration ,
. info = hdr - > info ,
} ;
if ( copy_to_user ( argp , & hdr32 , sizeof ( hdr32 ) ) )
return - EFAULT ;
return 0 ;
}
# endif
if ( copy_to_user ( argp , hdr , sizeof ( * hdr ) ) )
return - EFAULT ;
return 0 ;
}
EXPORT_SYMBOL ( put_sg_io_hdr ) ;
int get_sg_io_hdr ( struct sg_io_hdr * hdr , const void __user * argp )
{
# ifdef CONFIG_COMPAT
struct compat_sg_io_hdr hdr32 ;
if ( in_compat_syscall ( ) ) {
if ( copy_from_user ( & hdr32 , argp , sizeof ( hdr32 ) ) )
return - EFAULT ;
* hdr = ( struct sg_io_hdr ) {
. interface_id = hdr32 . interface_id ,
. dxfer_direction = hdr32 . dxfer_direction ,
. cmd_len = hdr32 . cmd_len ,
. mx_sb_len = hdr32 . mx_sb_len ,
. iovec_count = hdr32 . iovec_count ,
. dxfer_len = hdr32 . dxfer_len ,
. dxferp = compat_ptr ( hdr32 . dxferp ) ,
. cmdp = compat_ptr ( hdr32 . cmdp ) ,
. sbp = compat_ptr ( hdr32 . sbp ) ,
. timeout = hdr32 . timeout ,
. flags = hdr32 . flags ,
. pack_id = hdr32 . pack_id ,
. usr_ptr = compat_ptr ( hdr32 . usr_ptr ) ,
. status = hdr32 . status ,
. masked_status = hdr32 . masked_status ,
. msg_status = hdr32 . msg_status ,
. sb_len_wr = hdr32 . sb_len_wr ,
. host_status = hdr32 . host_status ,
. driver_status = hdr32 . driver_status ,
. resid = hdr32 . resid ,
. duration = hdr32 . duration ,
. info = hdr32 . info ,
} ;
return 0 ;
}
# endif
if ( copy_from_user ( hdr , argp , sizeof ( * hdr ) ) )
return - EFAULT ;
return 0 ;
}
EXPORT_SYMBOL ( get_sg_io_hdr ) ;
2019-11-28 15:44:48 +03:00
# ifdef CONFIG_COMPAT
struct compat_cdrom_generic_command {
unsigned char cmd [ CDROM_PACKET_SIZE ] ;
compat_caddr_t buffer ;
compat_uint_t buflen ;
compat_int_t stat ;
compat_caddr_t sense ;
unsigned char data_direction ;
2020-10-02 17:22:23 +03:00
unsigned char pad [ 3 ] ;
2019-11-28 15:44:48 +03:00
compat_int_t quiet ;
compat_int_t timeout ;
2020-10-03 02:10:33 +03:00
compat_caddr_t unused ;
2019-11-28 15:44:48 +03:00
} ;
# endif
static int scsi_get_cdrom_generic_arg ( struct cdrom_generic_command * cgc ,
const void __user * arg )
{
# ifdef CONFIG_COMPAT
if ( in_compat_syscall ( ) ) {
struct compat_cdrom_generic_command cgc32 ;
if ( copy_from_user ( & cgc32 , arg , sizeof ( cgc32 ) ) )
return - EFAULT ;
* cgc = ( struct cdrom_generic_command ) {
. buffer = compat_ptr ( cgc32 . buffer ) ,
. buflen = cgc32 . buflen ,
. stat = cgc32 . stat ,
. sense = compat_ptr ( cgc32 . sense ) ,
. data_direction = cgc32 . data_direction ,
. quiet = cgc32 . quiet ,
. timeout = cgc32 . timeout ,
2020-10-03 02:10:33 +03:00
. unused = compat_ptr ( cgc32 . unused ) ,
2019-11-28 15:44:48 +03:00
} ;
memcpy ( & cgc - > cmd , & cgc32 . cmd , CDROM_PACKET_SIZE ) ;
return 0 ;
}
# endif
if ( copy_from_user ( cgc , arg , sizeof ( * cgc ) ) )
return - EFAULT ;
return 0 ;
}
static int scsi_put_cdrom_generic_arg ( const struct cdrom_generic_command * cgc ,
void __user * arg )
{
# ifdef CONFIG_COMPAT
if ( in_compat_syscall ( ) ) {
struct compat_cdrom_generic_command cgc32 = {
. buffer = ( uintptr_t ) ( cgc - > buffer ) ,
. buflen = cgc - > buflen ,
. stat = cgc - > stat ,
. sense = ( uintptr_t ) ( cgc - > sense ) ,
. data_direction = cgc - > data_direction ,
. quiet = cgc - > quiet ,
. timeout = cgc - > timeout ,
2020-10-03 02:10:33 +03:00
. unused = ( uintptr_t ) ( cgc - > unused ) ,
2019-11-28 15:44:48 +03:00
} ;
memcpy ( & cgc32 . cmd , & cgc - > cmd , CDROM_PACKET_SIZE ) ;
if ( copy_to_user ( arg , & cgc32 , sizeof ( cgc32 ) ) )
return - EFAULT ;
return 0 ;
}
# endif
if ( copy_to_user ( arg , cgc , sizeof ( * cgc ) ) )
return - EFAULT ;
return 0 ;
}
static int scsi_cdrom_send_packet ( struct request_queue * q ,
struct gendisk * bd_disk ,
fmode_t mode , void __user * arg )
{
struct cdrom_generic_command cgc ;
struct sg_io_hdr hdr ;
int err ;
err = scsi_get_cdrom_generic_arg ( & cgc , arg ) ;
if ( err )
return err ;
cgc . timeout = clock_t_to_jiffies ( cgc . timeout ) ;
memset ( & hdr , 0 , sizeof ( hdr ) ) ;
hdr . interface_id = ' S ' ;
hdr . cmd_len = sizeof ( cgc . cmd ) ;
hdr . dxfer_len = cgc . buflen ;
switch ( cgc . data_direction ) {
case CGC_DATA_UNKNOWN :
hdr . dxfer_direction = SG_DXFER_UNKNOWN ;
break ;
case CGC_DATA_WRITE :
hdr . dxfer_direction = SG_DXFER_TO_DEV ;
break ;
case CGC_DATA_READ :
hdr . dxfer_direction = SG_DXFER_FROM_DEV ;
break ;
case CGC_DATA_NONE :
hdr . dxfer_direction = SG_DXFER_NONE ;
break ;
default :
return - EINVAL ;
}
hdr . dxferp = cgc . buffer ;
hdr . sbp = cgc . sense ;
if ( hdr . sbp )
hdr . mx_sb_len = sizeof ( struct request_sense ) ;
hdr . timeout = jiffies_to_msecs ( cgc . timeout ) ;
hdr . cmdp = ( ( struct cdrom_generic_command __user * ) arg ) - > cmd ;
hdr . cmd_len = sizeof ( cgc . cmd ) ;
err = sg_io ( q , bd_disk , & hdr , mode ) ;
if ( err = = - EFAULT )
return - EFAULT ;
if ( hdr . status )
return - EIO ;
cgc . stat = err ;
cgc . buflen = hdr . resid ;
if ( scsi_put_cdrom_generic_arg ( & cgc , arg ) )
return - EFAULT ;
return err ;
}
2007-08-27 23:38:10 +04:00
int scsi_cmd_ioctl ( struct request_queue * q , struct gendisk * bd_disk , fmode_t mode ,
unsigned int cmd , void __user * arg )
2005-04-17 02:20:36 +04:00
{
2005-12-19 22:49:24 +03:00
int err ;
2005-04-17 02:20:36 +04:00
2011-10-19 16:31:25 +04:00
if ( ! q )
2005-04-17 02:20:36 +04:00
return - ENXIO ;
switch ( cmd ) {
/*
* new sgv3 interface
*/
case SG_GET_VERSION_NUM :
err = sg_get_version ( arg ) ;
break ;
case SCSI_IOCTL_GET_IDLUN :
err = scsi_get_idlun ( q , arg ) ;
break ;
case SCSI_IOCTL_GET_BUS_NUMBER :
err = scsi_get_bus ( q , arg ) ;
break ;
case SG_SET_TIMEOUT :
err = sg_set_timeout ( q , arg ) ;
break ;
case SG_GET_TIMEOUT :
err = sg_get_timeout ( q ) ;
break ;
case SG_GET_RESERVED_SIZE :
err = sg_get_reserved_size ( q , arg ) ;
break ;
case SG_SET_RESERVED_SIZE :
err = sg_set_reserved_size ( q , arg ) ;
break ;
case SG_EMULATED_HOST :
err = sg_emulated_host ( q , arg ) ;
break ;
case SG_IO : {
struct sg_io_hdr hdr ;
2019-03-14 19:45:18 +03:00
err = get_sg_io_hdr ( & hdr , arg ) ;
if ( err )
2005-04-17 02:20:36 +04:00
break ;
2007-08-27 23:38:10 +04:00
err = sg_io ( q , bd_disk , & hdr , mode ) ;
2005-04-17 02:20:36 +04:00
if ( err = = - EFAULT )
break ;
2019-03-14 19:45:18 +03:00
if ( put_sg_io_hdr ( & hdr , arg ) )
2005-04-17 02:20:36 +04:00
err = - EFAULT ;
break ;
}
2019-11-28 15:44:48 +03:00
case CDROM_SEND_PACKET :
err = scsi_cdrom_send_packet ( q , bd_disk , mode , arg ) ;
2005-04-17 02:20:36 +04:00
break ;
/*
* old junk scsi send command ioctl
*/
case SCSI_IOCTL_SEND_COMMAND :
printk ( KERN_WARNING " program %s is using a deprecated SCSI ioctl, please convert it to SG_IO \n " , current - > comm ) ;
err = - EINVAL ;
if ( ! arg )
break ;
2007-08-27 23:38:10 +04:00
err = sg_scsi_ioctl ( q , bd_disk , mode , arg ) ;
2005-04-17 02:20:36 +04:00
break ;
case CDROMCLOSETRAY :
2005-12-19 22:49:24 +03:00
err = blk_send_start_stop ( q , bd_disk , 0x03 ) ;
break ;
2005-04-17 02:20:36 +04:00
case CDROMEJECT :
2005-12-19 22:49:24 +03:00
err = blk_send_start_stop ( q , bd_disk , 0x02 ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
err = - ENOTTY ;
}
return err ;
}
EXPORT_SYMBOL ( scsi_cmd_ioctl ) ;
2009-06-26 18:27:10 +04:00
2012-01-12 19:01:28 +04:00
int scsi_verify_blk_ioctl ( struct block_device * bd , unsigned int cmd )
{
2020-09-03 08:40:57 +03:00
if ( bd & & ! bdev_is_partition ( bd ) )
2012-01-12 19:01:28 +04:00
return 0 ;
2012-06-15 14:52:46 +04:00
if ( capable ( CAP_SYS_RAWIO ) )
return 0 ;
return - ENOIOCTLCMD ;
2012-01-12 19:01:28 +04:00
}
EXPORT_SYMBOL ( scsi_verify_blk_ioctl ) ;
2012-01-12 19:01:27 +04:00
int scsi_cmd_blk_ioctl ( struct block_device * bd , fmode_t mode ,
unsigned int cmd , void __user * arg )
{
2012-01-12 19:01:28 +04:00
int ret ;
ret = scsi_verify_blk_ioctl ( bd , cmd ) ;
if ( ret < 0 )
return ret ;
2012-01-12 19:01:27 +04:00
return scsi_cmd_ioctl ( bd - > bd_disk - > queue , bd - > bd_disk , mode , cmd , arg ) ;
}
EXPORT_SYMBOL ( scsi_cmd_blk_ioctl ) ;
2017-06-20 21:15:42 +03:00
/**
* scsi_req_init - initialize certain fields of a scsi_request structure
* @ req : Pointer to a scsi_request structure .
* Initializes . __cmd [ ] , . cmd , . cmd_len and . sense_len but no other members
* of struct scsi_request .
*/
void scsi_req_init ( struct scsi_request * req )
2017-01-27 11:46:29 +03:00
{
memset ( req - > __cmd , 0 , sizeof ( req - > __cmd ) ) ;
req - > cmd = req - > __cmd ;
req - > cmd_len = BLK_MAX_CDB ;
req - > sense_len = 0 ;
}
EXPORT_SYMBOL ( scsi_req_init ) ;
2009-11-04 11:10:33 +03:00
static int __init blk_scsi_ioctl_init ( void )
2009-06-26 18:27:10 +04:00
{
blk_set_cmd_filter_defaults ( & blk_default_cmd_filter ) ;
return 0 ;
}
2009-07-09 11:48:28 +04:00
fs_initcall ( blk_scsi_ioctl_init ) ;