Blame SOURCES/kvm-nbd-client-Refactor-nbd_receive_list.patch

7711c0
From ff100c505e2a1cdc0a405008513948caa935b6cb Mon Sep 17 00:00:00 2001
7711c0
From: John Snow <jsnow@redhat.com>
7711c0
Date: Wed, 27 Mar 2019 17:22:42 +0100
7711c0
Subject: [PATCH 104/163] nbd/client: Refactor nbd_receive_list()
7711c0
7711c0
RH-Author: John Snow <jsnow@redhat.com>
7711c0
Message-id: <20190327172308.31077-30-jsnow@redhat.com>
7711c0
Patchwork-id: 85210
7711c0
O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 29/55] nbd/client: Refactor nbd_receive_list()
7711c0
Bugzilla: 1691009
7711c0
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
7711c0
RH-Acked-by: Max Reitz <mreitz@redhat.com>
7711c0
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
7711c0
7711c0
From: Eric Blake <eblake@redhat.com>
7711c0
7711c0
Right now, nbd_receive_list() is only called by
7711c0
nbd_receive_query_exports(), which in turn is only called if the
7711c0
server lacks NBD_OPT_GO but has working option negotiation, and is
7711c0
merely used as a quality-of-implementation trick since servers
7711c0
can't give decent errors for NBD_OPT_EXPORT_NAME.  However, servers
7711c0
that lack NBD_OPT_GO are becoming increasingly rare (nbdkit was a
7711c0
latecomer, in Aug 2018, but qemu has been such a server since commit
7711c0
f37708f6 in July 2017 and released in 2.10), so it no longer makes
7711c0
sense to micro-optimize that function for performance.
7711c0
7711c0
Furthermore, when debugging a server's implementation, tracing the
7711c0
full reply (both names and descriptions) is useful, not to mention
7711c0
that upcoming patches adding 'qemu-nbd --list' will want to collect
7711c0
that data.  And when you consider that a server can send an export
7711c0
name up to the NBD protocol length limit of 4k; but our current
7711c0
NBD_MAX_NAME_SIZE is only 256, we can't trace all valid server
7711c0
names without more storage, but 4k is large enough that the heap
7711c0
is better than the stack for long names.
7711c0
7711c0
Thus, I'm changing the division of labor, with nbd_receive_list()
7711c0
now always malloc'ing a result on success (the malloc is bounded
7711c0
by the fact that we reject servers with a reply length larger
7711c0
than 32M), and moving the comparison to 'wantname' to the caller.
7711c0
7711c0
There is a minor change in behavior where a server with 0 exports
7711c0
(an immediate NBD_REP_ACK reply) is now no longer distinguished
7711c0
from a server without LIST support (NBD_REP_ERR_UNSUP); this
7711c0
information could be preserved with a complication to the calling
7711c0
contract to provide a bit more information, but I didn't see the
7711c0
point.  After all, the worst that can happen if our guess at a
7711c0
match is wrong is that the caller will get a cryptic disconnect
7711c0
when NBD_OPT_EXPORT_NAME fails (which is no different from what
7711c0
would happen if we had not tried LIST), while treating an empty
7711c0
list as immediate failure would prevent connecting to really old
7711c0
servers that really did lack LIST.  Besides, NBD servers with 0
7711c0
exports are rare (qemu can do it when using QMP nbd-server-start
7711c0
without nbd-server-add - but qemu understands NBD_OPT_GO and
7711c0
thus won't tickle this change in behavior).
7711c0
7711c0
Fix the spelling of foundExport to match coding standards while
7711c0
in the area.
7711c0
7711c0
Signed-off-by: Eric Blake <eblake@redhat.com>
7711c0
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
7711c0
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7711c0
Message-Id: <20190117193658.16413-9-eblake@redhat.com>
7711c0
(cherry picked from commit 091d0bf3c94737fc451a9b3f4eddf3a4d74c90b8)
7711c0
Signed-off-by: John Snow <jsnow@redhat.com>
7711c0
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
7711c0
---
7711c0
 nbd/client.c     | 91 ++++++++++++++++++++++++++++++++++++--------------------
7711c0
 nbd/trace-events |  1 +
7711c0
 2 files changed, 59 insertions(+), 33 deletions(-)
7711c0
7711c0
diff --git a/nbd/client.c b/nbd/client.c
7711c0
index f625c20..fd4ba8d 100644
7711c0
--- a/nbd/client.c
7711c0
+++ b/nbd/client.c
7711c0
@@ -234,18 +234,24 @@ static int nbd_handle_reply_err(QIOChannel *ioc, NBDOptionReply *reply,
7711c0
     return result;
7711c0
 }
7711c0
 
7711c0
-/* Process another portion of the NBD_OPT_LIST reply.  Set *@match if
7711c0
- * the current reply matches @want or if the server does not support
7711c0
- * NBD_OPT_LIST, otherwise leave @match alone.  Return 0 if iteration
7711c0
- * is complete, positive if more replies are expected, or negative
7711c0
- * with @errp set if an unrecoverable error occurred. */
7711c0
-static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
7711c0
+/* nbd_receive_list:
7711c0
+ * Process another portion of the NBD_OPT_LIST reply, populating any
7711c0
+ * name received into *@name. If @description is non-NULL, and the
7711c0
+ * server provided a description, that is also populated. The caller
7711c0
+ * must eventually call g_free() on success.
7711c0
+ * Returns 1 if name and description were set and iteration must continue,
7711c0
+ *         0 if iteration is complete (including if OPT_LIST unsupported),
7711c0
+ *         -1 with @errp set if an unrecoverable error occurred.
7711c0
+ */
7711c0
+static int nbd_receive_list(QIOChannel *ioc, char **name, char **description,
7711c0
                             Error **errp)
7711c0
 {
7711c0
+    int ret = -1;
7711c0
     NBDOptionReply reply;
7711c0
     uint32_t len;
7711c0
     uint32_t namelen;
7711c0
-    char name[NBD_MAX_NAME_SIZE + 1];
7711c0
+    char *local_name = NULL;
7711c0
+    char *local_desc = NULL;
7711c0
     int error;
7711c0
 
7711c0
     if (nbd_receive_option_reply(ioc, NBD_OPT_LIST, &reply, errp) < 0) {
7711c0
@@ -253,9 +259,6 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
7711c0
     }
7711c0
     error = nbd_handle_reply_err(ioc, &reply, errp);
7711c0
     if (error <= 0) {
7711c0
-        /* The server did not support NBD_OPT_LIST, so set *match on
7711c0
-         * the assumption that any name will be accepted.  */
7711c0
-        *match = true;
7711c0
         return error;
7711c0
     }
7711c0
     len = reply.length;
7711c0
@@ -292,33 +295,38 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
7711c0
         nbd_send_opt_abort(ioc);
7711c0
         return -1;
7711c0
     }
7711c0
-    if (namelen != strlen(want)) {
7711c0
-        if (nbd_drop(ioc, len, errp) < 0) {
7711c0
-            error_prepend(errp,
7711c0
-                          "failed to skip export name with wrong length: ");
7711c0
-            nbd_send_opt_abort(ioc);
7711c0
-            return -1;
7711c0
-        }
7711c0
-        return 1;
7711c0
-    }
7711c0
 
7711c0
-    assert(namelen < sizeof(name));
7711c0
-    if (nbd_read(ioc, name, namelen, errp) < 0) {
7711c0
+    local_name = g_malloc(namelen + 1);
7711c0
+    if (nbd_read(ioc, local_name, namelen, errp) < 0) {
7711c0
         error_prepend(errp, "failed to read export name: ");
7711c0
         nbd_send_opt_abort(ioc);
7711c0
-        return -1;
7711c0
+        goto out;
7711c0
     }
7711c0
-    name[namelen] = '\0';
7711c0
+    local_name[namelen] = '\0';
7711c0
     len -= namelen;
7711c0
-    if (nbd_drop(ioc, len, errp) < 0) {
7711c0
-        error_prepend(errp, "failed to read export description: ");
7711c0
-        nbd_send_opt_abort(ioc);
7711c0
-        return -1;
7711c0
+    if (len) {
7711c0
+        local_desc = g_malloc(len + 1);
7711c0
+        if (nbd_read(ioc, local_desc, len, errp) < 0) {
7711c0
+            error_prepend(errp, "failed to read export description: ");
7711c0
+            nbd_send_opt_abort(ioc);
7711c0
+            goto out;
7711c0
+        }
7711c0
+        local_desc[len] = '\0';
7711c0
     }
7711c0
-    if (!strcmp(name, want)) {
7711c0
-        *match = true;
7711c0
+
7711c0
+    trace_nbd_receive_list(local_name, local_desc ?: "");
7711c0
+    *name = local_name;
7711c0
+    local_name = NULL;
7711c0
+    if (description) {
7711c0
+        *description = local_desc;
7711c0
+        local_desc = NULL;
7711c0
     }
7711c0
-    return 1;
7711c0
+    ret = 1;
7711c0
+
7711c0
+ out:
7711c0
+    g_free(local_name);
7711c0
+    g_free(local_desc);
7711c0
+    return ret;
7711c0
 }
7711c0
 
7711c0
 
7711c0
@@ -493,7 +501,8 @@ static int nbd_receive_query_exports(QIOChannel *ioc,
7711c0
                                      const char *wantname,
7711c0
                                      Error **errp)
7711c0
 {
7711c0
-    bool foundExport = false;
7711c0
+    bool list_empty = true;
7711c0
+    bool found_export = false;
7711c0
 
7711c0
     trace_nbd_receive_query_exports_start(wantname);
7711c0
     if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) {
7711c0
@@ -501,14 +510,25 @@ static int nbd_receive_query_exports(QIOChannel *ioc,
7711c0
     }
7711c0
 
7711c0
     while (1) {
7711c0
-        int ret = nbd_receive_list(ioc, wantname, &foundExport, errp);
7711c0
+        char *name;
7711c0
+        int ret = nbd_receive_list(ioc, &name, NULL, errp);
7711c0
 
7711c0
         if (ret < 0) {
7711c0
             /* Server gave unexpected reply */
7711c0
             return -1;
7711c0
         } else if (ret == 0) {
7711c0
             /* Done iterating. */
7711c0
-            if (!foundExport) {
7711c0
+            if (list_empty) {
7711c0
+                /*
7711c0
+                 * We don't have enough context to tell a server that
7711c0
+                 * sent an empty list apart from a server that does
7711c0
+                 * not support the list command; but as this function
7711c0
+                 * is just used to trigger a nicer error message
7711c0
+                 * before trying NBD_OPT_EXPORT_NAME, assume the
7711c0
+                 * export is available.
7711c0
+                 */
7711c0
+                return 0;
7711c0
+            } else if (!found_export) {
7711c0
                 error_setg(errp, "No export with name '%s' available",
7711c0
                            wantname);
7711c0
                 nbd_send_opt_abort(ioc);
7711c0
@@ -517,6 +537,11 @@ static int nbd_receive_query_exports(QIOChannel *ioc,
7711c0
             trace_nbd_receive_query_exports_success(wantname);
7711c0
             return 0;
7711c0
         }
7711c0
+        list_empty = false;
7711c0
+        if (!strcmp(name, wantname)) {
7711c0
+            found_export = true;
7711c0
+        }
7711c0
+        g_free(name);
7711c0
     }
7711c0
 }
7711c0
 
7711c0
diff --git a/nbd/trace-events b/nbd/trace-events
7711c0
index 5492042..d1e1ca6 100644
7711c0
--- a/nbd/trace-events
7711c0
+++ b/nbd/trace-events
7711c0
@@ -3,6 +3,7 @@ nbd_send_option_request(uint32_t opt, const char *name, uint32_t len) "Sending o
7711c0
 nbd_receive_option_reply(uint32_t option, const char *optname, uint32_t type, const char *typename, uint32_t length) "Received option reply %" PRIu32" (%s), type %" PRIu32" (%s), len %" PRIu32
7711c0
 nbd_server_error_msg(uint32_t err, const char *type, const char *msg) "server reported error 0x%" PRIx32 " (%s) with additional message: %s"
7711c0
 nbd_reply_err_unsup(uint32_t option, const char *name) "server doesn't understand request %" PRIu32 " (%s), attempting fallback"
7711c0
+nbd_receive_list(const char *name, const char *desc) "export list includes '%s', description '%s'"
7711c0
 nbd_opt_go_start(const char *name) "Attempting NBD_OPT_GO for export '%s'"
7711c0
 nbd_opt_go_success(void) "Export is good to go"
7711c0
 nbd_opt_go_info_unknown(int info, const char *name) "Ignoring unknown info %d (%s)"
7711c0
-- 
7711c0
1.8.3.1
7711c0