[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-block] [PATCH v4 6/8] nbd: Implement NBD_OPT_GO on client
From: |
Eric Blake |
Subject: |
[Qemu-block] [PATCH v4 6/8] nbd: Implement NBD_OPT_GO on client |
Date: |
Mon, 20 Feb 2017 20:42:46 -0600 |
NBD_OPT_EXPORT_NAME is lousy: per the NBD protocol, any failure
requires the server to close the connection rather than report an
error to us. Therefore, upstream NBD recently added NBD_OPT_GO as
the improved version of the option that does what we want [1]: it
reports sane errors on failures, and on success provides at least
as much info as NBD_OPT_EXPORT_NAME.
[1] https://github.com/NetworkBlockDevice/nbd/blob/extension-info/doc/proto.md
This is a first cut at use of the information types. Note that we
do not need to use NBD_OPT_INFO, and that use of NBD_OPT_GO means
we no longer have to use NBD_OPT_LIST to learn whether a server
requires TLS (this requires servers that gracefully handle unknown
NBD_OPT, many servers prior to qemu 2.5 were buggy, but I have patched
qemu, upstream nbd, and nbdkit in the meantime, in part because of
interoperability testing with this patch). We still fall back to
NBD_OPT_LIST when NBD_OPT_GO is not supported on the server, as it
is still one last chance for a nicer error message. Later patches
will use further info, like NBD_INFO_BLOCK_SIZE.
Signed-off-by: Eric Blake <address@hidden>
---
v4: NBD protocol changes, again
v3: revamp to match latest version of NBD protocol
---
nbd/nbd-internal.h | 3 ++
nbd/client.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 131 insertions(+), 2 deletions(-)
diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
index aa5b2fd..96c204b 100644
--- a/nbd/nbd-internal.h
+++ b/nbd/nbd-internal.h
@@ -56,8 +56,11 @@
* 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)
+
#define NBD_REQUEST_MAGIC 0x25609513
#define NBD_REPLY_MAGIC 0x67446698
#define NBD_OPTS_MAGIC 0x49484156454F5054LL
diff --git a/nbd/client.c b/nbd/client.c
index f96539b..b408945 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -380,6 +380,118 @@ static int nbd_receive_list(QIOChannel *ioc, const char
*want, bool *match,
}
+/* Returns -1 if NBD_OPT_GO proves the export @wantname 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 @info populated). */
+static int nbd_opt_go(QIOChannel *ioc, const char *wantname,
+ NBDExportInfo *info, Error **errp)
+{
+ nbd_opt_reply reply;
+ uint32_t len = strlen(wantname);
+ uint16_t type;
+ int error;
+ char *buf;
+
+ /* The protocol requires that the server send NBD_INFO_EXPORT with
+ * a non-zero flags (at least NBD_FLAG_HAS_FLAGS must be set); so
+ * flags still 0 is a witness of a broken server. */
+ info->flags = 0;
+
+ TRACE("Attempting NBD_OPT_GO for export '%s'", wantname);
+ buf = g_malloc(2 + 4 + len + 1);
+ stw_be_p(buf, 0); /* No requests, live with whatever server sends */
+ stl_be_p(buf + 2, len);
+ memcpy(buf + 6, wantname, len);
+ if (nbd_send_option_request(ioc, NBD_OPT_GO, len + 6, buf, errp) < 0) {
+ return -1;
+ }
+
+ TRACE("Reading export info");
+ while (1) {
+ 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_ACK) {
+ /* Server is done sending info and moved into transmission
+ phase, but make sure it sent flags */
+ if (len) {
+ error_setg(errp, "server sent invalid NBD_REP_ACK");
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+ if (!info->flags) {
+ error_setg(errp, "broken server omitted NBD_INFO_EXPORT");
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+ TRACE("export is good to go");
+ return 1;
+ }
+ if (reply.type != NBD_REP_INFO) {
+ error_setg(errp, "unexpected reply type %" PRIx32 ", expected %x",
+ reply.type, NBD_REP_INFO);
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+ if (len < sizeof(type)) {
+ error_setg(errp, "NBD_REP_INFO length %" PRIu32 " is too short",
+ len);
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+ if (read_sync(ioc, &type, sizeof(type)) != sizeof(type)) {
+ error_setg(errp, "failed to read info type");
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+ len -= sizeof(type);
+ be16_to_cpus(&type);
+ switch (type) {
+ case NBD_INFO_EXPORT:
+ if (len != sizeof(info->size) + sizeof(info->flags)) {
+ error_setg(errp, "remaining export info len %" PRIu32
+ " is unexpected size", len);
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+ if (read_sync(ioc, &info->size, sizeof(info->size)) !=
+ sizeof(info->size)) {
+ error_setg(errp, "failed to read info size");
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+ be64_to_cpus(&info->size);
+ if (read_sync(ioc, &info->flags, sizeof(info->flags)) !=
+ sizeof(info->flags)) {
+ error_setg(errp, "failed to read info flags");
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+ be16_to_cpus(&info->flags);
+ TRACE("Size is %" PRIu64 ", export flags %" PRIx16,
+ info->size, info->flags);
+ break;
+
+ default:
+ TRACE("ignoring unknown export info %" PRIu16 " (%s)", type,
+ nbd_info_lookup(type));
+ if (drop_sync(ioc, len) != len) {
+ error_setg(errp, "Failed to read info payload");
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+ break;
+ }
+ }
+}
+
/* Return -1 on failure, 0 if wantname is an available export. */
static int nbd_receive_query_exports(QIOChannel *ioc,
const char *wantname,
@@ -574,11 +686,25 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char
*name,
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, info, errp);
+ if (result < 0) {
+ goto fail;
+ }
+ if (result > 0) {
+ return 0;
+ }
/* 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;
--
2.9.3
- Re: [Qemu-block] [Qemu-devel] [PATCH v4 3/8] block: Add blk_get_opt_transfer(), (continued)
- [Qemu-block] [PATCH v4 2/8] nbd: Create struct for tracking export info, Eric Blake, 2017/02/20
- [Qemu-block] [PATCH v4 7/8] nbd: Implement NBD_INFO_BLOCK_SIZE on server, Eric Blake, 2017/02/20
- Re: [Qemu-block] [PATCH v4 7/8] nbd: Implement NBD_INFO_BLOCK_SIZE on server, Paolo Bonzini, 2017/02/22
- Re: [Qemu-block] [PATCH v4 7/8] nbd: Implement NBD_INFO_BLOCK_SIZE on server, Eric Blake, 2017/02/22
- Re: [Qemu-block] [PATCH v4 7/8] nbd: Implement NBD_INFO_BLOCK_SIZE on server, Paolo Bonzini, 2017/02/22
- Re: [Qemu-block] [PATCH v4 7/8] nbd: Implement NBD_INFO_BLOCK_SIZE on server, Eric Blake, 2017/02/22
- Re: [Qemu-block] [PATCH v4 7/8] nbd: Implement NBD_INFO_BLOCK_SIZE on server, Paolo Bonzini, 2017/02/23
- Re: [Qemu-block] [PATCH v4 7/8] nbd: Implement NBD_INFO_BLOCK_SIZE on server, Eric Blake, 2017/02/23
[Qemu-block] [PATCH v4 6/8] nbd: Implement NBD_OPT_GO on client,
Eric Blake <=
[Qemu-block] [PATCH v4 4/8] nbd: Expose and debug more NBD constants, Eric Blake, 2017/02/20
[Qemu-block] [PATCH v4 5/8] nbd: Implement NBD_OPT_GO on server, Eric Blake, 2017/02/20
[Qemu-block] [PATCH v4 8/8] nbd: Implement NBD_INFO_BLOCK_SIZE on client, Eric Blake, 2017/02/20