Skip to content

Commit 3c81389

Browse files
committed
Allow disabling the NBD_OPT_GO protocol option
For backcompat with nbd-server before commit 2ab3a2d Fixes: gh-66
1 parent 684422b commit 3c81389

File tree

2 files changed

+72
-16
lines changed

2 files changed

+72
-16
lines changed

man/nbd-client.8.in.sgml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,41 @@ manpage.1: manpage.sgml
311311
</para>
312312
</listitem>
313313
</varlistentry>
314+
<varlistentry>
315+
<term><option>-no-optgo</option></term>
316+
<term><option>-g</option></term>
317+
<listitem>
318+
<para>Disable the use of the NBD_OPT_GO protocol message, and
319+
force the use of NBD_OPT_EXPORT_NAME instead.</para>
320+
<para>
321+
The NBD protocol has two phases: the negotiation phase, and
322+
the transmission phase. To move from negotation to
323+
transmission, older clients sent the NBD_OPT_EXPORT_NAME
324+
message, for which the server could not produce an error
325+
message in case the export name did not exist (or the client
326+
had insufficient permissions to access it). Due to those
327+
limitations, a replacement message NBD_OPT_GO was created
328+
instead, which allows the server to reply with an error in
329+
case of any problems.</para>
330+
<para>
331+
The protocol allows for a server to discard a message which
332+
it does not understand; however, unfortunately some
333+
implementations (including older versions of nbd-server) did
334+
not handle that situation correctly and would get out of
335+
sync with the client when it sent a message which the server
336+
did not understand.</para>
337+
<para>
338+
When sending NBD_OPT_GO, nbd-client will try to do the right
339+
thing and fall back to NBD_OPT_EXPORT_NAME. However, when
340+
the server has the above-described bug, then this does not
341+
work. In such a situation, the client will issue a
342+
diagnostic suggesting the use of this option.
343+
</para>
344+
<para>
345+
Note that there is a corresponding option for nbdtab, too.
346+
</para>
347+
</listitem>
348+
</varlistentry>
314349
<varlistentry>
315350
<term><option>-name</option></term>
316351
<term><option>-N</option></term>

nbd-client.c

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,20 @@ void parse_sizes(char *buf, uint64_t *size, uint16_t *flags) {
461461
printf("\n");
462462
}
463463

464-
void negotiate(int *sockp, u64 *rsize64, uint16_t *flags, char* name, uint32_t needed_flags, uint32_t client_flags, uint32_t do_opts, char *certfile, char *keyfile, char *cacertfile, char *tlshostname, bool tls) {
464+
void send_opt_exportname(int sock, u64 *rsize64, uint16_t *flags, bool can_opt_go, char* name, uint16_t global_flags) {
465+
send_request(sock, NBD_OPT_EXPORT_NAME, -1, name);
466+
char b[sizeof(*flags) + sizeof(*rsize64)];
467+
if(readit(sock, b, sizeof(b)) < 0 && can_opt_go) {
468+
err("E: server does not support NBD_OPT_GO and dropped connection after sending NBD_OPT_EXPORT_NAME. Try -g.");
469+
}
470+
parse_sizes(b, rsize64, flags);
471+
if(!global_flags & NBD_FLAG_NO_ZEROES) {
472+
char buf[125];
473+
readit(sock, buf, 124);
474+
}
475+
}
476+
477+
void negotiate(int *sockp, u64 *rsize64, uint16_t *flags, char* name, uint32_t needed_flags, uint32_t client_flags, uint32_t do_opts, char *certfile, char *keyfile, char *cacertfile, char *tlshostname, bool tls, bool can_opt_go) {
465478
u64 magic;
466479
uint16_t tmp;
467480
uint16_t global_flags;
@@ -594,25 +607,24 @@ void negotiate(int *sockp, u64 *rsize64, uint16_t *flags, char* name, uint32_t n
594607
exit(EXIT_SUCCESS);
595608
}
596609

597-
send_info_request(sock, NBD_OPT_GO, 0, NULL, name);
598-
599610
struct reply *rep = NULL;
600611

612+
if(!can_opt_go) {
613+
send_opt_exportname(sock, rsize64, flags, can_opt_go, name, global_flags);
614+
return;
615+
}
616+
617+
send_info_request(sock, NBD_OPT_GO, 0, NULL, name);
618+
601619
do {
602620
if(rep != NULL) free(rep);
603621
rep = read_reply(sock);
604-
if(rep->reply_type & NBD_REP_FLAG_ERROR) {
622+
if(rep && (rep->reply_type & NBD_REP_FLAG_ERROR)) {
605623
switch(rep->reply_type) {
606624
case NBD_REP_ERR_UNSUP:
607625
/* server doesn't support NBD_OPT_GO or NBD_OPT_INFO,
608626
* fall back to NBD_OPT_EXPORT_NAME */
609-
send_request(sock, NBD_OPT_EXPORT_NAME, -1, name);
610-
char b[sizeof(*flags) + sizeof(*rsize64)];
611-
readit(sock, b, sizeof(b));
612-
parse_sizes(b, rsize64, flags);
613-
if(!(global_flags & NBD_FLAG_NO_ZEROES)) {
614-
readit(sock, buf, 124);
615-
}
627+
send_opt_exportname(sock, rsize64, flags, can_opt_go, name, global_flags);
616628
free(rep);
617629
return;
618630
case NBD_REP_ERR_POLICY:
@@ -654,13 +666,13 @@ void negotiate(int *sockp, u64 *rsize64, uint16_t *flags, char* name, uint32_t n
654666
case NBD_REP_ACK:
655667
break;
656668
default:
657-
err_nonfatal("Unknown reply to NBD_OPT_GO received");
669+
err_nonfatal("Unknown reply to NBD_OPT_GO received, ignoring");
658670
}
659671
} while(rep->reply_type != NBD_REP_ACK);
660672
free(rep);
661673
}
662674

663-
bool get_from_config(char* cfgname, char** name_ptr, char** dev_ptr, char** hostn_ptr, int* bs, int* timeout, int* persist, int* swap, int* sdp, int* b_unix, char**port, int* num_conns, char **certfile, char **keyfile, char **cacertfile, char **tlshostname) {
675+
bool get_from_config(char* cfgname, char** name_ptr, char** dev_ptr, char** hostn_ptr, int* bs, int* timeout, int* persist, int* swap, int* sdp, int* b_unix, char**port, int* num_conns, char **certfile, char **keyfile, char **cacertfile, char **tlshostname, bool can_opt_go) {
664676
int fd = open(SYSCONFDIR "/nbdtab", O_RDONLY);
665677
bool retval = false;
666678
if(fd < 0) {
@@ -769,6 +781,10 @@ bool get_from_config(char* cfgname, char** name_ptr, char** dev_ptr, char** host
769781
*tlshostname = strndup(loc+9, strcspn(loc+9, ","));
770782
goto next;
771783
}
784+
if(!strncmp(loc, "no_optgo", 8)) {
785+
*can_opt_go = false;
786+
goto next;
787+
}
772788
// skip unknown options, with a warning unless they start with a '_'
773789
l = strcspn(loc, ",");
774790
if(*loc != '_') {
@@ -918,7 +934,7 @@ void disconnect(char* device) {
918934
#if HAVE_NETLINK
919935
static const char *short_opts = "-A:b:c:C:d:H:hK:LlnN:pSst:uVx";
920936
#else
921-
static const char *short_opts = "-A:b:c:C:d:H:hK:lnN:pSst:uVx";
937+
static const char *short_opts = "-A:b:c:C:d:gH:hK:lnN:pSst:uVx";
922938
#endif
923939

924940
int main(int argc, char *argv[]) {
@@ -958,6 +974,7 @@ int main(int argc, char *argv[]) {
958974
{ "check", required_argument, NULL, 'c' },
959975
{ "connections", required_argument, NULL, 'C'},
960976
{ "disconnect", required_argument, NULL, 'd' },
977+
{ "no-optgo", no_argument, NULL, 'g' },
961978
{ "help", no_argument, NULL, 'h' },
962979
{ "list", no_argument, NULL, 'l' },
963980
{ "name", required_argument, NULL, 'N' },
@@ -980,6 +997,7 @@ int main(int argc, char *argv[]) {
980997
{ 0, 0, 0, 0 },
981998
};
982999
int i;
1000+
bool can_opt_go = true;
9831001

9841002
logging(MY_NAME);
9851003

@@ -1043,6 +1061,9 @@ int main(int argc, char *argv[]) {
10431061
need_disconnect = 1;
10441062
nbddev = strdup(optarg);
10451063
break;
1064+
case 'g':
1065+
can_opt_go = false;
1066+
break;
10461067
case 'h':
10471068
usage(NULL);
10481069
exit(EXIT_SUCCESS);
@@ -1179,7 +1200,7 @@ int main(int argc, char *argv[]) {
11791200
if (sock < 0)
11801201
exit(EXIT_FAILURE);
11811202

1182-
negotiate(&sock, &size64, &flags, name, needed_flags, cflags, opts, certfile, keyfile, cacertfile, tlshostname, tls);
1203+
negotiate(&sock, &size64, &flags, name, needed_flags, cflags, opts, certfile, keyfile, cacertfile, tlshostname, tls, can_opt_go);
11831204
if (netlink) {
11841205
sockfds[i] = sock;
11851206
continue;
@@ -1293,7 +1314,7 @@ int main(int argc, char *argv[]) {
12931314
nbd = open(nbddev, O_RDWR);
12941315
if (nbd < 0)
12951316
err("Cannot open NBD: %m");
1296-
negotiate(&sock, &new_size, &new_flags, name, needed_flags, cflags, opts, certfile, keyfile, cacertfile, tlshostname, tls);
1317+
negotiate(&sock, &new_size, &new_flags, name, needed_flags, cflags, opts, certfile, keyfile, cacertfile, tlshostname, tls, can_opt_go);
12971318
if (size64 != new_size) {
12981319
err("Size of the device changed. Bye");
12991320
}

0 commit comments

Comments
 (0)