Add APIs to make it possible to request and display NBD_INFO_NAME and
NBD_INFO_DESCRIPTION, which will enable fixing one of the todo items
in nbdinfo when not used in --list mode. Note that at least qemu-nbd
does not advertise name and description during NBD_OPT_GO unless the
client requests it first; at the same time, always requesting it only
slows down handshaking, so exposing a knob seems better than always
doing this check (contrast that with NBD_INFO_BLOCK_SIZE, which
affects correctness of data access).
---
lib/internal.h | 13 ++++-
lib/nbd-protocol.h | 10 +++-
generator/API.ml | 81 +++++++++++++++++++++++++++++-
generator/states-newstyle-opt-go.c | 49 +++++++++++++++---
lib/handle.c | 61 ++++++++++++++++++++++
5 files changed, 204 insertions(+), 10 deletions(-)
diff --git a/lib/internal.h b/lib/internal.h
index 6106af3..aa7e9a2 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -104,6 +104,9 @@ struct nbd_handle {
size_t nr_exports;
struct export *exports;
+ /* Full info mode. */
+ bool full_info;
+
/* Global flags from the server. */
uint16_t gflags;
@@ -120,6 +123,10 @@ struct nbd_handle {
uint32_t block_preferred;
uint32_t block_maximum;
+ /* Server canonical name and description, or NULL if not advertised. */
+ char *canonical_name;
+ char *description;
+
/* Flags set by the state machine to tell what protocol and whether
* TLS was negotiated.
*/
@@ -180,6 +187,10 @@ struct nbd_handle {
} __attribute__((packed)) server;
struct nbd_fixed_new_option_reply_info_export export;
struct nbd_fixed_new_option_reply_info_block_size block_size;
+ struct {
+ struct nbd_fixed_new_option_reply_info_name_or_desc info;
+ char str[NBD_MAX_STRING];
+ } __attribute__((packed)) name_desc;
struct {
struct nbd_fixed_new_option_reply_meta_context context;
char str[NBD_MAX_STRING];
@@ -205,7 +216,7 @@ struct nbd_handle {
uint32_t cflags;
uint32_t len;
uint16_t nrinfos;
- uint16_t info;
+ uint16_t info[3];
uint32_t nrqueries;
} sbuf;
diff --git a/lib/nbd-protocol.h b/lib/nbd-protocol.h
index 627bc6e..e5d6404 100644
--- a/lib/nbd-protocol.h
+++ b/lib/nbd-protocol.h
@@ -154,6 +154,14 @@ struct nbd_fixed_new_option_reply_info_export {
uint16_t eflags; /* per-export flags */
} NBD_ATTRIBUTE_PACKED;
+/* NBD_INFO_NAME or NBD_INFO_DESCRIPTION reply (follows
+ * fixed_new_option_reply).
+ */
+struct nbd_fixed_new_option_reply_info_name_or_desc {
+ uint16_t info; /* NBD_INFO_NAME, NBD_INFO_DESCRIPTION */
+ /* followed by a string name or description */
+} NBD_ATTRIBUTE_PACKED;
+
/* NBD_INFO_BLOCK_SIZE reply (follows fixed_new_option_reply). */
struct nbd_fixed_new_option_reply_info_block_size {
uint16_t info; /* NBD_INFO_BLOCK_SIZE */
@@ -165,7 +173,7 @@ struct nbd_fixed_new_option_reply_info_block_size {
/* NBD_REP_SERVER reply (follows fixed_new_option_reply). */
struct nbd_fixed_new_option_reply_server {
uint32_t export_name_len; /* length of export name */
- /* followed by a string export name and description*/
+ /* followed by a string export name and description */
} NBD_ATTRIBUTE_PACKED;
/* NBD_REP_META_CONTEXT reply (follows fixed_new_option_reply). */
diff --git a/generator/API.ml b/generator/API.ml
index 2f3b57d..eff0ac4 100644
--- a/generator/API.ml
+++ b/generator/API.ml
@@ -294,8 +294,81 @@ to a URI that includes an export name.";
args = []; ret = RString;
shortdesc = "get the export name";
longdesc = "\
-Get the export name associated with the handle.";
- see_also = [Link "set_export_name"; Link "connect_uri"];
+Get the export name associated with the handle. This is the name
+that libnbd requests; see L<nbd_get_canonical_export_name(3)> for
+determining if the server has a different canonical name for the
+given export (most common when requesting the default export name
+of an empty string C<\"\">)";
+ see_also = [Link "set_export_name"; Link "connect_uri";
+ Link "get_canonical_export_name"];
+ };
+
+ "set_full_info", {
+ default_call with
+ args = [Bool "request"]; ret = RErr;
+ permitted_states = [ Created ];
+ shortdesc = "control whether NBD_OPT_GO requests extra details";
+ longdesc = "\
+By default, when connecting to an export, libnbd only requests the
+details it needs to service data operations. The NBD server says
+that a server can supply optional information, such as a canonical
+name of the export (see L<nbd_get_canonical_export_name(3)>) or
+a description of the export (see L<nbd_get_export_description(3)>),
+but that a hint from the client makes it more likely for this
+extra information to be provided. This function controls whether
+libnbd will provide that hint.
+
+Note that even when full info is requested, the server is not
+obligated to reply with all information that libnbd requested.
+Similarly, libnbd will ignore any optional server information that
+libnbd has not yet been taught to recognize.";
+ see_also = [Link "get_full_info"; Link
"get_canonical_export_name";
+ Link "get_export_description"];
+ };
+
+ "get_full_info", {
+ default_call with
+ args = []; ret = RBool;
+ permitted_states = [];
+ shortdesc = "see if NBD_OPT_GO requests extra details";
+ longdesc = "\
+Return the state of the full info request flag on this handle.";
+ see_also = [Link "set_full_info"];
+ };
+
+ "get_canonical_export_name", {
+ default_call with
+ args = []; ret = RString;
+ permitted_states = [ Connected; Closed ];
+ shortdesc = "return the canonical export name, if the server has one";
+ longdesc = "\
+The NBD protocol permits a server to report an optional canonical
+export name which may differ from the client's request (as set by
+L<nbd_set_export_name(3)> or L<nbd_connect_uri(3)>. This function
+accesses any name returned by the server; it may be the same as
+the client request, but is more likely to differ when the client
+requested a connection to the default export name (an empty string
+C<\"\">).
+
+Some servers are unlikely to report a canonical name unless the
+client specifically hinted about wanting it, via L<nbd_set_full_info(3)>.";
+ see_also = [Link "set_full_info"; Link "get_export_name"];
+ };
+
+ "get_export_description", {
+ default_call with
+ args = []; ret = RString;
+ permitted_states = [ Connected; Closed ];
+ shortdesc = "return the export description, if the server has one";
+ longdesc = "\
+The NBD protocol permits a server to report an optional export
+description. This function reports any description reported by
+the server.
+
+Some servers are unlikely to report description unless the
+client specifically hinted about wanting it, via L<nbd_set_full_info(3)>.
+For L<qemu-nbd(8)>, a description is set with I<-D>.";
+ see_also = [Link "set_full_info"];
};
"set_tls", {
@@ -2364,6 +2437,10 @@ let first_version = [
"get_list_export_name", (1, 4);
"get_list_export_description", (1, 4);
"get_block_size", (1, 4);
+ "set_full_info", (1, 4);
+ "get_full_info", (1, 4);
+ "get_canonical_export_name", (1, 4);
+ "get_export_description", (1, 4);
(* These calls are proposed for a future version of libnbd, but
* have not been added to any released version so far.
diff --git a/generator/states-newstyle-opt-go.c b/generator/states-newstyle-opt-go.c
index 99a1dcd..74f092e 100644
--- a/generator/states-newstyle-opt-go.c
+++ b/generator/states-newstyle-opt-go.c
@@ -20,11 +20,13 @@
STATE_MACHINE {
NEWSTYLE.OPT_GO.START:
+ uint16_t nrinfos = h->full_info ? 3 : 1;
+
h->sbuf.option.version = htobe64 (NBD_NEW_VERSION);
h->sbuf.option.option = htobe32 (NBD_OPT_GO);
h->sbuf.option.optlen =
htobe32 (/* exportnamelen */ 4 + strlen (h->export_name)
- + /* nrinfos */ 2 + /* INFO_BLOCK_SIZE */ 2);
+ + sizeof nrinfos + 2 * nrinfos);
h->wbuf = &h->sbuf;
h->wlen = sizeof h->sbuf.option;
h->wflags = MSG_MORE;
@@ -57,23 +59,29 @@ STATE_MACHINE {
return 0;
NEWSTYLE.OPT_GO.SEND_EXPORT:
+ uint16_t nrinfos = h->full_info ? 3 : 1;
+
switch (send_from_wbuf (h)) {
case -1: SET_NEXT_STATE (%.DEAD); return 0;
case 0:
- h->sbuf.nrinfos = htobe16 (1);
+ h->sbuf.nrinfos = htobe16 (nrinfos);
h->wbuf = &h->sbuf;
- h->wlen = 2;
+ h->wlen = sizeof h->sbuf.nrinfos;
SET_NEXT_STATE (%SEND_NRINFOS);
}
return 0;
NEWSTYLE.OPT_GO.SEND_NRINFOS:
+ uint16_t nrinfos = h->full_info ? 3 : 1;
+
switch (send_from_wbuf (h)) {
case -1: SET_NEXT_STATE (%.DEAD); return 0;
case 0:
- h->sbuf.info = htobe16 (NBD_INFO_BLOCK_SIZE);
+ h->sbuf.info[0] = htobe16 (NBD_INFO_BLOCK_SIZE);
+ h->sbuf.info[1] = htobe16 (NBD_INFO_NAME);
+ h->sbuf.info[2] = htobe16 (NBD_INFO_DESCRIPTION);
h->wbuf = &h->sbuf;
- h->wlen = 2;
+ h->wlen = sizeof h->sbuf.info[0] * nrinfos;
SET_NEXT_STATE (%SEND_INFO);
}
return 0;
@@ -154,8 +162,37 @@ STATE_MACHINE {
return 0;
}
break;
+ case NBD_INFO_NAME:
+ if (len > sizeof h->sbuf.or.payload.name_desc.info + NBD_MAX_STRING ||
+ len < sizeof h->sbuf.or.payload.name_desc.info) {
+ SET_NEXT_STATE (%.DEAD);
+ set_error (0, "handshake: incorrect NBD_INFO_NAME option reply
length");
+ return 0;
+ }
+ free (h->canonical_name);
+ h->canonical_name = strndup (h->sbuf.or.payload.name_desc.str, len - 2);
+ if (h->canonical_name == NULL) {
+ SET_NEXT_STATE (%.DEAD);
+ set_error (errno, "strndup");
+ return 0;
+ }
+ break;
+ case NBD_INFO_DESCRIPTION:
+ if (len > sizeof h->sbuf.or.payload.name_desc.info + NBD_MAX_STRING ||
+ len < sizeof h->sbuf.or.payload.name_desc.info) {
+ SET_NEXT_STATE (%.DEAD);
+ set_error (0, "handshake: incorrect NBD_INFO_DESCRIPTION option reply
length");
+ return 0;
+ }
+ free (h->description);
+ h->description = strndup (h->sbuf.or.payload.name_desc.str, len - 2);
+ if (h->description == NULL) {
+ SET_NEXT_STATE (%.DEAD);
+ set_error (errno, "strndup");
+ return 0;
+ }
+ break;
default:
- /* XXX Handle other info types, like NBD_INFO_DESCRIPTION */
debug (h, "skipping unknown NBD_REP_INFO type %d",
be16toh (h->sbuf.or.payload.export.info));
break;
diff --git a/lib/handle.c b/lib/handle.c
index 210ac7d..28c30dd 100644
--- a/lib/handle.c
+++ b/lib/handle.c
@@ -136,6 +136,8 @@ nbd_close (struct nbd_handle *h)
free (h->exports[i].description);
}
free (h->exports);
+ free (h->canonical_name);
+ free (h->description);
free_cmd_list (h->cmds_to_issue);
free_cmd_list (h->cmds_in_flight);
free_cmd_list (h->cmds_done);
@@ -232,6 +234,65 @@ nbd_unlocked_get_export_name (struct nbd_handle *h)
return copy;
}
+int
+nbd_unlocked_set_full_info (struct nbd_handle *h, bool request)
+{
+ h->full_info = request;
+ return 0;
+}
+
+int
+nbd_unlocked_get_full_info (struct nbd_handle *h)
+{
+ return h->full_info;
+}
+
+char *
+nbd_unlocked_get_canonical_export_name (struct nbd_handle *h)
+{
+ char *r;
+
+ if (h->eflags == 0) {
+ set_error (EINVAL, "server has not returned export flags, "
+ "you need to connect to the server first");
+ return NULL;
+ }
+ if (h->canonical_name == NULL) {
+ set_error (ENOTSUP, "server did not advertise a canonical name");
+ return NULL;
+ }
+
+ r = strdup (h->canonical_name);
+ if (r == NULL) {
+ set_error (errno, "strdup");
+ return NULL;
+ }
+ return r;
+}
+
+char *
+nbd_unlocked_get_export_description (struct nbd_handle *h)
+{
+ char *r;
+
+ if (h->eflags == 0) {
+ set_error (EINVAL, "server has not returned export flags, "
+ "you need to connect to the server first");
+ return NULL;
+ }
+ if (h->description == NULL) {
+ set_error (ENOTSUP, "server did not advertise a description");
+ return NULL;
+ }
+
+ r = strdup (h->description);
+ if (r == NULL) {
+ set_error (errno, "strdup");
+ return NULL;
+ }
+ return r;
+}
+
int
nbd_unlocked_set_list_exports (struct nbd_handle *h, bool list)
{
--
2.28.0