void
test_reserve6_target_warm_reset(void)
{
	int ret;
	struct scsi_device sd2;

	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test that RESERVE6 is released on target warm reset");

	if (sd->iscsi_ctx == NULL) {
		const char *err = "[SKIPPED] This RESERVE6 test is only "
			"supported for iSCSI backends";
		logging(LOG_NORMAL, "%s", err);
		CU_PASS(err);
		return;
	}

	logging(LOG_VERBOSE, "Take out a RESERVE6 from the first initiator");
	ret = reserve6(sd);
	if (ret == -2) {
		logging(LOG_VERBOSE, "[SKIPPED] Target does not support RESERVE6. Skipping test");
		CU_PASS("[SKIPPED] Target does not support RESERVE6. Skipping test");
		return;
	}
	CU_ASSERT_EQUAL(ret, 0);


	logging(LOG_VERBOSE, "Send a Warm Reset to the target");
	ret = iscsi_task_mgmt_target_warm_reset_sync(sd->iscsi_ctx);
	if (ret != 0) {
		logging(LOG_NORMAL, "Warm reset failed. %s", iscsi_get_error(sd->iscsi_ctx));
	}
	CU_ASSERT_EQUAL(ret, 0);

	logging(LOG_VERBOSE, "Sleep for three seconds incase the target is slow to reset");
	sleep(3);


	logging(LOG_VERBOSE, "Create a second connection to the target");
	memset(&sd2, 0, sizeof(sd2));
	sd2.iscsi_url = sd->iscsi_url;
	sd2.iscsi_lun = sd->iscsi_lun;
	sd2.iscsi_ctx = iscsi_context_login(initiatorname2, sd2.iscsi_url, &sd2.iscsi_lun);
	if (sd2.iscsi_ctx == NULL) {
		logging(LOG_VERBOSE, "Failed to login to target");
		return;
	}


	logging(LOG_VERBOSE, "RESERVE6 from the second initiator should work now");
	ret = reserve6(&sd2);
	CU_ASSERT_EQUAL(ret, 0);

	logging(LOG_VERBOSE, "RELEASE6 from the second initiator");
	ret = release6(&sd2);
	CU_ASSERT_EQUAL(ret, 0);

	iscsi_logout_sync(sd2.iscsi_ctx);
	iscsi_destroy_context(sd2.iscsi_ctx);
}
void
test_reserve6_logout(void)
{
	int ret;
	struct scsi_device *sd2;

	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test that RESERVE6 is released on logout");

	if (sd->iscsi_ctx == NULL) {
		const char *err = "[SKIPPED] This RESERVE6 test is only "
			"supported for iSCSI backends";
		logging(LOG_NORMAL, "%s", err);
		CU_PASS(err);
		return;
	}

	logging(LOG_NORMAL, "Take out a RESERVE6 from the first initiator");
	ret = reserve6(sd);
	if (ret == -2) {
		logging(LOG_VERBOSE, "[SKIPPED] Target does not support RESERVE6. Skipping test");
		CU_PASS("[SKIPPED] Target does not support RESERVE6. Skipping test");
		return;
	}
	CU_ASSERT_EQUAL(ret, 0);


	logging(LOG_VERBOSE, "Create a second connection to the target");
	ret = mpath_sd2_get_or_clone(sd, &sd2);
	CU_ASSERT_EQUAL(ret, 0);

	logging(LOG_NORMAL, "Try to take out a RESERVE6 from the second initiator");
	ret = reserve6_conflict(sd2);
	CU_ASSERT_EQUAL(ret, 0);

	logging(LOG_VERBOSE, "Logout from target");
	iscsi_logout_sync(sd->iscsi_ctx);
	iscsi_destroy_context(sd->iscsi_ctx);

	logging(LOG_VERBOSE, "Relogin to target");
	sd->iscsi_ctx = iscsi_context_login(initiatorname1, sd->iscsi_url, &sd->iscsi_lun);
	if (sd->iscsi_ctx == NULL) {
		logging(LOG_VERBOSE, "Failed to login to target");
		return;
	}

	logging(LOG_NORMAL, "RESERVE6 from the second initiator should work now");
	ret = reserve6(sd2);
	CU_ASSERT_EQUAL(ret, 0);

	logging(LOG_NORMAL, "RELEASE6 from the second initiator");
	ret = release6(sd2);
	CU_ASSERT_EQUAL(ret, 0);

	mpath_sd2_put(sd2);
}
void
test_reserve6_itnexus_loss(void)
{
	int ret;


	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test that RESERVE6 is released on it-nexus loss");


	logging(LOG_NORMAL, "Take out a RESERVE6 from the first initiator");
	ret = reserve6(iscsic, tgt_lun);
	if (ret == -2) {
		logging(LOG_VERBOSE, "[SKIPPED] Target does not support RESERVE6. Skipping test");
		CU_PASS("[SKIPPED] Target does not support RESERVE6. Skipping test");
		return;
	}
	CU_ASSERT_EQUAL(ret, 0);


	logging(LOG_VERBOSE, "Create a second connection to the target");
	iscsic2 = iscsi_context_login(initiatorname1, tgt_url, &tgt_lun);
	if (iscsic2 == NULL) {
		logging(LOG_VERBOSE, "Failed to login to target");
		return;
	}

	logging(LOG_NORMAL, "Try to take out a RESERVE6 from the second initiator");
	ret = reserve6_conflict(iscsic2, tgt_lun);
	CU_ASSERT_EQUAL(ret, 0);

	logging(LOG_VERBOSE, "Disconnect from the target.");
	iscsi_destroy_context(iscsic);

	logging(LOG_VERBOSE, "Sleep for three seconds incase the target is slow to reset");
	sleep(3);

	logging(LOG_VERBOSE, "Reconnect to target");
	iscsic = iscsi_context_login(initiatorname1, tgt_url, &tgt_lun);
	if (iscsic == NULL) {
		logging(LOG_VERBOSE, "Failed to login to target");
		return;
	}

	logging(LOG_NORMAL, "RESERVE6 from the second initiator should work now");
	ret = reserve6(iscsic2, tgt_lun);
	CU_ASSERT_EQUAL(ret, 0);

	logging(LOG_NORMAL, "RELEASE6 from the second initiator");
	ret = release6(iscsic2, tgt_lun);
	CU_ASSERT_EQUAL(ret, 0);
}
void
test_reserve6_logout(void)
{
	int ret;


	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test that RESERVE6 is released on logout");


	logging(LOG_NORMAL, "Take out a RESERVE6 from the first initiator");
	ret = reserve6(iscsic, tgt_lun);
	if (ret == -2) {
		logging(LOG_VERBOSE, "[SKIPPED] Target does not support RESERVE6. Skipping test");
		CU_PASS("[SKIPPED] Target does not support RESERVE6. Skipping test");
		return;
	}
	CU_ASSERT_EQUAL(ret, 0);


	logging(LOG_VERBOSE, "Create a second connection to the target");
	iscsic2 = iscsi_context_login(initiatorname1, tgt_url, &tgt_lun);
	if (iscsic2 == NULL) {
		logging(LOG_VERBOSE, "Failed to login to target");
		return;
	}

	logging(LOG_NORMAL, "Try to take out a RESERVE6 from the second initiator");
	ret = reserve6_conflict(iscsic2, tgt_lun);
	CU_ASSERT_EQUAL(ret, 0);

	logging(LOG_VERBOSE, "Logout from target");
	iscsi_logout_sync(iscsic);

	logging(LOG_VERBOSE, "Relogin to target");
	iscsic = iscsi_context_login(initiatorname1, tgt_url, &tgt_lun);
	if (iscsic == NULL) {
		logging(LOG_VERBOSE, "Failed to login to target");
		return;
	}

	logging(LOG_NORMAL, "RESERVE6 from the second initiator should work now");
	ret = reserve6(iscsic2, tgt_lun);
	CU_ASSERT_EQUAL(ret, 0);

	logging(LOG_NORMAL, "RELEASE6 from the second initiator");
	ret = release6(iscsic2, tgt_lun);
	CU_ASSERT_EQUAL(ret, 0);
}
void
test_reserve6_target_cold_reset(void)
{
	int ret;


	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test that RESERVE6 is released on target cold reset");


	logging(LOG_VERBOSE, "Take out a RESERVE6 from the first initiator");
	ret = reserve6(iscsic, tgt_lun);
	if (ret == -2) {
		logging(LOG_VERBOSE, "[SKIPPED] Target does not support RESERVE6. Skipping test");
		CU_PASS("[SKIPPED] Target does not support RESERVE6. Skipping test");
		return;
	}
	CU_ASSERT_EQUAL(ret, 0);


	logging(LOG_VERBOSE, "Send a Cold Reset to the target");
	ret = iscsi_task_mgmt_target_cold_reset_sync(iscsic);
	if (ret != 0) {
		logging(LOG_NORMAL, "Cold reset failed. %s", iscsi_get_error(iscsic));
	}
	CU_ASSERT_EQUAL(ret, 0);

	logging(LOG_VERBOSE, "Sleep for three seconds incase the target is slow to reset");
	sleep(3);

	logging(LOG_VERBOSE, "Create a second connection to the target");
	iscsic2 = iscsi_context_login(initiatorname1, tgt_url, &tgt_lun);
	if (iscsic2 == NULL) {
		logging(LOG_VERBOSE, "Failed to login to target");
		return;
	}

	logging(LOG_VERBOSE, "RESERVE6 from the second initiator should work now");
	ret = reserve6(iscsic2, tgt_lun);
	CU_ASSERT_EQUAL(ret, 0);

	logging(LOG_VERBOSE, "RELEASE6 from the second initiator");
	ret = release6(iscsic2, tgt_lun);
	CU_ASSERT_EQUAL(ret, 0);
}
void
test_reserve6_simple(void)
{
	int ret;


	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test basic RESERVE6/RELEASE6 commands if supported");

	ret = reserve6(iscsic, tgt_lun);
	if (ret == -2) {
		logging(LOG_VERBOSE, "[SKIPPED] Target does not support RESERVE6. Skipping test");
		CU_PASS("[SKIPPED] Target does not support RESERVE6. Skipping test");
		return;
	}
	CU_ASSERT_EQUAL(ret, 0);

	ret = release6(iscsic, tgt_lun);
	CU_ASSERT_EQUAL(ret, 0);
}
void
test_reserve6_2initiators(void)
{
    int ret;
    struct scsi_device sd2;
    memset(&sd2, 0, sizeof(sd2));
    sd2.iscsi_url = sd->iscsi_url;
    sd2.iscsi_lun = sd->iscsi_lun;

    logging(LOG_VERBOSE, LOG_BLANK_LINE);
    logging(LOG_VERBOSE, "Test RESERVE6/RELEASE6 across two initiators");

    if (sd->iscsi_ctx == NULL) {
        const char *err = "[SKIPPED] This RESERVE6 test is only "
                          "supported for iSCSI backends";
        logging(LOG_NORMAL, "%s", err);
        CU_PASS(err);
        return;
    }

    logging(LOG_NORMAL, "Take out a RESERVE6 from the first initiator");
    ret = reserve6(sd);
    if (ret == -2) {
        logging(LOG_VERBOSE, "[SKIPPED] Target does not support RESERVE6. Skipping test");
        CU_PASS("[SKIPPED] Target does not support RESERVE6. Skipping test");
        return;
    }
    CU_ASSERT_EQUAL(ret, 0);


    logging(LOG_NORMAL, "Verify that the first initiator can re-RESERVE6 the same reservation");
    ret = reserve6(sd);
    CU_ASSERT_EQUAL(ret, 0);


    logging(LOG_VERBOSE, "Create a second connection to the target");
    sd2.iscsi_ctx = iscsi_context_login(initiatorname2, sd2.iscsi_url, &sd2.iscsi_lun);
    if (sd2.iscsi_ctx == NULL) {
        logging(LOG_VERBOSE, "Failed to login to target");
        return;
    }

    logging(LOG_NORMAL, "Try to take out a RESERVE6 from the second initiator");
    ret = reserve6_conflict(&sd2);
    CU_ASSERT_EQUAL(ret, 0);


    logging(LOG_NORMAL, "Try to RELEASE from the second initiator. Should be a nop");
    ret = release6(&sd2);
    CU_ASSERT_EQUAL(ret, 0);


    logging(LOG_NORMAL, "Test we can still send MODE SENSE from the first initiator");
    ret = modesense6(sd, NULL, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255,
                     EXPECT_STATUS_GOOD);
    CU_ASSERT_EQUAL(ret, 0);

    logging(LOG_NORMAL, "MODE SENSE should fail from the second initiator");
    ret = modesense6(&sd2, NULL, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255,
                     EXPECT_RESERVATION_CONFLICT);


    logging(LOG_NORMAL, "RESERVE6 from the second initiator should still fail");
    ret = reserve6_conflict(&sd2);
    CU_ASSERT_EQUAL(ret, 0);

    logging(LOG_NORMAL, "RELEASE6 from the first initiator");
    ret = release6(sd);
    CU_ASSERT_EQUAL(ret, 0);

    logging(LOG_NORMAL, "RESERVE6 from the second initiator should work now");
    ret = reserve6(&sd2);
    CU_ASSERT_EQUAL(ret, 0);

    logging(LOG_NORMAL, "RELEASE6 from the second initiator");
    ret = release6(&sd2);
    CU_ASSERT_EQUAL(ret, 0);

    iscsi_logout_sync(sd2.iscsi_ctx);
    iscsi_destroy_context(sd2.iscsi_ctx);
}
void
test_sanitize_reservations(void)
{
	int ret;
	struct iscsi_data data;
	struct scsi_command_descriptor *cd;

	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test SANITIZE with RESERVATIONS");

	CHECK_FOR_SANITIZE;
	CHECK_FOR_DATALOSS;

	logging(LOG_VERBOSE, "Create a second connection to the target");
	iscsic2 = iscsi_context_login(initiatorname1, tgt_url, &tgt_lun);
	if (iscsic2 == NULL) {
		logging(LOG_VERBOSE, "Failed to login to target");
		return;
	}

	logging(LOG_VERBOSE, "Take out a RESERVE6 from the second "
			     "initiator");
	ret = reserve6(iscsic2, tgt_lun);
	CU_ASSERT_EQUAL(ret, 0);


	logging(LOG_VERBOSE, "Check if SANITIZE OVERWRITE is supported "
		"in REPORT_SUPPORTED_OPCODES");
	cd = get_command_descriptor(SCSI_OPCODE_SANITIZE,
				    SCSI_SANITIZE_OVERWRITE);
	if (cd == NULL) {
		logging(LOG_NORMAL, "[SKIPPED] SANITIZE OVERWRITE is not "
			"implemented according to REPORT_SUPPORTED_OPCODES.");
	} else {
		logging(LOG_VERBOSE, "Test SANITIZE OVERWRITE with "
			"initialization pattern of one full block");
		data.size = block_size + 4;
		data.data = alloca(data.size);
		memset(&data.data[4], 0xaa, block_size);

		data.data[0] = 0x01;
		data.data[1] = 0x00;
		data.data[2] = block_size >> 8;
		data.data[3] = block_size & 0xff;
		ret = sanitize_conflict(iscsic, tgt_lun,
		       0, 0, SCSI_SANITIZE_OVERWRITE, data.size, &data);
		CU_ASSERT_EQUAL(ret, 0);
	}


	logging(LOG_VERBOSE, "Check if SANITIZE BLOCK_ERASE is supported "
		"in REPORT_SUPPORTED_OPCODES");
	cd = get_command_descriptor(SCSI_OPCODE_SANITIZE,
				    SCSI_SANITIZE_BLOCK_ERASE);
	if (cd == NULL) {
		logging(LOG_NORMAL, "[SKIPPED] SANITIZE BLOCK_ERASE is not "
			"implemented according to REPORT_SUPPORTED_OPCODES.");
	} else {
		logging(LOG_VERBOSE, "Test SANITIZE BLOCK_ERASE");
		ret = sanitize_conflict(iscsic, tgt_lun,
		       0, 0, SCSI_SANITIZE_BLOCK_ERASE, 0, NULL);
		CU_ASSERT_EQUAL(ret, 0);
	}

	logging(LOG_VERBOSE, "Check if SANITIZE CRYPTO_ERASE is supported "
		"in REPORT_SUPPORTED_OPCODES");
	cd = get_command_descriptor(SCSI_OPCODE_SANITIZE,
				    SCSI_SANITIZE_CRYPTO_ERASE);
	if (cd == NULL) {
		logging(LOG_NORMAL, "[SKIPPED] SANITIZE CRYPTO_ERASE is not "
			"implemented according to REPORT_SUPPORTED_OPCODES.");
	} else {
		logging(LOG_VERBOSE, "Test SANITIZE CRYPTO_ERASE");
		ret = sanitize_conflict(iscsic, tgt_lun,
		       0, 0, SCSI_SANITIZE_CRYPTO_ERASE, 0, NULL);
		CU_ASSERT_EQUAL(ret, 0);
	}


	iscsi_destroy_context(iscsic2);
	iscsic2 = NULL;
}