target/user: Disallow BIDI and COMPARE AND WRITE cmds for TCMU I/O mode

I/O mode is a partial-passthrough mode that saves userspace from handling
the complete set of required SCSI commands. Due to some issues with
emulation, it is preferable to not include BIDI commands like XDWRITEREAD,
and also COMPARE AND WRITE. If these are needed, the forthcoming
full-passthrough mode will allow their use.

Signed-off-by: Andy Grover <agrover@redhat.com>
diff --git a/Documentation/target/tcmu-design.txt b/Documentation/target/tcmu-design.txt
index b495108..305b3bb 100644
--- a/Documentation/target/tcmu-design.txt
+++ b/Documentation/target/tcmu-design.txt
@@ -375,9 +375,7 @@
 READ
 WRITE
 WRITE_VERIFY
-XDWRITEREAD
 WRITE_SAME
-COMPARE_AND_WRITE
 SYNCHRONIZE_CACHE
 UNMAP
 
diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c
index 0e0feea..a01093d 100644
--- a/drivers/target/target_core_user.c
+++ b/drivers/target/target_core_user.c
@@ -21,6 +21,7 @@
 #include <linux/idr.h>
 #include <linux/timer.h>
 #include <linux/parser.h>
+#include <asm/unaligned.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_host.h>
 #include <linux/uio_driver.h>
@@ -1051,7 +1052,28 @@
 static sense_reason_t
 tcmu_parse_cdb(struct se_cmd *cmd)
 {
-	return sbc_parse_cdb(cmd, &tcmu_sbc_ops);
+	sense_reason_t ret;
+	unsigned char *cdb = cmd->t_task_cdb;
+
+	switch (cdb[0]) {
+	case COMPARE_AND_WRITE:
+	case XDWRITEREAD_10:
+		pr_err("TCMU I/O mode does not support XDWRITEREAD10 or "
+			"COMPARE AND WRITE opcodes\n");
+		ret = TCM_UNSUPPORTED_SCSI_OPCODE;
+		break;
+	case VARIABLE_LENGTH_CMD:
+		if (get_unaligned_be16(&cdb[8]) == XDWRITEREAD_32) {
+			pr_err("TCMU I/O mode does not support XDWRITEREAD32\n");
+			ret = TCM_UNSUPPORTED_SCSI_OPCODE;
+			break;
+		}
+		/* fallthrough */
+	default:
+		ret = sbc_parse_cdb(cmd, &tcmu_sbc_ops);
+	}
+
+	return ret;
 }
 
 DEF_TB_DEFAULT_ATTRIBS(tcmu);