diff options
author | Eric Blake <eblake@redhat.com> | 2017-11-08 15:57:03 -0600 |
---|---|---|
committer | Eric Blake <eblake@redhat.com> | 2017-11-09 10:25:11 -0600 |
commit | ef8c887ee01a4e4c8c5c28c86ea5b45162c51bcd (patch) | |
tree | e6eaa1e3a1825c56e6da57211be6d4b61d59a8e3 /nbd/server.c | |
parent | b4176cb314995ad225d6c2b531568801feb04f3f (diff) | |
download | qemu-ef8c887ee01a4e4c8c5c28c86ea5b45162c51bcd.zip qemu-ef8c887ee01a4e4c8c5c28c86ea5b45162c51bcd.tar.gz qemu-ef8c887ee01a4e4c8c5c28c86ea5b45162c51bcd.tar.bz2 |
nbd/server: Fix structured read of length 0
The NBD spec was recently clarified to state that a read of length 0
should not be attempted by a compliant client; but that a server must
still handle it correctly in an unspecified manner (that is, either
a successful no-op or an error reply, but not a crash) [1]. However,
it also implies that NBD_REPLY_TYPE_OFFSET_DATA must have a non-zero
payload length, but our existing code was replying with a chunk
that a picky client could reject as invalid because it was missing
a payload (our own client implementation was recently patched to be
that picky, after first fixing it to not send 0-length requests).
We are already doing successful no-ops for 0-length writes and for
non-structured reads; so for consistency, we want structured reply
reads to also be a no-op. The easiest way to do this is to return
a NBD_REPLY_TYPE_NONE chunk; this is best done via a new helper
function (especially since future patches for other structured
replies may benefit from using the same helper).
[1] https://github.com/NetworkBlockDevice/nbd/commit/ee926037
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20171108215703.9295-8-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Diffstat (limited to 'nbd/server.c')
-rw-r--r-- | nbd/server.c | 21 |
1 files changed, 20 insertions, 1 deletions
diff --git a/nbd/server.c b/nbd/server.c index 6ebb7d9..df771fd 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1273,6 +1273,21 @@ static inline void set_be_chunk(NBDStructuredReplyChunk *chunk, uint16_t flags, stl_be_p(&chunk->length, length); } +static int coroutine_fn nbd_co_send_structured_done(NBDClient *client, + uint64_t handle, + Error **errp) +{ + NBDStructuredReplyChunk chunk; + struct iovec iov[] = { + {.iov_base = &chunk, .iov_len = sizeof(chunk)}, + }; + + trace_nbd_co_send_structured_done(handle); + set_be_chunk(&chunk, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_NONE, handle, 0); + + return nbd_co_send_iov(client, iov, 1, errp); +} + static int coroutine_fn nbd_co_send_structured_read(NBDClient *client, uint64_t handle, uint64_t offset, @@ -1286,6 +1301,7 @@ static int coroutine_fn nbd_co_send_structured_read(NBDClient *client, {.iov_base = data, .iov_len = size} }; + assert(size); trace_nbd_co_send_structured_read(handle, offset, data, size); set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_OFFSET_DATA, handle, sizeof(chunk) - sizeof(chunk.h) + size); @@ -1544,10 +1560,13 @@ reply: if (ret < 0) { ret = nbd_co_send_structured_error(req->client, request.handle, -ret, msg, &local_err); - } else { + } else if (reply_data_len) { ret = nbd_co_send_structured_read(req->client, request.handle, request.from, req->data, reply_data_len, &local_err); + } else { + ret = nbd_co_send_structured_done(req->client, request.handle, + &local_err); } } else { ret = nbd_co_send_simple_reply(req->client, request.handle, |