On Mon, 2003-03-24 at 11:30, Matthew Dharm wrote:
> On Mon, Mar 24, 2003 at 09:15:57AM -0600, James Bottomley wrote:
> > OK, I can do this: A simple one with either a blacklist (reject these
> > commands) or whitelist (only accept these commands) going by the first
> > command byte OK?
> 
> Well, you need to go by more than the first command byte -- ex. INQUIRY is
> okay, unless length != 36 or EVPD.
> 
> I think a blacklist is probably in order, but with a BIG COMMENT mentioning
> that if someone adds new commands into the code paths they should at least
> consider if they belong in the blacklist.

OK, try the attached.  There is no central blacklist, you must construct
it on a per driver basis, so in the queuecommand of your driver you have
a:

        static struct scsi_cmd_filter filter =
                { SCSI_FILTER_BLACKLIST ,
                  { MODE_SENSE, SCSI_FILTER_INQUIRY_NOT36, 0 } };

        if(scsi_filter_cmd(SCp, &filter)) {
                SCp->scsi_done(SCp);
                return 0;
        }

to use the filter (I think, I've compiled but not tested it).

Let me know how it goes.

James

===== 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;
+}
+       

Reply via email to