void
test_extendedcopy_validate_seg_descr(void)
{
        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;
        memset(xcopybuf, 0, data.size);

        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);

        EXTENDEDCOPY(sd, &data, EXPECT_COPY_ABORTED);

        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);

        EXTENDEDCOPY(sd, &data, EXPECT_COPY_ABORTED);
}
void
test_extendedcopy_param(void)
{
        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;
        memset(xcopybuf, 0, data.size);

        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;
        EXTENDEDCOPY(sd, &data, EXPECT_PARAM_LIST_LEN_ERR);

        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;
        EXTENDEDCOPY(sd, &data, EXPECT_PARAM_LIST_LEN_ERR);

        logging(LOG_VERBOSE, "Test parameter list length = 0");
        data.size = 0;
        EXTENDEDCOPY(sd, &data, EXPECT_STATUS_GOOD);
}
void
test_extendedcopy_simple(void)
{
        int tgt_desc_len = 0, seg_desc_len = 0, offset = XCOPY_DESC_OFFSET;
        struct iscsi_data data;
        unsigned char *xcopybuf;
        unsigned int copied_blocks;
        unsigned char *buf1;
        unsigned char *buf2;


        copied_blocks = num_blocks / 2;
        if (copied_blocks > 2048)
                copied_blocks = 2048;
        buf1 = malloc(copied_blocks * block_size);
        buf2 = malloc(copied_blocks * block_size);

        logging(LOG_VERBOSE, LOG_BLANK_LINE);
        logging(LOG_VERBOSE,
                "Test EXTENDED COPY of %u blocks from start of LUN to end of LUN",
                copied_blocks);

        CHECK_FOR_DATALOSS;

        logging(LOG_VERBOSE, "Write %u blocks of 'A' at LBA:0", copied_blocks);
        memset(buf1, 'A', copied_blocks * block_size);
        WRITE16(sd, 0, copied_blocks * block_size, block_size, 0, 0, 0, 0, 0,
                buf1, EXPECT_STATUS_GOOD);

        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;
        memset(xcopybuf, 0, data.size);

        /* 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,
                        copied_blocks, 0, num_blocks - copied_blocks);
        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);

        EXTENDEDCOPY(sd, &data, EXPECT_STATUS_GOOD);

        logging(LOG_VERBOSE, "Read %u blocks from end of the LUN",
                copied_blocks);
        READ16(sd, NULL, num_blocks - copied_blocks, copied_blocks * block_size,
               block_size, 0, 0, 0, 0, 0, buf2,
               EXPECT_STATUS_GOOD);

        if (memcmp(buf1, buf2, copied_blocks * block_size)) {
                CU_FAIL("Blocks were not copied correctly");
        }
        
        free(buf1);
        free(buf2);
}
void
test_extendedcopy_validate_tgt_descr(void)
{
        int tgt_desc_len = 0, seg_desc_len = 0, offset = XCOPY_DESC_OFFSET;
        int ret;
        struct scsi_inquiry_standard *std_inq;
        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;

        ret = inquiry(sd, &task, 0, 0, 260,
                      EXPECT_STATUS_GOOD);
        CU_ASSERT_EQUAL(ret, 0);
        std_inq = scsi_datain_unmarshall(task);
        CU_ASSERT_NOT_EQUAL(std_inq, NULL);

        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;
        memset(xcopybuf, 0, data.size);

        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);

        if (std_inq->version >= 6) {
                /* SPC-4 - LU ID should be ignored "*/
                EXTENDEDCOPY(sd, &data, EXPECT_STATUS_GOOD);
        } else {
                /* SPC-3 - LU ID is reserved */
                EXTENDEDCOPY(sd, &data, EXPECT_INVALID_FIELD_IN_CDB);
        }

        if (std_inq->version >= 6) {
                /* NUL bit is obsolete in SPC-4 */
                CU_PASS("[SKIPPED] Target is SPC-4+. Skipping NUL bit test");
                return;
        }
        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);

        EXTENDEDCOPY(sd, &data, EXPECT_COPY_ABORTED);
}