===== drivers/scsi/scsi.h 1.68 vs edited ===== --- 1.68/drivers/scsi/scsi.h Mon Mar 24 11:23:37 2003 +++ edited/drivers/scsi/scsi.h Sat Apr 5 09:24:38 2003 @@ -959,4 +959,39 @@ 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 words. The format + * of the exceptions is: + * + * Byte 15: invert the specific condition + * Byte 14-12: Reserved + * Byte 11-8: command opcode (must be > 0) + * Byte 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 */ +#define SCSI_FILTER_INQUIRY_36 (SCSI_FILTER_INQUIRY | 36) +#define SCSI_FILTER_INQUIRY_NOT36 (SCSI_FILTER_INVERT | SCSI_FILTER_INQUIRY | 36) + +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 */ ===== drivers/scsi/scsi_lib.c 1.75 vs edited ===== --- 1.75/drivers/scsi/scsi_lib.c Fri Mar 14 18:35:27 2003 +++ edited/drivers/scsi/scsi_lib.c Sat Apr 5 09:27:35 2003 @@ -1375,3 +1375,71 @@ 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; + + switch(scsi_filter_opcode(command)) { + + case SCSI_FILTER_INQUIRY: + if(cmd->cmnd[0] != INQUIRY) + return 0; + /* 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; + return found; + default: + /* unrecognized filter */ + return 0; + } +} + +/** + * 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). + * + * 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; + + for(command = filter->commands; *command; command++) { + found = scsi_filter_exceptions(cmd, *command); + found += (cmd->cmnd[0] != *command); + if(found) + break; + } + + 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; +} +