From a14ec0e1c29740139dbc04104664f104a6cec401 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Mon, 11 Jan 2016 18:50:28 +0000 Subject: [PATCH] Add support for newstyle NBD protocol (RHBZ#1297100). --- 0001-Add-mandir-to-dump-config-output.patch | 39 + 0002-Update-TODO.patch | 35 + ...r-newstyle-NBD-protocol-RHBZ-1297100.patch | 760 ++++++++++++++++++ nbdkit.spec | 12 +- 4 files changed, 845 insertions(+), 1 deletion(-) create mode 100644 0001-Add-mandir-to-dump-config-output.patch create mode 100644 0002-Update-TODO.patch create mode 100644 0003-Add-support-for-newstyle-NBD-protocol-RHBZ-1297100.patch diff --git a/0001-Add-mandir-to-dump-config-output.patch b/0001-Add-mandir-to-dump-config-output.patch new file mode 100644 index 0000000..e55b4c1 --- /dev/null +++ b/0001-Add-mandir-to-dump-config-output.patch @@ -0,0 +1,39 @@ +From 5294b7d8cc011f8b5e068aad744f612df7414a82 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Sat, 31 Oct 2015 13:24:52 +0000 +Subject: [PATCH 1/3] Add mandir to --dump-config output. + +This is useful for external plugins that want to install +a man page. +--- + src/Makefile.am | 1 + + src/main.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/src/Makefile.am b/src/Makefile.am +index c0fb95a..fb46903 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -50,6 +50,7 @@ nbdkit_SOURCES = \ + nbdkit_CPPFLAGS = \ + -Dbindir=\"$(bindir)\" \ + -Dlibdir=\"$(libdir)\" \ ++ -Dmandir=\"$(mandir)\" \ + -Dplugindir=\"$(plugindir)\" \ + -Dsbindir=\"$(sbindir)\" \ + -Dsysconfdir=\"$(sysconfdir)\" \ +diff --git a/src/main.c b/src/main.c +index cd676c0..1248a8e 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -129,6 +129,7 @@ dump_config (void) + { + printf ("%s=%s\n", "bindir", bindir); + printf ("%s=%s\n", "libdir", libdir); ++ printf ("%s=%s\n", "mandir", mandir); + printf ("%s=%s\n", "name", PACKAGE_NAME); + printf ("%s=%s\n", "plugindir", plugindir); + printf ("%s=%s\n", "sbindir", sbindir); +-- +2.5.0 + diff --git a/0002-Update-TODO.patch b/0002-Update-TODO.patch new file mode 100644 index 0000000..e93c237 --- /dev/null +++ b/0002-Update-TODO.patch @@ -0,0 +1,35 @@ +From 4b562134c3dad1a84aa92c1658e72046569e1570 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Mon, 11 Jan 2016 15:34:57 +0000 +Subject: [PATCH 2/3] Update TODO. + +--- + TODO | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/TODO b/TODO +index bcf2276..a05aa5b 100644 +--- a/TODO ++++ b/TODO +@@ -1,6 +1,8 @@ + * There is a proposal to narrow the range of possible errnos that the + server can return, and also to encode them in an OS-independent way. + See: http://article.gmane.org/gmane.linux.drivers.nbd.general/3154 ++ Implemented in QEMU already: ++ http://git.qemu.org/?p=qemu.git;a=commitdiff;h=ca4414804114fd0095b317785bc0b51862e62ebb + + * Can we do language bindings using #!'s? + You would enter: +@@ -25,6 +27,9 @@ + default that accepts all exportnames, or to divide the export name + "space" up using regexps or wildcards. + ++ Annoyingly nbd-client dropped support for the oldstyle protocol (see ++ https://bugzilla.redhat.com/1297100). ++ + * Implement true parallel request handling. Currently + NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS and + NBDKIT_THREAD_MODEL_PARALLEL are the same, because we handle +-- +2.5.0 + diff --git a/0003-Add-support-for-newstyle-NBD-protocol-RHBZ-1297100.patch b/0003-Add-support-for-newstyle-NBD-protocol-RHBZ-1297100.patch new file mode 100644 index 0000000..212f6fa --- /dev/null +++ b/0003-Add-support-for-newstyle-NBD-protocol-RHBZ-1297100.patch @@ -0,0 +1,760 @@ +From f93807114634d58ca2ef0d64f7637ebd87e48a50 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Mon, 11 Jan 2016 17:08:51 +0000 +Subject: [PATCH 3/3] Add support for newstyle NBD protocol (RHBZ#1297100). + +--- + .gitignore | 1 + + TODO | 10 +-- + docs/nbdkit.pod | 67 +++++++++++++- + src/connections.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++-- + src/internal.h | 1 + + src/main.c | 26 ++++-- + src/protocol.h | 49 ++++++++++- + tests/Makefile.am | 8 ++ + tests/test-newstyle.c | 102 ++++++++++++++++++++++ + tests/test.c | 6 +- + tests/test.h | 2 +- + 11 files changed, 483 insertions(+), 24 deletions(-) + create mode 100644 tests/test-newstyle.c + +diff --git a/.gitignore b/.gitignore +index b1a8850..9ea072f 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -43,6 +43,7 @@ Makefile.in + /tests/test-connect + /tests/test-file + /tests/test-gzip ++/tests/test-newstyle + /tests/test-ocaml + /tests/test-ocaml-plugin.so + /tests/test-perl +diff --git a/TODO b/TODO +index a05aa5b..d39e64c 100644 +--- a/TODO ++++ b/TODO +@@ -17,9 +17,9 @@ + + * Performance - measure and improve it. + +-* Implement the new protocol and export names. With export names it +- should be possible to have multiple plugins on the command line +- (each responding to a different export of course): ++* Implement export names. With export names it should be possible to ++ have multiple plugins on the command line (each responding to a ++ different export of course): + + nbdkit --export /foo plugin.so --export /bar another-plugin.so + +@@ -27,8 +27,8 @@ + default that accepts all exportnames, or to divide the export name + "space" up using regexps or wildcards. + +- Annoyingly nbd-client dropped support for the oldstyle protocol (see +- https://bugzilla.redhat.com/1297100). ++ Export names are not actually paths (although that is how they are ++ often used), but arbitrary UTF-8 text strings. + + * Implement true parallel request handling. Currently + NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS and +diff --git a/docs/nbdkit.pod b/docs/nbdkit.pod +index 9ce75d3..7204a38 100644 +--- a/docs/nbdkit.pod ++++ b/docs/nbdkit.pod +@@ -7,8 +7,8 @@ nbdkit - A toolkit for creating NBD servers + =head1 SYNOPSIS + + nbdkit [--dump-config] [-f] [-g GROUP] [-i IPADDR] +- [-P PIDFILE] [-p PORT] [-r] [--run CMD] [-s] +- [-U SOCKET] [-u USER] [-v] [-V] ++ [--newstyle] [--oldstyle] [-P PIDFILE] [-p PORT] [-r] ++ [--run CMD] [-s] [-U SOCKET] [-u USER] [-v] [-V] + PLUGIN [key=value [key=value [...]]] + + =head1 DESCRIPTION +@@ -103,6 +103,25 @@ See also I<-u>. + Listen on the specified interface. The default is to listen on all + interfaces. See also I<-p>. + ++=item B<-n> ++ ++=item B<--new-style> ++ ++=item B<--newstyle> ++ ++Use the newstyle NBD protocol instead of the default (oldstyle) ++protocol. See L below. ++ ++=item B<-o> ++ ++=item B<--old-style> ++ ++=item B<--oldstyle> ++ ++Use the oldstyle NBD protocol. This is currently the default, so this ++flag does nothing, but it is possible we might change the default ++protocol in future. See L below. ++ + =item B<-P> PIDFILE + + =item B<--pid-file> PIDFILE +@@ -280,6 +299,50 @@ Unix socket, like this: + + nbdkit -U - plugin [args] --run '...' + ++=head1 NEW STYLE VS OLD STYLE PROTOCOL ++ ++The NBD protocol comes in two incompatible forms that we call ++"oldstyle" and "newstyle". Unfortunately which protocol you should ++use depends on the client and cannot be known in advance, nor can it ++be negotiated from the server side. ++ ++nbdkit currently defaults to the oldstyle protocol for compatibility ++with qemu and libguestfs. This is also the same behaviour as ++qemu-nbd. Use the I<-n> or I<--newstyle> flag on the command line to ++use the newstyle protocol. Use the I<-o> or I<--oldstyle> flag to ++force the oldstyle protocol. ++ ++Some common clients and the protocol they require: ++ ++ Client Protocol ++ ------------------------------------------------------------ ++ qemu without exportname oldstyle ++ qemu with exportname newstyle ++ nbd-client < 3.10 client can talk either protocol ++ nbd-client >= 3.10 newstyle ++ ++If you use qemu without the exportname field against a newstyle ++server, it will give the error: ++ ++ Server requires an export name ++ ++If you use qemu with the exportname field against an oldstyle server, ++it will give the error: ++ ++ Server does not support export names ++ ++If you use the oldstyle protocol with nbd-client E 3.10, it will ++give the error: ++ ++ Error: It looks like you're trying to connect to an oldstyle server. ++ ++If you want to claim compatibility with what the NBD proto.txt ++document says should be the case (which isn't based in reality), then ++you should always use newstyle when using port 10809, and use oldstyle ++on all other ports. ++ ++nbdkit ignores export names at present (see also the C file). ++ + =head1 SIGNALS + + C responds to the following signals: +diff --git a/src/connections.c b/src/connections.c +index 15f416b..6bdf4ef 100644 +--- a/src/connections.c ++++ b/src/connections.c +@@ -1,5 +1,5 @@ + /* nbdkit +- * Copyright (C) 2013 Red Hat Inc. ++ * Copyright (C) 2013-2016 Red Hat Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without +@@ -52,6 +52,12 @@ + /* Maximum read or write request that we will handle. */ + #define MAX_REQUEST_SIZE (64 * 1024 * 1024) + ++/* Maximum number of client options we allow before giving up. */ ++#define MAX_NR_OPTIONS 32 ++ ++/* Maximum length of any option data (bytes). */ ++#define MAX_OPTION_LENGTH 4096 ++ + static struct connection *new_connection (int sockin, int sockout); + static void free_connection (struct connection *conn); + static int negotiate_handshake (struct connection *conn); +@@ -143,11 +149,8 @@ free_connection (struct connection *conn) + free (conn); + } + +-/* XXX Note because we don't support multiple plugins or export names, +- * we are using the old-style handshake. This will be fixed. +- */ + static int +-_negotiate_handshake (struct connection *conn) ++_negotiate_handshake_oldstyle (struct connection *conn) + { + struct old_handshake handshake; + int64_t r; +@@ -201,7 +204,8 @@ _negotiate_handshake (struct connection *conn) + conn->can_trim = 1; + } + +- debug ("flags: global 0x%x export 0x%x", gflags, eflags); ++ debug ("oldstyle negotiation: flags: global 0x%x export 0x%x", ++ gflags, eflags); + + memset (&handshake, 0, sizeof handshake); + memcpy (handshake.nbdmagic, "NBDMAGIC", 8); +@@ -218,13 +222,230 @@ _negotiate_handshake (struct connection *conn) + return 0; + } + ++/* Receive newstyle options. ++ * ++ * Currently we ignore NBD_OPT_EXPORT_NAME (see TODO), we close the ++ * connection if sent NBD_OPT_ABORT, we send a canned list of ++ * options for NBD_OPT_LIST, and we send NBD_REP_ERR_UNSUP for ++ * everything else. ++ */ ++static int ++send_newstyle_option_reply (struct connection *conn, ++ uint32_t option, uint32_t reply) ++{ ++ struct fixed_new_option_reply fixed_new_option_reply; ++ ++ fixed_new_option_reply.magic = htobe64 (NEW_OPTION_REPLY); ++ fixed_new_option_reply.option = htobe32 (option); ++ fixed_new_option_reply.reply = htobe32 (reply); ++ fixed_new_option_reply.replylen = htobe32 (0); ++ ++ if (xwrite (conn->sockout, ++ &fixed_new_option_reply, sizeof fixed_new_option_reply) == -1) { ++ nbdkit_error ("write: %m"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ++_negotiate_handshake_newstyle_options (struct connection *conn) ++{ ++ struct new_option new_option; ++ size_t nr_options; ++ uint64_t version; ++ uint32_t option; ++ uint32_t optlen; ++ char data[MAX_OPTION_LENGTH+1]; ++ ++ for (nr_options = 0; nr_options < MAX_NR_OPTIONS; ++nr_options) { ++ if (xread (conn->sockin, &new_option, sizeof new_option) == -1) { ++ nbdkit_error ("read: %m"); ++ return -1; ++ } ++ ++ version = be64toh (new_option.version); ++ if (version != NEW_VERSION) { ++ nbdkit_error ("unknown option version %" PRIx64 ++ ", expecting %" PRIx64, ++ version, NEW_VERSION); ++ return -1; ++ } ++ ++ /* There is a maximum option length we will accept, regardless ++ * of the option type. ++ */ ++ optlen = be32toh (new_option.optlen); ++ if (optlen > MAX_OPTION_LENGTH) { ++ nbdkit_error ("client option data too long (%" PRIu32 ")", optlen); ++ return -1; ++ } ++ ++ option = be32toh (new_option.option); ++ switch (option) { ++ case NBD_OPT_EXPORT_NAME: ++ if (xread (conn->sockin, data, optlen) == -1) { ++ nbdkit_error ("read: %m"); ++ return -1; ++ } ++ /* Apart from printing it, ignore the export name. */ ++ data[optlen] = '\0'; ++ debug ("newstyle negotiation: client requested export '%s' (ignored)", ++ data); ++ break; ++ ++ case NBD_OPT_ABORT: ++ if (send_newstyle_option_reply (conn, option, NBD_REP_ACK) == -1) ++ return -1; ++ nbdkit_error ("client sent NBD_OPT_ABORT to abort the connection"); ++ return -1; ++ ++ case NBD_OPT_LIST: ++ if (optlen != 0) { ++ if (send_newstyle_option_reply (conn, option, NBD_REP_ERR_INVALID) ++ == -1) ++ return -1; ++ continue; ++ } ++ ++ /* Since we don't support export names, there is nothing to list. */ ++ if (send_newstyle_option_reply (conn, option, NBD_REP_ACK) == -1) ++ return -1; ++ break; ++ ++ default: ++ /* Unknown option. */ ++ if (send_newstyle_option_reply (conn, option, NBD_REP_ERR_UNSUP) == -1) ++ return -1; ++ } ++ ++ /* Note, since it's not very clear from the protocol doc, that the ++ * client must send NBD_OPT_EXPORT_NAME last, and that ends option ++ * negotiation. ++ */ ++ if (option == NBD_OPT_EXPORT_NAME) ++ break; ++ } ++ ++ if (nr_options >= MAX_NR_OPTIONS) { ++ nbdkit_error ("client exceeded maximum number of options (%d)", ++ MAX_NR_OPTIONS); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ++_negotiate_handshake_newstyle (struct connection *conn) ++{ ++ struct new_handshake handshake; ++ uint16_t gflags; ++ uint32_t cflags; ++ struct new_handshake_finish handshake_finish; ++ int64_t r; ++ uint64_t exportsize; ++ uint16_t eflags; ++ int fl; ++ ++ gflags = NBD_FLAG_FIXED_NEWSTYLE; ++ ++ debug ("newstyle negotiation: flags: global 0x%x", gflags); ++ ++ memcpy (handshake.nbdmagic, "NBDMAGIC", 8); ++ handshake.version = htobe64 (NEW_VERSION); ++ handshake.gflags = htobe16 (gflags); ++ ++ if (xwrite (conn->sockout, &handshake, sizeof handshake) == -1) { ++ nbdkit_error ("write: %m"); ++ return -1; ++ } ++ ++ /* Client now sends us its 32 bit flags word ... */ ++ if (xread (conn->sockin, &cflags, sizeof cflags) == -1) { ++ nbdkit_error ("read: %m"); ++ return -1; ++ } ++ cflags = be32toh (cflags); ++ /* ... which other than printing out, we ignore. */ ++ debug ("newstyle negotiation: client flags: 0x%x", cflags); ++ ++ /* Receive newstyle options. */ ++ if (_negotiate_handshake_newstyle_options (conn) == -1) ++ return -1; ++ ++ /* Finish the newstyle handshake. */ ++ r = plugin_get_size (conn); ++ if (r == -1) ++ return -1; ++ if (r < 0) { ++ nbdkit_error (".get_size function returned invalid value " ++ "(%" PRIi64 ")", r); ++ return -1; ++ } ++ exportsize = (uint64_t) r; ++ conn->exportsize = exportsize; ++ ++ eflags = NBD_FLAG_HAS_FLAGS; ++ ++ fl = plugin_can_write (conn); ++ if (fl == -1) ++ return -1; ++ if (readonly || !fl) { ++ eflags |= NBD_FLAG_READ_ONLY; ++ conn->readonly = 1; ++ } ++ ++ fl = plugin_can_flush (conn); ++ if (fl == -1) ++ return -1; ++ if (fl) { ++ eflags |= NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA; ++ conn->can_flush = 1; ++ } ++ ++ fl = plugin_is_rotational (conn); ++ if (fl == -1) ++ return -1; ++ if (fl) { ++ eflags |= NBD_FLAG_ROTATIONAL; ++ conn->is_rotational = 1; ++ } ++ ++ fl = plugin_can_trim (conn); ++ if (fl == -1) ++ return -1; ++ if (fl) { ++ eflags |= NBD_FLAG_SEND_TRIM; ++ conn->can_trim = 1; ++ } ++ ++ debug ("newstyle negotiation: flags: export 0x%x", eflags); ++ ++ memset (&handshake_finish, 0, sizeof handshake_finish); ++ handshake_finish.exportsize = htobe64 (exportsize); ++ handshake_finish.eflags = htobe16 (eflags); ++ ++ if (xwrite (conn->sockout, ++ &handshake_finish, sizeof handshake_finish) == -1) { ++ nbdkit_error ("write: %m"); ++ return -1; ++ } ++ ++ return 0; ++} ++ + static int + negotiate_handshake (struct connection *conn) + { + int r; + + plugin_lock_request (conn); +- r = _negotiate_handshake (conn); ++ if (!newstyle) ++ r = _negotiate_handshake_oldstyle (conn); ++ else ++ r = _negotiate_handshake_newstyle (conn); + plugin_unlock_request (conn); + + return r; +diff --git a/src/internal.h b/src/internal.h +index c834b14..0603779 100644 +--- a/src/internal.h ++++ b/src/internal.h +@@ -49,6 +49,7 @@ + + /* main.c */ + extern const char *ipaddr; ++extern int newstyle; + extern const char *port; + extern int readonly; + extern char *unixsocket; +diff --git a/src/main.c b/src/main.c +index 1248a8e..db4361e 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -1,5 +1,5 @@ + /* nbdkit +- * Copyright (C) 2013-2014 Red Hat Inc. ++ * Copyright (C) 2013-2016 Red Hat Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without +@@ -67,11 +67,12 @@ static gid_t parsegroup (const char *); + + int foreground; /* -f */ + const char *ipaddr; /* -i */ +-int listen_stdin; /* -s */ ++int newstyle; /* -n */ + char *pidfile; /* -P */ + const char *port; /* -p */ + int readonly; /* -r */ + char *run; /* --run */ ++int listen_stdin; /* -s */ + char *unixsocket; /* -U */ + const char *user, *group; /* -u & -g */ + int verbose; /* -v */ +@@ -83,7 +84,7 @@ static char *random_fifo = NULL; + + enum { HELP_OPTION = CHAR_MAX + 1 }; + +-static const char *short_options = "fg:i:p:P:rsu:U:vV"; ++static const char *short_options = "fg:i:nop:P:rsu:U:vV"; + static const struct option long_options[] = { + { "help", 0, NULL, HELP_OPTION }, + { "dump-config",0, NULL, 0 }, +@@ -92,6 +93,10 @@ static const struct option long_options[] = { + { "group", 1, NULL, 'g' }, + { "ip-addr", 1, NULL, 'i' }, + { "ipaddr", 1, NULL, 'i' }, ++ { "new-style", 1, NULL, 'n' }, ++ { "newstyle", 1, NULL, 'n' }, ++ { "old-style", 1, NULL, 'o' }, ++ { "oldstyle", 1, NULL, 'o' }, + { "pid-file", 1, NULL, 'P' }, + { "pidfile", 1, NULL, 'P' }, + { "port", 1, NULL, 'p' }, +@@ -111,8 +116,8 @@ static void + usage (void) + { + printf ("nbdkit [--dump-config] [-f] [-g GROUP] [-i IPADDR]\n" +- " [-P PIDFILE] [-p PORT] [-r] [--run CMD] [-s]\n" +- " [-U SOCKET] [-u USER] [-v] [-V]\n" ++ " [--newstyle] [--oldstyle] [-P PIDFILE] [-p PORT] [-r]\n" ++ " [--run CMD] [-s] [-U SOCKET] [-u USER] [-v] [-V]\n" + " PLUGIN [key=value [key=value [...]]]\n" + "\n" + "Please read the nbdkit(1) manual page for full usage.\n"); +@@ -180,6 +185,17 @@ main (int argc, char *argv[]) + ipaddr = optarg; + break; + ++ case 'n': ++ newstyle = 1; ++ break; ++ ++ case 'o': ++ /* XXX When we add support for exportnames, we will need to ++ * ensure that the user does not use -o + --export. ++ */ ++ newstyle = 0; ++ break; ++ + case 'P': + pidfile = nbdkit_absolute_path (optarg); + if (pidfile == NULL) +diff --git a/src/protocol.h b/src/protocol.h +index 4449171..2f9a341 100644 +--- a/src/protocol.h ++++ b/src/protocol.h +@@ -36,7 +36,7 @@ + + #include + +-/* Old-style handshake */ ++/* Old-style handshake. */ + struct old_handshake { + char nbdmagic[8]; /* "NBDMAGIC" */ + uint64_t version; /* OLD_VERSION, in network byte order */ +@@ -48,6 +48,41 @@ struct old_handshake { + + #define OLD_VERSION UINT64_C(0x420281861253) + ++/* New-style handshake. */ ++struct new_handshake { ++ char nbdmagic[8]; /* "NBDMAGIC" */ ++ uint64_t version; /* NEW_VERSION, in network byte order */ ++ uint16_t gflags; /* global flags, in network byte order */ ++} __attribute__((packed)); ++ ++#define NEW_VERSION UINT64_C(0x49484156454F5054) ++ ++/* New-style handshake option (sent by the client to us). */ ++struct new_option { ++ uint64_t version; /* NEW_VERSION, in network byte order */ ++ uint32_t option; /* NBD_OPT_* */ ++ uint32_t optlen; /* option data length */ ++ /* option data follows */ ++} __attribute__((packed)); ++ ++/* Fixed newstyle handshake reply message. */ ++struct fixed_new_option_reply { ++ uint64_t magic; /* NEW_OPTION_REPLY, network byte order */ ++ uint32_t option; /* option we are replying to */ ++ uint32_t reply; /* NBD_REP_* */ ++ uint32_t replylen; /* we always send zero at the moment */ ++ /* reply data follows, but we currently never send any */ ++}; ++ ++#define NEW_OPTION_REPLY UINT64_C(0x3e889045565a9) ++ ++/* New-style handshake server reply. */ ++struct new_handshake_finish { ++ uint64_t exportsize; /* in network byte order */ ++ uint16_t eflags; /* per-export flags, in network byte order */ ++ char zeroes[124]; /* must be sent as zero bytes */ ++} __attribute__((packed)); ++ + /* Global flags. */ + #define NBD_FLAG_FIXED_NEWSTYLE 1 + +@@ -59,6 +94,18 @@ struct old_handshake { + #define NBD_FLAG_ROTATIONAL 16 + #define NBD_FLAG_SEND_TRIM 32 + ++/* NBD options (new style handshake only). */ ++#define NBD_OPT_EXPORT_NAME 1 ++#define NBD_OPT_ABORT 2 ++#define NBD_OPT_LIST 3 ++ ++#define NBD_REP_ACK 1 ++#define NBD_REP_SERVER 2 ++#define NBD_REP_ERR_UNSUP 0x80000001 ++#define NBD_REP_ERR_POLICY 0x80000002 ++#define NBD_REP_ERR_INVALID 0x80000003 ++#define NBD_REP_ERR_PLATFORM 0x80000004 ++ + /* Request (client -> server). */ + struct request { + uint32_t magic; /* NBD_REQUEST_MAGIC. */ +diff --git a/tests/Makefile.am b/tests/Makefile.am +index 511c39c..8d27032 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -103,6 +103,14 @@ file-data: + for f in `seq 1 512`; do echo -ne '\x01\x02\x03\x04\x05\x06\x07\x08'; done > $@-t + mv $@-t $@ + ++# newstyle protocol test. ++check_PROGRAMS += test-newstyle ++TESTS += test-newstyle ++ ++test_newstyle_SOURCES = test-newstyle.c test.h ++test_newstyle_CFLAGS = $(WARNINGS_CFLAGS) $(LIBGUESTFS_CFLAGS) ++test_newstyle_LDADD = libtest.la $(LIBGUESTFS_LIBS) ++ + # gzip plugin test. + if HAVE_ZLIB + if HAVE_GUESTFISH +diff --git a/tests/test-newstyle.c b/tests/test-newstyle.c +new file mode 100644 +index 0000000..b1d8ce7 +--- /dev/null ++++ b/tests/test-newstyle.c +@@ -0,0 +1,102 @@ ++/* nbdkit ++ * Copyright (C) 2013-2016 Red Hat Inc. ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * * Neither the name of Red Hat nor the names of its contributors may be ++ * used to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A ++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ++ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ++ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "test.h" ++ ++int ++main (int argc, char *argv[]) ++{ ++ guestfs_h *g; ++ int r; ++ char *data; ++ size_t i, size; ++ ++ if (test_start_nbdkit ("-n", NBDKIT_PLUGIN ("file"), "file=file-data", ++ NULL) == -1) ++ exit (EXIT_FAILURE); ++ ++ g = guestfs_create (); ++ if (g == NULL) { ++ perror ("guestfs_create"); ++ exit (EXIT_FAILURE); ++ } ++ ++ /* Using any exportname causes qemu to use the newstyle protocol. */ ++ r = guestfs_add_drive_opts (g, "/" /* exportname */, ++ GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", ++ GUESTFS_ADD_DRIVE_OPTS_PROTOCOL, "nbd", ++ GUESTFS_ADD_DRIVE_OPTS_SERVER, server, ++ -1); ++ if (r == -1) ++ exit (EXIT_FAILURE); ++ ++ if (guestfs_launch (g) == -1) ++ exit (EXIT_FAILURE); ++ ++ /* Check the data in the file is \x01-\x08 repeated 512 times. */ ++ data = guestfs_pread_device (g, "/dev/sda", 8 * 512, 0, &size); ++ if (!data) ++ exit (EXIT_FAILURE); ++ if (size != 8 * 512) { ++ fprintf (stderr, "%s FAILED: unexpected size (actual: %zu, expected: 512)\n", ++ program_name, size); ++ exit (EXIT_FAILURE); ++ } ++ ++ for (i = 0; i < 512 * 8; i += 8) { ++ if (data[i] != 1 || data[i+1] != 2 || ++ data[i+2] != 3 || data[i+3] != 4 || ++ data[i+4] != 5 || data[i+5] != 6 || ++ data[i+6] != 7 || data[i+7] != 8) { ++ fprintf (stderr, "%s FAILED: unexpected data returned at offset %zu\n", ++ program_name, i); ++ exit (EXIT_FAILURE); ++ } ++ } ++ ++ free (data); ++ ++ guestfs_close (g); ++ exit (EXIT_SUCCESS); ++} +diff --git a/tests/test.c b/tests/test.c +index 55d13fd..111e6fa 100644 +--- a/tests/test.c ++++ b/tests/test.c +@@ -68,7 +68,7 @@ cleanup (void) + } + + int +-test_start_nbdkit (const char *plugin, ...) ++test_start_nbdkit (const char *arg, ...) + { + size_t i, len; + +@@ -95,10 +95,10 @@ test_start_nbdkit (const char *plugin, ...) + argv[4] = pidpath; + argv[5] = "-f"; + argv[6] = "-v"; +- argv[7] = plugin; ++ argv[7] = arg; + i = 8; + +- va_start (args, plugin); ++ va_start (args, arg); + while ((p = va_arg (args, const char *)) != NULL) { + if (i >= MAX_ARGS) + abort (); +diff --git a/tests/test.h b/tests/test.h +index 7abf5af..c369150 100644 +--- a/tests/test.h ++++ b/tests/test.h +@@ -45,7 +45,7 @@ + extern pid_t pid; /* PID of nbdkit process. */ + extern const char *server[2]; /* server parameter for add_drive */ + +-extern int test_start_nbdkit (const char *plugin, ...); ++extern int test_start_nbdkit (const char *arg, ...); + + /* Declare program_name. */ + #if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME == 1 +-- +2.5.0 + diff --git a/nbdkit.spec b/nbdkit.spec index f071ba5..e6bc020 100644 --- a/nbdkit.spec +++ b/nbdkit.spec @@ -6,13 +6,19 @@ Name: nbdkit Version: 1.1.11 -Release: 1%{?dist} +Release: 2%{?dist} Summary: NBD server License: BSD URL: https://github.com/libguestfs/nbdkit + Source0: http://libguestfs.org/download/nbdkit/%{name}-%{version}.tar.gz +# All patches are upstream since 1.1.11. +Patch1: 0001-Add-mandir-to-dump-config-output.patch +Patch2: 0002-Update-TODO.patch +Patch3: 0003-Add-support-for-newstyle-NBD-protocol-RHBZ-1297100.patch + BuildRequires: /usr/bin/pod2man %if 0%{?have_libguestfs} BuildRequires: libguestfs-devel @@ -216,6 +222,7 @@ plugins for %{name}. %prep %setup -q +%autopatch -p1 %build @@ -351,6 +358,9 @@ make check %changelog +* Mon Jan 11 2016 Richard W.M. Jones - 1.1.11-2 +- Add support for newstyle NBD protocol (RHBZ#1297100). + * Sat Oct 31 2015 Richard W.M. Jones - 1.1.11-1 - New upstream version 1.1.11.