Blame SOURCES/0019-fix-iovec-short-reads.patch

dc245c
From 73b0d4777f90ec6e1f0a81f9e4c21830fb58c9b7 Mon Sep 17 00:00:00 2001
dc245c
From: Paolo Bonzini <pbonzini@redhat.com>
dc245c
Subject: [RHEL7 libiscsi PATCH 18/18] Ignore padding when an iovector is supplied
dc245c
dc245c
The iSCSI protocol adds padding to a data packet if the data size is not
dc245c
a multiple of four.  The iovector provided by QEMU does not include such
dc245c
padding, and libiscsi then complains that there was a protocol error.
dc245c
This patch fixes this by reading the padding in a separate "recv"
dc245c
system call.  These packets anyway do not happen in the data path,
dc245c
where the packet size is a multiple of 512.
dc245c
dc245c
Similarly, we need to write padding after an outgoing data packet.
dc245c
dc245c
This fixes QEMU's scsi-generic backend, which triggered the problem when
dc245c
the target sent a 66-byte INQUIRY response.
dc245c
dc245c
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
dc245c
(cherry-picked from upstream commit 73b0d4777f90ec6e1f0a81f9e4c21830fb58c9b7)
dc245c
---
dc245c
diff --git a/include/iscsi-private.h b/include/iscsi-private.h
dc245c
index e9b9e7e..6cc8a20 100644
dc245c
--- a/include/iscsi-private.h
dc245c
+++ b/include/iscsi-private.h
dc245c
@@ -261,6 +261,7 @@ struct scsi_task;
dc245c
 void iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task);
dc245c
 
dc245c
 int iscsi_get_pdu_data_size(const unsigned char *hdr);
dc245c
+int iscsi_get_pdu_padding_size(const unsigned char *hdr);
dc245c
 int iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in);
dc245c
 
dc245c
 int iscsi_process_login_reply(struct iscsi_context *iscsi,
dc245c
diff --git a/lib/pdu.c b/lib/pdu.c
dc245c
index f53490a..043a986 100644
dc245c
--- a/lib/pdu.c
dc245c
+++ b/lib/pdu.c
dc245c
@@ -217,11 +217,22 @@ iscsi_get_pdu_data_size(const unsigned char *hdr)
dc245c
 	int size;
dc245c
 
dc245c
 	size = scsi_get_uint32(&hdr[4]) & 0x00ffffff;
dc245c
-	size = (size+3) & 0xfffffffc;
dc245c
 
dc245c
 	return size;
dc245c
 }
dc245c
 
dc245c
+
dc245c
+int
dc245c
+iscsi_get_pdu_padding_size(const unsigned char *hdr)
dc245c
+{
dc245c
+	int data_size, padded_size;
dc245c
+
dc245c
+	data_size = scsi_get_uint32(&hdr[4]) & 0x00ffffff;
dc245c
+	padded_size = (data_size+3) & 0xfffffffc;
dc245c
+
dc245c
+	return padded_size - data_size;
dc245c
+}
dc245c
+
dc245c
 enum iscsi_reject_reason {
dc245c
 	ISCSI_REJECT_RESERVED                 = 0x01,
dc245c
 	ISCSI_REJECT_DATA_DIGEST_ERROR        = 0x02,
dc245c
diff --git a/lib/socket.c b/lib/socket.c
dc245c
index 13020ee..7a668c2 100644
dc245c
--- a/lib/socket.c
dc245c
+++ b/lib/socket.c
dc245c
@@ -479,7 +479,7 @@ static int
dc245c
 iscsi_read_from_socket(struct iscsi_context *iscsi)
dc245c
 {
dc245c
 	struct iscsi_in_pdu *in;
dc245c
-	ssize_t data_size, count;
dc245c
+	ssize_t data_size, count, padding_size;
dc245c
 
dc245c
 	if (iscsi->incoming == NULL) {
dc245c
 		iscsi->incoming = iscsi_zmalloc(iscsi, sizeof(struct iscsi_in_pdu));
dc245c
@@ -516,31 +516,36 @@ iscsi_read_from_socket(struct iscsi_context *iscsi)
dc245c
 		return 0;
dc245c
 	}
dc245c
 
dc245c
-	data_size = iscsi_get_pdu_data_size(&in->hdr[0]);
dc245c
+	padding_size = iscsi_get_pdu_padding_size(&in->hdr[0]);
dc245c
+	data_size = iscsi_get_pdu_data_size(&in->hdr[0]) + padding_size;
dc245c
+
dc245c
 	if (data_size < 0 || data_size > (ssize_t)iscsi->initiator_max_recv_data_segment_length) {
dc245c
 		iscsi_set_error(iscsi, "Invalid data size received from target (%d)", (int)data_size);
dc245c
 		return -1;
dc245c
 	}
dc245c
 	if (data_size != 0) {
dc245c
-		unsigned char *buf = NULL;
dc245c
+		unsigned char padding_buf[3];
dc245c
+		unsigned char *buf = padding_buf;
dc245c
 		struct scsi_iovector * iovector_in;
dc245c
 
dc245c
 		count = data_size - in->data_pos;
dc245c
 
dc245c
 		/* first try to see if we already have a user buffer */
dc245c
 		iovector_in = iscsi_get_scsi_task_iovector_in(iscsi, in);
dc245c
-		if (iovector_in != NULL) {
dc245c
+		if (iovector_in != NULL && count > padding_size) {
dc245c
 			uint32_t offset = scsi_get_uint32(&in->hdr[40]);
dc245c
-			count = iscsi_iovector_readv_writev(iscsi, iovector_in, in->data_pos + offset, count, 0);
dc245c
+			count = iscsi_iovector_readv_writev(iscsi, iovector_in, in->data_pos + offset, count - padding_size, 0);
dc245c
 		} else {
dc245c
-			if (in->data == NULL) {
dc245c
-				in->data = iscsi_malloc(iscsi, data_size);
dc245c
+			if (iovector_in == NULL) {
dc245c
 				if (in->data == NULL) {
dc245c
-					iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu->data(%d)", (int)data_size);
dc245c
-					return -1;
dc245c
+					in->data = iscsi_malloc(iscsi, data_size);
dc245c
+					if (in->data == NULL) {
dc245c
+						iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu->data(%d)", (int)data_size);
dc245c
+						return -1;
dc245c
+					}
dc245c
 				}
dc245c
+				buf = &in->data[in->data_pos];
dc245c
 			}
dc245c
-			buf = &in->data[in->data_pos];
dc245c
 			count = recv(iscsi->fd, buf, count, 0);
dc245c
 		}
dc245c
 		
dc245c
@@ -586,6 +591,8 @@ iscsi_write_to_socket(struct iscsi_context *iscsi)
dc245c
 {
dc245c
 	ssize_t count;
dc245c
 	struct iscsi_pdu *pdu;
dc245c
+	static char padding_buf[3];
dc245c
+	size_t total;
dc245c
 
dc245c
 	if (iscsi->fd == -1) {
dc245c
 		iscsi_set_error(iscsi, "trying to write but not connected");
dc245c
@@ -663,6 +669,27 @@ iscsi_write_to_socket(struct iscsi_context *iscsi)
dc245c
 			pdu->out_written += count;
dc245c
 		}
dc245c
 
dc245c
+		total = pdu->out_len;
dc245c
+		total = (total + 3) & 0xfffffffc;
dc245c
+
dc245c
+		/* Write padding */
dc245c
+		if (pdu->out_written < total) {
dc245c
+			count = send(iscsi->fd, padding_buf, total - pdu->out_written, 0);
dc245c
+			if (count == -1) {
dc245c
+				if (errno == EAGAIN || errno == EWOULDBLOCK) {
dc245c
+					return 0;
dc245c
+				}
dc245c
+				iscsi_set_error(iscsi, "Error when writing to "
dc245c
+						"socket :%d", errno);
dc245c
+				return -1;
dc245c
+			}
dc245c
+			pdu->out_written += count;
dc245c
+		}
dc245c
+		/* if we havent written the full padding yet. */
dc245c
+		if (pdu->out_written != total) {
dc245c
+			return 0;
dc245c
+		}
dc245c
+
dc245c
 		if (pdu->flags & ISCSI_PDU_DELETE_WHEN_SENT) {
dc245c
 			iscsi_free_pdu(iscsi, pdu);
dc245c
 		}