diff --git a/Makefile.am b/Makefile.am index bb3a7ad..1c5c323 100644 --- a/Makefile.am +++ b/Makefile.am @@ -58,7 +58,8 @@ bin_iscsi_test_SOURCES = test-tool/iscsi-test.c \ test-tool/0104_read10_flags.c test-tool/0105_read10_invalid.c \ test-tool/0110_readcapacity10_simple.c \ test-tool/0111_readcapacity10_pmi.c test-tool/0120_read6_simple.c \ - test-tool/0121_read6_beyond_eol.c test-tool/0122_read6_invalid.c + test-tool/0121_read6_beyond_eol.c test-tool/0122_read6_invalid.c \ + test-tool/0130_verify10_simple.c endif # LD_PRELOAD library. diff --git a/include/iscsi-private.h b/include/iscsi-private.h index 4842fce..c061516 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -256,6 +256,7 @@ void iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, unsigned char *iscsi_get_user_in_buffer(struct iscsi_context *iscsi, struct iscsi_in_pdu *in, uint32_t pos, ssize_t *count); unsigned char *scsi_task_get_data_in_buffer(struct scsi_task *task, uint32_t pos, ssize_t *count); + unsigned long crc32c(char *buf, int len); #endif /* __iscsi_private_h__ */ diff --git a/include/iscsi.h b/include/iscsi.h index 0ee7d42..6e6fb26 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -593,6 +593,12 @@ iscsi_write10_task(struct iscsi_context *iscsi, int lun, int fuanv, int blocksize, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * +iscsi_verify10_task(struct iscsi_context *iscsi, int lun, + unsigned char *data, uint32_t datalen, uint32_t lba, + int vprotect, int dpo, int bytchk, + int blocksize, iscsi_command_cb cb, + void *private_data); +EXTERN struct scsi_task * iscsi_modesense6_task(struct iscsi_context *iscsi, int lun, int dbd, int pc, int page_code, int sub_page_code, unsigned char alloc_len, iscsi_command_cb cb, @@ -638,6 +644,11 @@ iscsi_write10_sync(struct iscsi_context *iscsi, int lun, unsigned char *data, uint32_t datalen, uint32_t lba, int fua, int fuanv, int blocksize); +EXTERN struct scsi_task * +iscsi_verify10_sync(struct iscsi_context *iscsi, int lun, + unsigned char *data, uint32_t datalen, uint32_t lba, + int vprotect, int dpo, int bytchk, + int blocksize); /* * This function is used when the application wants to specify its own buffers to read the data diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index ba47bfd..2cbf683 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -27,6 +27,7 @@ enum scsi_opcode { SCSI_OPCODE_READCAPACITY10 = 0x25, SCSI_OPCODE_READ10 = 0x28, SCSI_OPCODE_WRITE10 = 0x2A, + SCSI_OPCODE_VERIFY10 = 0x2F, SCSI_OPCODE_SYNCHRONIZECACHE10 = 0x35, SCSI_OPCODE_REPORTLUNS = 0xA0 }; @@ -83,6 +84,13 @@ struct scsi_write10_params { uint32_t lba; uint32_t num_blocks; }; +struct scsi_verify10_params { + uint32_t lba; + uint32_t num_blocks; + int vprotect; + int dpo; + int bytchk; +}; struct scsi_readcapacity10_params { int lba; int pmi; @@ -131,6 +139,7 @@ struct scsi_task { struct scsi_read6_params read6; struct scsi_read10_params read10; struct scsi_write10_params write10; + struct scsi_verify10_params verify10; struct scsi_readcapacity10_params readcapacity10; struct scsi_reportluns_params reportluns; struct scsi_inquiry_params inquiry; @@ -481,9 +490,11 @@ EXTERN struct scsi_task *scsi_cdb_read6(uint32_t lba, uint32_t xferlen, int bloc EXTERN struct scsi_task *scsi_cdb_read10(uint32_t lba, uint32_t xferlen, int blocksize); EXTERN struct scsi_task *scsi_cdb_write10(uint32_t lba, uint32_t xferlen, int fua, int fuanv, int blocksize); +EXTERN struct scsi_task *scsi_cdb_verify10(uint32_t lba, uint32_t xferlen, int vprotect, int dpo, int bytchk, int blocksize); EXTERN struct scsi_task *scsi_cdb_synchronizecache10(int lba, int num_blocks, int syncnv, int immed); + #endif /* __scsi_lowlevel_h__ */ diff --git a/lib/libiscsi.def b/lib/libiscsi.def index ff7e767..32ce0be 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -46,6 +46,7 @@ iscsi_readcapacity10_task iscsi_synchronizecache10_task iscsi_read6_task iscsi_read10_task +iscsi_verify10_task iscsi_write10_task iscsi_modesense6_task iscsi_scsi_command_sync @@ -56,6 +57,7 @@ iscsi_readcapacity10_sync iscsi_synchronizecache10_sync iscsi_read6_sync iscsi_read10_sync +iscsi_verify10_sync iscsi_write10_sync iscsi_task_cancel poll @@ -81,6 +83,7 @@ scsi_datain_getfullsize scsi_datain_unmarshall scsi_cdb_read6 scsi_cdb_read10 +scsi_cdb_verify10 scsi_cdb_write10 scsi_cdb_synchronizecache10 diff --git a/lib/scsi-command.c b/lib/scsi-command.c index e47defa..2ab38e2 100644 --- a/lib/scsi-command.c +++ b/lib/scsi-command.c @@ -677,7 +677,40 @@ iscsi_write10_task(struct iscsi_context *iscsi, int lun, unsigned char *data, task = scsi_cdb_write10(lba, datalen, fua, fuanv, blocksize); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " - "read10 cdb."); + "write10 cdb."); + return NULL; + } + + outdata.data = data; + outdata.size = datalen; + + if (iscsi_scsi_command_async(iscsi, lun, task, cb, &outdata, + private_data) != 0) { + scsi_free_scsi_task(task); + return NULL; + } + + return task; +} + +struct scsi_task * +iscsi_verify10_task(struct iscsi_context *iscsi, int lun, unsigned char *data, + uint32_t datalen, uint32_t lba, int vprotect, int dpo, int bytchk, int blocksize, + iscsi_command_cb cb, void *private_data) +{ + struct scsi_task *task; + struct iscsi_data outdata; + + if (datalen % blocksize != 0) { + iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the " + "blocksize:%d.", datalen, blocksize); + return NULL; + } + + task = scsi_cdb_verify10(lba, datalen, vprotect, dpo, bytchk, blocksize); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "verify10 cdb."); return NULL; } diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index 33541ab..e2db940 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -632,6 +632,52 @@ scsi_cdb_write10(uint32_t lba, uint32_t xferlen, int fua, int fuanv, int blocksi } +/* + * VERIFY10 + */ +struct scsi_task * +scsi_cdb_verify10(uint32_t lba, uint32_t xferlen, int vprotect, int dpo, int bytchk, int blocksize) +{ + struct scsi_task *task; + + task = malloc(sizeof(struct scsi_task)); + if (task == NULL) { + return NULL; + } + + memset(task, 0, sizeof(struct scsi_task)); + task->cdb[0] = SCSI_OPCODE_VERIFY10; + + if (vprotect) { + task->cdb[1] |= ((vprotect << 5) & 0xe0); + } + if (dpo) { + task->cdb[1] |= 0x10; + } + if (bytchk) { + task->cdb[1] |= 0x02; + } + + *(uint32_t *)&task->cdb[2] = htonl(lba); + *(uint16_t *)&task->cdb[7] = htons(xferlen/blocksize); + + task->cdb_size = 10; + if (xferlen != 0) { + task->xfer_dir = SCSI_XFER_WRITE; + } else { + task->xfer_dir = SCSI_XFER_NONE; + } + task->expxferlen = xferlen; + + task->params.verify10.lba = lba; + task->params.verify10.num_blocks = xferlen/blocksize; + task->params.verify10.vprotect = vprotect; + task->params.verify10.dpo = dpo; + task->params.verify10.bytchk = bytchk; + + return task; +} + /* * MODESENSE6 diff --git a/lib/sync.c b/lib/sync.c index c112f98..3b58e90 100644 --- a/lib/sync.c +++ b/lib/sync.c @@ -322,6 +322,26 @@ iscsi_write10_sync(struct iscsi_context *iscsi, int lun, unsigned char *data, ui return state.task; } +struct scsi_task * +iscsi_verify10_sync(struct iscsi_context *iscsi, int lun, unsigned char *data, uint32_t datalen, uint32_t lba, + int vprotect, int dpo, int bytchk, int blocksize) +{ + struct iscsi_sync_state state; + + memset(&state, 0, sizeof(state)); + + if (iscsi_verify10_task(iscsi, lun, data, datalen, lba, vprotect, dpo, bytchk, blocksize, + scsi_sync_cb, &state) == NULL) { + iscsi_set_error(iscsi, + "Failed to send Verify10 command"); + return NULL; + } + + event_loop(iscsi, &state); + + return state.task; +} + struct scsi_task * iscsi_scsi_command_sync(struct iscsi_context *iscsi, int lun, struct scsi_task *task, struct iscsi_data *data) diff --git a/test-tool/0130_verify10_simple.c b/test-tool/0130_verify10_simple.c new file mode 100644 index 0000000..2db44d4 --- /dev/null +++ b/test-tool/0130_verify10_simple.c @@ -0,0 +1,121 @@ +/* + Copyright (C) 2010 by 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 "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test.h" + +int T0130_verify10_simple(const char *initiator, const char *url) +{ + struct iscsi_context *iscsi; + struct scsi_task *task; + struct scsi_task *vtask; + struct scsi_readcapacity10 *rc10; + int ret, i, lun; + uint32_t block_size, num_blocks; + + iscsi = iscsi_context_login(initiator, url, &lun); + if (iscsi == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + /* find the size of the LUN */ + task = iscsi_readcapacity10_sync(iscsi, lun, 0, 0); + if (task == NULL) { + printf("Failed to send readcapacity10 command: %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("Readcapacity command: failed with sense. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + rc10 = scsi_datain_unmarshall(task); + if (rc10 == NULL) { + printf("failed to unmarshall readcapacity10 data. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + block_size = rc10->block_size; + num_blocks = rc10->lba; + scsi_free_scsi_task(task); + + + + ret = 0; + + /* read and verify the first 1 - 256 blocks at the start of the LUN */ + printf("Read+verify first 1-256 blocks ... "); + for (i = 1; i <= 256; i++) { + char *buf; + + task = iscsi_read10_sync(iscsi, lun, 0, i * block_size, block_size); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send read10 command: %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("Read10 command: failed with sense. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + + buf = task->datain.data; + if (buf == NULL) { + printf("[FAILED]\n"); + printf("Failed to access DATA-IN buffer %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + + vtask = iscsi_verify10_sync(iscsi, lun, buf, i * block_size, 0, 0, 1, 1, block_size); + if (vtask == NULL) { + printf("[FAILED]\n"); + printf("Failed to send verify10 command: %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + if (vtask->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("Verify10 command: failed with sense. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + scsi_free_scsi_task(vtask); + goto finished; + } + + scsi_free_scsi_task(task); + scsi_free_scsi_task(vtask); + } + printf("[OK]\n"); + +finished: + iscsi_logout_sync(iscsi); + iscsi_destroy_context(iscsi); + return ret; +} diff --git a/test-tool/iscsi-test.c b/test-tool/iscsi-test.c index 5e3f7d2..70d69ae 100644 --- a/test-tool/iscsi-test.c +++ b/test-tool/iscsi-test.c @@ -28,10 +28,10 @@ #include "iscsi.h" #include "iscsi-test.h" -char *initiator = "iqn.2010-11.iscsi-test"; +const char *initiator = "iqn.2010-11.iscsi-test"; struct scsi_test { - char *name; + const char *name; int (*test)(const char *initiator, const char *url); }; @@ -53,6 +53,9 @@ struct scsi_test tests[] = { { "T0121_read6_beyond_eol", T0121_read6_beyond_eol }, { "T0122_read6_invalid", T0122_read6_invalid }, +/* verify10*/ +{ "T0130_verify10_simple", T0130_verify10_simple }, + { NULL, NULL } }; diff --git a/test-tool/iscsi-test.h b/test-tool/iscsi-test.h index 24ccc74..527c324 100644 --- a/test-tool/iscsi-test.h +++ b/test-tool/iscsi-test.h @@ -33,3 +33,5 @@ int T0111_readcapacity10_pmi(const char *initiator, const char *url); int T0120_read6_simple(const char *initiator, const char *url); int T0121_read6_beyond_eol(const char *initiator, const char *url); int T0122_read6_invalid(const char *initiator, const char *url); + +int T0130_verify10_simple(const char *initiator, const char *url);