2008-06-26 15:48:27 +04:00
/*
* Copyright 2004 Peter M . Jones < pjones @ redhat . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
*
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public Licens
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 -
*
*/
# include <linux/list.h>
# include <linux/genhd.h>
# include <linux/spinlock.h>
# include <linux/capability.h>
# include <linux/bitops.h>
# include <scsi/scsi.h>
# include <linux/cdrom.h>
2008-07-26 13:03:25 +04:00
int blk_verify_command ( struct blk_cmd_filter * filter ,
2008-08-16 09:10:05 +04:00
unsigned char * cmd , int has_write_perm )
2008-06-26 15:48:27 +04:00
{
/* root can do any command. */
if ( capable ( CAP_SYS_RAWIO ) )
return 0 ;
/* if there's no filter set, assume we're filtering everything out */
if ( ! filter )
return - EPERM ;
/* 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 */
2008-08-16 09:10:05 +04:00
if ( test_bit ( cmd [ 0 ] , filter - > write_ok ) & & has_write_perm )
2008-06-26 15:48:27 +04:00
return 0 ;
return - EPERM ;
}
EXPORT_SYMBOL ( blk_verify_command ) ;
2008-09-11 16:20:23 +04:00
#if 0
2008-06-26 15:48:27 +04:00
/* and now, the sysfs stuff */
2008-07-26 13:03:25 +04:00
static ssize_t rcf_cmds_show ( struct blk_cmd_filter * filter , char * page ,
2008-06-26 15:48:27 +04:00
int rw )
{
char * npage = page ;
unsigned long * okbits ;
int i ;
if ( rw = = READ )
okbits = filter - > read_ok ;
else
okbits = filter - > write_ok ;
for ( i = 0 ; i < BLK_SCSI_MAX_CMDS ; i + + ) {
if ( test_bit ( i , okbits ) ) {
2008-08-16 09:21:06 +04:00
npage + = sprintf ( npage , " 0x%02x " , i ) ;
2008-06-26 15:48:27 +04:00
if ( i < BLK_SCSI_MAX_CMDS - 1 )
sprintf ( npage + + , " " ) ;
}
}
if ( npage ! = page )
npage + = sprintf ( npage , " \n " ) ;
return npage - page ;
}
2008-07-26 13:03:25 +04:00
static ssize_t rcf_readcmds_show ( struct blk_cmd_filter * filter , char * page )
2008-06-26 15:48:27 +04:00
{
return rcf_cmds_show ( filter , page , READ ) ;
}
2008-07-26 13:03:25 +04:00
static ssize_t rcf_writecmds_show ( struct blk_cmd_filter * filter ,
2008-06-26 15:48:27 +04:00
char * page )
{
return rcf_cmds_show ( filter , page , WRITE ) ;
}
2008-07-26 13:03:25 +04:00
static ssize_t rcf_cmds_store ( struct blk_cmd_filter * filter ,
2008-06-26 15:48:27 +04:00
const char * page , size_t count , int rw )
{
unsigned long okbits [ BLK_SCSI_CMD_PER_LONG ] , * target_okbits ;
2008-08-16 09:21:06 +04:00
int cmd , set ;
char * p , * status ;
if ( rw = = READ ) {
memcpy ( & okbits , filter - > read_ok , sizeof ( okbits ) ) ;
target_okbits = filter - > read_ok ;
} else {
memcpy ( & okbits , filter - > write_ok , sizeof ( okbits ) ) ;
target_okbits = filter - > write_ok ;
}
while ( ( p = strsep ( ( char * * ) & page , " " ) ) ! = NULL ) {
set = 1 ;
if ( p [ 0 ] = = ' + ' ) {
p + + ;
} else if ( p [ 0 ] = = ' - ' ) {
set = 0 ;
p + + ;
}
cmd = simple_strtol ( p , & status , 16 ) ;
2008-06-26 15:48:27 +04:00
/* either of these cases means invalid input, so do nothing. */
2008-08-16 09:21:06 +04:00
if ( ( status = = p ) | | cmd > = BLK_SCSI_MAX_CMDS )
2008-06-26 15:48:27 +04:00
return - EINVAL ;
2008-08-16 09:21:06 +04:00
if ( set )
__set_bit ( cmd , okbits ) ;
else
__clear_bit ( cmd , okbits ) ;
2008-06-26 15:48:27 +04:00
}
2008-08-16 09:21:06 +04:00
memcpy ( target_okbits , okbits , sizeof ( okbits ) ) ;
2008-06-26 15:48:27 +04:00
return count ;
}
2008-07-26 13:03:25 +04:00
static ssize_t rcf_readcmds_store ( struct blk_cmd_filter * filter ,
2008-06-26 15:48:27 +04:00
const char * page , size_t count )
{
return rcf_cmds_store ( filter , page , count , READ ) ;
}
2008-07-26 13:03:25 +04:00
static ssize_t rcf_writecmds_store ( struct blk_cmd_filter * filter ,
2008-06-26 15:48:27 +04:00
const char * page , size_t count )
{
return rcf_cmds_store ( filter , page , count , WRITE ) ;
}
struct rcf_sysfs_entry {
struct attribute attr ;
2008-07-26 13:03:25 +04:00
ssize_t ( * show ) ( struct blk_cmd_filter * , char * ) ;
ssize_t ( * store ) ( struct blk_cmd_filter * , const char * , size_t ) ;
2008-06-26 15:48:27 +04:00
} ;
static struct rcf_sysfs_entry rcf_readcmds_entry = {
. attr = { . name = " read_table " , . mode = S_IRUGO | S_IWUSR } ,
. show = rcf_readcmds_show ,
. store = rcf_readcmds_store ,
} ;
static struct rcf_sysfs_entry rcf_writecmds_entry = {
. attr = { . name = " write_table " , . mode = S_IRUGO | S_IWUSR } ,
. show = rcf_writecmds_show ,
. store = rcf_writecmds_store ,
} ;
static struct attribute * default_attrs [ ] = {
& rcf_readcmds_entry . attr ,
& rcf_writecmds_entry . attr ,
NULL ,
} ;
# define to_rcf(atr) container_of((atr), struct rcf_sysfs_entry, attr)
static ssize_t
rcf_attr_show ( struct kobject * kobj , struct attribute * attr , char * page )
{
struct rcf_sysfs_entry * entry = to_rcf ( attr ) ;
2008-07-26 13:03:25 +04:00
struct blk_cmd_filter * filter ;
2008-06-26 15:48:27 +04:00
2008-07-26 13:03:25 +04:00
filter = container_of ( kobj , struct blk_cmd_filter , kobj ) ;
2008-06-26 15:48:27 +04:00
if ( entry - > show )
return entry - > show ( filter , page ) ;
return 0 ;
}
static ssize_t
rcf_attr_store ( struct kobject * kobj , struct attribute * attr ,
const char * page , size_t length )
{
struct rcf_sysfs_entry * entry = to_rcf ( attr ) ;
2008-07-26 13:03:25 +04:00
struct blk_cmd_filter * filter ;
2008-06-26 15:48:27 +04:00
if ( ! capable ( CAP_SYS_RAWIO ) )
return - EPERM ;
if ( ! entry - > store )
return - EINVAL ;
2008-07-26 13:03:25 +04:00
filter = container_of ( kobj , struct blk_cmd_filter , kobj ) ;
2008-06-26 15:48:27 +04:00
return entry - > store ( filter , page , length ) ;
}
static struct sysfs_ops rcf_sysfs_ops = {
. show = rcf_attr_show ,
. store = rcf_attr_store ,
} ;
static struct kobj_type rcf_ktype = {
. sysfs_ops = & rcf_sysfs_ops ,
. default_attrs = default_attrs ,
} ;
int blk_register_filter ( struct gendisk * disk )
{
int ret ;
2008-07-26 13:03:25 +04:00
struct blk_cmd_filter * filter = & disk - > queue - > cmd_filter ;
2008-06-26 15:48:27 +04:00
struct kobject * parent = kobject_get ( disk - > holder_dir - > parent ) ;
if ( ! parent )
return - ENODEV ;
ret = kobject_init_and_add ( & filter - > kobj , & rcf_ktype , parent ,
2008-08-16 09:10:05 +04:00
" %s " , " cmd_filter " ) ;
2008-06-26 15:48:27 +04:00
if ( ret < 0 )
return ret ;
return 0 ;
}
2008-08-29 13:47:07 +04:00
EXPORT_SYMBOL ( blk_register_filter ) ;
2008-06-26 15:48:27 +04:00
void blk_unregister_filter ( struct gendisk * disk )
{
2008-07-26 13:03:25 +04:00
struct blk_cmd_filter * filter = & disk - > queue - > cmd_filter ;
2008-06-26 15:48:27 +04:00
kobject_put ( & filter - > kobj ) ;
kobject_put ( disk - > holder_dir - > parent ) ;
}
2008-08-29 13:47:07 +04:00
EXPORT_SYMBOL ( blk_unregister_filter ) ;
2008-09-11 16:20:23 +04:00
# endif