[Libguestfs] [libnbd PATCH 1/1] generator: Add support for NBD_INFO_INIT_STATE extension

Eric Blake eblake at redhat.com
Mon Feb 10 21:43:31 UTC 2020


The NBD protocol is adding an extension for servers to report the
initial state of the image (for now, whether it is sparse and/or reads
as all zeroes).  Time to expose this to clients, via the new API
nbd_get_init_flags().  The patch requires refreshing the
nbd-protocol.h file from a corresponding contemporary nbdkit commit.

Testing is possible with recent enough qemu.
---
 .gitignore                         |  1 +
 generator/generator                | 40 +++++++++++++--
 generator/states-newstyle-opt-go.c | 12 ++++-
 interop/Makefile.am                |  9 +++-
 interop/init-zero.c                | 78 ++++++++++++++++++++++++++++++
 interop/init-zero.sh               | 56 +++++++++++++++++++++
 lib/flags.c                        | 14 +++++-
 lib/internal.h                     | 10 ++--
 lib/nbd-protocol.h                 | 13 ++++-
 9 files changed, 220 insertions(+), 13 deletions(-)
 create mode 100644 interop/init-zero.c
 create mode 100755 interop/init-zero.sh

diff --git a/.gitignore b/.gitignore
index 7536021..e356392 100644
--- a/.gitignore
+++ b/.gitignore
@@ -70,6 +70,7 @@ Makefile.in
 /html/*.?.html
 /include/libnbd.h
 /interop/dirty-bitmap
+/interop/init-zero
 /interop/interop-nbdkit
 /interop/interop-nbdkit-tls-certs
 /interop/interop-nbdkit-tls-certs-allow-enabled
diff --git a/generator/generator b/generator/generator
index dea9bf7..0addc9d 100755
--- a/generator/generator
+++ b/generator/generator
@@ -979,7 +979,7 @@ let tls_enum = {
 }
 let all_enums = [ tls_enum ]

-(* Flags. *)
+(* Flags. See also Constants below. *)
 let cmd_flags = {
   flag_prefix = "CMD_FLAG";
   flags = [
@@ -1969,7 +1969,8 @@ are free to pass in other contexts."
 ^ non_blocking_test_call_description;
     see_also = [SectionLink "Flag calls";
                 Link "add_meta_context";
-                Link "block_status"; Link "aio_block_status"];
+                Link "block_status"; Link "aio_block_status";
+                Link "get_init_flags"];
   };

   "get_protocol", {
@@ -1990,6 +1991,30 @@ Most modern NBD servers use C<\"newstyle-fixed\">.
                 Link "get_tls_negotiated"];
   };

+  "get_init_flags", {
+    default_call with
+    args = []; ret = RInt;
+    permitted_states = [ Connected; Closed ];
+    shortdesc = "return any init state flags advertised by the server";
+    longdesc = "\
+Return the bitwise-OR of any initial state flags advertised by the
+server.  These flags may include C<LIBNBD_INIT_SPARSE> if the export
+is not fully allocated, or C<LIBNBD_INIT_ZERO> if the export is known
+to start with all contents reading as zeroes.  Other flags might be
+added in the future.
+
+Note that the flags advertised by the server refer only to the point
+in time when the connection was established; libnbd does not track if
+the results may have been rendered stale by intervening actions such
+as write requests.  Also, since both initial state bits and block
+status are orthogonal extensions within the NBD protocol, there is no
+requirement that a server's advertisement match what could be learned
+by checking block status.
+"
+^ non_blocking_test_call_description;
+    see_also = [Link "get_handshake_flags"; Link "block_status"];
+  };
+
   "get_size", {
     default_call with
     args = []; ret = RInt64;
@@ -2292,7 +2317,7 @@ return only one extent per metadata context where that extent
 does not exceed C<count> bytes; however, libnbd does not
 validate that the server obeyed the flag.";
     see_also = [Link "add_meta_context"; Link "can_meta_context";
-                Link "aio_block_status"];
+                Link "aio_block_status"; Link "get_init_flags"];
   };

   "poll", {
@@ -2619,7 +2644,8 @@ as described in L<libnbd(3)/Completion callbacks>.

 Other parameters behave as documented in L<nbd_block_status(3)>.";
     see_also = [SectionLink "Issuing asynchronous commands";
-                Link "can_meta_context"; Link "block_status"];
+                Link "can_meta_context"; Link "block_status";
+                Link "get_init_flags"];
   };

   "aio_get_fd", {
@@ -3031,6 +3057,7 @@ let first_version = [
   "set_uri_allow_transports", (1, 2);
   "set_uri_allow_tls", (1, 2);
   "set_uri_allow_local_file", (1, 2);
+  "get_init_flags", (1, 2);

   (* These calls are proposed for a future version of libnbd, but
    * have not been added to any released version so far.
@@ -3039,7 +3066,7 @@ let first_version = [
    *)
 ]

-(* Constants, etc. *)
+(* Constants, etc. See also Enums and Flags above. *)
 let constants = [
   "AIO_DIRECTION_READ",  1;
   "AIO_DIRECTION_WRITE", 2;
@@ -3048,6 +3075,9 @@ let constants = [
   "READ_DATA",           1;
   "READ_HOLE",           2;
   "READ_ERROR",          3;
+
+  "INIT_SPARSE",   1 lsl 0;
+  "INIT_ZERO",     1 lsl 1;
 ]

 let metadata_namespaces = [
diff --git a/generator/states-newstyle-opt-go.c b/generator/states-newstyle-opt-go.c
index 57ad55a..c2ef01e 100644
--- a/generator/states-newstyle-opt-go.c
+++ b/generator/states-newstyle-opt-go.c
@@ -1,5 +1,5 @@
 /* nbd client library in userspace: state machine
- * Copyright (C) 2013-2019 Red Hat Inc.
+ * Copyright (C) 2013-2020 Red Hat Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -59,6 +59,8 @@ STATE_MACHINE {
   switch (send_from_wbuf (h)) {
   case -1: SET_NEXT_STATE (%.DEAD); return 0;
   case 0:
+    /* For now, we don't allow the client to configure which infos to
+     * request, but instead rely on what the server volunteers. */
     h->sbuf.nrinfos = 0;
     h->wbuf = &h->sbuf;
     h->wlen = 2;
@@ -130,6 +132,14 @@ STATE_MACHINE {
           return 0;
         }
         break;
+      case NBD_INFO_INIT_STATE:
+        if (len != sizeof h->sbuf.or.payload.init) {
+          SET_NEXT_STATE (%.DEAD);
+          set_error (0, "handshake: incorrect NBD_INFO_INIT_STATE option reply length");
+          return 0;
+        }
+        h->initflags = be16toh (h->sbuf.or.payload.init.flags);
+        break;
       default:
         /* XXX Handle other info types, like NBD_INFO_BLOCK_SIZE */
         debug (h, "skipping unknown NBD_REP_INFO type %d",
diff --git a/interop/Makefile.am b/interop/Makefile.am
index 345be7c..ed5b553 100644
--- a/interop/Makefile.am
+++ b/interop/Makefile.am
@@ -1,5 +1,5 @@
 # nbd client library in userspace
-# Copyright (C) 2013-2019 Red Hat Inc.
+# Copyright (C) 2013-2020 Red Hat Inc.
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -50,12 +50,14 @@ check_PROGRAMS += \
 	dirty-bitmap \
 	socket-activation-qemu-nbd \
 	structured-read \
+	init-zero \
 	$(NULL)
 TESTS += \
 	interop-qemu-nbd \
 	dirty-bitmap.sh \
 	socket-activation-qemu-nbd \
 	structured-read.sh \
+	init-zero.sh \
 	$(NULL)

 # tls tests assume the pre-existence of files created in ../tests/Makefile.am,
@@ -140,6 +142,11 @@ structured_read_CPPFLAGS = -I$(top_srcdir)/include
 structured_read_CFLAGS = $(WARNINGS_CFLAGS)
 structured_read_LDADD = $(top_builddir)/lib/libnbd.la

+init_zero_SOURCES = init-zero.c
+init_zero_CPPFLAGS = -I$(top_srcdir)/include
+init_zero_CFLAGS = $(WARNINGS_CFLAGS)
+init_zero_LDADD = $(top_builddir)/lib/libnbd.la
+
 endif HAVE_QEMU_NBD

 if HAVE_NBDKIT
diff --git a/interop/init-zero.c b/interop/init-zero.c
new file mode 100644
index 0000000..0cf1d87
--- /dev/null
+++ b/interop/init-zero.c
@@ -0,0 +1,78 @@
+/* NBD client library in userspace
+ * Copyright (C) 2013-2020 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Test init state queries. */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <libnbd.h>
+
+/* Depends on init-zero.sh setting things up so that we are given a
+ * socket to a server advertising NBD_INIT_ZERO.
+ */
+
+int
+main (int argc, char *argv[])
+{
+  struct nbd_handle *nbd;
+  int r;
+
+  if (argc < 2) {
+    fprintf (stderr, "%s socket\n", argv[0]);
+    exit (EXIT_FAILURE);
+  }
+
+  nbd = nbd_create ();
+  if (nbd == NULL) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+
+  if (nbd_connect_unix (nbd, argv[1]) == -1) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+
+  r = nbd_get_init_flags (nbd);
+  if (r == -1) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+
+  if (!(r & LIBNBD_INIT_ZERO)) {
+    fprintf (stderr, "Missing expected zero init state\n");
+    exit (EXIT_FAILURE);
+  }
+
+  if (nbd_shutdown (nbd, 0) == -1) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+
+  nbd_close (nbd);
+
+  exit (EXIT_SUCCESS);
+}
diff --git a/interop/init-zero.sh b/interop/init-zero.sh
new file mode 100755
index 0000000..349f55e
--- /dev/null
+++ b/interop/init-zero.sh
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+# nbd client library in userspace
+# Copyright (C) 2019-2020 Red Hat Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+# Test structured read callbacks.
+
+source ../tests/functions.sh
+set -e
+set -x
+
+requires qemu-img --version
+requires qemu-nbd --version
+
+sock=`mktemp -u`
+files="init-zero.qcow2 $sock"
+rm -f $files
+cleanup_fn rm -f $files
+
+# qemu 5.0 added ability to track if a qcow2 image is all zeroes
+# Probe to see if qemu-nbd exposes that feature, before running test
+qemu-img create -f qcow2 init-zero.qcow2 64k
+qemu-nbd -f qcow2 -k $sock init-zero.qcow2&
+cleanup_fn kill $!
+tries=0
+while test ! -e $sock && test $tries -lt 5; do
+    sleep 1
+    tries=$((tries + 1))
+done
+
+case $(qemu-nbd --list -k $sock) in
+    *'init state'*zero*)
+        # Run the test.
+        $VG ./init-zero $sock
+	status=$?
+	;;
+    *)
+	echo "$0: skipping because qemu-nbd does not support all-zero bit"
+	status=77
+	;;
+esac
+
+exit $status
diff --git a/lib/flags.c b/lib/flags.c
index d55d10a..62c3252 100644
--- a/lib/flags.c
+++ b/lib/flags.c
@@ -1,5 +1,5 @@
 /* NBD client library in userspace
- * Copyright (C) 2013-2019 Red Hat Inc.
+ * Copyright (C) 2013-2020 Red Hat Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -143,6 +143,18 @@ nbd_unlocked_can_meta_context (struct nbd_handle *h, const char *name)
   return 0;
 }

+int
+nbd_unlocked_get_init_flags (struct nbd_handle *h)
+{
+  if (h->eflags == 0) {
+    set_error (EINVAL, "server has not returned init flags, "
+               "you need to connect to the server first");
+    return -1;
+  }
+
+  return h->initflags;
+}
+
 int64_t
 nbd_unlocked_get_size (struct nbd_handle *h)
 {
diff --git a/lib/internal.h b/lib/internal.h
index 6eb50d8..aa555b5 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -1,5 +1,5 @@
 /* nbd client library in userspace: internal definitions
- * Copyright (C) 2013-2019 Red Hat Inc.
+ * Copyright (C) 2013-2020 Red Hat Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -97,12 +97,13 @@ struct nbd_handle {
   uint16_t gflags;

   /* Export size and per-export flags, received during handshake.  NB:
-   * These are *both* *only* valid if eflags != 0.  This is because
-   * all servers should set NBD_FLAG_HAS_FLAGS, so eflags should
-   * always be != 0, and we set both fields at the same time.
+   * These are *only* valid if eflags != 0.  This is because all
+   * servers should set NBD_FLAG_HAS_FLAGS, so eflags should always be
+   * != 0, and we set the other fields at the same time.
    */
   uint64_t exportsize;
   uint16_t eflags;
+  uint16_t initflags;

   /* Flags set by the state machine to tell what protocol and whether
    * TLS was negotiated.
@@ -159,6 +160,7 @@ struct nbd_handle {
       struct nbd_fixed_new_option_reply option_reply;
       union {
         struct nbd_fixed_new_option_reply_info_export export;
+        struct nbd_fixed_new_option_reply_info_init init;
         struct {
           struct nbd_fixed_new_option_reply_meta_context context;
           char str[NBD_MAX_STRING];
diff --git a/lib/nbd-protocol.h b/lib/nbd-protocol.h
index df0b4c6..50531a0 100644
--- a/lib/nbd-protocol.h
+++ b/lib/nbd-protocol.h
@@ -1,5 +1,5 @@
 /* nbdkit
- * Copyright (C) 2013-2019 Red Hat Inc.
+ * Copyright (C) 2013-2020 Red Hat Inc.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -146,6 +146,7 @@ struct nbd_fixed_new_option_reply {
 #define NBD_INFO_NAME        1
 #define NBD_INFO_DESCRIPTION 2
 #define NBD_INFO_BLOCK_SIZE  3
+#define NBD_INFO_INIT_STATE  4

 /* NBD_INFO_EXPORT reply (follows fixed_new_option_reply). */
 struct nbd_fixed_new_option_reply_info_export {
@@ -154,6 +155,16 @@ struct nbd_fixed_new_option_reply_info_export {
   uint16_t eflags;              /* per-export flags */
 } NBD_ATTRIBUTE_PACKED;

+/* NBD_INFO_INIT_STATE reply. */
+struct nbd_fixed_new_option_reply_info_init {
+  uint16_t info;                /* NBD_INFO_INIT_STATE */
+  uint16_t flags;               /* per-export init flags */
+} NBD_ATTRIBUTE_PACKED;
+
+/* Constants for use in reply to NBD_INFO_INIT_STATE. */
+#define NBD_INIT_SPARSE   (1 << 0)
+#define NBD_INIT_ZERO     (1 << 1)
+
 /* NBD_REP_META_CONTEXT reply (follows fixed_new_option_reply). */
 struct nbd_fixed_new_option_reply_meta_context {
   uint32_t context_id;          /* metadata context ID */
-- 
2.24.1




More information about the Libguestfs mailing list