On 6/9/23 04:17, Eric Blake wrote: > In order to more easily add a third reply type with an even larger > header, but where the payload will look the same for both structured > and extended replies, it is nicer if simple and structured replies are > nested inside the same layer of sbuf.reply.hdr. Doing this also lets > us add an alias for accessing the cookie directly without picking > which header type we have on hand. > > Visually, this patch changes the layout (on x86_64) from: > > offset simple structured > +------------------------------------------------------------+ > | union sbuf | > | +---------------------+------------------------------+ | > | | struct simple_reply | struct sr | | > | | +-----------------+ | +--------------------------+ | | > | | | | | | struct structured_reply | | | > | | | | | | +----------------------+ | | | > | 0 | | uint32_t magic | | | | uint32_t magic | | | | > | 4 | | uint32_t error | | | | uint16_t flags | | | | > | 6 | | | | | | uint16_t type | | | | > | 8 | | uint64_t cookie | | | | uint64_t cookie | | | | > | | +-----------------+ | | | | | | | > | 16 | [padding] | | | uint32_t length | | | | > | | | | +----------------------+ | | | > | 20 | | | [padding] | | | > | | | | union payload | | | > | | | | +-----------+----------+ | | | > | 24 | | | | ... | ... | | | | > | | | | +-----------+----------+ | | | > | | | +--------------------------+ | | > | +---------------------+------------------------------+ | > +------------------------------------------------------------+ > > to: > > offset simple structured > +-------------------------------------------------------------+ > | union sbuf | > | +-----------------------------------------------------+ | > | | struct reply | | > | | +-------------------------------------------------+ | | > | | | union hdr | | | > | | | +--------------------+------------------------+ | | | > | | | | struct simple | struct structured | | | | > | | | | +----------------+ | +--------------------+ | | | | > | 0 | | | | uint32_t magic | | | uint32_t magic | | | | | > | 4 | | | | uint32_t error | | | uint16_t flags | | | | | > | 6 | | | | | | | uint16_t type | | | | | > | 8 | | | | uint64_t cookie| | | uint64_t cookie | | | | | > | | | | +----------------+ | | | | | | | > | 16 | | | [padding] | | uint32_t length | | | | | > | | | | | +--------------------+ | | | | > | 20 | | | | [padding] | | | | > | | | +--------------------+------------------------+ | | | > | | | union payload | | | > | | | +--------------------+------------------------+ | | | > | 24 | | | ... | ... | | | | > | | | +--------------------+------------------------+ | | | > | | +-------------------------------------------------+ | | > | +-----------------------------------------------------+ | > +-------------------------------------------------------------+ > > Although we do not use union payload with simple replies, a later > patch does use payload for two out of the three possible headers, and > it does not hurt for sbuf to call out storage for more than we use on > a particular state machine sequence. The commit is largely > mechanical, and there should be no semantic change. > > Signed-off-by: Eric Blake <ebl...@redhat.com> > --- > lib/internal.h | 16 +++++-- > generator/states-reply.c | 35 +++++++------- > generator/states-reply-chunk.c | 84 ++++++++++++++++----------------- > generator/states-reply-simple.c | 4 +- > 4 files changed, 76 insertions(+), 63 deletions(-) > > diff --git a/lib/internal.h b/lib/internal.h > index a8c49e16..f9b10774 100644 > --- a/lib/internal.h > +++ b/lib/internal.h > @@ -237,9 +237,19 @@ struct nbd_handle { > } payload; > } or; > struct nbd_export_name_option_reply export_name_reply; > - struct nbd_simple_reply simple_reply; > struct { > - struct nbd_structured_reply structured_reply; > + union reply_header { > + struct nbd_simple_reply simple; > + struct nbd_structured_reply structured; > + /* The wire formats share magic and cookie at the same offsets; > + * provide aliases for one less level of typing. > + */ > + struct { > + uint32_t magic; > + uint32_t pad_; > + uint64_t cookie; > + }; > + } hdr; > union { > uint64_t align_; /* Start sr.payload on an 8-byte alignment */ > struct nbd_structured_reply_offset_data offset_data; > @@ -250,7 +260,7 @@ struct nbd_handle { > uint64_t offset; /* Only used for NBD_REPLY_TYPE_ERROR_OFFSET */ > } NBD_ATTRIBUTE_PACKED error; > } payload; > - } sr; > + } reply; > uint16_t gflags; > uint32_t cflags; > uint32_t len; > diff --git a/generator/states-reply.c b/generator/states-reply.c > index bd6336a8..af5f6135 100644 > --- a/generator/states-reply.c > +++ b/generator/states-reply.c > @@ -70,14 +70,17 @@ REPLY.START: > * structs (an intentional design decision in the NBD spec when > * structured replies were added). > */ > - STATIC_ASSERT (offsetof (struct nbd_handle, sbuf.simple_reply.cookie) == > - offsetof (struct nbd_handle, > sbuf.sr.structured_reply.cookie), > - cookie_aliasing); > + STATIC_ASSERT (offsetof (union reply_header, simple.cookie) == > + offsetof (union reply_header, cookie), > + _cookie_aliasing_simple); > + STATIC_ASSERT (offsetof (union reply_header, structured.cookie) == > + offsetof (union reply_header, cookie), > + _cookie_aliasing_structured); > assert (h->reply_cmd == NULL); > assert (h->rlen == 0); > > - h->rbuf = &h->sbuf; > - h->rlen = sizeof h->sbuf.simple_reply; > + h->rbuf = &h->sbuf.reply.hdr; > + h->rlen = sizeof h->sbuf.reply.hdr.simple; > > r = h->sock->ops->recv (h, h->sock, h->rbuf, h->rlen); > if (r == -1) { > @@ -122,22 +125,22 @@ REPLY.CHECK_REPLY_MAGIC: > uint32_t magic; > uint64_t cookie; > > - magic = be32toh (h->sbuf.simple_reply.magic); > + magic = be32toh (h->sbuf.reply.hdr.magic); > if (magic == NBD_SIMPLE_REPLY_MAGIC) { > SET_NEXT_STATE (%SIMPLE_REPLY.START); > } > else if (magic == NBD_STRUCTURED_REPLY_MAGIC) { > - /* We've only read the bytes that fill simple_reply. The > - * structured_reply is longer, so prepare to read the remaining > + /* We've only read the bytes that fill hdr.simple. But > + * hdr.structured is longer, so prepare to read the remaining > * bytes. We depend on the memory aliasing in union sbuf to > * overlay the two reply types. > */ > - STATIC_ASSERT (sizeof h->sbuf.simple_reply == > + STATIC_ASSERT (sizeof h->sbuf.reply.hdr.simple == > offsetof (struct nbd_structured_reply, length), > simple_structured_overlap); > - assert (h->rbuf == (char *)&h->sbuf + sizeof h->sbuf.simple_reply); > - h->rlen = sizeof h->sbuf.sr.structured_reply; > - h->rlen -= sizeof h->sbuf.simple_reply; > + assert (h->rbuf == (char *)&h->sbuf + sizeof h->sbuf.reply.hdr.simple); > + h->rlen = sizeof h->sbuf.reply.hdr.structured; > + h->rlen -= sizeof h->sbuf.reply.hdr.simple; > SET_NEXT_STATE (%RECV_STRUCTURED_REMAINING); > } > else { > @@ -145,8 +148,8 @@ REPLY.CHECK_REPLY_MAGIC: > SET_NEXT_STATE (%.DEAD); > set_error (0, "invalid reply magic 0x%" PRIx32, magic); > #if 0 /* uncomment to see desynchronized data */ > - nbd_internal_hexdump (&h->sbuf.simple_reply, > - sizeof (h->sbuf.simple_reply), > + nbd_internal_hexdump (&h->sbuf.reply.hdr.simple, > + sizeof (h->sbuf.reply.hdr.simple), > stderr); > #endif > return 0; > @@ -158,7 +161,7 @@ REPLY.CHECK_REPLY_MAGIC: > * STATIC_ASSERT above in state REPLY.START that confirmed this. > */ > h->chunks_received++; > - cookie = be64toh (h->sbuf.simple_reply.cookie); > + cookie = be64toh (h->sbuf.reply.hdr.cookie); > /* Find the command amongst the commands in flight. If the server sends > * a reply for an unknown cookie, FINISH will diagnose that later. > */ > @@ -189,7 +192,7 @@ REPLY.FINISH_COMMAND: > * handle (our cookie) is stored at the same offset. See the > * STATIC_ASSERT above in state REPLY.START that confirmed this. > */ > - cookie = be64toh (h->sbuf.simple_reply.cookie); > + cookie = be64toh (h->sbuf.reply.hdr.cookie); > /* Find the command amongst the commands in flight. */ > for (cmd = h->cmds_in_flight, prev_cmd = NULL; > cmd != NULL; > diff --git a/generator/states-reply-chunk.c b/generator/states-reply-chunk.c > index 94f3a8ae..c42741fb 100644 > --- a/generator/states-reply-chunk.c > +++ b/generator/states-reply-chunk.c > @@ -50,9 +50,9 @@ REPLY.CHUNK_REPLY.START: > uint16_t flags, type; > uint32_t length; > > - flags = be16toh (h->sbuf.sr.structured_reply.flags); > - type = be16toh (h->sbuf.sr.structured_reply.type); > - length = be32toh (h->sbuf.sr.structured_reply.length); > + flags = be16toh (h->sbuf.reply.hdr.structured.flags); > + type = be16toh (h->sbuf.reply.hdr.structured.type); > + length = be32toh (h->sbuf.reply.hdr.structured.length); > > /* Reject a server that replies with too much information, but don't > * reject a single structured reply to NBD_CMD_READ on the largest > @@ -61,7 +61,7 @@ REPLY.CHUNK_REPLY.START: > * oversized reply is going to take long enough to resync that it is > * not worth keeping the connection alive. > */ > - if (length > MAX_REQUEST_SIZE + sizeof h->sbuf.sr.payload.offset_data) { > + if (length > MAX_REQUEST_SIZE + sizeof h->sbuf.reply.payload.offset_data) { > set_error (0, "invalid server reply length %" PRIu32, length); > SET_NEXT_STATE (%.DEAD); > return 0; > @@ -84,19 +84,19 @@ REPLY.CHUNK_REPLY.START: > * them as an extension, so we use < instead of <=. > */ > if (cmd->type != NBD_CMD_READ || > - length < sizeof h->sbuf.sr.payload.offset_data) > + length < sizeof h->sbuf.reply.payload.offset_data) > goto resync; > - h->rbuf = &h->sbuf.sr.payload.offset_data; > - h->rlen = sizeof h->sbuf.sr.payload.offset_data; > + h->rbuf = &h->sbuf.reply.payload.offset_data; > + h->rlen = sizeof h->sbuf.reply.payload.offset_data; > SET_NEXT_STATE (%RECV_OFFSET_DATA); > break; > > case NBD_REPLY_TYPE_OFFSET_HOLE: > if (cmd->type != NBD_CMD_READ || > - length != sizeof h->sbuf.sr.payload.offset_hole) > + length != sizeof h->sbuf.reply.payload.offset_hole) > goto resync; > - h->rbuf = &h->sbuf.sr.payload.offset_hole; > - h->rlen = sizeof h->sbuf.sr.payload.offset_hole; > + h->rbuf = &h->sbuf.reply.payload.offset_hole; > + h->rlen = sizeof h->sbuf.reply.payload.offset_hole; > SET_NEXT_STATE (%RECV_OFFSET_HOLE); > break; > > @@ -127,10 +127,10 @@ REPLY.CHUNK_REPLY.START: > * compliant, will favor the wire error over EPROTO during more > * length checks in RECV_ERROR_MESSAGE and RECV_ERROR_TAIL. > */ > - if (length < sizeof h->sbuf.sr.payload.error.error.error) > + if (length < sizeof h->sbuf.reply.payload.error.error.error) > goto resync; > - h->rbuf = &h->sbuf.sr.payload.error.error; > - h->rlen = MIN (length, sizeof h->sbuf.sr.payload.error.error); > + h->rbuf = &h->sbuf.reply.payload.error.error; > + h->rlen = MIN (length, sizeof h->sbuf.reply.payload.error.error); > SET_NEXT_STATE (%RECV_ERROR); > } > else > @@ -156,19 +156,19 @@ REPLY.CHUNK_REPLY.RECV_ERROR: > SET_NEXT_STATE (%.READY); > return 0; > case 0: > - length = be32toh (h->sbuf.sr.structured_reply.length); > - assert (length >= sizeof h->sbuf.sr.payload.error.error.error); > + length = be32toh (h->sbuf.reply.hdr.structured.length); > + assert (length >= sizeof h->sbuf.reply.payload.error.error.error); > assert (cmd); > > - if (length < sizeof h->sbuf.sr.payload.error.error) > + if (length < sizeof h->sbuf.reply.payload.error.error) > goto resync; > > - msglen = be16toh (h->sbuf.sr.payload.error.error.len); > - if (msglen > length - sizeof h->sbuf.sr.payload.error.error || > - msglen > sizeof h->sbuf.sr.payload.error.msg) > + msglen = be16toh (h->sbuf.reply.payload.error.error.len); > + if (msglen > length - sizeof h->sbuf.reply.payload.error.error || > + msglen > sizeof h->sbuf.reply.payload.error.msg) > goto resync; > > - h->rbuf = h->sbuf.sr.payload.error.msg; > + h->rbuf = h->sbuf.reply.payload.error.msg; > h->rlen = msglen; > SET_NEXT_STATE (%RECV_ERROR_MESSAGE); > } > @@ -176,11 +176,11 @@ REPLY.CHUNK_REPLY.RECV_ERROR: > > resync: > /* Favor the error packet's errno over RESYNC's EPROTO. */ > - error = be32toh (h->sbuf.sr.payload.error.error.error); > + error = be32toh (h->sbuf.reply.payload.error.error.error); > if (cmd->error == 0) > cmd->error = nbd_internal_errno_of_nbd_error (error); > h->rbuf = NULL; > - h->rlen = length - MIN (length, sizeof h->sbuf.sr.payload.error.error); > + h->rlen = length - MIN (length, sizeof h->sbuf.reply.payload.error.error); > SET_NEXT_STATE (%RESYNC); > return 0; > > @@ -195,15 +195,15 @@ REPLY.CHUNK_REPLY.RECV_ERROR_MESSAGE: > SET_NEXT_STATE (%.READY); > return 0; > case 0: > - length = be32toh (h->sbuf.sr.structured_reply.length); > - msglen = be16toh (h->sbuf.sr.payload.error.error.len); > - type = be16toh (h->sbuf.sr.structured_reply.type); > + length = be32toh (h->sbuf.reply.hdr.structured.length); > + msglen = be16toh (h->sbuf.reply.payload.error.error.len); > + type = be16toh (h->sbuf.reply.hdr.structured.type); > > - length -= sizeof h->sbuf.sr.payload.error.error + msglen; > + length -= sizeof h->sbuf.reply.payload.error.error + msglen; > > if (msglen) > debug (h, "structured error server message: %.*s", (int)msglen, > - h->sbuf.sr.payload.error.msg); > + h->sbuf.reply.payload.error.msg); > > /* Special case two specific errors; silently ignore tail for all others > */ > h->rbuf = NULL; > @@ -215,11 +215,11 @@ REPLY.CHUNK_REPLY.RECV_ERROR_MESSAGE: > "the server may have a bug"); > break; > case NBD_REPLY_TYPE_ERROR_OFFSET: > - if (length != sizeof h->sbuf.sr.payload.error.offset) > + if (length != sizeof h->sbuf.reply.payload.error.offset) > debug (h, "unable to safely extract error offset, " > "the server may have a bug"); > else > - h->rbuf = &h->sbuf.sr.payload.error.offset; > + h->rbuf = &h->sbuf.reply.payload.error.offset; > break; > } > SET_NEXT_STATE (%RECV_ERROR_TAIL); > @@ -238,8 +238,8 @@ REPLY.CHUNK_REPLY.RECV_ERROR_TAIL: > SET_NEXT_STATE (%.READY); > return 0; > case 0: > - error = be32toh (h->sbuf.sr.payload.error.error.error); > - type = be16toh (h->sbuf.sr.structured_reply.type); > + error = be32toh (h->sbuf.reply.payload.error.error.error); > + type = be16toh (h->sbuf.reply.hdr.structured.type); > > assert (cmd); /* guaranteed by CHECK */ > > @@ -254,7 +254,7 @@ REPLY.CHUNK_REPLY.RECV_ERROR_TAIL: > * user callback if present. Ignore the offset if it was bogus. > */ > if (type == NBD_REPLY_TYPE_ERROR_OFFSET && h->rbuf) { > - uint64_t offset = be64toh (h->sbuf.sr.payload.error.offset); > + uint64_t offset = be64toh (h->sbuf.reply.payload.error.offset); > if (structured_reply_in_bounds (offset, 0, cmd) && > cmd->type == NBD_CMD_READ && > CALLBACK_IS_NOT_NULL (cmd->cb.fn.chunk)) { > @@ -295,8 +295,8 @@ REPLY.CHUNK_REPLY.RECV_OFFSET_DATA: > SET_NEXT_STATE (%.READY); > return 0; > case 0: > - length = be32toh (h->sbuf.sr.structured_reply.length); > - offset = be64toh (h->sbuf.sr.payload.offset_data.offset); > + length = be32toh (h->sbuf.reply.hdr.structured.length); > + offset = be64toh (h->sbuf.reply.payload.offset_data.offset); > > assert (cmd); /* guaranteed by CHECK */ > > @@ -334,8 +334,8 @@ REPLY.CHUNK_REPLY.RECV_OFFSET_DATA_DATA: > SET_NEXT_STATE (%.READY); > return 0; > case 0: > - length = be32toh (h->sbuf.sr.structured_reply.length); > - offset = be64toh (h->sbuf.sr.payload.offset_data.offset); > + length = be32toh (h->sbuf.reply.hdr.structured.length); > + offset = be64toh (h->sbuf.reply.payload.offset_data.offset); > > assert (cmd); /* guaranteed by CHECK */ > if (CALLBACK_IS_NOT_NULL (cmd->cb.fn.chunk)) { > @@ -365,8 +365,8 @@ REPLY.CHUNK_REPLY.RECV_OFFSET_HOLE: > SET_NEXT_STATE (%.READY); > return 0; > case 0: > - offset = be64toh (h->sbuf.sr.payload.offset_hole.offset); > - length = be32toh (h->sbuf.sr.payload.offset_hole.length); > + offset = be64toh (h->sbuf.reply.payload.offset_hole.offset); > + length = be32toh (h->sbuf.reply.payload.offset_hole.length); > > assert (cmd); /* guaranteed by CHECK */ > > @@ -416,7 +416,7 @@ REPLY.CHUNK_REPLY.RECV_BS_ENTRIES: > SET_NEXT_STATE (%.READY); > return 0; > case 0: > - length = be32toh (h->sbuf.sr.structured_reply.length); > + length = be32toh (h->sbuf.reply.hdr.structured.length); > > assert (cmd); /* guaranteed by CHECK */ > assert (cmd->type == NBD_CMD_BLOCK_STATUS); > @@ -479,8 +479,8 @@ REPLY.CHUNK_REPLY.RESYNC: > SET_NEXT_STATE (%^FINISH_COMMAND); > return 0; > } > - type = be16toh (h->sbuf.sr.structured_reply.type); > - length = be32toh (h->sbuf.sr.structured_reply.length); > + type = be16toh (h->sbuf.reply.hdr.structured.type); > + length = be32toh (h->sbuf.reply.hdr.structured.length); > debug (h, "unexpected reply type %u or payload length %" PRIu32 > " for cookie %" PRIu64 " and command %" PRIu32 > ", this is probably a server bug", > @@ -494,7 +494,7 @@ REPLY.CHUNK_REPLY.RESYNC: > REPLY.CHUNK_REPLY.FINISH: > uint16_t flags; > > - flags = be16toh (h->sbuf.sr.structured_reply.flags); > + flags = be16toh (h->sbuf.reply.hdr.structured.flags); > if (flags & NBD_REPLY_FLAG_DONE) { > SET_NEXT_STATE (%^FINISH_COMMAND); > } > diff --git a/generator/states-reply-simple.c b/generator/states-reply-simple.c > index 19be5418..898bb84e 100644 > --- a/generator/states-reply-simple.c > +++ b/generator/states-reply-simple.c > @@ -23,7 +23,7 @@ REPLY.SIMPLE_REPLY.START: > struct command *cmd = h->reply_cmd; > uint32_t error; > > - error = be32toh (h->sbuf.simple_reply.error); > + error = be32toh (h->sbuf.reply.hdr.simple.error); > > if (cmd == NULL) { > /* Unexpected reply. If error was set or we have structured > @@ -39,7 +39,7 @@ REPLY.SIMPLE_REPLY.START: > if (error || h->structured_replies) > SET_NEXT_STATE (%^FINISH_COMMAND); > else { > - uint64_t cookie = be64toh (h->sbuf.simple_reply.cookie); > + uint64_t cookie = be64toh (h->sbuf.reply.hdr.simple.cookie); > SET_NEXT_STATE (%.DEAD); > set_error (EPROTO, > "no matching cookie %" PRIu64 " found for server reply, "
Acked-by: Laszlo Ersek <ler...@redhat.com> _______________________________________________ Libguestfs mailing list Libguestfs@redhat.com https://listman.redhat.com/mailman/listinfo/libguestfs