26.03.2019 20:13, Eric Blake wrote: > The NBD spec is clear that a server that advertises a minimum block > size should reply to NBD_CMD_BLOCK_STATUS with extents aligned > accordingly. However, we know that the qemu NBD server implementation > has had a corner-case bug where it is not compliant with the spec, > present since the introduction of NBD_CMD_BLOCK_STATUS in qemu 2.12 > (and unlikely to be patched in time for 4.0). Namely, when qemu is > serving a file that is not a multiple of 512 bytes, it rounds the size > advertised over NBD up to the next sector boundary (someday, I'd like > to fix that to be byte-accurate, but it's a much bigger audit not > appropriate for this release); yet if the final sector contains data > prior to EOF, lseek(SEEK_HOLE) will point to the implicit hole > mid-sector which qemu then reported over NBD. > > We are well within our rights to hang up on a server that can't follow > the spec, but it is more useful to try and keep the connection alive > in spite of the problem. Do so by tracing a message about the problem, > and then either truncating the request back to an aligned boundary (if > it covered more than the final sector) or widening it out to the full > boundary with a forced status of data (since truncating would result > in 0 bytes, but we have to make progress, and valid since data is a > default-safe answer). And in practice, since the problem only happens > on a sector that starts with data and ends with a hole, we are going > to want to read that full sector anyway (where qemu as the server > fills in the tail beyond EOF with appropriate NUL bytes). > > Easy reproduction: > $ printf %1000d 1 > file > $ qemu-nbd -f raw -t file & pid=$! > $ qemu-img map --output=json -f raw nbd://localhost:10809 > qemu-img: Could not read file metadata: Invalid argument > $ kill $pid
would be good to add it to iotests > > where the patched version instead succeeds with: > [{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true}] > > Signed-off-by: Eric Blake <ebl...@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsement...@virtuozzo.com> > --- > block/nbd-client.c | 28 +++++++++++++++++++++++++--- > 1 file changed, 25 insertions(+), 3 deletions(-) > > diff --git a/block/nbd-client.c b/block/nbd-client.c > index a3b70d14004..241cc555246 100644 > --- a/block/nbd-client.c > +++ b/block/nbd-client.c > @@ -269,14 +269,36 @@ static int > nbd_parse_blockstatus_payload(NBDClientSession *client, > extent->length = payload_advance32(&payload); > extent->flags = payload_advance32(&payload); > > - if (extent->length == 0 || > - (client->info.min_block && !QEMU_IS_ALIGNED(extent->length, > - > client->info.min_block))) { > + if (extent->length == 0) { > error_setg(errp, "Protocol error: server sent status chunk with " > "invalid length"); may be improved to s/invalid/zero/ > return -EINVAL; > } > > + /* > + * A server sending unaligned block status is in violation of the > + * protocol, but as qemu-nbd 3.1 is such a server (at least for > + * POSIX files that are not a multiple of 512 bytes, since qemu > + * rounds files up to 512-byte multiples but lseek(SEEK_HOLE) > + * still sees an implicit hole beyond the real EOF), it's nicer to > + * work around the misbehaving server. If the request included > + * more than the final unaligned block, truncate it back to an > + * aligned result; if the request was only the final block, round > + * up to the full block and change the status to fully-allocated > + * (always a safe status, even if it loses information). > + */ > + if (client->info.min_block && !QEMU_IS_ALIGNED(extent->length, > + client->info.min_block)) { > + trace_nbd_parse_blockstatus_compliance("extent length is unaligned"); > + if (extent->length > client->info.min_block) { > + extent->length = QEMU_ALIGN_DOWN(extent->length, > + client->info.min_block); > + } else { > + extent->length = client->info.min_block; > + extent->flags = 0; > + } > + } > + > /* > * We used NBD_CMD_FLAG_REQ_ONE, so the server should not have > * sent us any more than one extent, nor should it have included > -- Best regards, Vladimir