diff --git a/Makefile.am b/Makefile.am index 8836a8d..61a0d99 100644 --- a/Makefile.am +++ b/Makefile.am @@ -128,6 +128,7 @@ bin_iscsi_test_SOURCES = test-tool/iscsi-test.c \ test-tool/0386_preventallow_2_it_nexuses.c \ test-tool/0390_mandatory_opcodes_sbc.c \ test-tool/0400_inquiry_basic.c \ + test-tool/0410_readtoc_basic.c \ \ test-tool/1000_cmdsn_invalid.c \ test-tool/1010_datasn_invalid.c \ diff --git a/include/iscsi.h b/include/iscsi.h index fb55dbc..9c6ec56 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -727,6 +727,10 @@ iscsi_unmap_task(struct iscsi_context *iscsi, int lun, int anchor, int group, struct unmap_list *list, int list_len, iscsi_command_cb cb, void *private_data); +EXTERN struct scsi_task * +iscsi_readtoc_task(struct iscsi_context *iscsi, int lun, int msf, int format, + int track_session, int maxsize, + iscsi_command_cb cb, void *private_data); /* * Sync commands for SCSI @@ -881,6 +885,10 @@ EXTERN struct scsi_task * iscsi_unmap_sync(struct iscsi_context *iscsi, int lun, int anchor, int group, struct unmap_list *list, int list_len); +EXTERN struct scsi_task * +iscsi_readtoc_sync(struct iscsi_context *iscsi, int lun, int msf, + int format, int track_session, int maxsize); + /* * This function is used when the application wants to specify its own buffers to read the data * from the DATA-IN PDUs into. diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index 06abde6..0483ee1 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -39,6 +39,7 @@ enum scsi_opcode { SCSI_OPCODE_SYNCHRONIZECACHE10 = 0x35, SCSI_OPCODE_WRITE_SAME10 = 0x41, SCSI_OPCODE_UNMAP = 0x42, + SCSI_OPCODE_READTOC = 0x43, SCSI_OPCODE_READ16 = 0x88, SCSI_OPCODE_COMPARE_AND_WRITE = 0x89, SCSI_OPCODE_WRITE16 = 0x8A, @@ -106,6 +107,56 @@ enum scsi_xfer_dir { SCSI_XFER_WRITE = 2 }; +/* + * READTOC + */ +EXTERN struct scsi_task *scsi_cdb_readtoc(int msf, int format, int track_session, uint32_t xferlen); + +enum scsi_readtoc_fmt { + SCSI_READ_TOC = 0, + SCSI_READ_SESSION_INFO = 1, + SCSI_READ_FULL_TOC = 2, + SCSI_READ_PMA = 3, + SCSI_READ_ATIP = 4 +}; +struct scsi_readtoc_desc{ + union { + struct scsi_toc_desc { + int adr; + int control; + int track; + uint32_t lba; + } toc; + struct scsi_session_desc { + int adr; + int control; + int first_in_last; + uint32_t lba; + } ses; + struct scsi_fulltoc_desc { + int session; + int adr; + int control; + int tno; + int point; + int min; + int sec; + int frame; + int zero; + int pmin; + int psec; + int pframe; + } full; + } desc; +}; + +struct scsi_readtoc_list { + int num; + int first; + int last; + struct scsi_readtoc_desc desc[0]; +}; + struct scsi_reportluns_params { int report_type; }; @@ -206,6 +257,11 @@ struct scsi_modesense6_params { struct scsi_serviceactionin_params { enum scsi_service_action_in sa; }; +struct scsi_readtoc_params { + int msf; + int format; + int track_session; +}; struct scsi_sense { unsigned char error_type; @@ -259,6 +315,7 @@ struct scsi_task { struct scsi_inquiry_params inquiry; struct scsi_modesense6_params modesense6; struct scsi_serviceactionin_params serviceactionin; + struct scsi_readtoc_params readtoc; } params; enum scsi_residual residual_status; diff --git a/lib/libiscsi.def b/lib/libiscsi.def index 9ba792f..464b1c2 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -46,6 +46,8 @@ iscsi_readcapacity10_sync iscsi_readcapacity10_task iscsi_readcapacity16_sync iscsi_readcapacity16_task +iscsi_readtoc_sync +iscsi_readtoc_task iscsi_reconnect iscsi_set_noautoreconnect iscsi_reportluns_sync @@ -121,6 +123,7 @@ scsi_cdb_read16 scsi_cdb_read6 scsi_cdb_readcapacity10 scsi_cdb_readcapacity16 +scsi_cdb_readtoc scsi_cdb_serviceactionin16 scsi_cdb_startstopunit scsi_cdb_synchronizecache10 diff --git a/lib/libiscsi.syms b/lib/libiscsi.syms index 1828616..86373ec 100644 --- a/lib/libiscsi.syms +++ b/lib/libiscsi.syms @@ -44,6 +44,8 @@ iscsi_readcapacity10_sync iscsi_readcapacity10_task iscsi_readcapacity16_sync iscsi_readcapacity16_task +iscsi_readtoc_sync +iscsi_readtoc_task iscsi_reconnect iscsi_set_noautoreconnect iscsi_reportluns_sync @@ -119,6 +121,7 @@ scsi_cdb_read16 scsi_cdb_read6 scsi_cdb_readcapacity10 scsi_cdb_readcapacity16 +scsi_cdb_readtoc scsi_cdb_serviceactionin16 scsi_cdb_startstopunit scsi_cdb_synchronizecache10 diff --git a/lib/scsi-command.c b/lib/scsi-command.c index 1996ec6..bef793c 100644 --- a/lib/scsi-command.c +++ b/lib/scsi-command.c @@ -1477,6 +1477,28 @@ iscsi_get_user_in_buffer(struct iscsi_context *iscsi, struct iscsi_in_pdu *in, u return scsi_task_get_data_in_buffer(pdu->scsi_cbdata->task, offset + pos, count); } +struct scsi_task * +iscsi_readtoc_task(struct iscsi_context *iscsi, int lun, int msf, + int format, int track_session, int maxsize, + iscsi_command_cb cb, void *private_data) +{ + struct scsi_task *task; + + task = scsi_cdb_readtoc(msf, format, track_session, maxsize); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "read TOC 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_scsi_get_task_from_pdu(struct iscsi_pdu *pdu) { diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index 56a66d6..8857be7 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -298,6 +298,157 @@ scsi_cdb_readcapacity10(int lba, int pmi) return task; } + + +/* + * READTOC + */ +struct scsi_task * +scsi_cdb_readtoc(int msf, int format, int track_session, uint32_t xferlen) +{ + struct scsi_task *task; + + if (format != SCSI_READ_TOC && format != SCSI_READ_SESSION_INFO + && format != SCSI_READ_FULL_TOC){ + fprintf(stderr, "Read TOC format %d not fully supported yet\n", format); + return NULL; + } + + task = malloc(sizeof(struct scsi_task)); + if (task == NULL) { + return NULL; + } + + memset(task, 0, sizeof(struct scsi_task)); + task->cdb[0] = SCSI_OPCODE_READTOC; + + if (msf) { + task->cdb[1] |= 0x02; + } + + /* Prevent invalid setting of Track/Session Number */ + if (format == SCSI_READ_TOC || format == SCSI_READ_FULL_TOC) { + task->cdb[6] = 0xff & track_session; + } + + task->cdb_size = 10; + task->xfer_dir = SCSI_XFER_READ; + task->expxferlen = xferlen; + + task->params.readtoc.msf = msf; + task->params.readtoc.format = format; + task->params.readtoc.track_session = track_session; + + return task; +} + +/* + * parse the data in blob and calcualte the size of a full read TOC + * datain structure + */ +static int +scsi_readtoc_datain_getfullsize(struct scsi_task *task) +{ + uint16_t toc_data_len; + + toc_data_len = ntohs(*((uint16_t *)&task->datain.data[0])) + 2; + + return toc_data_len; +} + +static void +scsi_readtoc_desc_unmarshall(struct scsi_task *task, struct scsi_readtoc_list *list, int i) +{ + switch(task->params.readtoc.format){ + case SCSI_READ_TOC: + list->desc[i].desc.toc.adr + = task->datain.data[4+8*i+1] & 0xf0; + list->desc[i].desc.toc.control + = task->datain.data[4+8*i+1] & 0x0f; + list->desc[i].desc.toc.track + = task->datain.data[4+8*i+2]; + list->desc[i].desc.toc.lba + = ntohl(*(uint32_t *)&task->datain.data[4+8*i+4]); + break; + case SCSI_READ_SESSION_INFO: + list->desc[i].desc.ses.adr + = task->datain.data[4+8*i+1] & 0xf0; + list->desc[i].desc.ses.control + = task->datain.data[4+8*i+1] & 0x0f; + list->desc[i].desc.ses.first_in_last + = task->datain.data[4+8*i+2]; + list->desc[i].desc.ses.lba + = ntohl(*(uint32_t *)&task->datain.data[4+8*i+4]); + break; + case SCSI_READ_FULL_TOC: + list->desc[i].desc.full.session + = task->datain.data[4+11*i+0] & 0xf0; + list->desc[i].desc.full.adr + = task->datain.data[4+11*i+1] & 0xf0; + list->desc[i].desc.full.control + = task->datain.data[4+11*i+1] & 0x0f; + list->desc[i].desc.full.tno + = task->datain.data[4+11*i+2]; + list->desc[i].desc.full.point + = task->datain.data[4+11*i+3]; + list->desc[i].desc.full.min + = task->datain.data[4+11*i+4]; + list->desc[i].desc.full.sec + = task->datain.data[4+11*i+5]; + list->desc[i].desc.full.frame + = task->datain.data[4+11*i+6]; + list->desc[i].desc.full.zero + = task->datain.data[4+11*i+7]; + list->desc[i].desc.full.pmin + = task->datain.data[4+11*i+8]; + list->desc[i].desc.full.psec + = task->datain.data[4+11*i+9]; + list->desc[i].desc.full.pframe + = task->datain.data[4+11*i+10]; + break; + } +} +/* + * unmarshall the data in blob for read TOC into a structure + */ +static struct scsi_readtoc_list * +scsi_readtoc_datain_unmarshall(struct scsi_task *task) +{ + struct scsi_readtoc_list *list; + int data_len; + int i, num_desc; + + if (task->datain.size < 4) { + return NULL; + } + + /* Do we have all data? */ + data_len = scsi_readtoc_datain_getfullsize(task) - 2; + if(task->datain.size < data_len) { + return NULL; + } + + /* Remove header size (4) to get bytes in descriptor list */ + num_desc = (data_len - 4) / 8; + + list = scsi_malloc(task, offsetof(struct scsi_readtoc_list, desc) + + sizeof(struct scsi_readtoc_desc) * num_desc); + if (list == NULL) { + return NULL; + } + + list->num = num_desc; + list->first = task->datain.data[2]; + list->last = task->datain.data[3]; + + for (i = 0; i < num_desc; i++) { + scsi_readtoc_desc_unmarshall(task, list, i); + } + + return list; +} + + /* * service_action_in unmarshall */ @@ -1940,6 +2091,8 @@ scsi_datain_getfullsize(struct scsi_task *task) return scsi_readcapacity10_datain_getfullsize(task); case SCSI_OPCODE_SYNCHRONIZECACHE10: return 0; + case SCSI_OPCODE_READTOC: + return scsi_readtoc_datain_getfullsize(task); case SCSI_OPCODE_REPORTLUNS: return scsi_reportluns_datain_getfullsize(task); } @@ -1960,6 +2113,8 @@ scsi_datain_unmarshall(struct scsi_task *task) return scsi_readcapacity10_datain_unmarshall(task); case SCSI_OPCODE_SYNCHRONIZECACHE10: return NULL; + case SCSI_OPCODE_READTOC: + return scsi_readtoc_datain_unmarshall(task); case SCSI_OPCODE_REPORTLUNS: return scsi_reportluns_datain_unmarshall(task); case SCSI_OPCODE_SERVICE_ACTION_IN: diff --git a/lib/sync.c b/lib/sync.c index 8b286aa..209ca58 100644 --- a/lib/sync.c +++ b/lib/sync.c @@ -821,6 +821,25 @@ iscsi_unmap_sync(struct iscsi_context *iscsi, int lun, int anchor, int group, return state.task; } +struct scsi_task * +iscsi_readtoc_sync(struct iscsi_context *iscsi, int lun, int msf, int format, + int track_session, int maxsize) +{ + struct iscsi_sync_state state; + + memset(&state, 0, sizeof(state)); + + if (iscsi_readtoc_task(iscsi, lun, msf, format, track_session, + maxsize, scsi_sync_cb, &state) == NULL) { + iscsi_set_error(iscsi, "Failed to send Read TOC 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/0410_readtoc_basic.c b/test-tool/0410_readtoc_basic.c new file mode 100644 index 0000000..fd2a0af --- /dev/null +++ b/test-tool/0410_readtoc_basic.c @@ -0,0 +1,185 @@ +/* + 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 +#include +#include "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test.h" + +int T0410_readtoc_basic(const char *initiator, const char *url, int data_loss, int show_info) +{ + struct iscsi_context *iscsi; + struct scsi_task *task; + struct scsi_inquiry_standard *inq; + struct scsi_readtoc_list *list; + int ret, lun, i, toc_device; + int full_size; + + printf("0410_readtoc_basic:\n"); + printf("===================\n"); + if (show_info) { + printf("Test Read TOC command.\n"); + printf(" If device does not support, just verify appropriate error returned\n"); + printf("1, Check we can read the TOC: track 0, non-MSF.\n"); + printf("2, Make sure at least 4 bytes returned as header.\n"); + printf("\n"); + return 0; + } + + iscsi = iscsi_context_login(initiator, url, &lun); + if (iscsi == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + + ret = 0; + + printf("Read standard INQUIRY data ... "); + /* Submit INQUIRY so we can find out the device type */ + task = iscsi_inquiry_sync(iscsi, lun, 0, 0, 255); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send INQUIRY command : %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("INQUIRY command failed : %s\n", iscsi_get_error(iscsi)); + scsi_free_scsi_task(task); + ret = -1; + goto finished; + } + full_size = scsi_datain_getfullsize(task); + if (full_size > task->datain.size) { + scsi_free_scsi_task(task); + + /* we need more data for the full list */ + if ((task = iscsi_inquiry_sync(iscsi, lun, 0, 0, full_size)) == NULL) { + printf("[FAILED]\n"); + printf("Inquiry command failed : %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + } + inq = scsi_datain_unmarshall(task); + if (inq == NULL) { + printf("[FAILED]\n"); + printf("failed to unmarshall inquiry datain blob\n"); + scsi_free_scsi_task(task); + ret = -1; + goto finished; + } + printf("[OK]\n"); + + printf("Check device-type is either of DISK, TAPE or CD/DVD ... "); + switch (inq->device_type) { + case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS: + case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS: + toc_device = 0; + break; + case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC: + toc_device = 1; + break; + default: + printf("[FAILED]\n"); + printf("Device-type is not DISK, TAPE or CD/DVD. Device reported:%s\n", scsi_devtype_to_str(inq->device_type)); + ret = -1; + goto finished; + } + printf("[OK]\n"); + +test1: + + printf("Read TOC format 0000b (TOC)\n"); + if (toc_device) { + printf(" On MMC Device\n"); + } else { + printf(" On non-MMC Device\n"); + } + + /* See how big this inquiry data is */ + task = iscsi_readtoc_sync(iscsi, lun, 0, 0, 0, 255); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send READ TOC command : %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + + /* If this is a non-MMC device, just verify that that comand failed + as expected and then bail */ + if (!toc_device) { + if (task->status == SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("READ TOC Should have failed\n"); + ret = -1; + } else if (task->status != SCSI_STATUS_CHECK_CONDITION + || task->sense.key != SCSI_SENSE_ILLEGAL_REQUEST + || task->sense.ascq != SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { + printf("[FAILED]\n"); + printf("READ TOC failed but ascq was wrong. Should have failed with ILLEGAL_REQUEST/INVALID OPERATOR. Sense:%s\n", iscsi_get_error(iscsi)); + ret = -1; + } else { + printf("[OK]\n"); + ret = 0; + } + + scsi_free_scsi_task(task); + goto finished; + } + + + /* We should only still be here if we are an MMC device */ + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("READ TOC command failed : %s\n", iscsi_get_error(iscsi)); + scsi_free_scsi_task(task); + ret = -1; + goto finished; + } + +test2: + full_size = scsi_datain_getfullsize(task); + if (full_size < 4) { + printf("[FAILED]\n"); + printf("TOC Data Length %d < 4\n", full_size); + scsi_free_scsi_task(task); + ret = -1; + goto finished; + } + list = scsi_datain_unmarshall(task); + if (list == NULL) { + printf("[FAILED]\n"); + printf("Read TOC Unmarshall failed\n"); + scsi_free_scsi_task(task); + ret = -1; + goto finished; + } + + printf("[OK]\n"); + scsi_free_scsi_task(task); + + +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 6108256..5a4e928 100644 --- a/test-tool/iscsi-test.c +++ b/test-tool/iscsi-test.c @@ -204,6 +204,9 @@ struct scsi_test tests[] = { /* inquiry*/ { "T0400_inquiry_basic", T0400_inquiry_basic }, +/* read TOC/PMA/ATIP */ +{ "T0410_readtoc_basic", T0410_readtoc_basic }, + /* iSCSI protocol tests */ /* invalid cmdsn from initiator */ diff --git a/test-tool/iscsi-test.h b/test-tool/iscsi-test.h index 83ad14e..9834ca5 100644 --- a/test-tool/iscsi-test.h +++ b/test-tool/iscsi-test.h @@ -158,6 +158,8 @@ int T0390_mandatory_opcodes_sbc(const char *initiator, const char *url, int data int T0400_inquiry_basic(const char *initiator, const char *url, int data_loss, int show_info); +int T0410_readtoc_basic(const char *initiator, const char *url, int data_loss, int show_info); + int T1000_cmdsn_invalid(const char *initiator, const char *url, int data_loss, int show_info); int T1010_datasn_invalid(const char *initiator, const char *url, int data_loss, int show_info); int T1020_bufferoffset_invalid(const char *initiator, const char *url, int data_loss, int show_info);