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