aboutsummaryrefslogtreecommitdiff
path: root/nbd/client.c
diff options
context:
space:
mode:
authorEric Blake <eblake@redhat.com>2019-01-17 13:36:49 -0600
committerEric Blake <eblake@redhat.com>2019-01-21 15:49:52 -0600
commit0182c1aed9e6a9314023c7264c5c264da2f4a4ce (patch)
tree4dac04b8a559a32756a6618ebf4a78a0c382d82b /nbd/client.c
parent757b3ab989dea1c3143dd0d499441415ac7fcbc0 (diff)
nbd/client: Split out nbd_receive_one_meta_context()
Extract portions of nbd_negotiate_simple_meta_context() to a new function nbd_receive_one_meta_context() that copies the pattern of nbd_receive_list() for performing the argument validation of one reply. The error message when the server replies with more than one context changes slightly, but that shouldn't happen in the common case. Signed-off-by: Eric Blake <eblake@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> Message-Id: <20190117193658.16413-13-eblake@redhat.com>
Diffstat (limited to 'nbd/client.c')
-rw-r--r--nbd/client.c147
1 files changed, 90 insertions, 57 deletions
diff --git a/nbd/client.c b/nbd/client.c
index 96da68ee18..c7bb708155 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -669,7 +669,86 @@ static int nbd_send_meta_query(QIOChannel *ioc, uint32_t opt,
return ret;
}
-/* nbd_negotiate_simple_meta_context:
+/*
+ * nbd_receive_one_meta_context:
+ * Called in a loop to receive and trace one set/list meta context reply.
+ * Pass non-NULL @name or @id to collect results back to the caller, which
+ * must eventually call g_free().
+ * return 1 if name is set and iteration must continue,
+ * 0 if iteration is complete (including if option is unsupported),
+ * -1 with errp set for any error
+ */
+static int nbd_receive_one_meta_context(QIOChannel *ioc,
+ uint32_t opt,
+ char **name,
+ uint32_t *id,
+ Error **errp)
+{
+ int ret;
+ NBDOptionReply reply;
+ char *local_name = NULL;
+ uint32_t local_id;
+
+ if (nbd_receive_option_reply(ioc, opt, &reply, errp) < 0) {
+ return -1;
+ }
+
+ ret = nbd_handle_reply_err(ioc, &reply, errp);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ if (reply.type == NBD_REP_ACK) {
+ if (reply.length != 0) {
+ error_setg(errp, "Unexpected length to ACK response");
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+ return 0;
+ } else if (reply.type != NBD_REP_META_CONTEXT) {
+ error_setg(errp, "Unexpected reply type %u (%s), expected %u (%s)",
+ reply.type, nbd_rep_lookup(reply.type),
+ NBD_REP_META_CONTEXT, nbd_rep_lookup(NBD_REP_META_CONTEXT));
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+
+ if (reply.length <= sizeof(local_id) ||
+ reply.length > NBD_MAX_BUFFER_SIZE) {
+ error_setg(errp, "Failed to negotiate meta context, server "
+ "answered with unexpected length %" PRIu32,
+ reply.length);
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+
+ if (nbd_read(ioc, &local_id, sizeof(local_id), errp) < 0) {
+ return -1;
+ }
+ local_id = be32_to_cpu(local_id);
+
+ reply.length -= sizeof(local_id);
+ local_name = g_malloc(reply.length + 1);
+ if (nbd_read(ioc, local_name, reply.length, errp) < 0) {
+ g_free(local_name);
+ return -1;
+ }
+ local_name[reply.length] = '\0';
+ trace_nbd_opt_meta_reply(nbd_opt_lookup(opt), local_name, local_id);
+
+ if (name) {
+ *name = local_name;
+ } else {
+ g_free(local_name);
+ }
+ if (id) {
+ *id = local_id;
+ }
+ return 1;
+}
+
+/*
+ * nbd_negotiate_simple_meta_context:
* Request the server to set the meta context for export @info->name
* using @info->x_dirty_bitmap with a fallback to "base:allocation",
* setting @info->context_id to the resulting id. Fail if the server
@@ -690,50 +769,21 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc,
* function should lose the term _simple.
*/
int ret;
- NBDOptionReply reply;
const char *context = info->x_dirty_bitmap ?: "base:allocation";
bool received = false;
+ char *name = NULL;
if (nbd_send_meta_query(ioc, NBD_OPT_SET_META_CONTEXT,
info->name, context, errp) < 0) {
return -1;
}
- if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply,
- errp) < 0)
- {
+ ret = nbd_receive_one_meta_context(ioc, NBD_OPT_SET_META_CONTEXT,
+ &name, &info->context_id, errp);
+ if (ret < 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(info->context_id) + strlen(context)) {
- 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, &info->context_id, sizeof(info->context_id),
- errp) < 0) {
- return -1;
- }
- info->context_id = be32_to_cpu(info->context_id);
-
- reply.length -= sizeof(info->context_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 (ret == 1) {
if (strcmp(context, name)) {
error_setg(errp, "Failed to negotiate meta context '%s', server "
"answered with different context '%s'", context,
@@ -743,36 +793,19 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc,
return -1;
}
g_free(name);
-
- trace_nbd_opt_meta_reply(context, info->context_id);
received = true;
- /* receive NBD_REP_ACK */
- if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply,
- errp) < 0)
- {
+ ret = nbd_receive_one_meta_context(ioc, NBD_OPT_SET_META_CONTEXT,
+ NULL, NULL, errp);
+ if (ret < 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));
+ if (ret != 0) {
+ error_setg(errp, "Server answered with more than one context");
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;
- }
-
return received;
}