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 <ebl...@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