# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.1131 -> 1.1134 # drivers/scsi/scsi.h 1.73 -> 1.76 # drivers/scsi/scsi_syms.c 1.30 -> 1.31 # drivers/scsi/scsi_lib.c 1.83 -> 1.86 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 03/04/21 jejb@raven.il.steeleye.com 1.1132 # Add a command filter to scsi_lib.c # # To facilitate emulated hosts rejecting commands they do not # wish to process # -------------------------------------------- # 03/04/21 mdharm-scsi@one-eyed-alien.net 1.1131.1.1 # [PATCH] Re: PATCH: exclude certain commands from emulated SCSI hosts # # --=-ubLHM6H8VjG+svJp0RLA # Content-Disposition: inline # Content-Type: text/plain; charset=us-ascii # Content-Transfer-Encoding: 8bit # # How about this patch? It is based on James' work, but I cleaned up the # comments a bit and eliminated a structure which was used to only hold two # elements. I also added a filter type and fixed the symbol problems when # modules were used. # # I've tested this, and it works well. Linus, if you'll take this I've got # several more patches -- ones to make usb-storage use this to cut some # undesireable commands, and one to fix up the INQUIRY probing in scsi_scan.c # to be compatible with the filter code. # # Linus, please apply this to 2.5. # # Matt # # # This is a BitKeeper generated patch for the following project: # # Project Name: greg k-h's linux 2.5 USB kernel tree # # This patch format is intended for GNU patch command version 2.5 or higher. # # This patch includes the following deltas: # # ChangeSet 1.670 -> 1.671 # # drivers/scsi/scsi_syms.c 1.20 -> 1.21 # # drivers/scsi/scsi.h 1.31 -> 1.32 # # drivers/scsi/scsi_lib.c 1.34 -> 1.35 # # # # The following is the BitKeeper ChangeSet Log # # -------------------------------------------- # # 03/04/19 mdharm@zen.san.one-eyed-alien.net 1.671 # # Added SCSI command filter. # # -------------------------------------------- # # # -------------------------------------------- # 03/04/21 jejb@raven.il.steeleye.com 1.1133 # Merge jejb/mdharm # -------------------------------------------- # 03/04/21 jejb@raven.il.steeleye.com 1.1134 # Complete jejb/mdharm Merger in scsi_filter_list # -------------------------------------------- # diff -Nru a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h --- a/drivers/scsi/scsi.h Mon Apr 21 11:26:23 2003 +++ b/drivers/scsi/scsi.h Mon Apr 21 11:26:23 2003 @@ -971,4 +971,49 @@ extern int scsi_sysfs_register(void); extern void scsi_sysfs_unregister(void); +struct scsi_cmd_filter { + enum { + SCSI_FILTER_WHITELIST, + SCSI_FILTER_BLACKLIST + } type; + /* normal scsi commands are bytes, exceptions are longer. The format + * of the exceptions is: + * + * bit 15: invert the specific condition + * bits 14-12: Reserved + * bits 11-8: command opcode (must be > 0) + * bits 7-0: Command specific */ + unsigned short commands[]; +}; + +extern int scsi_filter_cmd(struct scsi_cmnd *, struct scsi_cmd_filter *); + +/* exception definitions for the filter */ +#define SCSI_FILTER_INVERT 0x8000 + +/* opcodes for the filter */ +#define SCSI_FILTER_INQUIRY 0x0100 + +/* useful filter commands */ + +/* of the FILTER_INQUIRY opcode, < 128 is a length bits above are inquiry + * type */ +#define SCSI_FILTER_QUALIFIER 0x80 +#define SCSI_FILTER_QUALIFIER_EVPD 0x01 +#define SCSI_FILTER_INQUIRY_EVPD (SCSI_FILTER_INQUIRY | SCSI_FILTER_QUALIFIER | SCSI_FILTER_QUALIFIER_EVPD) +#define SCSI_FILTER_INQUIRY_36 (SCSI_FILTER_INQUIRY | 36) +#define SCSI_FILTER_INQUIRY_NOT36 (SCSI_FILTER_INVERT | SCSI_FILTER_INQUIRY | 36) +#define SCSI_FILTER_INQUIRY_NOT_EVPD (SCSI_FILTER_INVERT | SCSI_FILTER_INQUIRY_EVPD) + +/* marker for end of filter list */ +#define SCSI_FILTER_LIST_END 0xFFFF + +static inline unsigned short scsi_filter_opcode(unsigned short command) { + return command & 0x7f00; +} + +static inline unsigned char scsi_filter_data(unsigned short command) { + return (unsigned char)(command & 0x00ff); +} + #endif /* _SCSI_H */ diff -Nru a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c --- a/drivers/scsi/scsi_lib.c Mon Apr 21 11:26:23 2003 +++ b/drivers/scsi/scsi_lib.c Mon Apr 21 11:26:23 2003 @@ -1423,3 +1423,88 @@ kmem_cache_destroy(sgp->slab); } } + +static inline int scsi_filter_exceptions(struct scsi_cmnd *cmd, + unsigned short command) +{ + int found = 0; + + /* specials begin at 0x100 */ + if(command < 0x100) + return 0; + + /* we check for inversion in each switch so that we can define each + * test to act only on a specific subset of commands + */ + switch (scsi_filter_opcode(command)) { + + case SCSI_FILTER_INQUIRY: + if (cmd->cmnd[0] != INQUIRY) + break; + + /* is it a special filter */ + if (command & SCSI_FILTER_QUALIFIER) { + if (command & SCSI_FILTER_QUALIFIER_EVPD) + /* is EVPD bit set? */ + found += (cmd->cmnd[1] & 0x1) ? 1 : 0; + } else { + /* does the transfer length match the data */ + found = (cmd->cmnd[4] == scsi_filter_data(command)); + } + /* now check for inversion */ + if (command & SCSI_FILTER_INVERT) + found = !found; + break; + default: + /* unrecognized filter */ + break; + } + + return found; +} + +/** + * scsi_filter_cmd - Filter a given command against a list + * @cmd: command to be filtered. + * @filter: pointer to the filter containing the type (black/white list) and + * zero terminated list of commands to filter against (first byte only). See + * scsi.h for details on the filter structure. + * + * Returns 0 if the filter passed successfully and the driver can continue + * processing the command or 1 if the filter failed and the command should + * be finished (via ->scsi_done). In the latter case, the command will have + * the sense fields filled in indicating the correct sense for an illegal + * request. + **/ +int scsi_filter_cmd(struct scsi_cmnd *cmd, struct scsi_cmd_filter *filter) +{ + int found = 0; + unsigned short *command; + + /* search command list for a match */ + for (command = filter->commands ; *command != SCSI_FILTER_LIST_END; + command++) { + found = scsi_filter_exceptions(cmd, *command); + found += (cmd->cmnd[0] == *command); + if(found) + break; + } + + /* pass behavior -- found on a whitelist or not found on a blacklist */ + if ((found && filter->type == SCSI_FILTER_WHITELIST) || + (!found && filter->type == SCSI_FILTER_BLACKLIST)) + return 0; + + /* fill in the Check Condition/Illegal request */ + cmd->result = SAM_STAT_CHECK_CONDITION; + memset(cmd->sense_buffer, '\0', sizeof(cmd->sense_buffer)); + cmd->sense_buffer[0] = 0xF0; /* valid, response code 0x70 */ + cmd->sense_buffer[2] = ILLEGAL_REQUEST; + + /* ASC 0x20, ASCQ 0x00: Invalid command operation code */ + cmd->sense_buffer[12] = 0x20; + cmd->sense_buffer[13] = 0x00; + + return 1; +} + diff -Nru a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c --- a/drivers/scsi/scsi_syms.c Mon Apr 21 11:26:23 2003 +++ b/drivers/scsi/scsi_syms.c Mon Apr 21 11:26:23 2003 @@ -55,6 +55,7 @@ #if defined(CONFIG_SCSI_LOGGING) /* { */ EXPORT_SYMBOL(scsi_logging_level); #endif +EXPORT_SYMBOL(scsi_filter_cmd); EXPORT_SYMBOL(scsi_allocate_request); EXPORT_SYMBOL(scsi_release_request);