As part of adding extended headers, the NBD spec debated about adding
support for reading 64-bit holes. It was documented in a separate
upstream commit XXX[*] to make it easier to decide whether 64-bit
holes should be required of all clients supporting extended headers,
or whether it is an unneeded feature; hence, the qemu work to support
it is also pulled out into a separate commit.
Note that we can also tolerate a non-compliant server sending the new
chunk even when it should not.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
[*] Fix commit id if we actually go with idea
---
include/block/nbd.h | 8 ++++++++
block/nbd.c | 26 ++++++++++++++++++++------
nbd/common.c | 4 +++-
3 files changed, 31 insertions(+), 7 deletions(-)
diff --git a/include/block/nbd.h b/include/block/nbd.h
index 2a65c606c9..18b6bad038 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -133,6 +133,13 @@ typedef struct NBDStructuredReadHole {
uint32_t length;
} QEMU_PACKED NBDStructuredReadHole;
+/* Complete chunk for NBD_REPLY_TYPE_OFFSET_HOLE_EXT */
+typedef struct NBDStructuredReadHoleExt {
+ /* header's length == 16 */
+ uint64_t offset;
+ uint64_t length;
+} QEMU_PACKED NBDStructuredReadHoleExt;
+
/* Header of all NBD_REPLY_TYPE_ERROR* errors */
typedef struct NBDStructuredError {
/* header's length >= 6 */
@@ -309,6 +316,7 @@ enum {
#define NBD_REPLY_TYPE_NONE 0
#define NBD_REPLY_TYPE_OFFSET_DATA 1
#define NBD_REPLY_TYPE_OFFSET_HOLE 2
+#define NBD_REPLY_TYPE_OFFSET_HOLE_EXT 3
#define NBD_REPLY_TYPE_BLOCK_STATUS 5
#define NBD_REPLY_TYPE_BLOCK_STATUS_EXT 6
#define NBD_REPLY_TYPE_ERROR NBD_REPLY_ERR(1)
diff --git a/block/nbd.c b/block/nbd.c
index 44ab5437ea..968d5d8a37 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -570,20 +570,26 @@ static inline uint64_t payload_advance64(uint8_t **payload)
static int nbd_parse_offset_hole_payload(BDRVNBDState *s,
NBDStructuredReplyChunk *chunk,
- uint8_t *payload, uint64_t orig_offset,
+ uint8_t *payload, bool wide,
+ uint64_t orig_offset,
QEMUIOVector *qiov, Error **errp)
{
uint64_t offset;
- uint32_t hole_size;
+ uint64_t hole_size;
+ size_t len = wide ? sizeof(hole_size) : sizeof(uint32_t);
- if (chunk->length != sizeof(offset) + sizeof(hole_size)) {
+ if (chunk->length != sizeof(offset) + len) {
error_setg(errp, "Protocol error: invalid payload for "
"NBD_REPLY_TYPE_OFFSET_HOLE");
return -EINVAL;
}
offset = payload_advance64(&payload);
- hole_size = payload_advance32(&payload);
+ if (wide) {
+ hole_size = payload_advance64(&payload);
+ } else {
+ hole_size = payload_advance32(&payload);
+ }
if (!hole_size || offset < orig_offset || hole_size > qiov->size ||
offset > orig_offset + qiov->size - hole_size) {
@@ -596,6 +602,7 @@ static int nbd_parse_offset_hole_payload(BDRVNBDState *s,
trace_nbd_structured_read_compliance("hole");
}
+ assert(hole_size <= SIZE_MAX);
qemu_iovec_memset(qiov, offset - orig_offset, 0, hole_size);
return 0;
@@ -1094,9 +1101,16 @@ static int coroutine_fn nbd_co_receive_cmdread_reply(BDRVNBDState
*s, uint64_t h
* in qiov
*/
break;
+ case NBD_REPLY_TYPE_OFFSET_HOLE_EXT:
+ if (!s->info.extended_headers) {
+ trace_nbd_extended_headers_compliance("hole_ext");
+ }
+ /* fallthrough */
case NBD_REPLY_TYPE_OFFSET_HOLE:
- ret = nbd_parse_offset_hole_payload(s, &reply.structured, payload,
- offset, qiov, &local_err);
+ ret = nbd_parse_offset_hole_payload(
+ s, &reply.structured, payload,
+ chunk->type == NBD_REPLY_TYPE_OFFSET_HOLE_EXT,
+ offset, qiov, &local_err);
if (ret < 0) {
nbd_channel_error(s, ret);
nbd_iter_channel_error(&iter, ret, &local_err);
diff --git a/nbd/common.c b/nbd/common.c
index 137466defd..54f7d6a4fd 100644
--- a/nbd/common.c
+++ b/nbd/common.c
@@ -174,7 +174,9 @@ const char *nbd_reply_type_lookup(uint16_t type)
case NBD_REPLY_TYPE_OFFSET_DATA:
return "data";
case NBD_REPLY_TYPE_OFFSET_HOLE:
- return "hole";
+ return "hole (32-bit)";
+ case NBD_REPLY_TYPE_OFFSET_HOLE_EXT:
+ return "hole (64-bit)";
case NBD_REPLY_TYPE_BLOCK_STATUS:
return "block status (32-bit)";
case NBD_REPLY_TYPE_BLOCK_STATUS_EXT:
--
2.38.1