From a3283fbafd00a26e939ac5fcb5399d7a911427a3 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 9 Jul 2013 02:27:30 -0700 Subject: [PATCH] TESTS: Add a test that SANITIZE fails when SWP is on. --- Makefile.am | 1 + test-tool/iscsi-support.c | 183 ++++++++++++++++++++++++- test-tool/iscsi-support.h | 3 + test-tool/iscsi-test-cu.c | 1 + test-tool/iscsi-test-cu.h | 1 + test-tool/test_sanitize_readonly.c | 114 +++++++++++++++ test-tool/test_sanitize_reservations.c | 3 - 7 files changed, 302 insertions(+), 4 deletions(-) create mode 100644 test-tool/test_sanitize_readonly.c diff --git a/Makefile.am b/Makefile.am index cd09c2b..b50f4db 100644 --- a/Makefile.am +++ b/Makefile.am @@ -277,6 +277,7 @@ bin_iscsi_test_cu_SOURCES = test-tool/iscsi-test-cu.c \ test-tool/test_sanitize_invalid_serviceaction.c \ test-tool/test_sanitize_overwrite.c \ test-tool/test_sanitize_overwrite_reserved.c \ + test-tool/test_sanitize_readonly.c \ test-tool/test_sanitize_reservations.c \ test-tool/test_startstopunit_simple.c \ test-tool/test_startstopunit_pwrcnd.c \ diff --git a/test-tool/iscsi-support.c b/test-tool/iscsi-support.c index a8a048a..ec33929 100644 --- a/test-tool/iscsi-support.c +++ b/test-tool/iscsi-support.c @@ -1418,8 +1418,16 @@ int sanitize_conflict(struct iscsi_context *iscsi, int lun, int immed, int ause, iscsi_get_error(iscsi)); return -1; } + if (task->status == SCSI_STATUS_GOOD) { + logging(LOG_NORMAL, + "[FAILED] SANITIZE successful but should have failed with RESERVATION_CONFLICT"); + scsi_free_scsi_task(task); + return -1; + } + if (task->status != SCSI_STATUS_RESERVATION_CONFLICT) { - logging(LOG_NORMAL, "[FAILED] Expected RESERVATION CONFLICT"); + logging(LOG_NORMAL, "[FAILED] Expected RESERVATION CONFLICT. " + "Sense:%s", iscsi_get_error(iscsi)); return -1; } @@ -1428,6 +1436,45 @@ int sanitize_conflict(struct iscsi_context *iscsi, int lun, int immed, int ause, return 0; } +int sanitize_writeprotected(struct iscsi_context *iscsi, int lun, int immed, int ause, int sa, int param_len, struct iscsi_data *data) +{ + struct scsi_task *task; + + logging(LOG_VERBOSE, "Send SANITIZE (Expecting RESERVATION_CONFLICT) " + "IMMED:%d AUSE:%d SA:%d " + "PARAM_LEN:%d", + immed, ause, sa, param_len); + + task = iscsi_sanitize_sync(iscsi, lun, immed, ause, sa, param_len, + data); + if (task == NULL) { + logging(LOG_NORMAL, + "[FAILED] Failed to send SANITIZE command: %s", + iscsi_get_error(iscsi)); + return -1; + } + if (task->status == SCSI_STATUS_GOOD) { + logging(LOG_NORMAL, "[FAILED] SANITIZE successful but should " + "have failed with DATA_PROTECTION/WRITE_PROTECTED"); + scsi_free_scsi_task(task); + return -1; + } + if (task->status != SCSI_STATUS_CHECK_CONDITION + || task->sense.key != SCSI_SENSE_DATA_PROTECTION + || task->sense.ascq != SCSI_SENSE_ASCQ_WRITE_PROTECTED) { + logging(LOG_NORMAL, "[FAILED] SANITIZE failed with wrong " + "sense. Should have failed with DATA_PRTOTECTION/" + "WRITE_PROTECTED. Sense:%s\n", + iscsi_get_error(iscsi)); + scsi_free_scsi_task(task); + return -1; + } + + scsi_free_scsi_task(task); + logging(LOG_VERBOSE, "[OK] SANITIZE returned DATA_PROTECTION/WRITE_PROTECTED."); + return 0; +} + int startstopunit(struct iscsi_context *iscsi, int lun, int immed, int pcm, int pc, int no_flush, int loej, int start) { struct scsi_task *task; @@ -5969,3 +6016,137 @@ get_command_descriptor(int opcode, int sa) return NULL; } + +int set_swp(struct iscsi_context *iscsi, int lun) +{ + int ret = 0; + struct scsi_task *sense_task = NULL; + struct scsi_task *select_task = NULL; + struct scsi_mode_sense *ms; + struct scsi_mode_page *mp; + + logging(LOG_VERBOSE, "Read CONTROL page"); + sense_task = iscsi_modesense6_sync(iscsi, lun, + 1, SCSI_MODESENSE_PC_CURRENT, + SCSI_MODESENSE_PAGECODE_CONTROL, + 0, 255); + if (sense_task == NULL) { + logging(LOG_NORMAL, "Failed to send MODE_SENSE6 command: %s", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (sense_task->status != SCSI_STATUS_GOOD) { + logging(LOG_NORMAL, "MODE_SENSE6 failed: %s", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + ms = scsi_datain_unmarshall(sense_task); + if (ms == NULL) { + logging(LOG_NORMAL, "failed to unmarshall mode sense datain " + "blob"); + ret = -1; + goto finished; + } + mp = scsi_modesense_get_page(ms, SCSI_MODESENSE_PAGECODE_CONTROL, 0); + if (mp == NULL) { + logging(LOG_NORMAL, "failed to read control mode page"); + ret = -1; + goto finished; + } + + logging(LOG_VERBOSE, "Turn SWP ON"); + mp->control.swp = 1; + + select_task = iscsi_modeselect6_sync(iscsi, lun, + 1, 0, mp); + if (select_task == NULL) { + logging(LOG_NORMAL, "Failed to send MODE_SELECT6 command: %s", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (select_task->status != SCSI_STATUS_GOOD) { + logging(LOG_NORMAL, "MODE_SELECT6 failed: %s", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + +finished: + if (sense_task != NULL) { + scsi_free_scsi_task(sense_task); + } + if (select_task != NULL) { + scsi_free_scsi_task(select_task); + } + return ret; +} + +int clear_swp(struct iscsi_context *iscsi, int lun) +{ + int ret = 0; + struct scsi_task *sense_task = NULL; + struct scsi_task *select_task = NULL; + struct scsi_mode_sense *ms; + struct scsi_mode_page *mp; + + logging(LOG_VERBOSE, "Read CONTROL page"); + sense_task = iscsi_modesense6_sync(iscsi, lun, + 1, SCSI_MODESENSE_PC_CURRENT, + SCSI_MODESENSE_PAGECODE_CONTROL, + 0, 255); + if (sense_task == NULL) { + logging(LOG_NORMAL, "Failed to send MODE_SENSE6 command: %s", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (sense_task->status != SCSI_STATUS_GOOD) { + logging(LOG_NORMAL, "MODE_SENSE6 failed: %s", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + ms = scsi_datain_unmarshall(sense_task); + if (ms == NULL) { + logging(LOG_NORMAL, "failed to unmarshall mode sense datain " + "blob"); + ret = -1; + goto finished; + } + mp = scsi_modesense_get_page(ms, SCSI_MODESENSE_PAGECODE_CONTROL, 0); + if (mp == NULL) { + logging(LOG_NORMAL, "failed to read control mode page"); + ret = -1; + goto finished; + } + + logging(LOG_VERBOSE, "Turn SWP OFF"); + mp->control.swp = 0; + + select_task = iscsi_modeselect6_sync(iscsi, lun, + 1, 0, mp); + if (select_task == NULL) { + logging(LOG_NORMAL, "Failed to send MODE_SELECT6 command: %s", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (select_task->status != SCSI_STATUS_GOOD) { + logging(LOG_NORMAL, "MODE_SELECT6 failed: %s", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + +finished: + if (sense_task != NULL) { + scsi_free_scsi_task(sense_task); + } + if (select_task != NULL) { + scsi_free_scsi_task(select_task); + } + return ret; +} diff --git a/test-tool/iscsi-support.h b/test-tool/iscsi-support.h index 3472fc8..3fa92ed 100644 --- a/test-tool/iscsi-support.h +++ b/test-tool/iscsi-support.h @@ -268,6 +268,7 @@ int reserve6_conflict(struct iscsi_context *iscsi, int lun); int sanitize(struct iscsi_context *iscsi, int lun, int immed, int ause, int sa, int param_len, struct iscsi_data *data); int sanitize_conflict(struct iscsi_context *iscsi, int lun, int immed, int ause, int sa, int param_len, struct iscsi_data *data); int sanitize_invalidfieldincdb(struct iscsi_context *iscsi, int lun, int immed, int ause, int sa, int param_len, struct iscsi_data *data); +int sanitize_writeprotected(struct iscsi_context *iscsi, int lun, int immed, int ause, int sa, int param_len, struct iscsi_data *data); int startstopunit(struct iscsi_context *iscsi, int lun, int immed, int pcm, int pc, int no_flush, int loej, int start); int startstopunit_preventremoval(struct iscsi_context *iscsi, int lun, int immed, int pcm, int pc, int no_flush, int loej, int start); int synchronizecache10(struct iscsi_context *iscsi, int lun, uint32_t lba, int num_blocks, int sync_nv, int immed); @@ -337,6 +338,8 @@ int writeverify16_lbaoutofrange(struct iscsi_context *iscsi, int lun, uint64_t l int writeverify16_writeprotected(struct iscsi_context *iscsi, int lun, uint64_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group, unsigned char *data); int writeverify16_nomedium(struct iscsi_context *iscsi, int lun, uint64_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group, unsigned char *data); +int set_swp(struct iscsi_context *iscsi, int lun); +int clear_swp(struct iscsi_context *iscsi, int lun); #endif /* _ISCSI_SUPPORT_H_ */ diff --git a/test-tool/iscsi-test-cu.c b/test-tool/iscsi-test-cu.c index ac27582..868c258 100644 --- a/test-tool/iscsi-test-cu.c +++ b/test-tool/iscsi-test-cu.c @@ -235,6 +235,7 @@ static CU_TestInfo tests_sanitize[] = { { (char *)"InvalidServiceAction", test_sanitize_invalid_serviceaction }, { (char *)"Overwrite", test_sanitize_overwrite }, { (char *)"OverwriteReserved", test_sanitize_overwrite_reserved }, + { (char *)"Readonly", test_sanitize_readonly }, { (char *)"Reservations", test_sanitize_reservations }, CU_TEST_INFO_NULL }; diff --git a/test-tool/iscsi-test-cu.h b/test-tool/iscsi-test-cu.h index 70e081c..9c0f345 100644 --- a/test-tool/iscsi-test-cu.h +++ b/test-tool/iscsi-test-cu.h @@ -161,6 +161,7 @@ void test_sanitize_exit_failure_mode(void); void test_sanitize_invalid_serviceaction(void); void test_sanitize_overwrite(void); void test_sanitize_overwrite_reserved(void); +void test_sanitize_readonly(void); void test_sanitize_reservations(void); void test_startstopunit_simple(void); diff --git a/test-tool/test_sanitize_readonly.c b/test-tool/test_sanitize_readonly.c new file mode 100644 index 0000000..a486373 --- /dev/null +++ b/test-tool/test_sanitize_readonly.c @@ -0,0 +1,114 @@ +/* + Copyright (C) 2013 Ronnie Sahlberg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include + +#include + +#include "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test-cu.h" + +void +test_sanitize_readonly(void) +{ + int ret; + struct iscsi_data data; + struct scsi_command_descriptor *cd; + + logging(LOG_VERBOSE, LOG_BLANK_LINE); + logging(LOG_VERBOSE, "Test SANITIZE with READONLY devices"); + + 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, "Set Software Write Protect on the second connection"); + ret = set_swp(iscsic2, tgt_lun); + CU_ASSERT_EQUAL(ret, 0); + if (ret != 0) { + return; + } + + 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_writeprotected(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_writeprotected(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_writeprotected(iscsic, tgt_lun, + 0, 0, SCSI_SANITIZE_CRYPTO_ERASE, 0, NULL); + CU_ASSERT_EQUAL(ret, 0); + } + + + logging(LOG_VERBOSE, "Clear Software Write Protect on the second connection"); + ret = clear_swp(iscsic2, tgt_lun); + + iscsi_destroy_context(iscsic2); + iscsic2 = NULL; +} diff --git a/test-tool/test_sanitize_reservations.c b/test-tool/test_sanitize_reservations.c index 1fdfd0e..32ba6e5 100644 --- a/test-tool/test_sanitize_reservations.c +++ b/test-tool/test_sanitize_reservations.c @@ -59,7 +59,6 @@ test_sanitize_reservations(void) if (cd == NULL) { logging(LOG_NORMAL, "[SKIPPED] SANITIZE OVERWRITE is not " "implemented according to REPORT_SUPPORTED_OPCODES."); - return; } else { logging(LOG_VERBOSE, "Test SANITIZE OVERWRITE with " "initialization pattern of one full block"); @@ -84,7 +83,6 @@ test_sanitize_reservations(void) if (cd == NULL) { logging(LOG_NORMAL, "[SKIPPED] SANITIZE BLOCK_ERASE is not " "implemented according to REPORT_SUPPORTED_OPCODES."); - return; } else { logging(LOG_VERBOSE, "Test SANITIZE BLOCK_ERASE"); ret = sanitize_conflict(iscsic, tgt_lun, @@ -99,7 +97,6 @@ test_sanitize_reservations(void) if (cd == NULL) { logging(LOG_NORMAL, "[SKIPPED] SANITIZE CRYPTO_ERASE is not " "implemented according to REPORT_SUPPORTED_OPCODES."); - return; } else { logging(LOG_VERBOSE, "Test SANITIZE CRYPTO_ERASE"); ret = sanitize_conflict(iscsic, tgt_lun,