diff --git a/Makefile.am b/Makefile.am index e014a93..e111680 100644 --- a/Makefile.am +++ b/Makefile.am @@ -62,7 +62,8 @@ bin_iscsi_test_SOURCES = test-tool/iscsi-test.c \ test-tool/0130_verify10_simple.c test-tool/0131_verify10_mismatch.c \ test-tool/0132_verify10_mismatch_no_cmp.c \ test-tool/0143_read12_rdprotect.c \ - test-tool/0153_read16_rdprotect.c + test-tool/0153_read16_rdprotect.c \ + test-tool/0160_readcapacity16_simple.c endif # LD_PRELOAD library. diff --git a/include/iscsi.h b/include/iscsi.h index 06fae78..84114ae 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -579,6 +579,10 @@ iscsi_readcapacity10_task(struct iscsi_context *iscsi, int lun, int lba, int pmi, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * +iscsi_readcapacity16_task(struct iscsi_context *iscsi, int lun, + iscsi_command_cb cb, + void *private_data); +EXTERN struct scsi_task * iscsi_synchronizecache10_task(struct iscsi_context *iscsi, int lun, int lba, int num_blocks, int syncnv, int immed, iscsi_command_cb cb, @@ -640,6 +644,9 @@ EXTERN struct scsi_task * iscsi_readcapacity10_sync(struct iscsi_context *iscsi, int lun, int lba, int pmi); +EXTERN struct scsi_task * +iscsi_readcapacity16_sync(struct iscsi_context *iscsi, int lun); + EXTERN struct scsi_task * iscsi_synchronizecache10_sync(struct iscsi_context *iscsi, int lun, int lba, int num_blocks, int syncnv, int immed); diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index c8b8b37..31970be 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -30,10 +30,15 @@ enum scsi_opcode { SCSI_OPCODE_VERIFY10 = 0x2F, SCSI_OPCODE_SYNCHRONIZECACHE10 = 0x35, SCSI_OPCODE_READ16 = 0x88, + SCSI_OPCODE_SERVICE_ACTION_IN = 0x9E, SCSI_OPCODE_REPORTLUNS = 0xA0, SCSI_OPCODE_READ12 = 0xA8 }; +enum scsi_service_action_in { + SCSI_READCAPACITY16 = 0x10 +}; + /* sense keys */ enum scsi_sense_key { SCSI_SENSE_NO_SENSE = 0x00, @@ -107,6 +112,9 @@ struct scsi_modesense6_params { int page_code; int sub_page_code; }; +struct scsi_serviceactionin_params { + enum scsi_service_action_in sa; +}; struct scsi_sense { unsigned char error_type; @@ -138,14 +146,15 @@ struct scsi_task { int expxferlen; unsigned char cdb[SCSI_CDB_MAX_SIZE]; union { - 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; - struct scsi_modesense6_params modesense6; + 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; + struct scsi_modesense6_params modesense6; + struct scsi_serviceactionin_params serviceactionin; } params; enum scsi_residual residual_status; @@ -507,6 +516,18 @@ EXTERN struct scsi_task *scsi_cdb_modesense6(int dbd, +struct scsi_readcapacity16 { + uint64_t returned_lba; + uint32_t block_length; + uint8_t p_type; + uint8_t prot_en; + uint8_t p_i_exp; + uint8_t lbppbe; + uint8_t lbpme; + uint8_t lbprz; + uint16_t lalba; +}; + EXTERN int scsi_datain_getfullsize(struct scsi_task *task); EXTERN void *scsi_datain_unmarshall(struct scsi_task *task); @@ -518,6 +539,8 @@ EXTERN struct scsi_task *scsi_cdb_verify10(uint32_t lba, uint32_t xferlen, int v EXTERN struct scsi_task *scsi_cdb_synchronizecache10(int lba, int num_blocks, int syncnv, int immed); +EXTERN struct scsi_task *scsi_cdb_serviceactionin16(enum scsi_service_action_in sa, uint32_t xferlen); +EXTERN struct scsi_task *scsi_cdb_readcapacity16(void); #endif /* __scsi_lowlevel_h__ */ diff --git a/lib/libiscsi.def b/lib/libiscsi.def index e9e8a33..988fa07 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -43,6 +43,7 @@ iscsi_reportluns_task iscsi_testunitready_task iscsi_inquiry_task iscsi_readcapacity10_task +iscsi_readcapacity16_task iscsi_synchronizecache10_task iscsi_read6_task iscsi_read10_task @@ -54,6 +55,7 @@ iscsi_reportluns_sync iscsi_testunitready_sync iscsi_inquiry_sync iscsi_readcapacity10_sync +iscsi_readcapacity16_sync iscsi_synchronizecache10_sync iscsi_read6_sync iscsi_read10_sync @@ -87,4 +89,6 @@ scsi_cdb_read10 scsi_cdb_verify10 scsi_cdb_write10 scsi_cdb_synchronizecache10 +scsi_cdb_readcapacity16 +scsi_cdb_serviceactionin16 diff --git a/lib/scsi-command.c b/lib/scsi-command.c index 35c2f75..e3e481b 100644 --- a/lib/scsi-command.c +++ b/lib/scsi-command.c @@ -604,6 +604,27 @@ iscsi_readcapacity10_task(struct iscsi_context *iscsi, int lun, int lba, return task; } +struct scsi_task * +iscsi_readcapacity16_task(struct iscsi_context *iscsi, int lun, + iscsi_command_cb cb, void *private_data) +{ + struct scsi_task *task; + + task = scsi_cdb_readcapacity16(); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "readcapacity16 cdb."); + return NULL; + } + if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, + private_data) != 0) { + scsi_free_scsi_task(task); + return NULL; + } + + return task; +} + struct scsi_task * iscsi_read6_task(struct iscsi_context *iscsi, int lun, uint32_t lba, uint32_t datalen, int blocksize, diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index 7f7a38b..7d46a98 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -258,6 +258,36 @@ scsi_cdb_readcapacity10(int lba, int pmi) return task; } +/* + * service_action_in unmarshall + */ +static void * +scsi_serviceactionin_datain_unmarshall(struct scsi_task *task) +{ + struct scsi_readcapacity16 *rc16; + + switch (task->params.serviceactionin.sa) { + case SCSI_READCAPACITY16: + rc16 = scsi_malloc(task, sizeof(struct scsi_readcapacity16)); + if (rc16 == NULL) { + return NULL; + } + rc16->returned_lba = htonl(*(uint32_t *)&(task->datain.data[0])); + rc16->returned_lba = (rc16->returned_lba << 32) | htonl(*(uint32_t *)&(task->datain.data[4])); + rc16->block_length = htonl(*(uint32_t *)&(task->datain.data[8])); + rc16->p_type = (task->datain.data[12] >> 1) & 0x07; + rc16->prot_en = task->datain.data[12] & 0x01; + rc16->p_i_exp = (task->datain.data[13] >> 4) & 0x0f; + rc16->lbppbe = task->datain.data[13] & 0x0f; + rc16->lbpme = !!(task->datain.data[14] & 0x80); + rc16->lbprz = !!(task->datain.data[14] & 0x40); + rc16->lalba = htons(*(uint16_t *)&(task->datain.data[14])) & 0x3fff; + return rc16; + } + + return NULL; +} + /* * parse the data in blob and calcualte the size of a full * readcapacity10 datain structure @@ -923,6 +953,43 @@ scsi_cdb_synchronizecache10(int lba, int num_blocks, int syncnv, int immed) } +/* + * SERVICEACTIONIN16 + */ +struct scsi_task * +scsi_cdb_serviceactionin16(enum scsi_service_action_in sa, uint32_t xferlen) +{ + 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_SERVICE_ACTION_IN; + + task->cdb[1] = sa; + + *(uint32_t *)&task->cdb[10] = htonl(xferlen); + + task->cdb_size = 16; + task->xfer_dir = SCSI_XFER_READ; + task->expxferlen = xferlen; + + task->params.serviceactionin.sa = sa; + + return task; +} + +/* + * READCAPACITY16 + */ +struct scsi_task * +scsi_cdb_readcapacity16(void) +{ + return scsi_cdb_serviceactionin16(SCSI_READCAPACITY16, 32); +} int scsi_datain_getfullsize(struct scsi_task *task) @@ -960,6 +1027,8 @@ scsi_datain_unmarshall(struct scsi_task *task) return NULL; case SCSI_OPCODE_REPORTLUNS: return scsi_reportluns_datain_unmarshall(task); + case SCSI_OPCODE_SERVICE_ACTION_IN: + return scsi_serviceactionin_datain_unmarshall(task); } return NULL; } diff --git a/lib/sync.c b/lib/sync.c index 3b58e90..352094d 100644 --- a/lib/sync.c +++ b/lib/sync.c @@ -281,6 +281,25 @@ iscsi_readcapacity10_sync(struct iscsi_context *iscsi, int lun, int lba, return state.task; } +struct scsi_task * +iscsi_readcapacity16_sync(struct iscsi_context *iscsi, int lun) +{ + struct iscsi_sync_state state; + + memset(&state, 0, sizeof(state)); + + if (iscsi_readcapacity16_task(iscsi, lun, + scsi_sync_cb, &state) == NULL) { + iscsi_set_error(iscsi, + "Failed to send ReadCapacity16 command"); + return NULL; + } + + event_loop(iscsi, &state); + + return state.task; +} + struct scsi_task * iscsi_synchronizecache10_sync(struct iscsi_context *iscsi, int lun, int lba, int num_blocks, int syncnv, int immed) diff --git a/m4/.gitignore b/m4/.gitignore index 764b428..94e6f26 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -1,5 +1,5 @@ /libtool.m4 -/lt~obsolete.m4 /ltoptions.m4 /ltsugar.m4 /ltversion.m4 +/lt~obsolete.m4 diff --git a/test-tool/0160_readcapacity16_simple.c b/test-tool/0160_readcapacity16_simple.c new file mode 100644 index 0000000..77d4498 --- /dev/null +++ b/test-tool/0160_readcapacity16_simple.c @@ -0,0 +1,68 @@ +/* + Copyright (C) 2012 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 T0160_readcapacity16_simple(const char *initiator, const char *url) +{ + struct iscsi_context *iscsi; + struct scsi_task *task; + struct scsi_readcapacity16 *rc16; + int ret, lun; + + ret = 0; + + iscsi = iscsi_context_login(initiator, url, &lun); + if (iscsi == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + printf("Test that Readcapacity10 is supported ... "); + task = iscsi_readcapacity16_sync(iscsi, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send readcapacity16 command: %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("Readcapacity command: failed with sense. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + rc16 = scsi_datain_unmarshall(task); + if (rc16 == NULL) { + printf("[FAILED]\n"); + printf("failed to unmarshall readcapacity16 data. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + scsi_free_scsi_task(task); + 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 68d7d67..ed6b1f3 100644 --- a/test-tool/iscsi-test.c +++ b/test-tool/iscsi-test.c @@ -64,18 +64,22 @@ struct scsi_test tests[] = { /* read16 */ { "T0153_read16_rdprotect", T0153_read16_rdprotect }, +/* readcapacity16*/ +{ "T0160_readcapacity16_simple", T0160_readcapacity16_simple }, + { NULL, NULL } }; void print_usage(void) { - fprintf(stderr, "Usage: iscsi-inq [-?] [-?|--help] [--usage] [-i|--initiator-name=iqn-name]\n" + fprintf(stderr, "Usage: iscsi-test [-?] [-?|--help] [--usage] [-t|--test=]\n" + "\t\t[-l|--list] [-i|--initiator-name=]\n" "\t\t\n"); } void print_help(void) { - fprintf(stderr, "Usage: iscsi-inq [OPTION...] \n"); + fprintf(stderr, "Usage: iscsi-test [OPTION...] \n"); fprintf(stderr, " -i, --initiator-name=iqn-name Initiatorname to use\n"); fprintf(stderr, " -t, --test=test-name Which test to run. Default is to run all tests.\n"); fprintf(stderr, " -l, --list List all tests.\n"); diff --git a/test-tool/iscsi-test.h b/test-tool/iscsi-test.h index 0c420a9..7847f09 100644 --- a/test-tool/iscsi-test.h +++ b/test-tool/iscsi-test.h @@ -41,3 +41,5 @@ int T0132_verify10_mismatch_no_cmp(const char *initiator, const char *url); int T0143_read12_rdprotect(const char *initiator, const char *url); int T0153_read16_rdprotect(const char *initiator, const char *url); + +int T0160_readcapacity16_simple(const char *initiator, const char *url);