/*
- * Copyright (C) 2016-2017 Red Hat, Inc.
+ * Copyright (C) 2016-2018 Red Hat, Inc.
*
* Network Block Device Client Side
#include "trace.h"
#include "nbd-internal.h"
-static int nbd_errno_to_system_errno(int err)
-{
- int ret;
- switch (err) {
- case NBD_SUCCESS:
- ret = 0;
- break;
- case NBD_EPERM:
- ret = EPERM;
- break;
- case NBD_EIO:
- ret = EIO;
- break;
- case NBD_ENOMEM:
- ret = ENOMEM;
- break;
- case NBD_ENOSPC:
- ret = ENOSPC;
- break;
- case NBD_ESHUTDOWN:
- ret = ESHUTDOWN;
- break;
- default:
- trace_nbd_unknown_error(err);
- /* fallthrough */
- case NBD_EINVAL:
- ret = EINVAL;
- break;
- }
- return ret;
-}
-
/* Definitions for opaque data types */
static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
uint32_t len, const char *data,
Error **errp)
{
- nbd_option req;
+ NBDOption req;
QEMU_BUILD_BUG_ON(sizeof(req) != 16);
if (len == -1) {
stl_be_p(&req.length, len);
if (nbd_write(ioc, &req, sizeof(req), errp) < 0) {
- error_prepend(errp, "Failed to send option request header");
+ error_prepend(errp, "Failed to send option request header: ");
return -1;
}
if (len && nbd_write(ioc, (char *) data, len, errp) < 0) {
- error_prepend(errp, "Failed to send option request data");
+ error_prepend(errp, "Failed to send option request data: ");
return -1;
}
* payload. Return 0 if successful, -1 with errp set if it is
* impossible to continue. */
static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt,
- nbd_opt_reply *reply, Error **errp)
+ NBDOptionReply *reply, Error **errp)
{
QEMU_BUILD_BUG_ON(sizeof(*reply) != 20);
if (nbd_read(ioc, reply, sizeof(*reply), errp) < 0) {
- error_prepend(errp, "failed to read option reply");
+ error_prepend(errp, "failed to read option reply: ");
nbd_send_opt_abort(ioc);
return -1;
}
- be64_to_cpus(&reply->magic);
- be32_to_cpus(&reply->option);
- be32_to_cpus(&reply->type);
- be32_to_cpus(&reply->length);
+ reply->magic = be64_to_cpu(reply->magic);
+ reply->option = be32_to_cpu(reply->option);
+ reply->type = be32_to_cpu(reply->type);
+ reply->length = be32_to_cpu(reply->length);
trace_nbd_receive_option_reply(reply->option, nbd_opt_lookup(reply->option),
reply->type, nbd_rep_lookup(reply->type),
return -1;
}
if (reply->option != opt) {
- error_setg(errp, "Unexpected option type %x expected %x",
- reply->option, opt);
+ error_setg(errp, "Unexpected option type %u (%s), expected %u (%s)",
+ reply->option, nbd_opt_lookup(reply->option),
+ opt, nbd_opt_lookup(opt));
nbd_send_opt_abort(ioc);
return -1;
}
* can fall back to other approaches), or -1 with errp set for other
* errors.
*/
-static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply,
+static int nbd_handle_reply_err(QIOChannel *ioc, NBDOptionReply *reply,
Error **errp)
{
char *msg = NULL;
if (reply->length) {
if (reply->length > NBD_MAX_BUFFER_SIZE) {
- error_setg(errp, "server error 0x%" PRIx32
+ error_setg(errp, "server error %" PRIu32
" (%s) message is too long",
reply->type, nbd_rep_lookup(reply->type));
goto cleanup;
}
msg = g_malloc(reply->length + 1);
if (nbd_read(ioc, msg, reply->length, errp) < 0) {
- error_prepend(errp, "failed to read option error 0x%" PRIx32
- " (%s) message",
+ error_prepend(errp, "failed to read option error %" PRIu32
+ " (%s) message: ",
reply->type, nbd_rep_lookup(reply->type));
goto cleanup;
}
msg[reply->length] = '\0';
+ trace_nbd_server_error_msg(reply->type,
+ nbd_reply_type_lookup(reply->type), msg);
}
switch (reply->type) {
goto cleanup;
case NBD_REP_ERR_POLICY:
- error_setg(errp, "Denied by server for option %" PRIx32 " (%s)",
+ error_setg(errp, "Denied by server for option %" PRIu32 " (%s)",
reply->option, nbd_opt_lookup(reply->option));
break;
case NBD_REP_ERR_INVALID:
- error_setg(errp, "Invalid data length for option %" PRIx32 " (%s)",
+ error_setg(errp, "Invalid parameters for option %" PRIu32 " (%s)",
reply->option, nbd_opt_lookup(reply->option));
break;
case NBD_REP_ERR_PLATFORM:
- error_setg(errp, "Server lacks support for option %" PRIx32 " (%s)",
+ error_setg(errp, "Server lacks support for option %" PRIu32 " (%s)",
reply->option, nbd_opt_lookup(reply->option));
break;
case NBD_REP_ERR_TLS_REQD:
- error_setg(errp, "TLS negotiation required before option %" PRIx32
+ error_setg(errp, "TLS negotiation required before option %" PRIu32
" (%s)", reply->option, nbd_opt_lookup(reply->option));
break;
break;
case NBD_REP_ERR_SHUTDOWN:
- error_setg(errp, "Server shutting down before option %" PRIx32 " (%s)",
+ error_setg(errp, "Server shutting down before option %" PRIu32 " (%s)",
reply->option, nbd_opt_lookup(reply->option));
break;
case NBD_REP_ERR_BLOCK_SIZE_REQD:
- error_setg(errp, "Server requires INFO_BLOCK_SIZE for option %" PRIx32
+ error_setg(errp, "Server requires INFO_BLOCK_SIZE for option %" PRIu32
" (%s)", reply->option, nbd_opt_lookup(reply->option));
break;
default:
- error_setg(errp, "Unknown error code when asking for option %" PRIx32
+ error_setg(errp, "Unknown error code when asking for option %" PRIu32
" (%s)", reply->option, nbd_opt_lookup(reply->option));
break;
}
static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
Error **errp)
{
- nbd_opt_reply reply;
+ NBDOptionReply reply;
uint32_t len;
uint32_t namelen;
char name[NBD_MAX_NAME_SIZE + 1];
}
return 0;
} else if (reply.type != NBD_REP_SERVER) {
- error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x",
- reply.type, NBD_REP_SERVER);
+ error_setg(errp, "Unexpected reply type %u (%s), expected %u (%s)",
+ reply.type, nbd_rep_lookup(reply.type),
+ NBD_REP_SERVER, nbd_rep_lookup(NBD_REP_SERVER));
nbd_send_opt_abort(ioc);
return -1;
}
return -1;
}
if (nbd_read(ioc, &namelen, sizeof(namelen), errp) < 0) {
- error_prepend(errp, "failed to read option name length");
+ error_prepend(errp, "failed to read option name length: ");
nbd_send_opt_abort(ioc);
return -1;
}
}
if (namelen != strlen(want)) {
if (nbd_drop(ioc, len, errp) < 0) {
- error_prepend(errp, "failed to skip export name with wrong length");
+ error_prepend(errp,
+ "failed to skip export name with wrong length: ");
nbd_send_opt_abort(ioc);
return -1;
}
assert(namelen < sizeof(name));
if (nbd_read(ioc, name, namelen, errp) < 0) {
- error_prepend(errp, "failed to read export name");
+ error_prepend(errp, "failed to read export name: ");
nbd_send_opt_abort(ioc);
return -1;
}
name[namelen] = '\0';
len -= namelen;
if (nbd_drop(ioc, len, errp) < 0) {
- error_prepend(errp, "failed to read export description");
+ error_prepend(errp, "failed to read export description: ");
nbd_send_opt_abort(ioc);
return -1;
}
static int nbd_opt_go(QIOChannel *ioc, const char *wantname,
NBDExportInfo *info, Error **errp)
{
- nbd_opt_reply reply;
+ NBDOptionReply reply;
uint32_t len = strlen(wantname);
uint16_t type;
int error;
return 1;
}
if (reply.type != NBD_REP_INFO) {
- error_setg(errp, "unexpected reply type %" PRIx32
- " (%s), expected %x",
- reply.type, nbd_rep_lookup(reply.type), NBD_REP_INFO);
+ error_setg(errp, "unexpected reply type %u (%s), expected %u (%s)",
+ reply.type, nbd_rep_lookup(reply.type),
+ NBD_REP_INFO, nbd_rep_lookup(NBD_REP_INFO));
nbd_send_opt_abort(ioc);
return -1;
}
return -1;
}
if (nbd_read(ioc, &type, sizeof(type), errp) < 0) {
- error_prepend(errp, "failed to read info type");
+ error_prepend(errp, "failed to read info type: ");
nbd_send_opt_abort(ioc);
return -1;
}
len -= sizeof(type);
- be16_to_cpus(&type);
+ type = be16_to_cpu(type);
switch (type) {
case NBD_INFO_EXPORT:
if (len != sizeof(info->size) + sizeof(info->flags)) {
return -1;
}
if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) {
- error_prepend(errp, "failed to read info size");
+ error_prepend(errp, "failed to read info size: ");
nbd_send_opt_abort(ioc);
return -1;
}
- be64_to_cpus(&info->size);
+ info->size = be64_to_cpu(info->size);
if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) {
- error_prepend(errp, "failed to read info flags");
+ error_prepend(errp, "failed to read info flags: ");
nbd_send_opt_abort(ioc);
return -1;
}
- be16_to_cpus(&info->flags);
+ info->flags = be16_to_cpu(info->flags);
trace_nbd_receive_negotiate_size_flags(info->size, info->flags);
break;
}
if (nbd_read(ioc, &info->min_block, sizeof(info->min_block),
errp) < 0) {
- error_prepend(errp, "failed to read info minimum block size");
+ error_prepend(errp, "failed to read info minimum block size: ");
nbd_send_opt_abort(ioc);
return -1;
}
- be32_to_cpus(&info->min_block);
+ info->min_block = be32_to_cpu(info->min_block);
if (!is_power_of_2(info->min_block)) {
- error_setg(errp, "server minimum block size %" PRId32
- "is not a power of two", info->min_block);
+ error_setg(errp, "server minimum block size %" PRIu32
+ " is not a power of two", info->min_block);
nbd_send_opt_abort(ioc);
return -1;
}
if (nbd_read(ioc, &info->opt_block, sizeof(info->opt_block),
errp) < 0) {
- error_prepend(errp, "failed to read info preferred block size");
+ error_prepend(errp,
+ "failed to read info preferred block size: ");
nbd_send_opt_abort(ioc);
return -1;
}
- be32_to_cpus(&info->opt_block);
+ info->opt_block = be32_to_cpu(info->opt_block);
if (!is_power_of_2(info->opt_block) ||
info->opt_block < info->min_block) {
- error_setg(errp, "server preferred block size %" PRId32
- "is not valid", info->opt_block);
+ error_setg(errp, "server preferred block size %" PRIu32
+ " is not valid", info->opt_block);
nbd_send_opt_abort(ioc);
return -1;
}
if (nbd_read(ioc, &info->max_block, sizeof(info->max_block),
errp) < 0) {
- error_prepend(errp, "failed to read info maximum block size");
+ error_prepend(errp, "failed to read info maximum block size: ");
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+ info->max_block = be32_to_cpu(info->max_block);
+ if (info->max_block < info->min_block) {
+ error_setg(errp, "server maximum block size %" PRIu32
+ " is not valid", info->max_block);
nbd_send_opt_abort(ioc);
return -1;
}
- be32_to_cpus(&info->max_block);
trace_nbd_opt_go_info_block_size(info->min_block, info->opt_block,
info->max_block);
break;
default:
trace_nbd_opt_go_info_unknown(type, nbd_info_lookup(type));
if (nbd_drop(ioc, len, errp) < 0) {
- error_prepend(errp, "Failed to read info payload");
+ error_prepend(errp, "Failed to read info payload: ");
nbd_send_opt_abort(ioc);
return -1;
}
}
}
-static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
- QCryptoTLSCreds *tlscreds,
- const char *hostname, Error **errp)
+/* nbd_request_simple_option: Send an option request, and parse the reply
+ * return 1 for successful negotiation,
+ * 0 if operation is unsupported,
+ * -1 with errp set for any other error
+ */
+static int nbd_request_simple_option(QIOChannel *ioc, int opt, Error **errp)
{
- nbd_opt_reply reply;
- QIOChannelTLS *tioc;
- struct NBDTLSHandshakeData data = { 0 };
+ NBDOptionReply reply;
+ int error;
- trace_nbd_receive_starttls_request();
- if (nbd_send_option_request(ioc, NBD_OPT_STARTTLS, 0, NULL, errp) < 0) {
- return NULL;
+ if (nbd_send_option_request(ioc, opt, 0, NULL, errp) < 0) {
+ return -1;
}
- trace_nbd_receive_starttls_reply();
- if (nbd_receive_option_reply(ioc, NBD_OPT_STARTTLS, &reply, errp) < 0) {
- return NULL;
+ if (nbd_receive_option_reply(ioc, opt, &reply, errp) < 0) {
+ return -1;
+ }
+ error = nbd_handle_reply_err(ioc, &reply, errp);
+ if (error <= 0) {
+ return error;
}
if (reply.type != NBD_REP_ACK) {
- error_setg(errp, "Server rejected request to start TLS %" PRIx32,
- reply.type);
+ error_setg(errp, "Server answered option %d (%s) with unexpected "
+ "reply %" PRIu32 " (%s)", opt, nbd_opt_lookup(opt),
+ reply.type, nbd_rep_lookup(reply.type));
nbd_send_opt_abort(ioc);
- return NULL;
+ return -1;
}
if (reply.length != 0) {
- error_setg(errp, "Start TLS response was not zero %" PRIu32,
+ error_setg(errp, "Option %d ('%s') response length is %" PRIu32
+ " (it should be zero)", opt, nbd_opt_lookup(opt),
reply.length);
nbd_send_opt_abort(ioc);
+ return -1;
+ }
+
+ return 1;
+}
+
+static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
+ QCryptoTLSCreds *tlscreds,
+ const char *hostname, Error **errp)
+{
+ int ret;
+ QIOChannelTLS *tioc;
+ struct NBDTLSHandshakeData data = { 0 };
+
+ ret = nbd_request_simple_option(ioc, NBD_OPT_STARTTLS, errp);
+ if (ret <= 0) {
+ if (ret == 0) {
+ error_setg(errp, "Server don't support STARTTLS option");
+ nbd_send_opt_abort(ioc);
+ }
return NULL;
}
qio_channel_tls_handshake(tioc,
nbd_tls_handshake,
&data,
+ NULL,
NULL);
if (!data.complete) {
return QIO_CHANNEL(tioc);
}
+/* nbd_negotiate_simple_meta_context:
+ * Set one meta context. Simple means that reply must contain zero (not
+ * negotiated) or one (negotiated) contexts. More contexts would be considered
+ * as a protocol error. It's also implied that meta-data query equals queried
+ * context name, so, if server replies with something different than @context,
+ * it is considered an error too.
+ * return 1 for successful negotiation, context_id is set
+ * 0 if operation is unsupported,
+ * -1 with errp set for any other error
+ */
+static int nbd_negotiate_simple_meta_context(QIOChannel *ioc,
+ const char *export,
+ const char *context,
+ uint32_t *context_id,
+ Error **errp)
+{
+ int ret;
+ NBDOptionReply reply;
+ uint32_t received_id = 0;
+ bool received = false;
+ uint32_t export_len = strlen(export);
+ uint32_t context_len = strlen(context);
+ uint32_t data_len = sizeof(export_len) + export_len +
+ sizeof(uint32_t) + /* number of queries */
+ sizeof(context_len) + context_len;
+ char *data = g_malloc(data_len);
+ char *p = data;
+
+ trace_nbd_opt_meta_request(context, export);
+ stl_be_p(p, export_len);
+ memcpy(p += sizeof(export_len), export, export_len);
+ stl_be_p(p += export_len, 1);
+ stl_be_p(p += sizeof(uint32_t), context_len);
+ memcpy(p += sizeof(context_len), context, context_len);
+
+ ret = nbd_send_option_request(ioc, NBD_OPT_SET_META_CONTEXT, data_len, data,
+ errp);
+ g_free(data);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply,
+ errp) < 0)
+ {
+ return -1;
+ }
+
+ ret = nbd_handle_reply_err(ioc, &reply, errp);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ if (reply.type == NBD_REP_META_CONTEXT) {
+ char *name;
+
+ if (reply.length != sizeof(received_id) + context_len) {
+ error_setg(errp, "Failed to negotiate meta context '%s', server "
+ "answered with unexpected length %" PRIu32, context,
+ reply.length);
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+
+ if (nbd_read(ioc, &received_id, sizeof(received_id), errp) < 0) {
+ return -1;
+ }
+ received_id = be32_to_cpu(received_id);
+
+ reply.length -= sizeof(received_id);
+ name = g_malloc(reply.length + 1);
+ if (nbd_read(ioc, name, reply.length, errp) < 0) {
+ g_free(name);
+ return -1;
+ }
+ name[reply.length] = '\0';
+ if (strcmp(context, name)) {
+ error_setg(errp, "Failed to negotiate meta context '%s', server "
+ "answered with different context '%s'", context,
+ name);
+ g_free(name);
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+ g_free(name);
+
+ trace_nbd_opt_meta_reply(context, received_id);
+ received = true;
+
+ /* receive NBD_REP_ACK */
+ if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply,
+ errp) < 0)
+ {
+ return -1;
+ }
+
+ ret = nbd_handle_reply_err(ioc, &reply, errp);
+ if (ret <= 0) {
+ return ret;
+ }
+ }
+
+ if (reply.type != NBD_REP_ACK) {
+ error_setg(errp, "Unexpected reply type %u (%s), expected %u (%s)",
+ reply.type, nbd_rep_lookup(reply.type),
+ NBD_REP_ACK, nbd_rep_lookup(NBD_REP_ACK));
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+ if (reply.length) {
+ error_setg(errp, "Unexpected length to ACK response");
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+
+ if (received) {
+ *context_id = received_id;
+ return 1;
+ }
+
+ return 0;
+}
int nbd_receive_negotiate(QIOChannel *ioc, const char *name,
QCryptoTLSCreds *tlscreds, const char *hostname,
uint64_t magic;
int rc;
bool zeroes = true;
+ bool structured_reply = info->structured_reply;
+ bool base_allocation = info->base_allocation;
trace_nbd_receive_negotiate(tlscreds, hostname ? hostname : "<null>");
+ info->structured_reply = false;
+ info->base_allocation = false;
rc = -EINVAL;
if (outioc) {
}
if (nbd_read(ioc, buf, 8, errp) < 0) {
- error_prepend(errp, "Failed to read data");
+ error_prepend(errp, "Failed to read data: ");
goto fail;
}
}
if (nbd_read(ioc, &magic, sizeof(magic), errp) < 0) {
- error_prepend(errp, "Failed to read magic");
+ error_prepend(errp, "Failed to read magic: ");
goto fail;
}
magic = be64_to_cpu(magic);
bool fixedNewStyle = false;
if (nbd_read(ioc, &globalflags, sizeof(globalflags), errp) < 0) {
- error_prepend(errp, "Failed to read server flags");
+ error_prepend(errp, "Failed to read server flags: ");
goto fail;
}
globalflags = be16_to_cpu(globalflags);
/* client requested flags */
clientflags = cpu_to_be32(clientflags);
if (nbd_write(ioc, &clientflags, sizeof(clientflags), errp) < 0) {
- error_prepend(errp, "Failed to send clientflags field");
+ error_prepend(errp, "Failed to send clientflags field: ");
goto fail;
}
if (tlscreds) {
if (fixedNewStyle) {
int result;
+ if (structured_reply) {
+ result = nbd_request_simple_option(ioc,
+ NBD_OPT_STRUCTURED_REPLY,
+ errp);
+ if (result < 0) {
+ goto fail;
+ }
+ info->structured_reply = result == 1;
+ }
+
+ if (info->structured_reply && base_allocation) {
+ result = nbd_negotiate_simple_meta_context(
+ ioc, name, info->x_dirty_bitmap ?: "base:allocation",
+ &info->meta_base_allocation_id, errp);
+ if (result < 0) {
+ goto fail;
+ }
+ info->base_allocation = result == 1;
+ }
+
/* Try NBD_OPT_GO first - if it works, we are done (it
* also gives us a good message if the server requires
* TLS). If it is not available, fall back to
/* Read the response */
if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) {
- error_prepend(errp, "Failed to read export length");
+ error_prepend(errp, "Failed to read export length: ");
goto fail;
}
- be64_to_cpus(&info->size);
+ info->size = be64_to_cpu(info->size);
if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) {
- error_prepend(errp, "Failed to read export flags");
+ error_prepend(errp, "Failed to read export flags: ");
goto fail;
}
- be16_to_cpus(&info->flags);
+ info->flags = be16_to_cpu(info->flags);
} else if (magic == NBD_CLIENT_MAGIC) {
uint32_t oldflags;
}
if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) {
- error_prepend(errp, "Failed to read export length");
+ error_prepend(errp, "Failed to read export length: ");
goto fail;
}
- be64_to_cpus(&info->size);
+ info->size = be64_to_cpu(info->size);
if (nbd_read(ioc, &oldflags, sizeof(oldflags), errp) < 0) {
- error_prepend(errp, "Failed to read export flags");
+ error_prepend(errp, "Failed to read export flags: ");
goto fail;
}
- be32_to_cpus(&oldflags);
+ oldflags = be32_to_cpu(oldflags);
if (oldflags & ~0xffff) {
error_setg(errp, "Unexpected export flags %0x" PRIx32, oldflags);
goto fail;
trace_nbd_receive_negotiate_size_flags(info->size, info->flags);
if (zeroes && nbd_drop(ioc, 124, errp) < 0) {
- error_prepend(errp, "Failed to read reserved block");
+ error_prepend(errp, "Failed to read reserved block: ");
goto fail;
}
rc = 0;
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;
+ }
+
+ reply->error = be32_to_cpu(reply->error);
+ reply->handle = be64_to_cpu(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;
+ }
+
+ chunk->flags = be16_to_cpu(chunk->flags);
+ chunk->type = be16_to_cpu(chunk->type);
+ chunk->handle = be64_to_cpu(chunk->handle);
+ chunk->length = be32_to_cpu(chunk->length);
+
+ return 0;
+}
+
/* nbd_receive_reply
* Returns 1 on success
* 0 on eof, when no data was read (errp is not set)
*/
int nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp)
{
- uint8_t buf[NBD_REPLY_SIZE];
- uint32_t magic;
int ret;
+ const char *type;
- 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
- */
-
- magic = ldl_be_p(buf);
- reply->error = ldl_be_p(buf + 4);
- reply->handle = ldq_be_p(buf + 8);
+ reply->magic = be32_to_cpu(reply->magic);
- reply->error = nbd_errno_to_system_errno(reply->error);
-
- if (reply->error == ESHUTDOWN) {
- /* This works even on mingw which lacks a native ESHUTDOWN */
- error_setg(errp, "server shutting down");
+ switch (reply->magic) {
+ case NBD_SIMPLE_REPLY_MAGIC:
+ ret = nbd_receive_simple_reply(ioc, &reply->simple, errp);
+ if (ret < 0) {
+ break;
+ }
+ trace_nbd_receive_simple_reply(reply->simple.error,
+ nbd_err_lookup(reply->simple.error),
+ reply->handle);
+ break;
+ case NBD_STRUCTURED_REPLY_MAGIC:
+ ret = nbd_receive_structured_reply_chunk(ioc, &reply->structured, errp);
+ if (ret < 0) {
+ break;
+ }
+ type = nbd_reply_type_lookup(reply->structured.type);
+ trace_nbd_receive_structured_reply_chunk(reply->structured.flags,
+ reply->structured.type, type,
+ reply->structured.handle,
+ reply->structured.length);
+ break;
+ default:
+ error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", reply->magic);
return -EINVAL;
}
- trace_nbd_receive_reply(magic, reply->error, reply->handle);
-
- if (magic != NBD_SIMPLE_REPLY_MAGIC) {
- error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", magic);
- return -EINVAL;
+ if (ret < 0) {
+ return ret;
}
return 1;