qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 14/18] nbd: Implement NBD_OPT_GO on client


From: Eric Blake
Subject: [Qemu-devel] [PATCH 14/18] nbd: Implement NBD_OPT_GO on client
Date: Fri, 8 Apr 2016 16:05:54 -0600

NBD_OPT_EXPORT_NAME is lousy: it doesn't have any sane error
reporting.  Upstream NBD recently added NBD_OPT_GO as the
improved version of the option that does what we want: it
reports sane errors on failures (including when a server
requires TLS but does not have NBD_OPT_GO!), and on success
it concludes with the same data as NBD_OPT_EXPORT_NAME sends.

Signed-off-by: Eric Blake <address@hidden>
---
 include/block/nbd.h |  1 +
 nbd/nbd-internal.h  |  7 +++++
 nbd/client.c        | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 92 insertions(+), 2 deletions(-)

diff --git a/include/block/nbd.h b/include/block/nbd.h
index 35c0ea3..d261dbc 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -88,6 +88,7 @@ typedef struct nbd_reply nbd_reply;
 #define NBD_REP_ERR_POLICY      ((UINT32_C(1) << 31) | 2) /* Server denied */
 #define NBD_REP_ERR_INVALID     ((UINT32_C(1) << 31) | 3) /* Invalid length. */
 #define NBD_REP_ERR_TLS_REQD    ((UINT32_C(1) << 31) | 5) /* TLS required */
+#define NBD_REP_ERR_UNKNOWN     ((UINT32_C(1) << 31) | 6) /* Export unknown */

 /* Request flags, sent from client to server during transmission phase */
 #define NBD_CMD_FLAG_FUA        (1 << 0)
diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
index b78d249..ddba1d0 100644
--- a/nbd/nbd-internal.h
+++ b/nbd/nbd-internal.h
@@ -55,8 +55,13 @@
  * https://github.com/yoe/nbd/blob/master/doc/proto.md
  */

+/* Size of all NBD_OPT_*, without payload */
 #define NBD_REQUEST_SIZE        (4 + 2 + 2 + 8 + 8 + 4)
+/* Size of all NBD_REP_* sent in answer to most NBD_OPT_*, without payload */
 #define NBD_REPLY_SIZE          (4 + 4 + 8)
+/* Size of reply to NBD_OPT_EXPORT_NAME, without trailing zeroes */
+#define NBD_FINAL_REPLY_SIZE    (8 + 2)
+
 #define NBD_REQUEST_MAGIC       0x25609513
 #define NBD_REPLY_MAGIC         0x67446698
 #define NBD_OPTS_MAGIC          0x49484156454F5054LL
@@ -80,6 +85,8 @@
 #define NBD_OPT_LIST            (3)
 #define NBD_OPT_PEEK_EXPORT     (4)
 #define NBD_OPT_STARTTLS        (5)
+#define NBD_OPT_INFO            (6)
+#define NBD_OPT_GO              (7)

 /* NBD errors are based on errno numbers, so there is a 1:1 mapping,
  * but only a limited set of errno values is specified in the protocol.
diff --git a/nbd/client.c b/nbd/client.c
index 507ddc1..af17d4c 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -215,6 +215,11 @@ static int nbd_handle_reply_err(QIOChannel *ioc, 
nbd_opt_reply *reply,
                    reply->option);
         break;

+    case NBD_REP_ERR_UNKNOWN:
+        error_setg(errp, "Requested export not available for option %" PRIx32,
+                   reply->option);
+        break;
+
     default:
         error_setg(errp, "Unknown error code when asking for option %" PRIx32,
                    reply->option);
@@ -299,6 +304,67 @@ static int nbd_receive_list(QIOChannel *ioc, const char 
*want, Error **errp)
 }


+/* Returns -1 if NBD_OPT_GO proves the export cannot be used, 0 if
+ * NBD_OPT_GO is unsupported (fall back to NBD_OPT_LIST and
+ * NBD_OPT_EXPORT_NAME in that case), and > 0 if the export is good to
+ * go (with the server data at the same point as it would be right
+ * after sending NBD_OPT_EXPORT_NAME). */
+static int nbd_opt_go(QIOChannel *ioc, const char *wantname, Error **errp)
+{
+    nbd_opt_reply reply;
+    uint32_t len;
+    uint32_t namelen;
+    int error;
+    char buf[NBD_MAX_NAME_SIZE];
+
+    TRACE("Attempting NBD_OPT_GO for export '%s'", wantname);
+    if (nbd_send_option_request(ioc, NBD_OPT_GO, -1, wantname, errp) < 0) {
+        return -1;
+    }
+
+    TRACE("Reading export info");
+    if (nbd_receive_option_reply(ioc, NBD_OPT_GO, &reply, errp) < 0) {
+        return -1;
+    }
+    error = nbd_handle_reply_err(ioc, &reply, errp);
+    if (error <= 0) {
+        return error;
+    }
+    len = reply.length;
+
+    if (reply.type != NBD_REP_SERVER) {
+        error_setg(errp, "unexpected reply type %" PRIx32 ", expected %x",
+                   reply.type, NBD_REP_SERVER);
+        return -1;
+    }
+
+    if (len < sizeof(namelen) + NBD_FINAL_REPLY_SIZE ||
+        len > sizeof(namelen) + sizeof(buf) + NBD_FINAL_REPLY_SIZE) {
+        error_setg(errp, "reply with %" PRIu32 " bytes is unexpected size",
+                   len);
+        return -1;
+    }
+
+    if (read_sync(ioc, &namelen, sizeof(namelen)) != sizeof(namelen)) {
+        error_setg(errp, "failed to read namelen");
+        return -1;
+    }
+    be32_to_cpus(&namelen);
+    if (len != sizeof(namelen) + namelen + NBD_FINAL_REPLY_SIZE) {
+        error_setg(errp, "namelen %" PRIu32 " is unexpected size",
+                   len);
+        return -1;
+    }
+
+    if (read_sync(ioc, buf, namelen) != namelen) {
+        error_setg(errp, "failed to read name");
+        return -1;
+    }
+
+    TRACE("export is good to go");
+    return 1;
+}
+
 /* Return -1 on failure, 0 if wantname is an available export. */
 static int nbd_receive_query_exports(QIOChannel *ioc,
                                      const char *wantname,
@@ -505,11 +571,26 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char 
*name, uint32_t *flags,
             name = "";
         }
         if (fixedNewStyle) {
+            int result;
+
+            /* 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
+             * NBD_OPT_LIST for nicer error messages about a missing
+             * export, then use NBD_OPT_EXPORT_NAME.  */
+            result = nbd_opt_go(ioc, name, errp);
+            if (result < 0) {
+                goto fail;
+            }
+            if (result > 0) {
+                zeroes = false;
+                goto success;
+            }
             /* Check our desired export is present in the
              * server export list. Since NBD_OPT_EXPORT_NAME
              * cannot return an error message, running this
-             * query gives us good error reporting if the
-             * server required TLS
+             * query gives us better error reporting if the
+             * export name is not available.
              */
             if (nbd_receive_query_exports(ioc, name, errp) < 0) {
                 goto fail;
@@ -522,6 +603,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char 
*name, uint32_t *flags,
         }

         /* Read the response */
+    success:
         if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) {
             error_setg(errp, "Failed to read export length");
             goto fail;
-- 
2.5.5




reply via email to

[Prev in Thread] Current Thread [Next in Thread]