diff options
Diffstat (limited to 'nbd/client.c')
-rw-r--r-- | nbd/client.c | 104 |
1 files changed, 82 insertions, 22 deletions
diff --git a/nbd/client.c b/nbd/client.c index 9acf745b79..4f0745f601 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -908,6 +908,57 @@ int nbd_send_request(QIOChannel *ioc, NBDRequest *request) return nbd_write(ioc, buf, sizeof(buf), NULL); } +/* nbd_receive_simple_reply + * Read simple reply except magic field (which should be already read). + * Payload is not read (payload is possible for CMD_READ, but here we even + * don't know whether it take place or not). + */ +static int nbd_receive_simple_reply(QIOChannel *ioc, NBDSimpleReply *reply, + Error **errp) +{ + int ret; + + assert(reply->magic == NBD_SIMPLE_REPLY_MAGIC); + + ret = nbd_read(ioc, (uint8_t *)reply + sizeof(reply->magic), + sizeof(*reply) - sizeof(reply->magic), errp); + if (ret < 0) { + return ret; + } + + be32_to_cpus(&reply->error); + be64_to_cpus(&reply->handle); + + return 0; +} + +/* nbd_receive_structured_reply_chunk + * Read structured reply chunk except magic field (which should be already + * read). + * Payload is not read. + */ +static int nbd_receive_structured_reply_chunk(QIOChannel *ioc, + NBDStructuredReplyChunk *chunk, + Error **errp) +{ + int ret; + + assert(chunk->magic == NBD_STRUCTURED_REPLY_MAGIC); + + ret = nbd_read(ioc, (uint8_t *)chunk + sizeof(chunk->magic), + sizeof(*chunk) - sizeof(chunk->magic), errp); + if (ret < 0) { + return ret; + } + + be16_to_cpus(&chunk->flags); + be16_to_cpus(&chunk->type); + be64_to_cpus(&chunk->handle); + be32_to_cpus(&chunk->length); + + return 0; +} + /* nbd_receive_reply * Returns 1 on success * 0 on eof, when no data was read (errp is not set) @@ -915,38 +966,47 @@ int nbd_send_request(QIOChannel *ioc, NBDRequest *request) */ int nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp) { - uint8_t buf[NBD_REPLY_SIZE]; - uint32_t magic; int ret; - ret = nbd_read_eof(ioc, buf, sizeof(buf), errp); + ret = nbd_read_eof(ioc, &reply->magic, sizeof(reply->magic), errp); if (ret <= 0) { return ret; } - /* Reply - [ 0 .. 3] magic (NBD_SIMPLE_REPLY_MAGIC) - [ 4 .. 7] error (0 == no error) - [ 7 .. 15] handle - */ + be32_to_cpus(&reply->magic); - magic = ldl_be_p(buf); - reply->error = ldl_be_p(buf + 4); - reply->handle = ldq_be_p(buf + 8); - - trace_nbd_receive_reply(magic, reply->error, nbd_err_lookup(reply->error), - reply->handle); - reply->error = nbd_errno_to_system_errno(reply->error); + switch (reply->magic) { + case NBD_SIMPLE_REPLY_MAGIC: + ret = nbd_receive_simple_reply(ioc, &reply->simple, errp); + if (ret < 0) { + break; + } - if (reply->error == ESHUTDOWN) { - /* This works even on mingw which lacks a native ESHUTDOWN */ - error_setg(errp, "server shutting down"); + trace_nbd_receive_simple_reply(reply->simple.error, + nbd_err_lookup(reply->simple.error), + reply->handle); + if (reply->simple.error == NBD_ESHUTDOWN) { + /* This works even on mingw which lacks a native ESHUTDOWN */ + error_setg(errp, "server shutting down"); + return -EINVAL; + } + break; + case NBD_STRUCTURED_REPLY_MAGIC: + ret = nbd_receive_structured_reply_chunk(ioc, &reply->structured, errp); + if (ret < 0) { + break; + } + trace_nbd_receive_structured_reply_chunk(reply->structured.flags, + reply->structured.type, + reply->structured.handle, + reply->structured.length); + break; + default: + error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", reply->magic); return -EINVAL; } - - if (magic != NBD_SIMPLE_REPLY_MAGIC) { - error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", magic); - return -EINVAL; + if (ret < 0) { + return ret; } return 1; |