diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index 2561b01..e10620d 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -536,6 +536,7 @@ enum scsi_inquiry_pagecode { SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES = 0x00, SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER = 0x80, SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION = 0x83, + SCSI_INQUIRY_PAGECODE_THIRD_PARTY_COPY = 0x8F, SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS = 0xB0, SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS = 0xB1, SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING = 0xB2 @@ -687,6 +688,32 @@ struct scsi_inquiry_device_identification { struct scsi_inquiry_device_designator *designators; }; +enum third_party_copy_descriptor_type { + THIRD_PARTY_COPY_TYPE_SUPPORTED_COMMANDS = 0x0001 +}; + +struct third_party_copy_command_support { + struct third_party_copy_command_support *next; + + int operation_code; + int service_action_length; + int *service_action; +}; + +struct third_party_copy_supported_commands { + enum third_party_copy_descriptor_type descriptor_type; + + struct third_party_copy_command_support *commands_supported; +}; + +struct scsi_inquiry_third_party_copy { + enum scsi_inquiry_peripheral_qualifier qualifier; + enum scsi_inquiry_peripheral_device_type device_type; + enum scsi_inquiry_pagecode pagecode; + + struct third_party_copy_supported_commands *supported_commands; +}; + /* * MODESENSE */ diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index 50ec515..4039cea 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -1495,6 +1495,97 @@ scsi_inquiry_unmarshall_device_identification(struct scsi_task *task) return NULL; } +static struct third_party_copy_supported_commands * +third_party_copy_unmarshall_supported_commands(struct scsi_task *task, + unsigned char *dptr) +{ + struct third_party_copy_supported_commands *supported_commands = + scsi_malloc(task, sizeof(*supported_commands)); + int remaining; + unsigned char *lptr; + + if (supported_commands == NULL) { + return NULL; + } + + supported_commands->descriptor_type = scsi_get_uint16(&dptr[0]); + + remaining = dptr[4]; + lptr = &dptr[5]; + while (remaining > 0) { + struct third_party_copy_command_support *command = + scsi_malloc(task, sizeof(*command)); + int i; + + if (command == NULL) { + goto err; + } + + command->next = supported_commands->commands_supported; + supported_commands->commands_supported = command; + + command->operation_code = lptr[0]; + command->service_action_length = lptr[1]; + command->service_action = scsi_malloc(task, + sizeof(*command->service_action) * (command->service_action_length + 1)); + if (command->service_action == NULL) { + goto err; + } + command->service_action[command->service_action_length] = 0; + for (i = 0; i < command->service_action_length; i++) { + command->service_action[i] = lptr[2 + i]; + } + + remaining -= command->service_action_length + 2; + lptr += command->service_action_length + 2; + } + return supported_commands; + +err: + return NULL; +} + +static struct scsi_inquiry_third_party_copy * +scsi_inquiry_unmarshall_third_party_copy(struct scsi_task *task) +{ + struct scsi_inquiry_third_party_copy *inq = scsi_malloc(task, + sizeof(*inq)); + int remaining; + unsigned char *dptr; + + if (inq == NULL) { + return NULL; + } + + inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07; + inq->device_type = task_get_uint8(task, 0) & 0x1f; + inq->pagecode = task_get_uint8(task, 1); + + remaining = task_get_uint16(task, 2); + dptr = &task->datain.data[4]; + while (remaining > 0) { + int copy_desc_type = scsi_get_uint16(&dptr[0]); + int copy_desc_len = scsi_get_uint16(&dptr[2]); + + switch (copy_desc_type) { + case THIRD_PARTY_COPY_TYPE_SUPPORTED_COMMANDS: + inq->supported_commands = + third_party_copy_unmarshall_supported_commands(task, dptr); + if (inq->supported_commands == NULL) { + goto err; + } + break; + } + + remaining -= copy_desc_len + 4; + dptr += copy_desc_len + 4; + } + return inq; + +err: + return NULL; +} + static struct scsi_inquiry_block_limits * scsi_inquiry_unmarshall_block_limits(struct scsi_task *task) { @@ -1597,6 +1688,8 @@ scsi_inquiry_datain_unmarshall(struct scsi_task *task) return scsi_inquiry_unmarshall_unit_serial_number(task); case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION: return scsi_inquiry_unmarshall_device_identification(task); + case SCSI_INQUIRY_PAGECODE_THIRD_PARTY_COPY: + return scsi_inquiry_unmarshall_third_party_copy(task); case SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS: return scsi_inquiry_unmarshall_block_limits(task); case SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS: diff --git a/test-tool/test_receive_copy_results_copy_status.c b/test-tool/test_receive_copy_results_copy_status.c index 71af29a..3cda526 100644 --- a/test-tool/test_receive_copy_results_copy_status.c +++ b/test-tool/test_receive_copy_results_copy_status.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -29,16 +30,107 @@ void test_receive_copy_results_copy_status(void) { - struct scsi_task *cs_task; + struct scsi_task *cs_task = NULL; struct scsi_copy_results_copy_status *csp; int tgt_desc_len = 0, seg_desc_len = 0; int offset = XCOPY_DESC_OFFSET, list_id = 1; struct iscsi_data data; unsigned char *xcopybuf; + struct scsi_inquiry_supported_pages *sup_inq; + struct scsi_inquiry_third_party_copy *third_party_inq; + struct third_party_copy_command_support *command; + bool third_party_page_supported = false; + bool receive_copy_results_supported = false; + int ret, i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test RECEIVE COPY RESULTS, COPY STATUS"); + logging(LOG_VERBOSE, "Get VPD pages supported list."); + ret = inquiry(sd, &task, + 1, SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES, 255, + EXPECT_STATUS_GOOD); + CU_ASSERT_EQUAL(ret, 0); + if (ret != 0) { + logging(LOG_NORMAL, "[FAILED] Failed to get Supported VPD Pages."); + return; + } + + logging(LOG_VERBOSE, "Check that RECEIVE COPY RESULTS is supported."); + sup_inq = scsi_datain_unmarshall(task); + CU_ASSERT_NOT_EQUAL(sup_inq, NULL); + if (sup_inq == NULL) { + logging(LOG_NORMAL, "[FAILED] Failed to unmarshall DATA-IN " + "buffer."); + return; + } + + for (i = 0; i < sup_inq->num_pages; i++) { + if (sup_inq->pages[i] == SCSI_INQUIRY_PAGECODE_THIRD_PARTY_COPY) { + third_party_page_supported = true; + break; + } + } + if (!third_party_page_supported) { + logging(LOG_NORMAL, "[SKIPPED] Third-party Copy VPD page is not " + "implemented."); + CU_PASS("RECEIVE COPY RESULTS is not implemented."); + goto finished; + } + logging(LOG_VERBOSE, "Third-party Copy VPD page is supported."); + + scsi_free_scsi_task(task); + task = NULL; + + ret = inquiry(sd, &task, + 1, SCSI_INQUIRY_PAGECODE_THIRD_PARTY_COPY, 1024, + EXPECT_STATUS_GOOD); + CU_ASSERT_EQUAL(ret, 0); + if (ret != 0) { + logging(LOG_NORMAL, "[FAILED] Failed to get Third-party Copy " + "VPD page."); + return; + } + + third_party_inq = scsi_datain_unmarshall(task); + CU_ASSERT_NOT_EQUAL(third_party_inq, NULL); + if (third_party_inq == NULL) { + logging(LOG_NORMAL, "[FAILED] Failed to unmarshall DATA-IN " + "buffer."); + return; + } + CU_ASSERT_NOT_EQUAL(third_party_inq->supported_commands, NULL); + if (third_party_inq->supported_commands == NULL) { + logging(LOG_NORMAL, "[FAILED] Supported Commands descriptor is " + "is not found."); + return; + } + + command = third_party_inq->supported_commands->commands_supported; + while (command && !receive_copy_results_supported) { + if (command->operation_code == SCSI_OPCODE_RECEIVE_COPY_RESULTS) { + for (i = 0; i < command->service_action_length; i++) { + if (command->service_action[i] == + SCSI_COPY_RESULTS_COPY_STATUS) { + receive_copy_results_supported = true; + break; + } + } + } + + command = command->next; + } + if (!receive_copy_results_supported) { + logging(LOG_NORMAL, "[SKIPPED] RECEIVE COPY RESULTS is not " + "implemented."); + CU_PASS("RECEIVE COPY RESULTS is not implemented."); + goto finished; + } + logging(LOG_VERBOSE, "RECEIVE COPY RESULTS is supported."); + + scsi_free_scsi_task(task); + task = NULL; + logging(LOG_VERBOSE, "No copy in progress"); RECEIVE_COPY_RESULTS(&cs_task, sd, SCSI_COPY_RESULTS_COPY_STATUS, list_id, NULL, EXPECT_INVALID_FIELD_IN_CDB); @@ -76,5 +168,13 @@ test_receive_copy_results_copy_status(void) RECEIVE_COPY_RESULTS(&cs_task, sd, SCSI_COPY_RESULTS_COPY_STATUS, list_id, (void **)&csp, EXPECT_STATUS_GOOD); - scsi_free_scsi_task(cs_task); +finished: + if (task != NULL) { + scsi_free_scsi_task(task); + task = NULL; + } + if (cs_task != NULL) { + scsi_free_scsi_task(cs_task); + cs_task = NULL; + } }