|
|
7711c0 |
From 00edcf411c7d8e58faa15f2cb07829b8f7f74a60 Mon Sep 17 00:00:00 2001
|
|
|
7711c0 |
From: John Snow <jsnow@redhat.com>
|
|
|
7711c0 |
Date: Mon, 6 May 2019 17:56:16 +0200
|
|
|
7711c0 |
Subject: [PATCH 06/53] nbd-client: Work around server BLOCK_STATUS
|
|
|
7711c0 |
misalignment at EOF
|
|
|
7711c0 |
|
|
|
7711c0 |
RH-Author: John Snow <jsnow@redhat.com>
|
|
|
7711c0 |
Message-id: <20190506175629.11079-7-jsnow@redhat.com>
|
|
|
7711c0 |
Patchwork-id: 87194
|
|
|
7711c0 |
O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 06/19] nbd-client: Work around server BLOCK_STATUS misalignment at EOF
|
|
|
7711c0 |
Bugzilla: 1692018
|
|
|
7711c0 |
RH-Acked-by: Max Reitz <mreitz@redhat.com>
|
|
|
7711c0 |
RH-Acked-by: Stefano Garzarella <sgarzare@redhat.com>
|
|
|
7711c0 |
RH-Acked-by: Thomas Huth <thuth@redhat.com>
|
|
|
7711c0 |
|
|
|
7711c0 |
From: Eric Blake <eblake@redhat.com>
|
|
|
7711c0 |
|
|
|
7711c0 |
The NBD spec is clear that a server that advertises a minimum block
|
|
|
7711c0 |
size should reply to NBD_CMD_BLOCK_STATUS with extents aligned
|
|
|
7711c0 |
accordingly. However, we know that the qemu NBD server implementation
|
|
|
7711c0 |
has had a corner-case bug where it is not compliant with the spec,
|
|
|
7711c0 |
present since the introduction of NBD_CMD_BLOCK_STATUS in qemu 2.12
|
|
|
7711c0 |
(and unlikely to be patched in time for 4.0). Namely, when qemu is
|
|
|
7711c0 |
serving a file that is not a multiple of 512 bytes, it rounds the size
|
|
|
7711c0 |
advertised over NBD up to the next sector boundary (someday, I'd like
|
|
|
7711c0 |
to fix that to be byte-accurate, but it's a much bigger audit not
|
|
|
7711c0 |
appropriate for this release); yet if the final sector contains data
|
|
|
7711c0 |
prior to EOF, lseek(SEEK_HOLE) will point to the implicit hole
|
|
|
7711c0 |
mid-sector which qemu then reported over NBD.
|
|
|
7711c0 |
|
|
|
7711c0 |
We are well within our rights to hang up on a server that can't follow
|
|
|
7711c0 |
the spec, but it is more useful to try and keep the connection alive
|
|
|
7711c0 |
in spite of the problem. Do so by tracing a message about the problem,
|
|
|
7711c0 |
and then either truncating the request back to an aligned boundary (if
|
|
|
7711c0 |
it covered more than the final sector) or widening it out to the full
|
|
|
7711c0 |
boundary with a forced status of data (since truncating would result
|
|
|
7711c0 |
in 0 bytes, but we have to make progress, and valid since data is a
|
|
|
7711c0 |
default-safe answer). And in practice, since the problem only happens
|
|
|
7711c0 |
on a sector that starts with data and ends with a hole, we are going
|
|
|
7711c0 |
to want to read that full sector anyway (where qemu as the server
|
|
|
7711c0 |
fills in the tail beyond EOF with appropriate NUL bytes).
|
|
|
7711c0 |
|
|
|
7711c0 |
Easy reproduction:
|
|
|
7711c0 |
$ printf %1000d 1 > file
|
|
|
7711c0 |
$ qemu-nbd -f raw -t file & pid=$!
|
|
|
7711c0 |
$ qemu-img map --output=json -f raw nbd://localhost:10809
|
|
|
7711c0 |
qemu-img: Could not read file metadata: Invalid argument
|
|
|
7711c0 |
$ kill $pid
|
|
|
7711c0 |
|
|
|
7711c0 |
where the patched version instead succeeds with:
|
|
|
7711c0 |
[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true}]
|
|
|
7711c0 |
|
|
|
7711c0 |
Signed-off-by: Eric Blake <eblake@redhat.com>
|
|
|
7711c0 |
Message-Id: <20190326171317.4036-1-eblake@redhat.com>
|
|
|
7711c0 |
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
|
|
7711c0 |
(cherry picked from commit 737d3f524481bb2ef68d3eba1caa636ff143e16a)
|
|
|
7711c0 |
Signed-off-by: John Snow <jsnow@redhat.com>
|
|
|
7711c0 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
7711c0 |
---
|
|
|
7711c0 |
block/nbd-client.c | 30 ++++++++++++++++++++++++++----
|
|
|
7711c0 |
1 file changed, 26 insertions(+), 4 deletions(-)
|
|
|
7711c0 |
|
|
|
7711c0 |
diff --git a/block/nbd-client.c b/block/nbd-client.c
|
|
|
7711c0 |
index eee909f..09e20b2 100644
|
|
|
7711c0 |
--- a/block/nbd-client.c
|
|
|
7711c0 |
+++ b/block/nbd-client.c
|
|
|
7711c0 |
@@ -256,15 +256,37 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client,
|
|
|
7711c0 |
extent->length = payload_advance32(&payload);
|
|
|
7711c0 |
extent->flags = payload_advance32(&payload);
|
|
|
7711c0 |
|
|
|
7711c0 |
- if (extent->length == 0 ||
|
|
|
7711c0 |
- (client->info.min_block && !QEMU_IS_ALIGNED(extent->length,
|
|
|
7711c0 |
- client->info.min_block))) {
|
|
|
7711c0 |
+ if (extent->length == 0) {
|
|
|
7711c0 |
error_setg(errp, "Protocol error: server sent status chunk with "
|
|
|
7711c0 |
- "invalid length");
|
|
|
7711c0 |
+ "zero length");
|
|
|
7711c0 |
return -EINVAL;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
/*
|
|
|
7711c0 |
+ * A server sending unaligned block status is in violation of the
|
|
|
7711c0 |
+ * protocol, but as qemu-nbd 3.1 is such a server (at least for
|
|
|
7711c0 |
+ * POSIX files that are not a multiple of 512 bytes, since qemu
|
|
|
7711c0 |
+ * rounds files up to 512-byte multiples but lseek(SEEK_HOLE)
|
|
|
7711c0 |
+ * still sees an implicit hole beyond the real EOF), it's nicer to
|
|
|
7711c0 |
+ * work around the misbehaving server. If the request included
|
|
|
7711c0 |
+ * more than the final unaligned block, truncate it back to an
|
|
|
7711c0 |
+ * aligned result; if the request was only the final block, round
|
|
|
7711c0 |
+ * up to the full block and change the status to fully-allocated
|
|
|
7711c0 |
+ * (always a safe status, even if it loses information).
|
|
|
7711c0 |
+ */
|
|
|
7711c0 |
+ if (client->info.min_block && !QEMU_IS_ALIGNED(extent->length,
|
|
|
7711c0 |
+ client->info.min_block)) {
|
|
|
7711c0 |
+ trace_nbd_parse_blockstatus_compliance("extent length is unaligned");
|
|
|
7711c0 |
+ if (extent->length > client->info.min_block) {
|
|
|
7711c0 |
+ extent->length = QEMU_ALIGN_DOWN(extent->length,
|
|
|
7711c0 |
+ client->info.min_block);
|
|
|
7711c0 |
+ } else {
|
|
|
7711c0 |
+ extent->length = client->info.min_block;
|
|
|
7711c0 |
+ extent->flags = 0;
|
|
|
7711c0 |
+ }
|
|
|
7711c0 |
+ }
|
|
|
7711c0 |
+
|
|
|
7711c0 |
+ /*
|
|
|
7711c0 |
* We used NBD_CMD_FLAG_REQ_ONE, so the server should not have
|
|
|
7711c0 |
* sent us any more than one extent, nor should it have included
|
|
|
7711c0 |
* status beyond our request in that extent. However, it's easy
|
|
|
7711c0 |
--
|
|
|
7711c0 |
1.8.3.1
|
|
|
7711c0 |
|