void
test_writesame16_simple(void)
{
	int i, ret;
	unsigned char *buf = alloca(block_size);

	
	CHECK_FOR_DATALOSS;
	CHECK_FOR_SBC;

	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test WRITESAME16 of 1-256 blocks at the start of the LUN");

	for (i = 1; i <= 256; i++) {
		ret = writesame16(iscsic, tgt_lun, 0,
				  block_size, i,
				  0, 0, 0, 0, buf);
		if (ret == -2) {
			logging(LOG_NORMAL, "[SKIPPED] WRITEVERIFY16 is not implemented.");
			CU_PASS("[SKIPPED] Target does not support WRITESAME16. Skipping test");
			return;
		}
		CU_ASSERT_EQUAL(ret, 0);
	}

	logging(LOG_VERBOSE, "Test WRITESAME16 of 1-256 blocks at the end of the LUN");
	for (i = 1; i <= 256; i++) {
		ret = writesame16(iscsic, tgt_lun, num_blocks - i,
				  block_size, i,
				  0, 0, 0, 0, buf);
		CU_ASSERT_EQUAL(ret, 0);
	}

}
void
test_writesame16_wrprotect(void)
{
	int i, ret;
	unsigned char *buf = alloca(block_size);

	/*
	 * Try out different non-zero values for WRPROTECT.
	 */
	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test WRITESAME16 with non-zero WRPROTECT");

	CHECK_FOR_DATALOSS;
	CHECK_FOR_SBC;

	memset(buf, 0, block_size);
	if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) {
		logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail.");
		for (i = 1; i < 8; i++) {
			ret = writesame16(sd, 0,
					  block_size, 1, 0, 0, i, 0, buf,
					  EXPECT_INVALID_FIELD_IN_CDB);
			if (ret == -2) {
				logging(LOG_NORMAL, "[SKIPPED] WRITESAME16 is not implemented.");
				CU_PASS("[SKIPPED] Target does not support WRITESAME16. Skipping test");
				return;
			}
			CU_ASSERT_EQUAL(ret, 0);
		}
		return;
	}

	logging(LOG_NORMAL, "No tests for devices that support protection information yet.");
}
void
test_writesame16_unmap_until_end(void)
{
	int ret;
	unsigned int i, j;
	unsigned char *buf = alloca(256 * block_size);
	unsigned char *zeroBlock;

	CHECK_FOR_DATALOSS;
	CHECK_FOR_THIN_PROVISIONING;
	CHECK_FOR_LBPWS;
	CHECK_FOR_SBC;

	zeroBlock = malloc(block_size);
	memset(zeroBlock, 0, block_size);

	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test WRITESAME16 of 1-256 blocks at the end of the LUN by setting number-of-blocks==0");
	for (i = 1; i <= 256; i++) {
		logging(LOG_VERBOSE, "Write %d blocks of 0xFF", i);
		memset(buf, 0xff, block_size * i);
		ret = write16(iscsic, tgt_lun, num_blocks - i,
			      i * block_size, block_size,
			      0, 0, 0, 0, 0, buf);

		logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME16", i);
		ret = writesame16(iscsic, tgt_lun, num_blocks - i,
				  0, i,
				  0, 1, 0, 0, NULL);
		if (ret == -2) {
			logging(LOG_NORMAL, "[SKIPPED] WRITESAME16 is not implemented.");
			CU_PASS("[SKIPPED] Target does not support WRITESAME16. Skipping test");
			return;
		}
		CU_ASSERT_EQUAL(ret, 0);

		if (rc16->lbprz) {
			logging(LOG_VERBOSE, "LBPRZ is set. Read the unmapped "
				"blocks back and verify they are all zero");

			logging(LOG_VERBOSE, "Read %d blocks and verify they "
				"are now zero", i);
			ret = read16(iscsic, tgt_lun, num_blocks - i,
				     i * block_size, block_size,
				     0, 0, 0, 0, 0, buf);
			for (j = 0; j < i; j++) {
				CU_ASSERT_EQUAL(memcmp(buf + j*block_size, zeroBlock, block_size), 0);
			}
		} else {
			logging(LOG_VERBOSE, "LBPRZ is clear. Skip the read "
				"and verify zero test");
		}
	}
	free(zeroBlock);
}
void
test_writesame16_unmap_vpd(void)
{
        int ret;

        logging(LOG_VERBOSE, LOG_BLANK_LINE);
        logging(LOG_VERBOSE, "Test WRITESAME16 UNMAP availability is "
                "consistent with VPD settings");

        CHECK_FOR_DATALOSS;
        CHECK_FOR_SBC;

        logging(LOG_VERBOSE, "Check if WRITESAME16 can be used for UNMAP.");

        logging(LOG_VERBOSE, "Unmap 1 block using WRITESAME16");
        memset(scratch, 0, block_size);
        ret = writesame16(sd, 0, block_size, 1, 0, 1, 0, 0, scratch,
                          EXPECT_STATUS_GOOD);
        if (ret != 0) {
                logging(LOG_VERBOSE, "WRITESAME16 UNMAP is not available. "
                        "Verify that VPD settings reflect this.");

                logging(LOG_VERBOSE, "Verify that LBPWS is clear.");
                if (inq_lbp && inq_lbp->lbpws) {
                        logging(LOG_NORMAL, "[FAILED] WRITESAME16 UNMAP is not "
                                "implemented but LBPWS is set");
                        CU_FAIL("[FAILED] WRITESAME16 UNMAP is unavailable but "
                                "LBPWS==1");
                } else {
                        logging(LOG_VERBOSE, "[SUCCESS] LBPWS is clear.");
                }
        } else {
                logging(LOG_VERBOSE, "WRITESAME16 UNMAP is available. Verify "
                        "that VPD settings reflect this.");

                logging(LOG_VERBOSE, "Verify that LBPME is set.");
                if (rc16 && rc16->lbpme) {
                        logging(LOG_VERBOSE, "[SUCCESS] LBPME is set.");
                } else {
                        logging(LOG_NORMAL, "[FAILED] WRITESAME16 UNMAP is "
                                "implemented but LBPME is not set");
                        CU_FAIL("[FAILED] UNMAP is available but LBPME==0");
                }

                logging(LOG_VERBOSE, "Verify that LBPWS is set.");
                if (inq_lbp && inq_lbp->lbpws) {
                        logging(LOG_VERBOSE, "[SUCCESS] LBPWS is set.");
                } else {
                        logging(LOG_NORMAL, "[FAILED] WRITESAME16 UNMAP is "
                                "implemented but LBPWS is not set");
                        CU_FAIL("[FAILED] UNMAP is available but LBPWS==0");
                }
        }
}
void
test_writesame16_0blocks(void)
{
	int ret;

	CHECK_FOR_DATALOSS;
	CHECK_FOR_SBC;

	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test WRITESAME16 0-blocks at LBA==0 (WSNZ=%d)",
		inq_bl->wsnz);
	ret = writesame16(iscsic, tgt_lun, 0,
			  block_size, 0,
			  0, 0, 0, 0, NULL);
	if (ret == -2) {
		logging(LOG_NORMAL, "[SKIPPED] WRITESAME16 is not implemented.");
		CU_PASS("[SKIPPED] Target does not support WRITESAME16. Skipping test");
		return;
	} else if (ret == -3) {
		CU_PASS("[SKIPPED] Target does not support WRITESAME16 with NUMBER OF LOGICAL BLOCKS == 0");
	} else if (ret == -4) {
		CU_PASS("[SKIPPED] Number of WRITESAME16 logical blocks to be written exceeds MAXIMUM WRITE SAME LENGTH");
	} else {
		if (inq_bl->wsnz) {
			CU_ASSERT_EQUAL(ret, -1);
		} else {
			CU_ASSERT_EQUAL(ret, 0);
		}
	}

	logging(LOG_VERBOSE, "Test WRITESAME16 0-blocks one block past end-of-LUN");
	ret = writesame16_lbaoutofrange(iscsic, tgt_lun, num_blocks + 1,
					block_size, inq_bl->wsnz,
					0, 0, 0, 0, NULL);
	CU_ASSERT_EQUAL(ret, 0);


	logging(LOG_VERBOSE, "Test WRITESAME16 0-blocks at LBA==2^63");
	ret = writesame16_lbaoutofrange(iscsic, tgt_lun, 0x8000000000000000ULL,
					block_size, inq_bl->wsnz,
					0, 0, 0, 0, NULL);
	CU_ASSERT_EQUAL(ret, 0);


	logging(LOG_VERBOSE, "Test WRITESAME16 0-blocks at LBA==-1");
	ret = writesame16_lbaoutofrange(iscsic, tgt_lun, -1,
					block_size, inq_bl->wsnz,
					0, 0, 0, 0, NULL);
	CU_ASSERT_EQUAL(ret, 0);
}
void
test_writesame16_unmap(void)
{
	int ret;
	unsigned int i;
	unsigned char *buf;

	CHECK_FOR_DATALOSS;
	CHECK_FOR_THIN_PROVISIONING;
	CHECK_FOR_LBPWS;
	CHECK_FOR_SBC;

	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test WRITESAME16 of 1-256 blocks at the start of the LUN");
	buf = calloc(65536, block_size);
	for (i = 1; i <= 256; i++) {
		logging(LOG_VERBOSE, "Write %d blocks of 0xFF", i);
		memset(buf, 0xff, i * block_size);
		ret = write16(sd, 0,
			      i * block_size, block_size, 0, 0, 0, 0, 0, buf,
			      EXPECT_STATUS_GOOD);
		logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME16", i);
		memset(buf, 0, block_size);
		ret = writesame16(sd, 0,
				  block_size, i, 0, 1, 0, 0, buf,
				  EXPECT_STATUS_GOOD);
		if (ret == -2) {
			logging(LOG_NORMAL, "[SKIPPED] WRITESAME16 is not implemented.");
			CU_PASS("[SKIPPED] Target does not support WRITESAME16. Skipping test");
			goto finished;
		}
		CU_ASSERT_EQUAL(ret, 0);

		if (rc16->lbprz) {
			logging(LOG_VERBOSE, "LBPRZ is set. Read the unmapped "
				"blocks back and verify they are all zero");
			logging(LOG_VERBOSE, "Read %d blocks and verify they "
				"are now zero", i);
			ret = read16(sd, NULL, 0,
				     i * block_size, block_size,
				     0, 0, 0, 0, 0, buf,
				     EXPECT_STATUS_GOOD);
			CU_ASSERT(all_zeroes(buf, i * block_size));
		} else {
			logging(LOG_VERBOSE, "LBPRZ is clear. Skip the read "
				"and verify zero test");
		}
	}


	logging(LOG_VERBOSE, "Test WRITESAME16 of 1-256 blocks at the end of the LUN");
	for (i = 1; i <= 256; i++) {
		logging(LOG_VERBOSE, "Write %d blocks of 0xFF", i);
		memset(buf, 0xff, i * block_size);
		ret = write16(sd, num_blocks - i,
			      i * block_size, block_size, 0, 0, 0, 0, 0, buf,
			      EXPECT_STATUS_GOOD);
		CU_ASSERT_EQUAL(ret, 0);

		logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME16", i);
		memset(buf, 0, block_size);
		ret = writesame16(sd, num_blocks - i,
				  block_size, i, 0, 1, 0, 0, buf,
				  EXPECT_STATUS_GOOD);
		CU_ASSERT_EQUAL(ret, 0);

		if (rc16->lbprz) {
			logging(LOG_VERBOSE, "LBPRZ is set. Read the unmapped "
				"blocks back and verify they are all zero");
			logging(LOG_VERBOSE, "Read %d blocks and verify they "
				"are now zero", i);
			ret = read16(sd, NULL, num_blocks - i,
				     i * block_size, block_size,
				     0, 0, 0, 0, 0, buf,
				     EXPECT_STATUS_GOOD);
			CU_ASSERT(all_zeroes(buf, i * block_size));
		} else {
			logging(LOG_VERBOSE, "LBPRZ is clear. Skip the read "
				"and verify zero test");
		}
	}

	logging(LOG_VERBOSE, "Verify that WRITESAME16 ANCHOR==1 + UNMAP==0 is invalid");
	ret = writesame16(sd, 0,
			  block_size, 1, 1, 0, 0, 0, buf,
			  EXPECT_INVALID_FIELD_IN_CDB);
	CU_ASSERT_EQUAL(ret, 0);



	if (inq_lbp->anc_sup) {
		logging(LOG_VERBOSE, "Test WRITESAME16 ANCHOR==1 + UNMAP==0");
		memset(buf, 0, block_size);
		ret = writesame16(sd, 0,
				  block_size, 1, 1, 1, 0, 0, buf,
				  EXPECT_STATUS_GOOD);
	} else {
		logging(LOG_VERBOSE, "Test WRITESAME16 ANCHOR==1 + UNMAP==0 no ANC_SUP so expecting to fail");
		ret = writesame16(sd, 0,
				  block_size, 1, 1, 1, 0, 0, buf,
				  EXPECT_INVALID_FIELD_IN_CDB);
	}

	CU_ASSERT_EQUAL(ret, 0);


	if (inq_bl == NULL) {
		logging(LOG_VERBOSE, "[FAILED] WRITESAME16 works but "
			"BlockLimits VPD is missing.");
		CU_FAIL("[FAILED] WRITESAME16 works but "
			"BlockLimits VPD is missing.");
		goto finished;
	}

	i = 256;
	if (i <= num_blocks
	    && (inq_bl->max_ws_len == 0 || inq_bl->max_ws_len >= i)) {
		logging(LOG_VERBOSE, "Block Limits VPD page reports MAX_WS_LEN "
			"as either 0 (==no limit) or >= %d. Test Unmapping "
			"%d blocks to verify that it can handle 2-byte "
			"lengths", i, i);

		logging(LOG_VERBOSE, "Write %d blocks of 0xFF", i);
		memset(buf, 0xff, i * block_size);
		ret = write16(sd, 0,
			      i * block_size, block_size, 0, 0, 0, 0, 0, buf,
			      EXPECT_STATUS_GOOD);
		CU_ASSERT_EQUAL(ret, 0);

		logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME16", i);
		memset(buf, 0, block_size);
		ret = writesame16(sd, 0,
				  block_size, i, 0, 1, 0, 0, buf,
				  EXPECT_STATUS_GOOD);
		CU_ASSERT_EQUAL(ret, 0);

		if (rc16->lbprz) {
			logging(LOG_VERBOSE, "LBPRZ is set. Read the unmapped "
				"blocks back and verify they are all zero");

			logging(LOG_VERBOSE, "Read %d blocks and verify they "
				"are now zero", i);
			ret = read16(sd, NULL, 0,
				     i * block_size, block_size,
				     0, 0, 0, 0, 0, buf,
				     EXPECT_STATUS_GOOD);
			CU_ASSERT(all_zeroes(buf, i * block_size));
		} else {
			logging(LOG_VERBOSE, "LBPRZ is clear. Skip the read "
				"and verify zero test");
		}
	} else if (i <= num_blocks) {
		logging(LOG_VERBOSE, "Block Limits VPD page reports MAX_WS_LEN "
			"as <256. Verify that a 256 block unmap fails with "
			"INVALID_FIELD_IN_CDB.");

		logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME16", i);
		ret = writesame16(sd, 0,
				  block_size, i, 0, 1, 0, 0, buf,
				  EXPECT_INVALID_FIELD_IN_CDB);
		CU_ASSERT_EQUAL(ret, 0);
	}


	i = 65536;
	if (i <= num_blocks
	    && (inq_bl->max_ws_len == 0 || inq_bl->max_ws_len >= i)) {
		logging(LOG_VERBOSE, "Block Limits VPD page reports MAX_WS_LEN "
			"as either 0 (==no limit) or >= %d. Test Unmapping "
			"%d blocks to verify that it can handle 4-byte "
			"lengths", i, i);

		logging(LOG_VERBOSE, "Write %d blocks of 0xFF", i);
		memset(buf, 0xff, i * block_size);
		ret = write16(sd, 0,
			      i * block_size, block_size, 0, 0, 0, 0, 0, buf,
			      EXPECT_STATUS_GOOD);
		CU_ASSERT_EQUAL(ret, 0);

		logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME16", i);
		memset(buf, 0, block_size);
		ret = writesame16(sd, 0,
				  block_size, i, 0, 1, 0, 0, buf,
				  EXPECT_STATUS_GOOD);
		CU_ASSERT_EQUAL(ret, 0);

		if (rc16->lbprz) {
			logging(LOG_VERBOSE, "LBPRZ is set. Read the unmapped "
				"blocks back and verify they are all zero");

			logging(LOG_VERBOSE, "Read %d blocks and verify they "
				"are now zero", i);
			ret = read16(sd, NULL, 0,
				     i * block_size, block_size,
				     0, 0, 0, 0, 0, buf,
				     EXPECT_STATUS_GOOD);
			CU_ASSERT(all_zeroes(buf, i * block_size));
		} else {
			logging(LOG_VERBOSE, "LBPRZ is clear. Skip the read "
				"and verify zero test");
		}
	} else if (i <= num_blocks) {
		logging(LOG_VERBOSE, "Block Limits VPD page reports MAX_WS_LEN "
			"as <256. Verify that a 256 block unmap fails with "
			"INVALID_FIELD_IN_CDB.");

		logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME16", i);
		ret = writesame16(sd, 0,
				  block_size, i, 0, 1, 0, 0, buf,
				  EXPECT_INVALID_FIELD_IN_CDB);
		CU_ASSERT_EQUAL(ret, 0);
	}

finished:
	free(buf);
}
void
test_writesame16_0blocks(void)
{
	int ret;
	unsigned char *buf = alloca(block_size);

	CHECK_FOR_DATALOSS;
	CHECK_FOR_SBC;

	if (!inq_bl) {
		CU_PASS("BlockLimits VPD is not available. Skipping test.\n");
		return;
	}

	logging(LOG_VERBOSE, LOG_BLANK_LINE);
	logging(LOG_VERBOSE, "Test WRITESAME16 0-blocks at LBA==0 (WSNZ=%d)",
		inq_bl->wsnz);
	memset(buf, 0, block_size);

	if (inq_bl->wsnz) {
		ret = writesame16(sd, 0,
				  block_size, 0, 0, 0, 0, 0, buf,
				  EXPECT_INVALID_FIELD_IN_CDB);
		logging(LOG_NORMAL, "[SKIPPED] WRITESAME16 does not support 0-blocks.");
		CU_ASSERT_EQUAL(ret, 0);
		return;
	}

	ret = writesame16(sd, 0,
			  block_size, 0, 0, 0, 0, 0, buf,
			  EXPECT_STATUS_GOOD);
	if (ret == -2) {
		logging(LOG_NORMAL, "[SKIPPED] WRITESAME16 is not implemented.");
		CU_PASS("[SKIPPED] Target does not support WRITESAME16. Skipping test");
		return;
	} else if (ret == -3) {
		CU_PASS("[SKIPPED] Target does not support WRITESAME16 with NUMBER OF LOGICAL BLOCKS == 0");
	} else if (ret == -4) {
		CU_PASS("[SKIPPED] Number of WRITESAME16 logical blocks to be written exceeds MAXIMUM WRITE SAME LENGTH");
	} else {
		CU_ASSERT_EQUAL(ret, 0);
	}

	logging(LOG_VERBOSE, "Test WRITESAME16 0-blocks one block past end-of-LUN");
	ret = writesame16(sd, num_blocks + 1,
			  block_size, 0, 0, 0, 0, 0, buf,
			  EXPECT_LBA_OOB);
	CU_ASSERT_EQUAL(ret, 0);


	logging(LOG_VERBOSE, "Test WRITESAME16 0-blocks at LBA==2^63");
	ret = writesame16(sd, 0x8000000000000000ULL,
			  block_size, 0, 0, 0, 0, 0, buf,
			  EXPECT_LBA_OOB);
	CU_ASSERT_EQUAL(ret, 0);


	logging(LOG_VERBOSE, "Test WRITESAME16 0-blocks at LBA==-1");
	ret = writesame16(sd, -1,
			  block_size, 0, 0, 0, 0, 0, buf,
			  EXPECT_LBA_OOB);
	CU_ASSERT_EQUAL(ret, 0);
}