virStorageSourceParseNBDColonString: Rewrite to match what qemu does

Our implementation wasn't quite able to parse everything that qemu does.
This patch rewrites the parser to a code that semantically resembles the
combination of 'nbd_parse_filename' and 'inet_parse' methods in qemu to
be able to parse the strings in an equivalent manner.

The only thing that libvirt doesn't do is to check the lengths of
various components in the nbd string in places where qemu uses constant
size buffers.

The test cases validate that some of the corner cases involving colons
are parsed properly.

https://bugzilla.redhat.com/show_bug.cgi?id=1826652

Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
Peter Krempa 2020-04-24 13:59:21 +02:00
parent 6ebb00cd78
commit f8d6b319a6
2 changed files with 74 additions and 40 deletions

View File

@ -3072,59 +3072,77 @@ static int
virStorageSourceParseNBDColonString(const char *nbdstr, virStorageSourceParseNBDColonString(const char *nbdstr,
virStorageSourcePtr src) virStorageSourcePtr src)
{ {
VIR_AUTOSTRINGLIST backing = NULL; g_autofree char *nbd = g_strdup(nbdstr);
const char *exportname; char *export_name;
char *host_spec;
if (!(backing = virStringSplit(nbdstr, ":", 0))) char *unixpath;
return -1; char *port;
/* we know that backing[0] now equals to "nbd" */
if (VIR_ALLOC_N(src->hosts, 1) < 0)
return -1;
src->hosts = g_new0(virStorageNetHostDef, 1);
src->nhosts = 1; src->nhosts = 1;
src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
/* We extract the parameters in a similar way qemu does it */
/* format: [] denotes optional sections, uppercase are variable strings /* format: [] denotes optional sections, uppercase are variable strings
* nbd:unix:/PATH/TO/SOCKET[:exportname=EXPORTNAME] * nbd:unix:/PATH/TO/SOCKET[:exportname=EXPORTNAME]
* nbd:HOSTNAME:PORT[:exportname=EXPORTNAME] * nbd:HOSTNAME:PORT[:exportname=EXPORTNAME]
*/ */
if (!backing[1]) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("missing remote information in '%s' for protocol nbd"),
nbdstr);
return -1;
} else if (STREQ(backing[1], "unix")) {
if (!backing[2]) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("missing unix socket path in nbd backing string %s"),
nbdstr);
return -1;
}
src->hosts->socket = g_strdup(backing[2]); /* first look for ':exportname=' and cut it off */
src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_UNIX; if ((export_name = strstr(nbd, ":exportname="))) {
} else { src->path = g_strdup(export_name + strlen(":exportname="));
src->hosts->name = g_strdup(backing[1]); export_name[0] = '\0';
if (!backing[2]) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("missing port in nbd string '%s'"),
nbdstr);
return -1;
}
if (virStringParsePort(backing[2], &src->hosts->port) < 0)
return -1;
} }
if ((exportname = strstr(nbdstr, "exportname="))) { /* Verify the prefix and contents. Note that we require a
exportname += strlen("exportname="); * "host_spec" part to be present. */
src->path = g_strdup(exportname); if (!(host_spec = STRSKIP(nbd, "nbd:")) || host_spec[0] == '\0')
goto malformed;
if ((unixpath = STRSKIP(host_spec, "unix:"))) {
src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_UNIX;
if (unixpath[0] == '\0')
goto malformed;
src->hosts->socket = g_strdup(unixpath);
} else {
src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
if (host_spec[0] == ':') {
/* no host given */
goto malformed;
} else if (host_spec[0] == '[') {
host_spec++;
/* IPv6 addr */
if (!(port = strstr(host_spec, "]:")))
goto malformed;
port[0] = '\0';
port += 2;
if (host_spec[0] == '\0')
goto malformed;
} else {
if (!(port = strchr(host_spec, ':')))
goto malformed;
port[0] = '\0';
port++;
}
if (virStringParsePort(port, &src->hosts->port) < 0)
return -1;
src->hosts->name = g_strdup(host_spec);
} }
return 0; return 0;
malformed:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("malformed nbd string '%s'"), nbdstr);
return -1;
} }

View File

@ -1261,10 +1261,26 @@ mymain(void)
"<source protocol='nbd' name=':test'>\n" "<source protocol='nbd' name=':test'>\n"
" <host name='example.org' port='6000'/>\n" " <host name='example.org' port='6000'/>\n"
"</source>\n"); "</source>\n");
TEST_BACKING_PARSE("nbd:[::1]:6000:exportname=:test",
"<source protocol='nbd' name=':test'>\n"
" <host name='::1' port='6000'/>\n"
"</source>\n");
TEST_BACKING_PARSE("nbd:127.0.0.1:6000:exportname=:test",
"<source protocol='nbd' name=':test'>\n"
" <host name='127.0.0.1' port='6000'/>\n"
"</source>\n");
TEST_BACKING_PARSE("nbd:unix:/tmp/sock:exportname=/", TEST_BACKING_PARSE("nbd:unix:/tmp/sock:exportname=/",
"<source protocol='nbd' name='/'>\n" "<source protocol='nbd' name='/'>\n"
" <host transport='unix' socket='/tmp/sock'/>\n" " <host transport='unix' socket='/tmp/sock'/>\n"
"</source>\n"); "</source>\n");
TEST_BACKING_PARSE("nbd:unix:/tmp/sock:",
"<source protocol='nbd'>\n"
" <host transport='unix' socket='/tmp/sock:'/>\n"
"</source>\n");
TEST_BACKING_PARSE("nbd:unix:/tmp/sock::exportname=:",
"<source protocol='nbd' name=':'>\n"
" <host transport='unix' socket='/tmp/sock:'/>\n"
"</source>\n");
TEST_BACKING_PARSE("nbd://example.org:1234", TEST_BACKING_PARSE("nbd://example.org:1234",
"<source protocol='nbd'>\n" "<source protocol='nbd'>\n"
" <host name='example.org' port='1234'/>\n" " <host name='example.org' port='1234'/>\n"