void
test_report_supported_opcodes_servactv(void)
{
	int i, ret;
	struct scsi_task *rso_task;
	struct scsi_report_supported_op_codes *rsoc;

	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test READ_SUPPORTED_OPCODES SERVACTV flag");


	ret = report_supported_opcodes(
		sd, &rso_task,
		0, SCSI_REPORT_SUPPORTING_OPS_ALL, 0, 0,
		65535,
		EXPECT_STATUS_GOOD);
	if (ret == -2) {
		logging(LOG_NORMAL, "[SKIPPED] READ_SUPPORTED_OPCODES is not "
			"implemented.");
		CU_PASS("READ_SUPPORTED_OPCODES is not implemented.");
		scsi_free_scsi_task(rso_task);
		return;
	}
	CU_ASSERT_EQUAL(ret, 0);
	if (ret != 0) {
		scsi_free_scsi_task(rso_task);
		return;
	}
	
	logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer");
	rsoc = scsi_datain_unmarshall(rso_task);
	CU_ASSERT_NOT_EQUAL(rsoc, NULL);
	if (!rsoc) {
		logging(LOG_NORMAL, "[FAILED] Target did not return any data "
			"for ReportSupportedOpcodes\n");
		CU_FAIL("Target did not return any data for "
			"ReportSupportedOpcodes");
		return;
	}


	logging(LOG_VERBOSE, "Verify that when SERVACTV is clear then "
		"ServiceAction must be zero.");
	for (i = 0; i < rsoc->num_descriptors; i++) {
		if (!rsoc->descriptors[i].servactv && rsoc->descriptors[i].sa) {
			logging(LOG_NORMAL, "[FAILED] ServiceAction is "
				"non-zero but SERVACTV is clear");
			CU_FAIL("[FAILED] ServiceAction is "
				"non-zero but SERVACTV is clear");
		}
	}

	scsi_free_scsi_task(rso_task);
}
void
test_report_supported_opcodes_simple(void)
{
	int ret;

	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test basic READ_SUPPORTED_OPCODES");

	ret = report_supported_opcodes(
		sd, NULL,
		0, SCSI_REPORT_SUPPORTING_OPS_ALL, 0, 0,
		1024,
		EXPECT_STATUS_GOOD);
	if (ret == -2) {
		logging(LOG_NORMAL, "[SKIPPED] READ_SUPPORTED_OPCODES is not "
			"implemented.");
		CU_PASS("READ_SUPPORTED_OPCODES is not implemented.");
		return;
	}
	CU_ASSERT_EQUAL(ret, 0);
}
Ejemplo n.º 3
0
void
test_read12_dpofua(void)
{ 
	int ret, dpofua, usage_data_dpofua;
	struct scsi_task *ms_task = NULL;
	struct scsi_mode_sense *ms;
	struct scsi_task *rso_task = NULL;
	struct scsi_report_supported_op_codes_one_command *rsoc;

	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test READ12 DPO/FUA flags");

	CHECK_FOR_SBC;

	logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data");
	ret = modesense6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT,
			 SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255,
			 EXPECT_STATUS_GOOD);
	CU_ASSERT_EQUAL(ret, 0);
	logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD");
	ms = scsi_datain_unmarshall(ms_task);
	dpofua = ms && (ms->device_specific_parameter & 0x10);
	scsi_free_scsi_task(ms_task);

	if (dpofua) {
		logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow "
			"DPO/FUA flags in CDBs");
	} else {
		logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail "
			"CDBs with DPO/FUA set");
	}

	logging(LOG_VERBOSE, "Test READ12 with DPO==1");
	if (dpofua) {
		ret = read12(sd, 0,
			     block_size, block_size, 0, 1, 0, 0, 0, NULL,
			     EXPECT_STATUS_GOOD);
		CU_ASSERT_EQUAL(ret, 0);
	} else {
		ret = read12(sd, 0,
			     block_size, block_size, 0, 1, 0, 0, 0, NULL,
			     EXPECT_INVALID_FIELD_IN_CDB);
		CU_ASSERT_EQUAL(ret, 0);
	}

	logging(LOG_VERBOSE, "Test READ12 with FUA==1");
	if (dpofua) {
		ret = read12(sd, 0,
			     block_size, block_size, 0, 0, 1, 0, 0, NULL,
			     EXPECT_STATUS_GOOD);
		CU_ASSERT_EQUAL(ret, 0);
	} else {
		ret = read12(sd, 0,
			     block_size, block_size, 0, 0, 1, 0, 0, NULL,
			     EXPECT_INVALID_FIELD_IN_CDB);
		CU_ASSERT_EQUAL(ret, 0);
	}

	logging(LOG_VERBOSE, "Test READ12 with DPO==1 FUA==1");
	if (dpofua) {
		ret = read12(sd, 0,
			     block_size, block_size, 0, 1, 1, 0, 0, NULL,
			     EXPECT_STATUS_GOOD);
		CU_ASSERT_EQUAL(ret, 0);
	} else {
		ret = read12(sd, 0,
			     block_size, block_size, 0, 1, 1, 0, 0, NULL,
			     EXPECT_INVALID_FIELD_IN_CDB);
		CU_ASSERT_EQUAL(ret, 0);
	}

	logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES "
		"for READ12");
	ret = report_supported_opcodes(sd, &rso_task,
				       0, SCSI_REPORT_SUPPORTING_OPCODE,
				       SCSI_OPCODE_READ12,
				       0,
				       65535,
				       EXPECT_STATUS_GOOD);
	if (ret == -2) {
		logging(LOG_NORMAL, "REPORT_SUPPORTED_OPCODES not implemented. "
			"Skipping this part of the test");
		return;
	}
	logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer");
	rsoc = scsi_datain_unmarshall(rso_task);
	CU_ASSERT_NOT_EQUAL(rsoc, NULL);
	usage_data_dpofua = rsoc ? rsoc->cdb_usage_data[1] & 0x18 : -1;
	if (dpofua) {
		logging(LOG_VERBOSE, "DPOFUA is set. Verify the DPO/FUA flags "
			"are set in the CDB_USAGE_DATA");
		CU_ASSERT_EQUAL(usage_data_dpofua, 0x18);
	} else {
		logging(LOG_VERBOSE, "DPOFUA is clear. Verify the DPO/FUA "
			"flags are clear in the CDB_USAGE_DATA");
		CU_ASSERT_EQUAL(usage_data_dpofua, 0x00);
	}
	scsi_free_scsi_task(rso_task);
}
void
test_report_supported_opcodes_one_command(void)
{
	int i, ret;
	struct scsi_task *rso_task;
	struct scsi_task *one_task;
	struct scsi_report_supported_op_codes *rsoc;
	struct scsi_report_supported_op_codes_one_command *rsoc_one;

	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test READ_SUPPORTED_OPCODES reading one-command");


	logging(LOG_VERBOSE, "Fetch list of all supported opcodes");
	ret = report_supported_opcodes(sd, &rso_task,
				       0, SCSI_REPORT_SUPPORTING_OPS_ALL,
				       0, 0, 65535,
				       EXPECT_STATUS_GOOD);
	if (ret == -2) {
		logging(LOG_NORMAL, "[SKIPPED] READ_SUPPORTED_OPCODES is not "
			"implemented.");
		CU_PASS("READ_SUPPORTED_OPCODES is not implemented.");
		scsi_free_scsi_task(rso_task);
		return;
	}
	CU_ASSERT_EQUAL(ret, 0);
	if (ret != 0) {
		scsi_free_scsi_task(rso_task);
		return;
	}
	
	logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer");
	rsoc = scsi_datain_unmarshall(rso_task);
	CU_ASSERT_NOT_EQUAL(rsoc, NULL);
	if (!rsoc) {
		logging(LOG_NORMAL, "[FAILED] Target did not return any data "
			"for ReportSupportedOpcodes\n");
		CU_FAIL("Target did not return any data for "
			"ReportSupportedOpcodes");
		return;
	}

	logging(LOG_VERBOSE, "Verify read one-command works for all supported "
		"opcodes");
	for (i = 0; i < rsoc->num_descriptors; i++) {
		logging(LOG_VERBOSE, "Check opcode:0x%02x ServiceAction:0x%02x",
			rsoc->descriptors[i].opcode,
			rsoc->descriptors[i].sa);
		if (rsoc->descriptors[i].servactv) {
			logging(LOG_VERBOSE, "This opcode has service actions. "
				"Reporting Options 001b should fail");
			ret = report_supported_opcodes(sd, NULL,
				0, SCSI_REPORT_SUPPORTING_OPCODE,
				rsoc->descriptors[i].opcode,
				rsoc->descriptors[i].sa,
				65535,
				EXPECT_INVALID_FIELD_IN_CDB);
		} else {
			logging(LOG_VERBOSE, "This opcode does not have "
				"service actions. Reporting Options 001b "
				"should work");
			ret = report_supported_opcodes(
				sd, NULL,
				0, SCSI_REPORT_SUPPORTING_OPCODE,
				rsoc->descriptors[i].opcode,
				rsoc->descriptors[i].sa,
				65535,
				EXPECT_STATUS_GOOD);
		}
		if (ret == -2) {
			logging(LOG_NORMAL, "[SKIPPED] SCSI_REPORT_SUPPORTING_OPCODE is not "
			"implemented.");
			CU_PASS("SCSI_REPORT_SUPPORTING_OPCODE is not implemented.");
		} else {
			CU_ASSERT_EQUAL(ret, 0);
		}
		if (ret != 0 && ret != -2) {
			if (rsoc->descriptors[i].servactv)
				logging(LOG_NORMAL, "[FAILED] Opcode"
					" %#02x/%#02x: got unexpected response"
					" for reporting option 001b",
					rsoc->descriptors[i].opcode,
					rsoc->descriptors[i].sa);
			else
				logging(LOG_NORMAL, "[FAILED] Opcode %#02x: got"
					" unexpected response for reporting"
					" option 001b",
					rsoc->descriptors[i].opcode);
		}

		if (rsoc->descriptors[i].servactv) {
			logging(LOG_VERBOSE, "This opcode has service actions. "
				"Reporting Options 002b should work");
			ret = report_supported_opcodes(
				sd, NULL,
				0, SCSI_REPORT_SUPPORTING_SERVICEACTION,
				rsoc->descriptors[i].opcode,
				rsoc->descriptors[i].sa,
				65535,
				EXPECT_STATUS_GOOD);
		} else {
			logging(LOG_VERBOSE, "This opcode does not have "
				"service actions. Reporting Options 002b "
				"should fail");
			ret = report_supported_opcodes(
				sd, NULL,
				0, SCSI_REPORT_SUPPORTING_SERVICEACTION,
				rsoc->descriptors[i].opcode,
				rsoc->descriptors[i].sa,
				65535,
				EXPECT_INVALID_FIELD_IN_CDB);
		}
		if (ret == -2) {
			logging(LOG_NORMAL, "[SKIPPED] SCSI_REPORT_SUPPORTING_SERVICEACTION is not "
			"implemented.");
			CU_PASS("SCSI_REPORT_SUPPORTING_SERVICEACTION is not implemented.");
		} else {
			CU_ASSERT_EQUAL(ret, 0);
		}
		if (ret != 0 && ret != -2) {
			if (rsoc->descriptors[i].servactv)
				logging(LOG_NORMAL, "[FAILED] Opcode"
					" %#02x/%#02x: got unexpected response"
					" for reporting option 002b",
					rsoc->descriptors[i].opcode,
					rsoc->descriptors[i].sa);
			else
				logging(LOG_NORMAL, "[FAILED] Opcode %#02x: got"
					" unexpected response for reporting"
					" option 002b",
					rsoc->descriptors[i].opcode);
		}
	}


	logging(LOG_VERBOSE, "Verify read one-command CDB looks sane");
	for (i = 0; i < rsoc->num_descriptors; i++) {
		logging(LOG_VERBOSE, "Check CDB for opcode:0x%02x "
			"ServiceAction:0x%02x",
			rsoc->descriptors[i].opcode,
			rsoc->descriptors[i].sa);
		ret = report_supported_opcodes(
			sd, &one_task, 0,
			rsoc->descriptors[i].servactv ?
				SCSI_REPORT_SUPPORTING_SERVICEACTION :
				SCSI_REPORT_SUPPORTING_OPCODE,
			rsoc->descriptors[i].opcode,
			rsoc->descriptors[i].sa,
			65535,
			EXPECT_STATUS_GOOD);

		logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer");
		rsoc_one = scsi_datain_unmarshall(one_task);
		CU_ASSERT_NOT_EQUAL(rsoc_one, NULL);

		logging(LOG_VERBOSE, "Verify CDB length is not 0");
		CU_ASSERT_NOT_EQUAL(rsoc_one->cdb_length, 0);
		if (rsoc_one->cdb_length == 0) {
			logging(LOG_NORMAL, "[FAILED] CDB length is 0");
		}
		
		logging(LOG_VERBOSE, "Verify CDB[0] Usage Data == <opcode>");
		CU_ASSERT_EQUAL(rsoc_one->cdb_usage_data[0],
				rsoc->descriptors[i].opcode);
		if (rsoc_one->cdb_usage_data[0] != rsoc->descriptors[i].opcode) {
			logging(LOG_NORMAL, "[FAILED] CDB[0] Usage Data was "
				"0x%02x, expected 0x%02x for opcode 0x%02x",
				rsoc_one->cdb_usage_data[0],
				rsoc->descriptors[i].opcode,
				rsoc->descriptors[i].opcode);
		}

		scsi_free_scsi_task(one_task);
	}


	scsi_free_scsi_task(rso_task);
}
Ejemplo n.º 5
0
int
main(int argc, char *argv[])
{
	char *testname_re = NULL;
	CU_BasicRunMode mode = CU_BRM_VERBOSE;
	CU_ErrorAction error_action = CUEA_IGNORE;
	int res;
	struct scsi_readcapacity10 *rc10;
	struct scsi_task *inq_task = NULL;
	struct scsi_task *inq_lbp_task = NULL;
	struct scsi_task *inq_bdc_task = NULL;
	struct scsi_task *inq_bl_task = NULL;
	struct scsi_task *rc16_task = NULL;
	struct scsi_task *rsop_task = NULL;
	int full_size;
	int xml_mode = 0;
	static struct option long_opts[] = {
		{ "help", no_argument, 0, '?' },
		{ "list", no_argument, 0, 'l' },
		{ "initiator-name", required_argument, 0, 'i' },
		{ "initiator-name-2", required_argument, 0, 'I' },
		{ "test", required_argument, 0, 't' },
		{ "dataloss", no_argument, 0, 'd' },
		{ "allow-sanitize", no_argument, 0, 'S' },
		{ "ignore", no_argument, 0, 'g' },
		{ "fail", no_argument, 0, 'f' },
		{ "abort", no_argument, 0, 'A' },
		{ "silent", no_argument, 0, 's' },
		{ "normal", no_argument, 0, 'n' },
		{ "verbose", no_argument, 0, 'v' },
		{ "xml", no_argument, 0, 'x' },
		{ "Verbose-scsi", no_argument, 0, 'V' },
		{ NULL, 0, 0, 0 }
	};
	int i, c;
	int opt_idx = 0;

	while ((c = getopt_long(argc, argv, "?hli:I:t:sdgfAsSnvxV", long_opts,
		    &opt_idx)) > 0) {
		switch (c) {
		case 'h':
		case '?':
			print_usage();
			return 0;
		case 'l':
			list_all_tests();
			return 0;
		case 'i':
			initiatorname1 = strdup(optarg);
			break;
		case 'I':
			initiatorname2 = strdup(optarg);
			break;
		case 't':
			testname_re = strdup(optarg);
			break;
		case 'd':
			data_loss++;
			break;
		case 'g':
			error_action = CUEA_IGNORE; /* default */
			break;
		case 'f':
			error_action = CUEA_FAIL;
			break;
		case 'A':
			error_action = CUEA_ABORT;
			break;
		case 's':
			mode = CU_BRM_SILENT;
			break;
		case 'S':
			allow_sanitize = 1;
			break;
		case 'n':
			mode = CU_BRM_NORMAL;
			break;
		case 'v':
			mode = CU_BRM_VERBOSE;	/* default */
			break;
		case 'x':
		        xml_mode = 1;
			break;
		case 'V':
			loglevel = LOG_VERBOSE;
			break;
		default:
			fprintf(stderr,
			    "error: unknown option return: %c (option %s)\n",
			    c, argv[optind]);
			return 1;
		}
	}

	/* parse all trailing arguments as device paths */
	mp_num_sds = 0;
	while (optind < argc) {
		if (mp_num_sds >= MPATH_MAX_DEVS) {
			fprintf(stderr, "Too many multipath device URLs\n");
			print_usage();
			free(testname_re);
			return 10;
		}

		mp_sds[mp_num_sds] = malloc(sizeof(struct scsi_device));
		memset(mp_sds[mp_num_sds], '\0', sizeof(struct scsi_device));
		mp_sds[mp_num_sds]->sgio_fd = -1;

		if (!strncmp(argv[optind], "iscsi://", 8)) {
			mp_sds[mp_num_sds]->iscsi_url = strdup(argv[optind++]);
#ifdef HAVE_SG_IO
		} else {
			mp_sds[mp_num_sds]->sgio_dev = strdup(argv[optind++]);
#endif
		}
		mp_num_sds++;
	}

	/* So that we can override iscsi_queue_pdu in tests
	 * and replace or mutate the blob that we are about to write to the
	 * wire.
	 * This allows such tests to do their mutates and then call out
	 * to the real queueing function once they have modified the data.
	 */
	real_iscsi_queue_pdu = dlsym(RTLD_NEXT, "iscsi_queue_pdu");

	if ((mp_num_sds == 0) || (mp_sds[0]->iscsi_url == NULL
					&& mp_sds[0]->sgio_dev == NULL)) {
#ifdef HAVE_SG_IO
		fprintf(stderr, "You must specify either an iSCSI URL or a device file\n");
#else
		fprintf(stderr, "You must specify an iSCSI URL\n");
#endif
		print_usage();
		if (testname_re)
			free(testname_re);
		return 10;
	}

	/* sd remains an alias for the first device */
	sd = mp_sds[0];

	for (i = 0; i < mp_num_sds; i++) {
		res = connect_scsi_device(mp_sds[i], initiatorname1);
		if (res < 0) {
			fprintf(stderr,
				"Failed to connect to SCSI device %d\n", i);
			goto err_sds_free;
		}
	}

	if (mp_num_sds > 1) {
		/* check that all multipath sds identify as the same LU */
		res = mpath_check_matching_ids(mp_num_sds, mp_sds);
		if (res < 0) {
			fprintf(stderr, "multipath devices don't match\n");
			goto err_sds_free;
		}
	}

	/*
	 * find the size of the LUN
	 * All devices support readcapacity10 but only some support
	 * readcapacity16
	 */
	task = NULL;
	readcapacity10(sd, &task, 0, 0, EXPECT_STATUS_GOOD);
	if (task == NULL) {
		printf("Failed to send READCAPACITY10 command: %s\n", sd->error_str);
		goto err_sds_free;
	}
	if (task->status != SCSI_STATUS_GOOD) {
		printf("READCAPACITY10 command: failed with sense. %s\n", sd->error_str);
		scsi_free_scsi_task(task);
		goto err_sds_free;
	}
	rc10 = scsi_datain_unmarshall(task);
	if (rc10 == NULL) {
		printf("failed to unmarshall READCAPACITY10 data.\n");
		scsi_free_scsi_task(task);
		goto err_sds_free;
	}
	block_size = rc10->block_size;
	num_blocks = rc10->lba + 1;
	scsi_free_scsi_task(task);

	rc16_task = NULL;
	readcapacity16(sd, &rc16_task, 96, EXPECT_STATUS_GOOD);
	if (rc16_task == NULL) {
		printf("Failed to send READCAPACITY16 command: %s\n", sd->error_str);
		goto err_sds_free;
	}
	if (rc16_task->status == SCSI_STATUS_GOOD) {
		rc16 = scsi_datain_unmarshall(rc16_task);
		if (rc16 == NULL) {
			printf("failed to unmarshall READCAPACITY16 data. %s\n", sd->error_str);
			scsi_free_scsi_task(rc16_task);
			goto err_sds_free;
		}
		block_size = rc16->block_length;
		num_blocks = rc16->returned_lba + 1;
		lbppb = 1 << rc16->lbppbe;
	}

	inq_task = NULL;
	inquiry(sd, &inq_task, 0, 0, 64, EXPECT_STATUS_GOOD);
	if (inq_task == NULL || inq_task->status != SCSI_STATUS_GOOD) {
		printf("Inquiry command failed : %s\n", sd->error_str);
		goto err_sds_free;
	}
	full_size = scsi_datain_getfullsize(inq_task);
	if (full_size > inq_task->datain.size) {
		scsi_free_scsi_task(inq_task);

		/* we need more data for the full list */
		inq_task = NULL;
		inquiry(sd, &inq_task, 0, 0, full_size, EXPECT_STATUS_GOOD);
		if (inq_task == NULL) {
			printf("Inquiry command failed : %s\n", sd->error_str);
			goto err_sds_free;
		}
	}
	inq = scsi_datain_unmarshall(inq_task);
	if (inq == NULL) {
		printf("failed to unmarshall inquiry datain blob\n");
		scsi_free_scsi_task(inq_task);
		goto err_sds_free;
	}

	sbc3_support = 0;
	for (i = 0; i < 8; i++) {
		if (inq->version_descriptor[i] == 0x04C0) {
			sbc3_support = 1;
		}
	}

	/* try reading block limits vpd */
	inq_bl_task = NULL;
	inquiry(sd, &inq_bl_task, 1, SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS, 64, EXPECT_STATUS_GOOD);
	if (inq_bl_task && inq_bl_task->status != SCSI_STATUS_GOOD) {
		scsi_free_scsi_task(inq_bl_task);
		inq_bl_task = NULL;
	}
	if (inq_bl_task) {
		full_size = scsi_datain_getfullsize(inq_bl_task);
		if (full_size > inq_bl_task->datain.size) {
			scsi_free_scsi_task(inq_bl_task);

			inq_bl_task = NULL;
			inquiry(sd, &inq_bl_task, 1, SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS, full_size,
				EXPECT_STATUS_GOOD);
			if (inq_bl_task == NULL) {
				printf("Inquiry command failed : %s\n", sd->error_str);
				goto err_sds_free;
			}
		}

		inq_bl = scsi_datain_unmarshall(inq_bl_task);
		if (inq_bl == NULL) {
			printf("failed to unmarshall inquiry datain blob\n");
			goto err_sds_free;
		}
	}

	/* try reading block device characteristics vpd */
	inquiry(sd, &inq_bdc_task, 1, SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS, 255,
		EXPECT_STATUS_GOOD);
	if (inq_bdc_task == NULL || inq_bdc_task->status != SCSI_STATUS_GOOD) {
		printf("Failed to read Block Device Characteristics page\n");
	} else {
		inq_bdc = scsi_datain_unmarshall(inq_bdc_task);
		if (inq_bdc == NULL) {
			printf("failed to unmarshall inquiry datain blob\n");
			goto err_sds_free;
		}
	}

	/* if thin provisioned we also need to read the VPD page for it */
	if (rc16 && rc16->lbpme != 0){
		inq_lbp_task = NULL;
		inquiry(sd, &inq_lbp_task, 1, SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING, 64,
			EXPECT_STATUS_GOOD);
		if (inq_lbp_task == NULL || inq_lbp_task->status != SCSI_STATUS_GOOD) {
			printf("Inquiry command failed : %s\n", sd->error_str);
			goto err_sds_free;
		}
		full_size = scsi_datain_getfullsize(inq_lbp_task);
		if (full_size > inq_lbp_task->datain.size) {
			scsi_free_scsi_task(inq_lbp_task);

			/* we need more data for the full list */
			inq_lbp_task = NULL;
			inquiry(sd, &inq_lbp_task, 1, SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING,
				full_size, EXPECT_STATUS_GOOD);
			if (inq_lbp_task == NULL) {
				printf("Inquiry command failed : %s\n", sd->error_str);
				goto err_sds_free;
			}
		}

		inq_lbp = scsi_datain_unmarshall(inq_lbp_task);
		if (inq_lbp == NULL) {
			printf("failed to unmarshall inquiry datain blob\n");
			goto err_sds_free;
		}
	}

	rsop_task = NULL;
	report_supported_opcodes(sd, &rsop_task, 1, SCSI_REPORT_SUPPORTING_OPS_ALL, 0, 0, 65535,
				 EXPECT_STATUS_GOOD);
	if (rsop_task == NULL) {
		printf("Failed to send REPORT_SUPPORTED_OPCODES command: %s\n", sd->error_str);
		goto err_sds_free;
	}
	if (rsop_task->status == SCSI_STATUS_GOOD) {
		rsop = scsi_datain_unmarshall(rsop_task);
		if (rsop == NULL) {
			printf("failed to unmarshall REPORT_SUPPORTED_OPCODES data.\n");
			scsi_free_scsi_task(rsop_task);
			rsop_task = NULL;
		}
	}

	/* check if the device is write protected or not */
	task = NULL;
	modesense6(sd, &task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255,
		   EXPECT_STATUS_GOOD);
	if (task == NULL) {
		printf("Failed to send MODE_SENSE6 command: %s\n", sd->error_str);
		goto err_sds_free;
	}
	if (task->status == SCSI_STATUS_GOOD) {
		struct scsi_mode_sense *ms;

		ms = scsi_datain_unmarshall(task);
		if (ms == NULL) {
			printf("failed to unmarshall mode sense datain blob\n");
			scsi_free_scsi_task(task);
			goto err_sds_free;
		}
		readonly = !!(ms->device_specific_parameter & 0x80);
	}
	scsi_free_scsi_task(task);

	if (maxsectbytes) {
		maximum_transfer_length = maxsectbytes / block_size;
		printf("Bus transfer size is limited to %d bytes. Clamping "
		       "max transfers accordingly.\n", maxsectbytes);
	}

	if (CU_initialize_registry() != 0) {
		fprintf(stderr, "error: unable to initialize test registry\n");
		goto err_sds_free;
	}
	if (CU_is_test_running()) {
		fprintf(stderr, "error: test suite(s) already running!?\n");
		exit(1);
	}

	parse_and_add_tests(testname_re);
	if (testname_re)
		free(testname_re);

	CU_basic_set_mode(mode);
	CU_set_error_action(error_action);
	printf("\n");

	/*
	 * this actually runs the tests ...
	 */

	if (xml_mode) {
	  CU_list_tests_to_file();
	  CU_automated_run_tests();
	} else {
	  res = CU_basic_run_tests();
	  printf("Tests completed with return value: %d\n", res);
	}

	CU_cleanup_registry();

	if (inq_task != NULL) {
		scsi_free_scsi_task(inq_task);
	}
	if (inq_bl_task != NULL) {
		scsi_free_scsi_task(inq_bl_task);
	}
	if (inq_lbp_task != NULL) {
		scsi_free_scsi_task(inq_lbp_task);
	}
	if (inq_bdc_task != NULL) {
		scsi_free_scsi_task(inq_bdc_task);
	}
	if (rc16_task != NULL) {
		scsi_free_scsi_task(rc16_task);
	}
	if (rsop_task != NULL) {
		scsi_free_scsi_task(rsop_task);
	}
	for (i = 0; i < mp_num_sds; i++) {
		free_scsi_device(mp_sds[i]);
	}

	return 0;

err_sds_free:
	for (i = 0; i < mp_num_sds; i++) {
		free_scsi_device(mp_sds[i]);
	}
	return -1;
}