diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h
index 307f12f..79c020f 100644
--- a/include/scsi-lowlevel.h
+++ b/include/scsi-lowlevel.h
@@ -54,6 +54,8 @@ enum scsi_opcode {
SCSI_OPCODE_MODESENSE10 = 0x5A,
SCSI_OPCODE_PERSISTENT_RESERVE_IN = 0x5E,
SCSI_OPCODE_PERSISTENT_RESERVE_OUT = 0x5F,
+ SCSI_OPCODE_EXTENDED_COPY = 0x83,
+ SCSI_OPCODE_RECEIVE_COPY_RESULTS = 0x84,
SCSI_OPCODE_READ16 = 0x88,
SCSI_OPCODE_COMPARE_AND_WRITE = 0x89,
SCSI_OPCODE_WRITE16 = 0x8A,
@@ -148,7 +150,10 @@ enum scsi_sense_key {
EXTERN const char *scsi_sense_key_str(int key);
/* ascq */
+#define SCSI_SENSE_ASCQ_NO_ADDL_SENSE 0x0000
#define SCSI_SENSE_ASCQ_SANITIZE_IN_PROGRESS 0x041b
+#define SCSI_SENSE_ASCQ_UNREACHABLE_COPY_TARGET 0x0804
+#define SCSI_SENSE_ASCQ_COPY_TARGET_DEVICE_NOT_REACHABLE 0x0d02
#define SCSI_SENSE_ASCQ_WRITE_AFTER_SANITIZE_REQUIRED 0x1115
#define SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR 0x1a00
#define SCSI_SENSE_ASCQ_MISCOMPARE_DURING_VERIFY 0x1d00
@@ -158,6 +163,10 @@ EXTERN const char *scsi_sense_key_str(int key);
#define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB 0x2400
#define SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED 0x2500
#define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST 0x2600
+#define SCSI_SENSE_ASCQ_TOO_MANY_TARGET_DESCRIPTORS 0x2606
+#define SCSI_SENSE_ASCQ_UNSUPPORTED_TARGET_DESCRIPTOR_TYPE_CODE 0x2607
+#define SCSI_SENSE_ASCQ_TOO_MANY_SEGMENT_DESCRIPTORS 0x2608
+#define SCSI_SENSE_ASCQ_UNSUPPORTED_SEGMENT_DESCRIPTOR_TYPE_CODE 0x2609
#define SCSI_SENSE_ASCQ_WRITE_PROTECTED 0x2700
#define SCSI_SENSE_ASCQ_HARDWARE_WRITE_PROTECTED 0x2701
#define SCSI_SENSE_ASCQ_SOFTWARE_WRITE_PROTECTED 0x2702
@@ -348,7 +357,6 @@ EXTERN struct scsi_task *scsi_cdb_testunitready(void);
EXTERN struct scsi_task *scsi_cdb_sanitize(int immed, int ause, int sa,
int param_len);
-
/*
* REPORTLUNS
*/
@@ -1095,6 +1103,78 @@ EXTERN struct scsi_task *scsi_cdb_writeverify12(uint32_t lba, uint32_t xferlen,
EXTERN struct scsi_task *scsi_cdb_writeverify16(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number);
+/*
+ * EXTENDED COPY
+ */
+#define XCOPY_DESC_OFFSET 16
+#define SEG_DESC_SRC_INDEX_OFFSET 4
+enum list_id_usage {
+ LIST_ID_USAGE_HOLD = 0,
+ LIST_ID_USAGE_DISCARD = 2,
+ LIST_ID_USAGE_DISABLE = 3
+};
+
+enum ec_descr_type_code {
+ /* Segment descriptors : 0x00 to 0xBF */
+ BLK_TO_STRM_SEG_DESCR = 0x00,
+ STRM_TO_BLK_SEG_DESCR = 0x01,
+ BLK_TO_BLK_SEG_DESCR = 0x02,
+ STRM_TO_STRM_SEG_DESCR = 0x03,
+
+ /* Target descriptors : 0xEO to 0xFE */
+ IDENT_DESCR_TGT_DESCR = 0xE4,
+ IPV4_TGT_DESCR = 0xE5,
+ IPV6_TGT_DESCR = 0xEA,
+ IP_COPY_SVC_TGT_DESCR = 0xEB
+};
+
+enum lu_id_type {
+ LU_ID_TYPE_LUN = 0x00,
+ LU_ID_TYPE_PROXY_TOKEN = 0x01,
+ LU_ID_TYPE_RSVD = 0x02,
+ LU_ID_TYPE_RSVD1 = 0x03
+};
+
+EXTERN struct scsi_task *scsi_cdb_extended_copy(int immed);
+
+/*
+ * RECEIVE COPY RESULTS
+ */
+enum scsi_copy_results_sa {
+ SCSI_COPY_RESULTS_COPY_STATUS = 0,
+ SCSI_COPY_RESULTS_RECEIVE_DATA = 1,
+ SCSI_COPY_RESULTS_OP_PARAMS = 3,
+ SCSI_COPY_RESULTS_FAILED_SEGMENT = 4,
+};
+
+EXTERN struct scsi_task *scsi_cdb_receive_copy_results(enum scsi_copy_results_sa sa, int list_id, int xferlen);
+
+struct scsi_copy_results_copy_status {
+ uint32_t available_data;
+ uint8_t copy_manager_status;
+ uint8_t hdd;
+ uint16_t segments_processed;
+ uint8_t transfer_count_units;
+ uint32_t transfer_count;
+};
+
+struct scsi_copy_results_op_params {
+ uint32_t available_data;
+ uint16_t max_target_desc_count;
+ uint16_t max_segment_desc_count;
+ uint32_t max_desc_list_length;
+ uint32_t max_segment_length;
+ uint32_t max_inline_data_length;
+ uint32_t held_data_limit;
+ uint32_t max_stream_device_transfer_size;
+ uint16_t total_concurrent_copies;
+ uint8_t max_concurrent_copies;
+ uint8_t data_segment_granularity;
+ uint8_t inline_data_granularity;
+ uint8_t held_data_granularity;
+ uint8_t impl_desc_list_length;
+ uint8_t imp_desc_type_codes[0];
+};
void *scsi_malloc(struct scsi_task *task, size_t size);
uint64_t scsi_get_uint64(const unsigned char *c);
diff --git a/lib/libiscsi.def b/lib/libiscsi.def
index 1491666..63dd6bc 100644
--- a/lib/libiscsi.def
+++ b/lib/libiscsi.def
@@ -159,6 +159,7 @@ iscsi_writesame16_sync
iscsi_writesame16_task
scsi_association_to_str
scsi_cdb_compareandwrite
+scsi_cdb_extended_copy
scsi_cdb_inquiry
scsi_cdb_get_lba_status
scsi_cdb_modeselect6
@@ -177,6 +178,7 @@ scsi_cdb_read6
scsi_cdb_readcapacity10
scsi_cdb_readcapacity16
scsi_cdb_readtoc
+scsi_cdb_receive_copy_results
scsi_cdb_reserve6
scsi_cdb_release6
scsi_cdb_report_supported_opcodes
diff --git a/lib/libiscsi.syms b/lib/libiscsi.syms
index 7b8a051..a8db966 100644
--- a/lib/libiscsi.syms
+++ b/lib/libiscsi.syms
@@ -157,6 +157,7 @@ iscsi_writesame16_sync
iscsi_writesame16_task
scsi_association_to_str
scsi_cdb_compareandwrite
+scsi_cdb_extended_copy
scsi_cdb_inquiry
scsi_cdb_get_lba_status
scsi_cdb_modeselect6
@@ -175,6 +176,7 @@ scsi_cdb_read6
scsi_cdb_readcapacity10
scsi_cdb_readcapacity16
scsi_cdb_readtoc
+scsi_cdb_receive_copy_results
scsi_cdb_reserve6
scsi_cdb_release6
scsi_cdb_report_supported_opcodes
diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c
index e1ebb20..3d6e412 100644
--- a/lib/scsi-lowlevel.c
+++ b/lib/scsi-lowlevel.c
@@ -834,6 +834,61 @@ scsi_persistentreservein_datain_getfullsize(struct scsi_task *task)
}
}
+static void *
+scsi_receivecopyresults_datain_unmarshall(struct scsi_task *task)
+{
+ int sa = task->cdb[1] & 0x1f;
+ int len, i;
+ struct scsi_copy_results_copy_status *cs;
+ struct scsi_copy_results_op_params *op;
+
+ switch (sa) {
+ case SCSI_COPY_RESULTS_COPY_STATUS:
+ len = task_get_uint32(task, 0);
+
+ cs = scsi_malloc(task, len+4);
+ if (cs == NULL) {
+ return NULL;
+ }
+ cs->available_data = len;
+ cs->copy_manager_status = task_get_uint8(task, 4) & 0x7F;
+ cs->hdd = (task_get_uint8(task, 4) & 0x80) >> 7;
+ cs->segments_processed = task_get_uint16(task, 5);
+ cs->transfer_count_units = task_get_uint8(task, 7);
+ cs->transfer_count = task_get_uint32(task, 8);
+ return cs;
+
+ case SCSI_COPY_RESULTS_OP_PARAMS:
+ len = task_get_uint32(task, 0);
+
+ op = scsi_malloc(task, len+4);
+ if (op == NULL) {
+ return NULL;
+ }
+ op->available_data = len;
+ op->max_target_desc_count = task_get_uint16(task, 8);
+ op->max_segment_desc_count = task_get_uint16(task, 10);
+ op->max_desc_list_length = task_get_uint32(task, 12);
+ op->max_segment_length = task_get_uint32(task, 16);
+ op->max_inline_data_length = task_get_uint32(task, 20);
+ op->held_data_limit = task_get_uint32(task, 24);
+ op->max_stream_device_transfer_size = task_get_uint32(task, 28);
+ op->total_concurrent_copies = task_get_uint16(task, 34);
+ op->max_concurrent_copies = task_get_uint8(task, 36);
+ op->data_segment_granularity = task_get_uint8(task, 37);
+ op->inline_data_granularity = task_get_uint8(task, 38);
+ op->held_data_granularity = task_get_uint8(task, 39);
+ op->impl_desc_list_length = task_get_uint8(task, 43);
+ for (i = 0; i < (int)op->impl_desc_list_length; i++) {
+ op->imp_desc_type_codes[i] = task_get_uint8(task, 44+i);
+ }
+ return op;
+ default:
+ return NULL;
+ }
+}
+
+
static void *
scsi_persistentreservein_datain_unmarshall(struct scsi_task *task)
{
@@ -3230,6 +3285,63 @@ scsi_cdb_writeverify16(uint64_t lba, uint32_t xferlen, int blocksize, int wrprot
return task;
}
+/*
+ * EXTENDED COPY
+ */
+struct scsi_task *
+scsi_cdb_extended_copy(int param_len)
+{
+ 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_EXTENDED_COPY;
+ task->cdb[10] = (param_len >> 24) & 0xFF;
+ task->cdb[11] = (param_len >> 16) & 0xFF;
+ task->cdb[12] = (param_len >> 8) & 0xFF;
+ task->cdb[13] = param_len & 0xFF;
+ /* Inititalize other fields in CDB */
+ task->cdb_size = 16;
+ task->xfer_dir = SCSI_XFER_WRITE;
+ task->expxferlen = param_len;
+
+ return task;
+}
+
+/*
+ * RECEIVE COPY RESULTS
+ */
+struct scsi_task *
+scsi_cdb_receive_copy_results(enum scsi_copy_results_sa sa, int list_id, int 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_RECEIVE_COPY_RESULTS;
+ task->cdb[1] |= sa & 0x1f;
+ task->cdb[2] = list_id & 0xFF;
+
+ scsi_set_uint32(&task->cdb[10], xferlen);
+
+ task->cdb_size = 16;
+ if (xferlen != 0) {
+ task->xfer_dir = SCSI_XFER_READ;
+ } else {
+ task->xfer_dir = SCSI_XFER_NONE;
+ }
+ task->expxferlen = xferlen;
+
+ return task;
+}
+
int
scsi_datain_getfullsize(struct scsi_task *task)
{
@@ -3281,6 +3393,8 @@ scsi_datain_unmarshall(struct scsi_task *task)
return scsi_persistentreservein_datain_unmarshall(task);
case SCSI_OPCODE_MAINTENANCE_IN:
return scsi_maintenancein_datain_unmarshall(task);
+ case SCSI_OPCODE_RECEIVE_COPY_RESULTS:
+ return scsi_receivecopyresults_datain_unmarshall(task);
}
return NULL;
}
diff --git a/test-tool/Makefile.am b/test-tool/Makefile.am
index d177f60..ec03a09 100644
--- a/test-tool/Makefile.am
+++ b/test-tool/Makefile.am
@@ -19,6 +19,12 @@ iscsi_test_cu_SOURCES = iscsi-test-cu.c \
test_compareandwrite_simple.c \
test_compareandwrite_dpofua.c \
test_compareandwrite_miscompare.c \
+ test_extendedcopy_simple.c \
+ test_extendedcopy_param.c \
+ test_extendedcopy_descr_limits.c \
+ test_extendedcopy_descr_type.c \
+ test_extendedcopy_validate_tgt_descr.c \
+ test_extendedcopy_validate_seg_descr.c \
test_get_lba_status_simple.c \
test_get_lba_status_beyond_eol.c \
test_get_lba_status_unmap_single.c \
@@ -93,6 +99,8 @@ iscsi_test_cu_SOURCES = iscsi-test-cu.c \
test_readcapacity16_protection.c \
test_readcapacity16_simple.c \
test_readonly_sbc.c \
+ test_receive_copy_results_copy_status.c \
+ test_receive_copy_results_op_params.c \
test_report_supported_opcodes_one_command.c \
test_report_supported_opcodes_rctd.c \
test_report_supported_opcodes_servactv.c \
diff --git a/test-tool/iscsi-support.c b/test-tool/iscsi-support.c
index 5983a69..4dd271d 100644
--- a/test-tool/iscsi-support.c
+++ b/test-tool/iscsi-support.c
@@ -63,8 +63,12 @@ int no_medium_ascqs[3] = {
int lba_oob_ascqs[1] = {
SCSI_SENSE_ASCQ_LBA_OUT_OF_RANGE
};
-int invalid_cdb_ascqs[1] = {
- SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB
+int invalid_cdb_ascqs[2] = {
+ SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB,
+ SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST
+};
+int param_list_len_err_ascqs[1] = {
+ SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR
};
int write_protect_ascqs[3] = {
SCSI_SENSE_ASCQ_WRITE_PROTECTED,
@@ -80,6 +84,19 @@ int removal_ascqs[1] = {
int miscompare_ascqs[1] = {
SCSI_SENSE_ASCQ_MISCOMPARE_DURING_VERIFY
};
+int too_many_desc_ascqs[2] = {
+ SCSI_SENSE_ASCQ_TOO_MANY_TARGET_DESCRIPTORS,
+ SCSI_SENSE_ASCQ_TOO_MANY_SEGMENT_DESCRIPTORS,
+};
+int unsupp_desc_code_ascqs[2] = {
+ SCSI_SENSE_ASCQ_UNSUPPORTED_TARGET_DESCRIPTOR_TYPE_CODE,
+ SCSI_SENSE_ASCQ_UNSUPPORTED_SEGMENT_DESCRIPTOR_TYPE_CODE
+};
+int copy_aborted_ascqs[3] = {
+ SCSI_SENSE_ASCQ_NO_ADDL_SENSE,
+ SCSI_SENSE_ASCQ_UNREACHABLE_COPY_TARGET,
+ SCSI_SENSE_ASCQ_COPY_TARGET_DEVICE_NOT_REACHABLE
+};
struct scsi_inquiry_standard *inq;
struct scsi_inquiry_logical_block_provisioning *inq_lbp;
@@ -2528,3 +2545,217 @@ finished:
}
return ret;
}
+
+/* Extended Copy */
+int extendedcopy(struct scsi_device *sdev, struct iscsi_data *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq)
+{
+ struct scsi_task *task;
+ int ret;
+
+ logging(LOG_VERBOSE, "Send EXTENDED COPY (Expecting %s)",
+ scsi_status_str(status));
+
+ if (!data_loss) {
+ logging(LOG_NORMAL, "--dataloss flag is not set in. Skipping extendedcopy\n");
+ return -1;
+ }
+
+ task = scsi_cdb_extended_copy(data->size);
+
+ assert(task != NULL);
+
+ send_scsi_command(sdev, task, data);
+
+ ret = check_result("EXTENDEDCOPY", sdev, task, status, key, ascq, num_ascq);
+ scsi_free_scsi_task(task);
+
+ return ret;
+}
+
+int get_desc_len(enum ec_descr_type_code desc_type)
+{
+ int desc_len = 0;
+ switch (desc_type) {
+ /* Segment Descriptors */
+ case BLK_TO_STRM_SEG_DESCR:
+ case STRM_TO_BLK_SEG_DESCR:
+ desc_len = 0x18;
+ break;
+ case BLK_TO_BLK_SEG_DESCR:
+ desc_len = 0x1C;
+ break;
+ case STRM_TO_STRM_SEG_DESCR:
+ desc_len = 0x14;
+ break;
+
+ /* Target Descriptors */
+ case IPV6_TGT_DESCR:
+ case IP_COPY_SVC_TGT_DESCR:
+ desc_len = 64;
+ break;
+ case IDENT_DESCR_TGT_DESCR:
+ default:
+ if (desc_type >= 0xE0 && desc_type <= 0xE9)
+ desc_len = 32;
+ }
+
+ return desc_len;
+}
+
+void populate_ident_tgt_desc(unsigned char *buf, struct scsi_device *dev)
+{
+ int ret;
+ struct scsi_task *inq_di_task = NULL;
+ struct scsi_inquiry_device_identification *inq_di = NULL;
+ struct scsi_inquiry_device_designator *desig, *tgt_desig = NULL;
+ enum scsi_designator_type prev_type = 0;
+
+ ret = inquiry(dev, &inq_di_task, 1, SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION, 255, EXPECT_STATUS_GOOD);
+ if (ret < 0 || inq_di_task == NULL) {
+ logging(LOG_NORMAL, "Failed to read Device Identification page");
+ goto finished;
+ } else {
+ inq_di = scsi_datain_unmarshall(inq_di_task);
+ if (inq_di == NULL) {
+ logging(LOG_NORMAL, "Failed to unmarshall inquiry datain blob");
+ goto finished;
+ }
+ }
+
+ for (desig = inq_di->designators; desig; desig = desig->next) {
+ switch (desig->designator_type) {
+ case SCSI_DESIGNATOR_TYPE_VENDOR_SPECIFIC:
+ case SCSI_DESIGNATOR_TYPE_T10_VENDORT_ID:
+ case SCSI_DESIGNATOR_TYPE_EUI_64:
+ case SCSI_DESIGNATOR_TYPE_NAA:
+ if (prev_type <= desig->designator_type) {
+ tgt_desig = desig;
+ prev_type = desig->designator_type;
+ }
+ default:
+ continue;
+ }
+ }
+ if (tgt_desig == NULL) {
+ logging(LOG_NORMAL, "No suitalble target descriptor format found");
+ goto finished;
+ }
+
+ buf[0] = tgt_desig->code_set;
+ buf[1] = (tgt_desig->designator_type & 0xF) | ((tgt_desig->association & 3) << 4);
+ buf[3] = tgt_desig->designator_length;
+ memcpy(buf + 4, tgt_desig->designator, tgt_desig->designator_length);
+
+ finished:
+ scsi_free_scsi_task(inq_di_task);
+}
+
+int populate_tgt_desc(unsigned char *desc, enum ec_descr_type_code desc_type, int luid_type, int nul, int peripheral_type, int rel_init_port_id, int pad, struct scsi_device *dev)
+{
+ desc[0] = desc_type;
+ desc[1] = (luid_type << 6) | (nul << 5) | peripheral_type;
+ desc[2] = (rel_init_port_id >> 8) & 0xFF;
+ desc[3] = rel_init_port_id & 0xFF;
+
+ if (desc_type == IDENT_DESCR_TGT_DESCR)
+ populate_ident_tgt_desc(desc+4, dev);
+
+ if (peripheral_type == 0) {
+ // Issue readcapacity for each sd if testing with different LUs
+ // If single LU, use block_size from prior readcapacity involcation
+ desc[28] = pad << 2;
+ desc[29] = (block_size >> 16) & 0xFF;
+ desc[30] = (block_size >> 8) & 0xFF;
+ desc[31] = block_size & 0xFF;
+ }
+ return get_desc_len(desc_type);
+}
+
+int populate_seg_desc_hdr(unsigned char *hdr, enum ec_descr_type_code desc_type, int dc, int cat, int src_index, int dst_index)
+{
+ int desc_len = get_desc_len(desc_type);
+
+ hdr[0] = desc_type;
+ hdr[1] = ((dc << 1) | cat) & 0xFF;
+ hdr[2] = (desc_len >> 8) & 0xFF;
+ hdr[3] = (desc_len - SEG_DESC_SRC_INDEX_OFFSET) & 0xFF; /* don't account for the first 4 bytes in descriptor header*/
+ hdr[4] = (src_index >> 8) & 0xFF;
+ hdr[5] = src_index & 0xFF;
+ hdr[6] = (dst_index >> 8) & 0xFF;
+ hdr[7] = dst_index & 0xFF;
+
+ return desc_len;
+}
+
+int populate_seg_desc_b2b(unsigned char *desc, int dc, int cat, int src_index, int dst_index, int num_blks, uint64_t src_lba, uint64_t dst_lba)
+{
+ int desc_len = populate_seg_desc_hdr(desc, BLK_TO_BLK_SEG_DESCR, dc, cat, src_index, dst_index);
+
+ desc[10] = (num_blks >> 8) & 0xFF;
+ desc[11] = num_blks & 0xFF;
+ desc[12] = (src_lba >> 56) & 0xFF;
+ desc[13] = (src_lba >> 48) & 0xFF;
+ desc[14] = (src_lba >> 40) & 0xFF;
+ desc[15] = (src_lba >> 32) & 0xFF;
+ desc[16] = (src_lba >> 24) & 0xFF;
+ desc[17] = (src_lba >> 16) & 0xFF;
+ desc[18] = (src_lba >> 8) & 0xFF;
+ desc[19] = src_lba & 0xFF;
+ desc[20] = (dst_lba >> 56) & 0xFF;
+ desc[21] = (dst_lba >> 48) & 0xFF;
+ desc[22] = (dst_lba >> 40) & 0xFF;
+ desc[23] = (dst_lba >> 32) & 0xFF;
+ desc[24] = (dst_lba >> 24) & 0xFF;
+ desc[25] = (dst_lba >> 16) & 0xFF;
+ desc[26] = (dst_lba >> 8) & 0xFF;
+ desc[27] = dst_lba & 0xFF;
+
+ return desc_len;
+}
+
+void populate_param_header(unsigned char *buf, int list_id, int str, int list_id_usage, int prio, int tgt_desc_len, int seg_desc_len, int inline_data_len)
+{
+ buf[0] = list_id;
+ buf[1] = ((str & 1) << 5) | ((list_id_usage & 3) << 3) | (prio & 7);
+ buf[2] = (tgt_desc_len >> 8) & 0xFF;
+ buf[3] = tgt_desc_len & 0xFF;
+ buf[8] = (seg_desc_len >> 24) & 0xFF;
+ buf[9] = (seg_desc_len >> 16) & 0xFF;
+ buf[10] = (seg_desc_len >> 8) & 0xFF;
+ buf[11] = seg_desc_len & 0xFF;
+ buf[12] = (inline_data_len >> 24) & 0xFF;
+ buf[13] = (inline_data_len >> 16) & 0xFF;
+ buf[14] = (inline_data_len >> 8) & 0xFF;
+ buf[15] = inline_data_len & 0xFF;
+}
+
+int receive_copy_results(struct scsi_device *sdev, enum scsi_copy_results_sa sa, int list_id, void **datap, int status, enum scsi_sense_key key, int *ascq, int num_ascq)
+{
+ int ret;
+ struct scsi_task *task;
+
+ logging(LOG_VERBOSE, "Send RECEIVE COPY RESULTS");
+
+ task = scsi_cdb_receive_copy_results(sa, list_id, 1024);
+ assert(task != NULL);
+
+ task = send_scsi_command(sdev, task, NULL);
+
+ ret = check_result("RECEIVECOPYRESULT", sdev, task, status, key, ascq, num_ascq);
+
+ if (task->status == SCSI_STATUS_GOOD && datap != NULL) {
+ *datap = scsi_datain_unmarshall(task);
+ if (*datap == NULL) {
+ logging(LOG_NORMAL,
+ "[FAIL] failed to unmarshall RECEIVE COPY RESULTS data. %s",
+ iscsi_get_error(sdev->iscsi_ctx));
+ return -1;
+ }
+ }
+
+ ret = check_result("RECEIVECOPYRESULT", sdev, task, status, key, ascq, num_ascq);
+ if (task)
+ scsi_free_scsi_task(task);
+
+ return ret;
+}
diff --git a/test-tool/iscsi-support.h b/test-tool/iscsi-support.h
index 498122b..6c4ba87 100644
--- a/test-tool/iscsi-support.h
+++ b/test-tool/iscsi-support.h
@@ -37,20 +37,28 @@ extern const char *initiatorname2;
#define EXPECT_STATUS_GOOD SCSI_STATUS_GOOD, SCSI_SENSE_NO_SENSE, NULL, 0
#define EXPECT_NO_MEDIUM SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_NOT_READY, no_medium_ascqs, 3
#define EXPECT_LBA_OOB SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, lba_oob_ascqs, 1
-#define EXPECT_INVALID_FIELD_IN_CDB SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, invalid_cdb_ascqs, 1
+#define EXPECT_INVALID_FIELD_IN_CDB SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, invalid_cdb_ascqs,2
+#define EXPECT_PARAM_LIST_LEN_ERR SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, param_list_len_err_ascqs, 1
+#define EXPECT_TOO_MANY_DESCR SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, too_many_desc_ascqs, 2
+#define EXPECT_UNSUPP_DESCR_CODE SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, unsupp_desc_code_ascqs, 2
#define EXPECT_MISCOMPARE SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_MISCOMPARE, miscompare_ascqs, 1
#define EXPECT_WRITE_PROTECTED SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_DATA_PROTECTION, write_protect_ascqs, 3
#define EXPECT_SANITIZE SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_NOT_READY, sanitize_ascqs, 1
#define EXPECT_REMOVAL_PREVENTED SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, removal_ascqs, 1
#define EXPECT_RESERVATION_CONFLICT SCSI_STATUS_RESERVATION_CONFLICT, 0, NULL, 0
+#define EXPECT_COPY_ABORTED SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_COPY_ABORTED, copy_aborted_ascqs, 3
int no_medium_ascqs[3];
int lba_oob_ascqs[1];
-int invalid_cdb_ascqs[1];
+int invalid_cdb_ascqs[2];
+int param_list_len_err_ascqs[1];
+int too_many_desc_ascqs[2];
+int unsupp_desc_code_ascqs[2];
int write_protect_ascqs[3];
int sanitize_ascqs[1];
int removal_ascqs[1];
int miscompare_ascqs[1];
+int copy_aborted_ascqs[3];
extern int loglevel;
#define LOG_SILENT 0
@@ -314,5 +322,11 @@ int writeverify16(struct scsi_device *sdev, uint64_t lba, uint32_t datalen, int
int set_swp(struct scsi_device *sdev);
int clear_swp(struct scsi_device *sdev);
-
+int extendedcopy(struct scsi_device *sdev, struct iscsi_data *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq);
+int get_desc_len(enum ec_descr_type_code desc_type);
+int populate_tgt_desc(unsigned char *desc, enum ec_descr_type_code desc_type, int luid_type, int nul, int peripheral_type, int rel_init_port_id, int pad, struct scsi_device *dev);
+int populate_seg_desc_hdr(unsigned char *hdr, enum ec_descr_type_code desc_type, int dc, int cat, int src_index, int dst_index);
+int populate_seg_desc_b2b(unsigned char *desc, int dc, int cat, int src_index, int dst_index, int num_blks, uint64_t src_lba, uint64_t dst_lba);
+void populate_param_header(unsigned char *buf, int list_id, int str, int list_id_usage, int prio, int tgt_desc_len, int seg_desc_len, int inline_data_len);
+int receive_copy_results(struct scsi_device *sdev, enum scsi_copy_results_sa sa, int list_id, void **datap, int status, enum scsi_sense_key key, int *ascq, int num_ascq);
#endif /* _ISCSI_SUPPORT_H_ */
diff --git a/test-tool/iscsi-test-cu.c b/test-tool/iscsi-test-cu.c
index 13df041..8cf7cb1 100644
--- a/test-tool/iscsi-test-cu.c
+++ b/test-tool/iscsi-test-cu.c
@@ -262,6 +262,22 @@ static CU_TestInfo tests_sanitize[] = {
CU_TEST_INFO_NULL
};
+static CU_TestInfo tests_extended_copy[] = {
+ { (char *)"Simple", test_extendedcopy_simple },
+ { (char *)"ParamHdr", test_extendedcopy_param },
+ { (char *)"DescrLimits", test_extendedcopy_descr_limits },
+ { (char *)"DescrType", test_extendedcopy_descr_type },
+ { (char *)"ValidTgtDescr", test_extendedcopy_validate_tgt_descr },
+ { (char *)"ValidSegDescr", test_extendedcopy_validate_seg_descr },
+ CU_TEST_INFO_NULL
+};
+
+static CU_TestInfo tests_receive_copy_results[] = {
+ { (char *)"CopyStatus", test_receive_copy_results_copy_status },
+ { (char *)"OpParams", test_receive_copy_results_op_params },
+ CU_TEST_INFO_NULL
+};
+
static CU_TestInfo tests_report_supported_opcodes[] = {
{ (char *)"Simple", test_report_supported_opcodes_simple },
{ (char *)"OneCommand", test_report_supported_opcodes_one_command },
@@ -436,6 +452,7 @@ typedef struct libiscsi_suite_info {
/* SCSI protocol tests */
static libiscsi_suite_info scsi_suites[] = {
{ "CompareAndWrite", NON_PGR_FUNCS, tests_compareandwrite },
+ { "ExtendedCopy", NON_PGR_FUNCS, tests_extended_copy },
{ "GetLBAStatus", NON_PGR_FUNCS, tests_get_lba_status },
{ "Inquiry", NON_PGR_FUNCS, tests_inquiry },
{ "Mandatory", NON_PGR_FUNCS, tests_mandatory },
@@ -456,6 +473,7 @@ static libiscsi_suite_info scsi_suites[] = {
{ "ReadCapacity10", NON_PGR_FUNCS, tests_readcapacity10 },
{ "ReadCapacity16", NON_PGR_FUNCS, tests_readcapacity16 },
{ "ReadOnly", NON_PGR_FUNCS, tests_readonly },
+ { "ReceiveCopyResults", NON_PGR_FUNCS, tests_receive_copy_results },
{ "ReportSupportedOpcodes", NON_PGR_FUNCS,
tests_report_supported_opcodes },
{ "Reserve6", NON_PGR_FUNCS, tests_reserve6 },
@@ -517,6 +535,7 @@ static libiscsi_suite_info iscsi_suites[] = {
/* All tests */
static libiscsi_suite_info all_suites[] = {
{ "CompareAndWrite", NON_PGR_FUNCS, tests_compareandwrite },
+ { "ExtendedCopy", NON_PGR_FUNCS, tests_extended_copy },
{ "GetLBAStatus", NON_PGR_FUNCS, tests_get_lba_status },
{ "Inquiry", NON_PGR_FUNCS, tests_inquiry },
{ "Mandatory", NON_PGR_FUNCS, tests_mandatory },
@@ -538,6 +557,7 @@ static libiscsi_suite_info all_suites[] = {
{ "ReadCapacity10", NON_PGR_FUNCS, tests_readcapacity10 },
{ "ReadCapacity16", NON_PGR_FUNCS, tests_readcapacity16 },
{ "ReadOnly", NON_PGR_FUNCS, tests_readonly },
+ { "ReceiveCopyResults", NON_PGR_FUNCS, tests_receive_copy_results },
{ "ReportSupportedOpcodes", NON_PGR_FUNCS,
tests_report_supported_opcodes },
{ "Reserve6", NON_PGR_FUNCS, tests_reserve6 },
diff --git a/test-tool/iscsi-test-cu.h b/test-tool/iscsi-test-cu.h
index c82a6e9..1aeb978 100644
--- a/test-tool/iscsi-test-cu.h
+++ b/test-tool/iscsi-test-cu.h
@@ -52,6 +52,13 @@ void test_compareandwrite_simple(void);
void test_compareandwrite_dpofua(void);
void test_compareandwrite_miscompare(void);
+void test_extendedcopy_simple(void);
+void test_extendedcopy_param(void);
+void test_extendedcopy_descr_limits(void);
+void test_extendedcopy_descr_type(void);
+void test_extendedcopy_validate_tgt_descr(void);
+void test_extendedcopy_validate_seg_descr(void);
+
void test_get_lba_status_simple(void);
void test_get_lba_status_beyond_eol(void);
void test_get_lba_status_unmap_single(void);
@@ -158,6 +165,9 @@ void test_readcapacity16_simple(void);
void test_readonly_sbc(void);
+void test_receive_copy_results_copy_status(void);
+void test_receive_copy_results_op_params(void);
+
void test_report_supported_opcodes_one_command(void);
void test_report_supported_opcodes_rctd(void);
void test_report_supported_opcodes_servactv(void);
diff --git a/test-tool/test_extendedcopy_descr_limits.c b/test-tool/test_extendedcopy_descr_limits.c
new file mode 100644
index 0000000..53e96d2
--- /dev/null
+++ b/test-tool/test_extendedcopy_descr_limits.c
@@ -0,0 +1,120 @@
+/*
+ Copyright (c) 2015 SanDisk Corp.
+
+ 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"
+
+int init_xcopy_descr(unsigned char *buf, int offset, int num_tgt_desc,
+ int num_seg_desc, int *tgt_desc_len, int *seg_desc_len)
+{
+ int i;
+
+ /* Initialize target descriptor list with num_tgt_desc
+ * target descriptor */
+ for (i = 0; i < num_tgt_desc; i++)
+ offset += populate_tgt_desc(buf+offset, IDENT_DESCR_TGT_DESCR,
+ LU_ID_TYPE_LUN, 0, 0, 0, 0, sd);
+ *tgt_desc_len = offset - XCOPY_DESC_OFFSET;
+
+ /* Iniitialize segment descriptor list with num_seg_desc
+ * segment descriptor */
+ for (i = 0; i < num_seg_desc; i++)
+ offset += populate_seg_desc_b2b(buf+offset, 0, 0, 0, 0,
+ 2048, 0, num_blocks - 2048);
+ *seg_desc_len = offset - XCOPY_DESC_OFFSET - *tgt_desc_len;
+
+ return offset;
+}
+
+void
+test_extendedcopy_descr_limits(void)
+{
+ int ret;
+ struct iscsi_data data;
+ unsigned char *xcopybuf;
+ struct scsi_copy_results_op_params *opp;
+ int tgt_desc_len = 0, seg_desc_len = 0;
+ unsigned int alloc_len;
+
+ logging(LOG_VERBOSE, LOG_BLANK_LINE);
+ logging(LOG_VERBOSE, "Test EXTENDED COPY descriptor limits");
+
+ CHECK_FOR_DATALOSS;
+
+ logging(LOG_VERBOSE, "Issue RECEIVE COPY RESULTS (OPERATING PARAMS)");
+ ret = receive_copy_results(sd, SCSI_COPY_RESULTS_OP_PARAMS, 0,
+ (void **)&opp, EXPECT_STATUS_GOOD);
+ CU_ASSERT_EQUAL(ret, 0);
+
+ /* Allocate buffer to accommodate (MAX+1) target and
+ * segment descriptors */
+ alloc_len = XCOPY_DESC_OFFSET +
+ (opp->max_target_desc_count+1) *
+ get_desc_len(IDENT_DESCR_TGT_DESCR) +
+ (opp->max_segment_desc_count+1) *
+ get_desc_len(BLK_TO_BLK_SEG_DESCR);
+ data.data = alloca(alloc_len);
+ xcopybuf = data.data;
+
+ logging(LOG_VERBOSE,
+ "Test sending more than supported target descriptors");
+ data.size = init_xcopy_descr(xcopybuf, XCOPY_DESC_OFFSET,
+ (opp->max_target_desc_count+1), 1,
+ &tgt_desc_len, &seg_desc_len);
+ populate_param_header(xcopybuf, 1, 0, 0, 0,
+ tgt_desc_len, seg_desc_len, 0);
+ ret = extendedcopy(sd, &data, EXPECT_TOO_MANY_DESCR);
+ if (ret == -2) {
+ CU_PASS("[SKIPPED] Target does not support "
+ "EXTENDED_COPY. Skipping test");
+ return;
+ }
+ CU_ASSERT_EQUAL(ret, 0);
+
+ logging(LOG_VERBOSE,
+ "Test sending more than supported segment descriptors");
+ memset(xcopybuf, 0, alloc_len);
+ data.size = init_xcopy_descr(xcopybuf, XCOPY_DESC_OFFSET, 1,
+ (opp->max_segment_desc_count+1),
+ &tgt_desc_len, &seg_desc_len);
+ populate_param_header(xcopybuf, 2, 0, 0, 0,
+ tgt_desc_len, seg_desc_len, 0);
+ ret = extendedcopy(sd, &data, EXPECT_TOO_MANY_DESCR);
+ CU_ASSERT_EQUAL(ret, 0);
+
+ logging(LOG_VERBOSE,
+ "Test sending descriptors > Maximum Descriptor List Length");
+ memset(xcopybuf, 0, alloc_len);
+ if (opp->max_desc_list_length < alloc_len) {
+ data.size = init_xcopy_descr(xcopybuf, XCOPY_DESC_OFFSET,
+ (opp->max_target_desc_count+1),
+ (opp->max_segment_desc_count+1),
+ &tgt_desc_len, &seg_desc_len);
+ populate_param_header(xcopybuf, 3, 0, 0, 0,
+ tgt_desc_len, seg_desc_len, 0);
+ ret = extendedcopy(sd, &data, EXPECT_PARAM_LIST_LEN_ERR);
+ CU_ASSERT_EQUAL(ret, 0);
+ }
+}
diff --git a/test-tool/test_extendedcopy_descr_type.c b/test-tool/test_extendedcopy_descr_type.c
new file mode 100644
index 0000000..28c0572
--- /dev/null
+++ b/test-tool/test_extendedcopy_descr_type.c
@@ -0,0 +1,93 @@
+/*
+ Copyright (c) 2015 SanDisk Corp.
+
+ 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"
+
+int init_xcopybuf(unsigned char *buf, int tgt_desc_type, int seg_desc_type,
+ int *tgt_desc_len, int *seg_desc_len)
+{
+ int offset = XCOPY_DESC_OFFSET;
+
+ offset += populate_tgt_desc(buf+offset, tgt_desc_type, LU_ID_TYPE_LUN,
+ 0, 0, 0, 0, sd);
+ *tgt_desc_len = offset - XCOPY_DESC_OFFSET;
+ if (seg_desc_type == BLK_TO_BLK_SEG_DESCR)
+ offset += populate_seg_desc_b2b(buf+offset, 0, 0, 0, 0, 2048, 0,
+ num_blocks - 2048);
+ else
+ offset += populate_seg_desc_hdr(buf+offset, seg_desc_type,
+ 0, 0, 0, 0);
+ *seg_desc_len = offset - XCOPY_DESC_OFFSET - *tgt_desc_len;
+
+ return offset;
+}
+
+void
+test_extendedcopy_descr_type(void)
+{
+ int ret;
+ int tgt_desc_len = 0, seg_desc_len = 0, alloc_len;
+ struct iscsi_data data;
+ unsigned char *xcopybuf;
+
+ logging(LOG_VERBOSE, LOG_BLANK_LINE);
+ logging(LOG_VERBOSE,
+ "Test EXTENDED COPY unsupported descriptor types");
+
+ CHECK_FOR_DATALOSS;
+
+ alloc_len = XCOPY_DESC_OFFSET +
+ get_desc_len(IDENT_DESCR_TGT_DESCR) +
+ get_desc_len(BLK_TO_BLK_SEG_DESCR);
+ data.data = alloca(alloc_len);
+ xcopybuf = data.data;
+
+ logging(LOG_VERBOSE,
+ "Send Fibre Channel N_Port_Name target descriptor");
+ data.size = init_xcopybuf(xcopybuf, 0xE0, BLK_TO_BLK_SEG_DESCR,
+ &tgt_desc_len, &seg_desc_len);
+ populate_param_header(xcopybuf, 1, 0, 0, 0,
+ tgt_desc_len, seg_desc_len, 0);
+
+ ret = extendedcopy(sd, &data, EXPECT_UNSUPP_DESCR_CODE);
+ if (ret == -2) {
+ CU_PASS("[SKIPPED] Target does not support "
+ "EXTENDED_COPY. Skipping test");
+ return;
+ }
+ CU_ASSERT_EQUAL(ret, 0);
+
+ logging(LOG_VERBOSE, "Send Stream-to-Stream Copy segment descriptor");
+ memset(xcopybuf, 0, alloc_len);
+ data.size = init_xcopybuf(xcopybuf, IDENT_DESCR_TGT_DESCR,
+ STRM_TO_STRM_SEG_DESCR,
+ &tgt_desc_len, &seg_desc_len);
+ populate_param_header(xcopybuf, 1, 0, 0, 0,
+ tgt_desc_len, seg_desc_len, 0);
+
+ ret = extendedcopy(sd, &data, EXPECT_UNSUPP_DESCR_CODE);
+ CU_ASSERT_EQUAL(ret, 0);
+}
diff --git a/test-tool/test_extendedcopy_param.c b/test-tool/test_extendedcopy_param.c
new file mode 100644
index 0000000..ee5d4db
--- /dev/null
+++ b/test-tool/test_extendedcopy_param.c
@@ -0,0 +1,83 @@
+/*
+ Copyright (c) 2015 SanDisk Corp.
+
+ 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_extendedcopy_param(void)
+{
+ int ret;
+ int tgt_desc_len = 0, seg_desc_len = 0, offset = XCOPY_DESC_OFFSET;
+ struct iscsi_data data;
+ unsigned char *xcopybuf;
+
+ logging(LOG_VERBOSE, LOG_BLANK_LINE);
+ logging(LOG_VERBOSE, "Test EXTENDED COPY parameter list length");
+
+ CHECK_FOR_DATALOSS;
+
+ data.size = XCOPY_DESC_OFFSET +
+ get_desc_len(IDENT_DESCR_TGT_DESCR) +
+ get_desc_len(BLK_TO_BLK_SEG_DESCR);
+ data.data = alloca(data.size);
+ xcopybuf = data.data;
+
+ offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR,
+ LU_ID_TYPE_LUN, 0, 0, 0, 0, sd);
+ tgt_desc_len = offset - XCOPY_DESC_OFFSET;
+
+ offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0,
+ 2048, 0, num_blocks - 2048);
+ seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len;
+
+ populate_param_header(xcopybuf, 1, 0, 0, 0,
+ tgt_desc_len, seg_desc_len, 0);
+
+ logging(LOG_VERBOSE,
+ "Test parameter list length truncating target descriptor");
+ data.size = XCOPY_DESC_OFFSET +
+ get_desc_len(IDENT_DESCR_TGT_DESCR) - 1;
+ ret = extendedcopy(sd, &data, EXPECT_PARAM_LIST_LEN_ERR);
+ if (ret == -2) {
+ CU_PASS("[SKIPPED] Target does not support "
+ "EXTENDED_COPY. Skipping test");
+ return;
+ }
+ CU_ASSERT_EQUAL(ret, 0);
+
+ logging(LOG_VERBOSE,
+ "Test parameter list length truncating segment descriptor");
+ data.size = XCOPY_DESC_OFFSET +
+ get_desc_len(IDENT_DESCR_TGT_DESCR) +
+ get_desc_len(BLK_TO_BLK_SEG_DESCR) - 1;
+ ret = extendedcopy(sd, &data, EXPECT_PARAM_LIST_LEN_ERR);
+ CU_ASSERT_EQUAL(ret, 0);
+
+ logging(LOG_VERBOSE, "Test parameter list length = 0");
+ data.size = 0;
+ ret = extendedcopy(sd, &data, EXPECT_STATUS_GOOD);
+ CU_ASSERT_EQUAL(ret, 0);
+}
diff --git a/test-tool/test_extendedcopy_simple.c b/test-tool/test_extendedcopy_simple.c
new file mode 100644
index 0000000..1bbc4f3
--- /dev/null
+++ b/test-tool/test_extendedcopy_simple.c
@@ -0,0 +1,93 @@
+/*
+ Copyright (c) 2015 SanDisk Corp.
+
+ 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_extendedcopy_simple(void)
+{
+ int ret;
+ int tgt_desc_len = 0, seg_desc_len = 0, offset = XCOPY_DESC_OFFSET;
+ struct iscsi_data data;
+ unsigned char *xcopybuf;
+ unsigned char *buf1 = alloca(2048*block_size);
+ unsigned char *buf2 = alloca(2048*block_size);
+
+ logging(LOG_VERBOSE, LOG_BLANK_LINE);
+ logging(LOG_VERBOSE,
+ "Test EXTENDED COPY of 2048 blocks from start of LUN to end of LUN");
+
+ CHECK_FOR_DATALOSS;
+
+ logging(LOG_VERBOSE, "Write 2048 blocks of 'A' at LBA:0");
+ memset(buf1, 'A', 2048*block_size);
+ ret = write16(sd, 0, 2048*block_size, block_size, 0, 0, 0, 0, 0,
+ buf1, EXPECT_STATUS_GOOD);
+ if (ret == -2) {
+ logging(LOG_NORMAL, "[SKIPPED] WRITE16 is not implemented.");
+ CU_PASS("WRITE16 is not implemented.");
+ return;
+ }
+ CU_ASSERT_EQUAL(ret, 0);
+
+ data.size = XCOPY_DESC_OFFSET +
+ get_desc_len(IDENT_DESCR_TGT_DESCR) +
+ get_desc_len(BLK_TO_BLK_SEG_DESCR);
+ data.data = alloca(data.size);
+ xcopybuf = data.data;
+
+ /* Initialize target descriptor list with one target descriptor */
+ offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR,
+ LU_ID_TYPE_LUN, 0, 0, 0, 0, sd);
+ tgt_desc_len = offset - XCOPY_DESC_OFFSET;
+
+ /* Iniitialize segment descriptor list with one segment descriptor */
+ offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0,
+ 2048, 0, num_blocks - 2048);
+ seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len;
+
+ /* Initialize the parameter list header */
+ populate_param_header(xcopybuf, 1, 0, LIST_ID_USAGE_DISCARD, 0,
+ tgt_desc_len, seg_desc_len, 0);
+
+ ret = extendedcopy(sd, &data, EXPECT_STATUS_GOOD);
+ if (ret == -2) {
+ CU_PASS("[SKIPPED] Target does not support "
+ "EXTENDED_COPY. Skipping test");
+ return;
+ }
+ CU_ASSERT_EQUAL(ret, 0);
+
+ logging(LOG_VERBOSE, "Read 2048 blocks from end of the LUN");
+ ret = read16(sd, NULL, num_blocks - 2048, 2048*block_size,
+ block_size, 0, 0, 0, 0, 0, buf2,
+ EXPECT_STATUS_GOOD);
+ CU_ASSERT_EQUAL(ret, 0);
+
+ ret = memcmp(buf1, buf2, 2048);
+ if (ret != 0)
+ CU_FAIL("Blocks were not copied correctly");
+}
diff --git a/test-tool/test_extendedcopy_validate_seg_descr.c b/test-tool/test_extendedcopy_validate_seg_descr.c
new file mode 100644
index 0000000..36208f7
--- /dev/null
+++ b/test-tool/test_extendedcopy_validate_seg_descr.c
@@ -0,0 +1,83 @@
+/*
+ Copyright (c) 2015 SanDisk Corp.
+
+ 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_extendedcopy_validate_seg_descr(void)
+{
+ int ret;
+ int tgt_desc_len = 0, seg_desc_len = 0, offset = XCOPY_DESC_OFFSET;
+ struct iscsi_data data;
+ unsigned char *xcopybuf;
+
+ logging(LOG_VERBOSE, LOG_BLANK_LINE);
+ logging(LOG_VERBOSE, "Test EXTENDED COPY segment descriptor fields");
+
+ CHECK_FOR_DATALOSS;
+
+ data.size = XCOPY_DESC_OFFSET +
+ get_desc_len(IDENT_DESCR_TGT_DESCR) +
+ get_desc_len(BLK_TO_BLK_SEG_DESCR);
+ data.data = alloca(data.size);
+ xcopybuf = data.data;
+
+ logging(LOG_VERBOSE, "Send invalid target descriptor index");
+ offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR,
+ LU_ID_TYPE_LUN, 0, 0, 0, 0, sd);
+ tgt_desc_len = offset - XCOPY_DESC_OFFSET;
+ /* Inaccessible DESTINATION TARGET DESCRIPTOR INDEX */
+ offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 1,
+ 2048, 0, num_blocks - 2048);
+ seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len;
+ populate_param_header(xcopybuf, 1, 0, 0, 0,
+ tgt_desc_len, seg_desc_len, 0);
+
+ ret = extendedcopy(sd, &data, EXPECT_COPY_ABORTED);
+ if (ret == -2) {
+ CU_PASS("[SKIPPED] Target does not support "
+ "EXTENDED_COPY. Skipping test");
+ return;
+ }
+ CU_ASSERT_EQUAL(ret, 0);
+
+ logging(LOG_VERBOSE,
+ "Number of copy blocks beyond destination block device capacity");
+ memset(xcopybuf, 0, data.size);
+ offset = XCOPY_DESC_OFFSET;
+ offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR,
+ LU_ID_TYPE_LUN, 0, 0, 0, 0, sd);
+ tgt_desc_len = offset - XCOPY_DESC_OFFSET;
+ /* Beyond EOL */
+ offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0,
+ 2048, 0, num_blocks - 1);
+ seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len;
+ populate_param_header(xcopybuf, 1, 0, 0, 0,
+ tgt_desc_len, seg_desc_len, 0);
+
+ ret = extendedcopy(sd, &data, EXPECT_COPY_ABORTED);
+ CU_ASSERT_EQUAL(ret, 0);
+}
diff --git a/test-tool/test_extendedcopy_validate_tgt_descr.c b/test-tool/test_extendedcopy_validate_tgt_descr.c
new file mode 100644
index 0000000..ec197c7
--- /dev/null
+++ b/test-tool/test_extendedcopy_validate_tgt_descr.c
@@ -0,0 +1,82 @@
+/*
+ Copyright (c) 2015 SanDisk Corp.
+
+ 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_extendedcopy_validate_tgt_descr(void)
+{
+ int ret;
+ int tgt_desc_len = 0, seg_desc_len = 0, offset = XCOPY_DESC_OFFSET;
+ struct iscsi_data data;
+ unsigned char *xcopybuf;
+
+ logging(LOG_VERBOSE, LOG_BLANK_LINE);
+ logging(LOG_VERBOSE, "Test EXTENDED COPY target descriptor fields");
+
+ CHECK_FOR_DATALOSS;
+
+ data.size = XCOPY_DESC_OFFSET +
+ get_desc_len(IDENT_DESCR_TGT_DESCR) +
+ get_desc_len(BLK_TO_BLK_SEG_DESCR);
+ data.data = alloca(data.size);
+ xcopybuf = data.data;
+
+ logging(LOG_VERBOSE, "Unsupported LU_ID TYPE");
+ /* Unsupported LU ID TYPE */
+ offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR,
+ LU_ID_TYPE_RSVD, 0, 0, 0, 0, sd);
+ tgt_desc_len = offset - XCOPY_DESC_OFFSET;
+ offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0,
+ 2048, 0, num_blocks - 2048);
+ seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len;
+ populate_param_header(xcopybuf, 1, 0, 0, 0,
+ tgt_desc_len, seg_desc_len, 0);
+
+ ret = extendedcopy(sd, &data, EXPECT_INVALID_FIELD_IN_CDB);
+ if (ret == -2) {
+ CU_PASS("[SKIPPED] Target does not support "
+ "EXTENDED_COPY. Skipping test");
+ return;
+ }
+ CU_ASSERT_EQUAL(ret, 0);
+
+ logging(LOG_VERBOSE, "Test NUL bit in target descriptor");
+ /* NUL bit */
+ memset(xcopybuf, 0, data.size);
+ offset = XCOPY_DESC_OFFSET;
+ offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR,
+ LU_ID_TYPE_LUN, 1, 0, 0, 0, sd);
+ tgt_desc_len = offset - XCOPY_DESC_OFFSET;
+ offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0,
+ 2048, 0, num_blocks - 2048);
+ seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len;
+ populate_param_header(xcopybuf, 1, 0, 0, 0,
+ tgt_desc_len, seg_desc_len, 0);
+
+ ret = extendedcopy(sd, &data, EXPECT_COPY_ABORTED);
+ CU_ASSERT_EQUAL(ret, 0);
+}
diff --git a/test-tool/test_receive_copy_results_copy_status.c b/test-tool/test_receive_copy_results_copy_status.c
new file mode 100644
index 0000000..d8dd95c
--- /dev/null
+++ b/test-tool/test_receive_copy_results_copy_status.c
@@ -0,0 +1,83 @@
+/*
+ Copyright (c) 2015 SanDisk Corp.
+
+ 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_receive_copy_results_copy_status(void)
+{
+ int ret;
+ 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;
+
+ logging(LOG_VERBOSE, LOG_BLANK_LINE);
+ logging(LOG_VERBOSE, "Test RECEIVE COPY RESULTS, COPY STATUS");
+
+ logging(LOG_VERBOSE, "No copy in progress");
+ ret = receive_copy_results(sd, SCSI_COPY_RESULTS_COPY_STATUS,
+ list_id, NULL, EXPECT_INVALID_FIELD_IN_CDB);
+ CU_ASSERT_EQUAL(ret, 0);
+
+ CHECK_FOR_DATALOSS;
+
+ logging(LOG_VERBOSE, "Issue Extended Copy");
+ data.size = XCOPY_DESC_OFFSET +
+ get_desc_len(IDENT_DESCR_TGT_DESCR) +
+ get_desc_len(BLK_TO_BLK_SEG_DESCR);
+ data.data = alloca(data.size);
+ xcopybuf = data.data;
+
+ /* Initialize target descriptor list with one target descriptor */
+ offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR,
+ LU_ID_TYPE_LUN, 0, 0, 0, 0, sd);
+ tgt_desc_len = offset - XCOPY_DESC_OFFSET;
+
+ /* Iniitialize segment descriptor list with one segment descriptor */
+ offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0,
+ 2048, 0, num_blocks - 2048);
+ seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len;
+
+ /* Initialize the parameter list header */
+ populate_param_header(xcopybuf, list_id, 0, 0, 0,
+ tgt_desc_len, seg_desc_len, 0);
+
+ ret = extendedcopy(sd, &data, EXPECT_STATUS_GOOD);
+ if (ret == -2) {
+ CU_PASS("[SKIPPED] Target does not support "
+ "EXTENDED_COPY. Skipping test");
+ return;
+ }
+ CU_ASSERT_EQUAL(ret, 0);
+
+ logging(LOG_VERBOSE,
+ "Copy Status for the above Extended Copy command");
+ ret = receive_copy_results(sd, SCSI_COPY_RESULTS_COPY_STATUS,
+ list_id, (void **)&csp, EXPECT_STATUS_GOOD);
+ CU_ASSERT_EQUAL(ret, 0);
+}
diff --git a/test-tool/test_receive_copy_results_op_params.c b/test-tool/test_receive_copy_results_op_params.c
new file mode 100644
index 0000000..4f8e151
--- /dev/null
+++ b/test-tool/test_receive_copy_results_op_params.c
@@ -0,0 +1,46 @@
+/*
+ Copyright (c) 2015 SanDisk Corp.
+
+ 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_receive_copy_results_op_params(void)
+{
+ int ret;
+ struct scsi_copy_results_op_params *opp;
+
+ logging(LOG_VERBOSE, LOG_BLANK_LINE);
+ logging(LOG_VERBOSE, "Test RECEIVE COPY RESULTS, OPERATING PARAMS");
+
+ ret = receive_copy_results(sd, SCSI_COPY_RESULTS_OP_PARAMS, 0,
+ (void **)&opp, EXPECT_STATUS_GOOD);
+ CU_ASSERT_EQUAL(ret, 0);
+
+ logging(LOG_NORMAL,
+ "max_target_desc=%d, max_seg_desc=%d",
+ opp->max_target_desc_count,
+ opp->max_segment_desc_count);
+}