[PATCH 00/12] virStorageSource cleanups part 1

Peter Krempa (12): virStorageSourceGetBackingStoreStr: Move the function earlier virStorageSourceGetBackingStoreStr: Return relative paths only util: virstoragefile: Move virStorageIs[File|Relative] to storage_source storage_source: Move backing store parsers into new file virStorageSourceGetMetadata: Refactor cleanup virt-aa-helper: Use proper check for empty disk in 'get_files' virt-aa-helper: Don't probe image metadata for terminated chains virStorageSourceChainLookup: Don't break error message strings test: storage: Remove double testing in testStorageLookup virStorageSourceChainLookup: Handle names like 'vda[4]' internally tests: storage: Replace index testing in testStorageLookup util: Remove unused 'virStorageFileParseChainIndex' po/POTFILES.in | 1 + src/libvirt_private.syms | 5 +- src/qemu/qemu_block.c | 6 +- src/qemu/qemu_driver.c | 19 +- src/qemu/qemu_snapshot.c | 13 +- src/security/virt-aa-helper.c | 4 +- src/storage_file/meson.build | 1 + src/storage_file/storage_source.c | 1681 +++-------------- src/storage_file/storage_source.h | 6 +- .../storage_source_backingstore.c | 1240 ++++++++++++ .../storage_source_backingstore.h | 40 + src/util/virstoragefile.c | 66 - src/util/virstoragefile.h | 8 - tests/virstoragetest.c | 66 +- 14 files changed, 1582 insertions(+), 1574 deletions(-) create mode 100644 src/storage_file/storage_source_backingstore.c create mode 100644 src/storage_file/storage_source_backingstore.h -- 2.29.2

Move it together with virStorageSourceGetRelativeBackingPath which is the main reason why it exists. Upcoming patch will modify the comment and arguments refering to virStorageSourceGetRelativeBackingPath so it's better if they are together. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/storage_file/storage_source.c | 100 +++++++++++++++--------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/src/storage_file/storage_source.c b/src/storage_file/storage_source.c index 85734d9275..430e3214c4 100644 --- a/src/storage_file/storage_source.c +++ b/src/storage_file/storage_source.c @@ -1880,6 +1880,56 @@ virStorageSourceGetRelativeBackingPath(virStorageSourcePtr top, } +/** + * virStorageSourceGetBackingStoreStr: + * @src: storage object + * + * Extracts the backing store string as stored in the storage volume described + * by @src and returns it to the user. Caller is responsible for freeing it. + * In case when the string can't be retrieved or does not exist NULL is + * returned. + */ +int +virStorageSourceGetBackingStoreStr(virStorageSourcePtr src, + char **backing) +{ + ssize_t headerLen; + int rv; + g_autofree char *buf = NULL; + g_autoptr(virStorageSource) tmp = NULL; + + *backing = NULL; + + /* exit if we can't load information about the current image */ + if (!virStorageSourceSupportsBackingChainTraversal(src)) + return 0; + + rv = virStorageSourceAccess(src, F_OK); + if (rv == -2) + return 0; + if (rv < 0) { + virStorageSourceReportBrokenChain(errno, src, src); + return -1; + } + + if ((headerLen = virStorageSourceRead(src, 0, VIR_STORAGE_MAX_HEADER, + &buf)) < 0) { + if (headerLen == -2) + return 0; + return -1; + } + + if (!(tmp = virStorageSourceCopy(src, false))) + return -1; + + if (virStorageFileProbeGetMetadata(tmp, buf, headerLen) < 0) + return -1; + + *backing = g_steal_pointer(&tmp->backingStoreRaw); + return 0; +} + + static bool virStorageSourceIsInitialized(const virStorageSource *src) { @@ -2562,53 +2612,3 @@ virStorageSourceGetMetadata(virStorageSourcePtr src, virHashFree(cycle); return ret; } - - -/** - * virStorageSourceGetBackingStoreStr: - * @src: storage object - * - * Extracts the backing store string as stored in the storage volume described - * by @src and returns it to the user. Caller is responsible for freeing it. - * In case when the string can't be retrieved or does not exist NULL is - * returned. - */ -int -virStorageSourceGetBackingStoreStr(virStorageSourcePtr src, - char **backing) -{ - ssize_t headerLen; - int rv; - g_autofree char *buf = NULL; - g_autoptr(virStorageSource) tmp = NULL; - - *backing = NULL; - - /* exit if we can't load information about the current image */ - if (!virStorageSourceSupportsBackingChainTraversal(src)) - return 0; - - rv = virStorageSourceAccess(src, F_OK); - if (rv == -2) - return 0; - if (rv < 0) { - virStorageSourceReportBrokenChain(errno, src, src); - return -1; - } - - if ((headerLen = virStorageSourceRead(src, 0, VIR_STORAGE_MAX_HEADER, - &buf)) < 0) { - if (headerLen == -2) - return 0; - return -1; - } - - if (!(tmp = virStorageSourceCopy(src, false))) - return -1; - - if (virStorageFileProbeGetMetadata(tmp, buf, headerLen) < 0) - return -1; - - *backing = g_steal_pointer(&tmp->backingStoreRaw); - return 0; -} -- 2.29.2

Rename the function to virStorageSourceFetchRelativeBackingPath and return relative paths only. The function is only used to restore the relative relationship between images so there's no need for it to be universal. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/libvirt_private.syms | 2 +- src/qemu/qemu_block.c | 6 +----- src/qemu/qemu_snapshot.c | 13 +++---------- src/storage_file/storage_source.c | 22 +++++++++++++--------- src/storage_file/storage_source.h | 4 ++-- 5 files changed, 20 insertions(+), 27 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fbaf16704b..c03b769c37 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1691,7 +1691,7 @@ virStorageSourceChainLookup; virStorageSourceChown; virStorageSourceCreate; virStorageSourceDeinit; -virStorageSourceGetBackingStoreStr; +virStorageSourceFetchRelativeBackingPath; virStorageSourceGetMetadata; virStorageSourceGetMetadataFromBuf; virStorageSourceGetMetadataFromFD; diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index d845a3312d..6456100170 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -3420,7 +3420,6 @@ qemuBlockUpdateRelativeBacking(virDomainObjPtr vm, virStorageSourcePtr n; for (n = src; virStorageSourceHasBacking(n); n = n->backingStore) { - g_autofree char *backingStoreStr = NULL; int rc; if (n->backingStore->relPath) @@ -3432,15 +3431,12 @@ qemuBlockUpdateRelativeBacking(virDomainObjPtr vm, if (qemuDomainStorageFileInit(driver, vm, n, topsrc) < 0) return -1; - rc = virStorageSourceGetBackingStoreStr(n, &backingStoreStr); + rc = virStorageSourceFetchRelativeBackingPath(n, &n->backingStore->relPath); virStorageSourceDeinit(n); if (rc < 0) return rc; - - if (backingStoreStr && virStorageIsRelative(backingStoreStr)) - n->backingStore->relPath = g_steal_pointer(&backingStoreStr); } return 0; diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index bfc5a1a46b..39445738a2 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -1088,16 +1088,9 @@ qemuSnapshotDiskPrepareOne(virDomainObjPtr vm, dd->initialized = true; if (reuse) { - if (updateRelativeBacking) { - g_autofree char *backingStoreStr = NULL; - - if (virStorageSourceGetBackingStoreStr(dd->src, &backingStoreStr) < 0) - return -1; - if (backingStoreStr != NULL) { - if (virStorageIsRelative(backingStoreStr)) - dd->relPath = g_steal_pointer(&backingStoreStr); - } - } + if (updateRelativeBacking && + virStorageSourceFetchRelativeBackingPath(dd->src, &dd->relPath) < 0) + return -1; } else { /* pre-create the image file so that we can label it before handing it to qemu */ if (supportsCreate && dd->src->type != VIR_STORAGE_TYPE_BLOCK) { diff --git a/src/storage_file/storage_source.c b/src/storage_file/storage_source.c index 430e3214c4..23d36507ea 100644 --- a/src/storage_file/storage_source.c +++ b/src/storage_file/storage_source.c @@ -1881,24 +1881,26 @@ virStorageSourceGetRelativeBackingPath(virStorageSourcePtr top, /** - * virStorageSourceGetBackingStoreStr: + * virStorageSourceFetchRelativeBackingPath: * @src: storage object + * @relPath: filled with the relative path to the backing image of @src if + * the metadata of @src refer to it as relative. * - * Extracts the backing store string as stored in the storage volume described - * by @src and returns it to the user. Caller is responsible for freeing it. - * In case when the string can't be retrieved or does not exist NULL is - * returned. + * Fetches the backing store definition of @src by updating the metadata from + * disk and fills 'relPath' if the backing store string is relative. The data + * is used by virStorageSourceGetRelativeBackingPath to establish the relative + * path between two images. */ int -virStorageSourceGetBackingStoreStr(virStorageSourcePtr src, - char **backing) +virStorageSourceFetchRelativeBackingPath(virStorageSourcePtr src, + char **relPath) { ssize_t headerLen; int rv; g_autofree char *buf = NULL; g_autoptr(virStorageSource) tmp = NULL; - *backing = NULL; + g_clear_pointer(relPath, g_free); /* exit if we can't load information about the current image */ if (!virStorageSourceSupportsBackingChainTraversal(src)) @@ -1925,7 +1927,9 @@ virStorageSourceGetBackingStoreStr(virStorageSourcePtr src, if (virStorageFileProbeGetMetadata(tmp, buf, headerLen) < 0) return -1; - *backing = g_steal_pointer(&tmp->backingStoreRaw); + if (virStorageIsRelative(tmp->backingStoreRaw)) + *relPath = g_steal_pointer(&tmp->backingStoreRaw); + return 0; } diff --git a/src/storage_file/storage_source.h b/src/storage_file/storage_source.h index 480333d37a..58f88a3c01 100644 --- a/src/storage_file/storage_source.h +++ b/src/storage_file/storage_source.h @@ -138,8 +138,8 @@ virStorageSourceGetMetadata(virStorageSourcePtr src, ATTRIBUTE_NONNULL(1); int -virStorageSourceGetBackingStoreStr(virStorageSourcePtr src, - char **backing) +virStorageSourceFetchRelativeBackingPath(virStorageSourcePtr src, + char **relPath) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); void -- 2.29.2

There are no other files using it. Move it and make the functions static. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/libvirt_private.syms | 2 -- src/storage_file/storage_source.c | 42 ++++++++++++++++++++++++++++--- src/util/virstoragefile.c | 34 ------------------------- src/util/virstoragefile.h | 3 --- 4 files changed, 38 insertions(+), 43 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c03b769c37..0a2a54dfdf 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3218,8 +3218,6 @@ virStorageFileGetNPIVKey; virStorageFileGetSCSIKey; virStorageFileParseBackingStoreStr; virStorageFileParseChainIndex; -virStorageIsFile; -virStorageIsRelative; # util/virstring.h diff --git a/src/storage_file/storage_source.c b/src/storage_file/storage_source.c index 23d36507ea..df9fb6c055 100644 --- a/src/storage_file/storage_source.c +++ b/src/storage_file/storage_source.c @@ -45,6 +45,40 @@ VIR_LOG_INIT("storage_source"); +static bool +virStorageSourceBackinStoreStringIsFile(const char *backing) +{ + char *colon; + char *slash; + + if (!backing) + return false; + + colon = strchr(backing, ':'); + slash = strchr(backing, '/'); + + /* Reject anything that looks like a protocol (such as nbd: or + * rbd:); if someone really does want a relative file name that + * includes ':', they can always prefix './'. */ + if (colon && (!slash || colon < slash)) + return false; + return true; +} + + +static bool +virStorageSourceBackinStoreStringIsRelative(const char *backing) +{ + if (backing[0] == '/') + return false; + + if (!virStorageSourceBackinStoreStringIsFile(backing)) + return false; + + return true; +} + + static virStorageSourcePtr virStorageSourceMetadataNew(const char *path, int format) @@ -185,7 +219,7 @@ virStorageSourceChainLookup(virStorageSourcePtr chain, { virStorageSourcePtr prev; const char *start = chain->path; - bool nameIsFile = virStorageIsFile(name); + bool nameIsFile = virStorageSourceBackinStoreStringIsFile(name); if (!parent) parent = &prev; @@ -1532,7 +1566,7 @@ virStorageSourceNewFromBackingAbsolute(const char *path, *src = NULL; - if (virStorageIsFile(path)) { + if (virStorageSourceBackinStoreStringIsFile(path)) { def->type = VIR_STORAGE_TYPE_FILE; def->path = g_strdup(path); @@ -1604,7 +1638,7 @@ virStorageSourceNewFromChild(virStorageSourcePtr parent, *child = NULL; - if (virStorageIsRelative(parentRaw)) { + if (virStorageSourceBackinStoreStringIsRelative(parentRaw)) { if (!(def = virStorageSourceNewFromBackingRelative(parent, parentRaw))) return -1; } else { @@ -1927,7 +1961,7 @@ virStorageSourceFetchRelativeBackingPath(virStorageSourcePtr src, if (virStorageFileProbeGetMetadata(tmp, buf, headerLen) < 0) return -1; - if (virStorageIsRelative(tmp->backingStoreRaw)) + if (virStorageSourceBackinStoreStringIsRelative(tmp->backingStoreRaw)) *relPath = g_steal_pointer(&tmp->backingStoreRaw); return 0; diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 85ccd9f52c..d1e56db708 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -37,40 +37,6 @@ VIR_LOG_INIT("util.storagefile"); -bool -virStorageIsFile(const char *backing) -{ - char *colon; - char *slash; - - if (!backing) - return false; - - colon = strchr(backing, ':'); - slash = strchr(backing, '/'); - - /* Reject anything that looks like a protocol (such as nbd: or - * rbd:); if someone really does want a relative file name that - * includes ':', they can always prefix './'. */ - if (colon && (!slash || colon < slash)) - return false; - return true; -} - - -bool -virStorageIsRelative(const char *backing) -{ - if (backing[0] == '/') - return false; - - if (!virStorageIsFile(backing)) - return false; - - return true; -} - - #ifdef WITH_UDEV /* virStorageFileGetSCSIKey * @path: Path to the SCSI device diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 2c1a250f20..455a978a8d 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -33,9 +33,6 @@ int virStorageFileParseBackingStoreStr(const char *str, unsigned int *chainIndex) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); -bool virStorageIsFile(const char *path); -bool virStorageIsRelative(const char *backing); - int virStorageFileGetSCSIKey(const char *path, char **key, bool ignoreError); -- 2.29.2

The parsers for the backing store strings are relatively self-contained and rather massive piece of code. Move them to a new module called storage_source_backingstore. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- po/POTFILES.in | 1 + src/storage_file/meson.build | 1 + src/storage_file/storage_source.c | 1207 +--------------- .../storage_source_backingstore.c | 1240 +++++++++++++++++ .../storage_source_backingstore.h | 40 + 5 files changed, 1283 insertions(+), 1206 deletions(-) create mode 100644 src/storage_file/storage_source_backingstore.c create mode 100644 src/storage_file/storage_source_backingstore.h diff --git a/po/POTFILES.in b/po/POTFILES.in index a2c46dd239..b09e58f14a 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -230,6 +230,7 @@ @SRCDIR@src/storage_file/storage_file_backend_gluster.c @SRCDIR@src/storage_file/storage_file_probe.c @SRCDIR@src/storage_file/storage_source.c +@SRCDIR@src/storage_file/storage_source_backingstore.c @SRCDIR@src/test/test_driver.c @SRCDIR@src/util/iohelper.c @SRCDIR@src/util/viralloc.c diff --git a/src/storage_file/meson.build b/src/storage_file/meson.build index 4f8068848c..d40e98befa 100644 --- a/src/storage_file/meson.build +++ b/src/storage_file/meson.build @@ -1,5 +1,6 @@ storage_file_sources = [ 'storage_source.c', + 'storage_source_backingstore.c', 'storage_file_backend.c', 'storage_file_probe.c', ] diff --git a/src/storage_file/storage_source.c b/src/storage_file/storage_source.c index df9fb6c055..9d3761f5bd 100644 --- a/src/storage_file/storage_source.c +++ b/src/storage_file/storage_source.c @@ -28,16 +28,15 @@ #include "storage_file_backend.h" #include "storage_file_probe.h" #include "storage_source.h" +#include "storage_source_backingstore.h" #include "viralloc.h" #include "virerror.h" #include "virfile.h" #include "virhash.h" -#include "virjson.h" #include "virlog.h" #include "virobject.h" #include "virstoragefile.h" #include "virstring.h" -#include "viruri.h" #include "virutil.h" #define VIR_FROM_THIS VIR_FROM_STORAGE @@ -341,1210 +340,6 @@ virStorageSourceNewFromBackingRelative(virStorageSourcePtr parent, } -static int -virStorageSourceParseBackingURI(virStorageSourcePtr src, - const char *uristr) -{ - g_autoptr(virURI) uri = NULL; - const char *path = NULL; - g_auto(GStrv) scheme = NULL; - - if (!(uri = virURIParse(uristr))) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("failed to parse backing file location '%s'"), - uristr); - return -1; - } - - src->hosts = g_new0(virStorageNetHostDef, 1); - src->nhosts = 1; - - if (!(scheme = virStringSplit(uri->scheme, "+", 2))) - return -1; - - if (!scheme[0] || - (src->protocol = virStorageNetProtocolTypeFromString(scheme[0])) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("invalid backing protocol '%s'"), - NULLSTR(scheme[0])); - return -1; - } - - if (scheme[1] && - (src->hosts->transport = virStorageNetHostTransportTypeFromString(scheme[1])) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("invalid protocol transport type '%s'"), - scheme[1]); - return -1; - } - - if (uri->query) { - if (src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTP || - src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS) { - src->query = g_strdup(uri->query); - } else { - /* handle socket stored as a query */ - if (STRPREFIX(uri->query, "socket=")) - src->hosts->socket = g_strdup(STRSKIP(uri->query, "socket=")); - } - } - - /* uri->path is NULL if the URI does not contain slash after host: - * transport://host:port */ - if (uri->path) - path = uri->path; - else - path = ""; - - /* possibly skip the leading slash */ - if (path[0] == '/') - path++; - - /* NBD allows empty export name (path) */ - if (src->protocol == VIR_STORAGE_NET_PROTOCOL_NBD && - path[0] == '\0') - path = NULL; - - src->path = g_strdup(path); - - if (src->protocol == VIR_STORAGE_NET_PROTOCOL_GLUSTER) { - char *tmp; - - if (!src->path) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("missing volume name and path for gluster volume")); - return -1; - } - - if (!(tmp = strchr(src->path, '/')) || - tmp == src->path) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("missing volume name or file name in " - "gluster source path '%s'"), src->path); - return -1; - } - - src->volume = src->path; - - src->path = g_strdup(tmp + 1); - - tmp[0] = '\0'; - } - - src->hosts->port = uri->port; - - src->hosts->name = g_strdup(uri->server); - - /* Libvirt doesn't handle inline authentication. Make the caller aware. */ - if (uri->user) - return 1; - - return 0; -} - - -static int -virStorageSourceRBDAddHost(virStorageSourcePtr src, - char *hostport) -{ - char *port; - size_t skip; - g_auto(GStrv) parts = NULL; - - if (VIR_EXPAND_N(src->hosts, src->nhosts, 1) < 0) - return -1; - - if ((port = strchr(hostport, ']'))) { - /* ipv6, strip brackets */ - hostport += 1; - skip = 3; - } else { - port = strstr(hostport, "\\:"); - skip = 2; - } - - if (port) { - *port = '\0'; - port += skip; - if (virStringParsePort(port, &src->hosts[src->nhosts - 1].port) < 0) - goto error; - } - - parts = virStringSplit(hostport, "\\:", 0); - if (!parts) - goto error; - src->hosts[src->nhosts-1].name = virStringListJoin((const char **)parts, ":"); - if (!src->hosts[src->nhosts-1].name) - goto error; - - src->hosts[src->nhosts-1].transport = VIR_STORAGE_NET_HOST_TRANS_TCP; - src->hosts[src->nhosts-1].socket = NULL; - - return 0; - - error: - VIR_FREE(src->hosts[src->nhosts-1].name); - return -1; -} - - -int -virStorageSourceParseRBDColonString(const char *rbdstr, - virStorageSourcePtr src) -{ - char *p, *e, *next; - g_autofree char *options = NULL; - g_autoptr(virStorageAuthDef) authdef = NULL; - - /* optionally skip the "rbd:" prefix if provided */ - if (STRPREFIX(rbdstr, "rbd:")) - rbdstr += strlen("rbd:"); - - src->path = g_strdup(rbdstr); - - p = strchr(src->path, ':'); - if (p) { - options = g_strdup(p + 1); - *p = '\0'; - } - - /* snapshot name */ - if ((p = strchr(src->path, '@'))) { - src->snapshot = g_strdup(p + 1); - *p = '\0'; - } - - /* pool vs. image name */ - if ((p = strchr(src->path, '/'))) { - src->volume = g_steal_pointer(&src->path); - src->path = g_strdup(p + 1); - *p = '\0'; - } - - /* options */ - if (!options) - return 0; /* all done */ - - p = options; - while (*p) { - /* find : delimiter or end of string */ - for (e = p; *e && *e != ':'; ++e) { - if (*e == '\\') { - e++; - if (*e == '\0') - break; - } - } - if (*e == '\0') { - next = e; /* last kv pair */ - } else { - next = e + 1; - *e = '\0'; - } - - if (STRPREFIX(p, "id=")) { - /* formulate authdef for src->auth */ - if (src->auth) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("duplicate 'id' found in '%s'"), src->path); - return -1; - } - - authdef = g_new0(virStorageAuthDef, 1); - - authdef->username = g_strdup(p + strlen("id=")); - - authdef->secrettype = g_strdup(virSecretUsageTypeToString(VIR_SECRET_USAGE_TYPE_CEPH)); - src->auth = g_steal_pointer(&authdef); - - /* Cannot formulate a secretType (eg, usage or uuid) given - * what is provided. - */ - } - if (STRPREFIX(p, "mon_host=")) { - char *h, *sep; - - h = p + strlen("mon_host="); - while (h < e) { - for (sep = h; sep < e; ++sep) { - if (*sep == '\\' && (sep[1] == ',' || - sep[1] == ';' || - sep[1] == ' ')) { - *sep = '\0'; - sep += 2; - break; - } - } - - if (virStorageSourceRBDAddHost(src, h) < 0) - return -1; - - h = sep; - } - } - - if (STRPREFIX(p, "conf=")) - src->configFile = g_strdup(p + strlen("conf=")); - - p = next; - } - return 0; -} - - -static int -virStorageSourceParseNBDColonString(const char *nbdstr, - virStorageSourcePtr src) -{ - g_autofree char *nbd = g_strdup(nbdstr); - char *export_name; - char *host_spec; - char *unixpath; - char *port; - - src->hosts = g_new0(virStorageNetHostDef, 1); - src->nhosts = 1; - - /* We extract the parameters in a similar way qemu does it */ - - /* format: [] denotes optional sections, uppercase are variable strings - * nbd:unix:/PATH/TO/SOCKET[:exportname=EXPORTNAME] - * nbd:HOSTNAME:PORT[:exportname=EXPORTNAME] - */ - - /* first look for ':exportname=' and cut it off */ - if ((export_name = strstr(nbd, ":exportname="))) { - src->path = g_strdup(export_name + strlen(":exportname=")); - export_name[0] = '\0'; - } - - /* Verify the prefix and contents. Note that we require a - * "host_spec" part to be present. */ - 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; - - malformed: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("malformed nbd string '%s'"), nbdstr); - return -1; -} - - -static int -virStorageSourceParseBackingColon(virStorageSourcePtr src, - const char *path) -{ - const char *p; - g_autofree char *protocol = NULL; - - if (!(p = strchr(path, ':'))) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("invalid backing protocol string '%s'"), - path); - return -1; - } - - protocol = g_strndup(path, p - path); - - if ((src->protocol = virStorageNetProtocolTypeFromString(protocol)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("invalid backing protocol '%s'"), - protocol); - return -1; - } - - switch ((virStorageNetProtocol) src->protocol) { - case VIR_STORAGE_NET_PROTOCOL_NBD: - if (virStorageSourceParseNBDColonString(path, src) < 0) - return -1; - break; - - case VIR_STORAGE_NET_PROTOCOL_RBD: - if (virStorageSourceParseRBDColonString(path, src) < 0) - return -1; - break; - - case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: - case VIR_STORAGE_NET_PROTOCOL_LAST: - case VIR_STORAGE_NET_PROTOCOL_NONE: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("backing store parser is not implemented for protocol %s"), - protocol); - return -1; - - case VIR_STORAGE_NET_PROTOCOL_HTTP: - case VIR_STORAGE_NET_PROTOCOL_HTTPS: - case VIR_STORAGE_NET_PROTOCOL_FTP: - case VIR_STORAGE_NET_PROTOCOL_FTPS: - case VIR_STORAGE_NET_PROTOCOL_TFTP: - case VIR_STORAGE_NET_PROTOCOL_ISCSI: - case VIR_STORAGE_NET_PROTOCOL_GLUSTER: - case VIR_STORAGE_NET_PROTOCOL_SSH: - case VIR_STORAGE_NET_PROTOCOL_VXHS: - case VIR_STORAGE_NET_PROTOCOL_NFS: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("malformed backing store path for protocol %s"), - protocol); - return -1; - } - - return 0; -} - - -static int -virStorageSourceParseBackingJSONInternal(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr, - bool allowformat); - - -static int -virStorageSourceParseBackingJSONPath(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int type) -{ - const char *path; - - if (!(path = virJSONValueObjectGetString(json, "filename"))) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'filename' field in JSON backing volume " - "definition")); - return -1; - } - - src->path = g_strdup(path); - - src->type = type; - return 0; -} - - -static int -virStorageSourceParseBackingJSONUriStr(virStorageSourcePtr src, - const char *uri, - int protocol) -{ - int rc; - - if ((rc = virStorageSourceParseBackingURI(src, uri)) < 0) - return -1; - - if (src->protocol != protocol) { - virReportError(VIR_ERR_INVALID_ARG, - _("expected protocol '%s' but got '%s' in URI JSON volume " - "definition"), - virStorageNetProtocolTypeToString(protocol), - virStorageNetProtocolTypeToString(src->protocol)); - return -1; - } - - return rc; -} - - -static int -virStorageSourceParseBackingJSONUriCookies(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr) -{ - const char *cookiestr; - g_auto(GStrv) cookies = NULL; - size_t ncookies = 0; - size_t i; - - if (!virJSONValueObjectHasKey(json, "cookie")) - return 0; - - if (!(cookiestr = virJSONValueObjectGetString(json, "cookie"))) { - virReportError(VIR_ERR_INVALID_ARG, - _("wrong format of 'cookie' field in backing store definition '%s'"), - jsonstr); - return -1; - } - - if (!(cookies = virStringSplitCount(cookiestr, ";", 0, &ncookies))) - return -1; - - src->cookies = g_new0(virStorageNetCookieDefPtr, ncookies); - src->ncookies = ncookies; - - for (i = 0; i < ncookies; i++) { - char *cookiename = cookies[i]; - char *cookievalue; - - virSkipSpaces((const char **) &cookiename); - - if (!(cookievalue = strchr(cookiename, '='))) { - virReportError(VIR_ERR_INVALID_ARG, - _("malformed http cookie '%s' in backing store definition '%s'"), - cookies[i], jsonstr); - return -1; - } - - *cookievalue = '\0'; - cookievalue++; - - src->cookies[i] = g_new0(virStorageNetCookieDef, 1); - src->cookies[i]->name = g_strdup(cookiename); - src->cookies[i]->value = g_strdup(cookievalue); - } - - return 0; -} - - -static int -virStorageSourceParseBackingJSONUri(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr, - int protocol) -{ - const char *uri; - - if (!(uri = virJSONValueObjectGetString(json, "url"))) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'url' in JSON backing volume definition")); - return -1; - } - - if (protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS || - protocol == VIR_STORAGE_NET_PROTOCOL_FTPS) { - if (virJSONValueObjectHasKey(json, "sslverify")) { - const char *tmpstr; - bool tmp; - - /* libguestfs still uses undocumented legacy value of 'off' */ - if ((tmpstr = virJSONValueObjectGetString(json, "sslverify")) && - STREQ(tmpstr, "off")) { - src->sslverify = VIR_TRISTATE_BOOL_NO; - } else { - if (virJSONValueObjectGetBoolean(json, "sslverify", &tmp) < 0) { - virReportError(VIR_ERR_INVALID_ARG, - _("malformed 'sslverify' field in backing store definition '%s'"), - jsonstr); - return -1; - } - - src->sslverify = virTristateBoolFromBool(tmp); - } - } - } - - if (protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS || - protocol == VIR_STORAGE_NET_PROTOCOL_HTTP) { - if (virStorageSourceParseBackingJSONUriCookies(src, json, jsonstr) < 0) - return -1; - } - - if (virJSONValueObjectHasKey(json, "readahead") && - virJSONValueObjectGetNumberUlong(json, "readahead", &src->readahead) < 0) { - virReportError(VIR_ERR_INVALID_ARG, - _("malformed 'readahead' field in backing store definition '%s'"), - jsonstr); - return -1; - } - - if (virJSONValueObjectHasKey(json, "timeout") && - virJSONValueObjectGetNumberUlong(json, "timeout", &src->timeout) < 0) { - virReportError(VIR_ERR_INVALID_ARG, - _("malformed 'timeout' field in backing store definition '%s'"), - jsonstr); - return -1; - } - - return virStorageSourceParseBackingJSONUriStr(src, uri, protocol); -} - - -static int -virStorageSourceParseBackingJSONInetSocketAddress(virStorageNetHostDefPtr host, - virJSONValuePtr json) -{ - const char *hostname; - const char *port; - - if (!json) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing remote server specification in JSON " - "backing volume definition")); - return -1; - } - - hostname = virJSONValueObjectGetString(json, "host"); - port = virJSONValueObjectGetString(json, "port"); - - if (!hostname) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing hostname for tcp backing server in " - "JSON backing volume definition")); - return -1; - } - - host->transport = VIR_STORAGE_NET_HOST_TRANS_TCP; - host->name = g_strdup(hostname); - - if (virStringParsePort(port, &host->port) < 0) - return -1; - - return 0; -} - - -static int -virStorageSourceParseBackingJSONSocketAddress(virStorageNetHostDefPtr host, - virJSONValuePtr json) -{ - const char *type; - const char *socket; - - if (!json) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing remote server specification in JSON " - "backing volume definition")); - return -1; - } - - if (!(type = virJSONValueObjectGetString(json, "type"))) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing socket address type in " - "JSON backing volume definition")); - return -1; - } - - if (STREQ(type, "tcp") || STREQ(type, "inet")) { - return virStorageSourceParseBackingJSONInetSocketAddress(host, json); - - } else if (STREQ(type, "unix")) { - host->transport = VIR_STORAGE_NET_HOST_TRANS_UNIX; - - socket = virJSONValueObjectGetString(json, "path"); - - /* check for old spelling for gluster protocol */ - if (!socket) - socket = virJSONValueObjectGetString(json, "socket"); - - if (!socket) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing socket path for udp backing server in " - "JSON backing volume definition")); - return -1; - } - - host->socket = g_strdup(socket); - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("backing store protocol '%s' is not yet supported"), - type); - return -1; - } - - return 0; -} - - -static int -virStorageSourceParseBackingJSONGluster(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - const char *uri = virJSONValueObjectGetString(json, "filename"); - const char *volume = virJSONValueObjectGetString(json, "volume"); - const char *path = virJSONValueObjectGetString(json, "path"); - virJSONValuePtr server = virJSONValueObjectGetArray(json, "server"); - size_t nservers; - size_t i; - - /* legacy URI based syntax passed via 'filename' option */ - if (uri) - return virStorageSourceParseBackingJSONUriStr(src, uri, - VIR_STORAGE_NET_PROTOCOL_GLUSTER); - - if (!volume || !path || !server) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'volume', 'path' or 'server' attribute in " - "JSON backing definition for gluster volume")); - return -1; - } - - src->type = VIR_STORAGE_TYPE_NETWORK; - src->protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER; - - src->volume = g_strdup(volume); - src->path = g_strdup(path); - - nservers = virJSONValueArraySize(server); - if (nservers == 0) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("at least 1 server is necessary in " - "JSON backing definition for gluster volume")); - - return -1; - } - - src->hosts = g_new0(virStorageNetHostDef, nservers); - src->nhosts = nservers; - - for (i = 0; i < nservers; i++) { - if (virStorageSourceParseBackingJSONSocketAddress(src->hosts + i, - virJSONValueArrayGet(server, i)) < 0) - return -1; - } - - return 0; -} - - -static int -virStorageSourceParseBackingJSONiSCSI(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - const char *transport = virJSONValueObjectGetString(json, "transport"); - const char *portal = virJSONValueObjectGetString(json, "portal"); - const char *target = virJSONValueObjectGetString(json, "target"); - const char *lun = virJSONValueObjectGetStringOrNumber(json, "lun"); - const char *uri; - char *port; - - /* legacy URI based syntax passed via 'filename' option */ - if ((uri = virJSONValueObjectGetString(json, "filename"))) - return virStorageSourceParseBackingJSONUriStr(src, uri, - VIR_STORAGE_NET_PROTOCOL_ISCSI); - - src->type = VIR_STORAGE_TYPE_NETWORK; - src->protocol = VIR_STORAGE_NET_PROTOCOL_ISCSI; - - if (!lun) - lun = "0"; - - src->hosts = g_new0(virStorageNetHostDef, 1); - src->nhosts = 1; - - if (STRNEQ_NULLABLE(transport, "tcp")) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("only TCP transport is supported for iSCSI volumes")); - return -1; - } - - src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP; - - if (!portal) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'portal' address in iSCSI backing definition")); - return -1; - } - - if (!target) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'target' in iSCSI backing definition")); - return -1; - } - - src->hosts->name = g_strdup(portal); - - if ((port = strrchr(src->hosts->name, ':')) && - !strchr(port, ']')) { - if (virStringParsePort(port + 1, &src->hosts->port) < 0) - return -1; - - *port = '\0'; - } - - src->path = g_strdup_printf("%s/%s", target, lun); - - /* Libvirt doesn't handle inline authentication. Make the caller aware. */ - if (virJSONValueObjectGetString(json, "user") || - virJSONValueObjectGetString(json, "password")) - return 1; - - return 0; -} - - -static int -virStorageSourceParseBackingJSONNbd(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - const char *path = virJSONValueObjectGetString(json, "path"); - const char *host = virJSONValueObjectGetString(json, "host"); - const char *port = virJSONValueObjectGetString(json, "port"); - const char *export = virJSONValueObjectGetString(json, "export"); - virJSONValuePtr server = virJSONValueObjectGetObject(json, "server"); - - if (!path && !host && !server) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing host specification of NBD server in JSON " - "backing volume definition")); - return -1; - } - - src->type = VIR_STORAGE_TYPE_NETWORK; - src->protocol = VIR_STORAGE_NET_PROTOCOL_NBD; - - src->path = g_strdup(export); - - src->hosts = g_new0(virStorageNetHostDef, 1); - src->nhosts = 1; - - if (server) { - if (virStorageSourceParseBackingJSONSocketAddress(src->hosts, server) < 0) - return -1; - } else { - if (path) { - src->hosts[0].transport = VIR_STORAGE_NET_HOST_TRANS_UNIX; - src->hosts[0].socket = g_strdup(path); - } else { - src->hosts[0].transport = VIR_STORAGE_NET_HOST_TRANS_TCP; - src->hosts[0].name = g_strdup(host); - - if (virStringParsePort(port, &src->hosts[0].port) < 0) - return -1; - } - } - - return 0; -} - - -static int -virStorageSourceParseBackingJSONSheepdog(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - const char *filename; - const char *vdi = virJSONValueObjectGetString(json, "vdi"); - virJSONValuePtr server = virJSONValueObjectGetObject(json, "server"); - - /* legacy URI based syntax passed via 'filename' option */ - if ((filename = virJSONValueObjectGetString(json, "filename"))) { - if (strstr(filename, "://")) - return virStorageSourceParseBackingJSONUriStr(src, filename, - VIR_STORAGE_NET_PROTOCOL_SHEEPDOG); - - /* libvirt doesn't implement a parser for the legacy non-URI syntax */ - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing sheepdog URI in JSON backing volume definition")); - return -1; - } - - src->type = VIR_STORAGE_TYPE_NETWORK; - src->protocol = VIR_STORAGE_NET_PROTOCOL_SHEEPDOG; - - if (!vdi) { - virReportError(VIR_ERR_INVALID_ARG, "%s", _("missing sheepdog vdi name")); - return -1; - } - - src->path = g_strdup(vdi); - - src->hosts = g_new0(virStorageNetHostDef, 1); - src->nhosts = 1; - - if (virStorageSourceParseBackingJSONSocketAddress(src->hosts, server) < 0) - return -1; - - return 0; -} - - -static int -virStorageSourceParseBackingJSONSSH(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - const char *path = virJSONValueObjectGetString(json, "path"); - const char *host = virJSONValueObjectGetString(json, "host"); - const char *port = virJSONValueObjectGetString(json, "port"); - const char *user = virJSONValueObjectGetString(json, "user"); - const char *host_key_check = virJSONValueObjectGetString(json, "host_key_check"); - virJSONValuePtr server = virJSONValueObjectGetObject(json, "server"); - - if (!(host || server) || !path) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing host/server or path of SSH JSON backing " - "volume definition")); - return -1; - } - - src->type = VIR_STORAGE_TYPE_NETWORK; - src->protocol = VIR_STORAGE_NET_PROTOCOL_SSH; - - src->path = g_strdup(path); - - src->hosts = g_new0(virStorageNetHostDef, 1); - src->nhosts = 1; - - if (server) { - if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts, - server) < 0) - return -1; - } else { - src->hosts[0].transport = VIR_STORAGE_NET_HOST_TRANS_TCP; - src->hosts[0].name = g_strdup(host); - - if (virStringParsePort(port, &src->hosts[0].port) < 0) - return -1; - } - - /* these two are parsed just to be passed back as we don't model them yet */ - src->ssh_user = g_strdup(user); - if (STREQ_NULLABLE(host_key_check, "no")) - src->ssh_host_key_check_disabled = true; - - return 0; -} - - -static int -virStorageSourceParseBackingJSONRBD(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - const char *filename; - const char *pool = virJSONValueObjectGetString(json, "pool"); - const char *image = virJSONValueObjectGetString(json, "image"); - const char *conf = virJSONValueObjectGetString(json, "conf"); - const char *snapshot = virJSONValueObjectGetString(json, "snapshot"); - virJSONValuePtr servers = virJSONValueObjectGetArray(json, "server"); - size_t nservers; - size_t i; - - src->type = VIR_STORAGE_TYPE_NETWORK; - src->protocol = VIR_STORAGE_NET_PROTOCOL_RBD; - - /* legacy syntax passed via 'filename' option */ - if ((filename = virJSONValueObjectGetString(json, "filename"))) - return virStorageSourceParseRBDColonString(filename, src); - - if (!pool || !image) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing pool or image name in ceph backing volume " - "JSON specification")); - return -1; - } - - src->volume = g_strdup(pool); - src->path = g_strdup(image); - src->snapshot = g_strdup(snapshot); - src->configFile = g_strdup(conf); - - if (servers) { - nservers = virJSONValueArraySize(servers); - - src->hosts = g_new0(virStorageNetHostDef, nservers); - src->nhosts = nservers; - - for (i = 0; i < nservers; i++) { - if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts + i, - virJSONValueArrayGet(servers, i)) < 0) - return -1; - } - } - - return 0; -} - -static int -virStorageSourceParseBackingJSONRaw(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr, - int opaque G_GNUC_UNUSED) -{ - bool has_offset = virJSONValueObjectHasKey(json, "offset"); - bool has_size = virJSONValueObjectHasKey(json, "size"); - virJSONValuePtr file; - - if (has_offset || has_size) { - src->sliceStorage = g_new0(virStorageSourceSlice, 1); - - if (has_offset && - virJSONValueObjectGetNumberUlong(json, "offset", &src->sliceStorage->offset) < 0) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("malformed 'offset' property of 'raw' driver")); - return -1; - } - - if (has_size && - virJSONValueObjectGetNumberUlong(json, "size", &src->sliceStorage->size) < 0) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("malformed 'size' property of 'raw' driver")); - return -1; - } - } - - /* 'raw' is a format driver so it can have protocol driver children */ - if (!(file = virJSONValueObjectGetObject(json, "file"))) { - virReportError(VIR_ERR_INVALID_ARG, - _("JSON backing volume definition '%s' lacks 'file' object"), - jsonstr); - return -1; - } - - return virStorageSourceParseBackingJSONInternal(src, file, jsonstr, false); -} - - -static int -virStorageSourceParseBackingJSONVxHS(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - const char *vdisk_id = virJSONValueObjectGetString(json, "vdisk-id"); - virJSONValuePtr server = virJSONValueObjectGetObject(json, "server"); - - if (!vdisk_id || !server) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'vdisk-id' or 'server' attribute in " - "JSON backing definition for VxHS volume")); - return -1; - } - - src->type = VIR_STORAGE_TYPE_NETWORK; - src->protocol = VIR_STORAGE_NET_PROTOCOL_VXHS; - - src->path = g_strdup(vdisk_id); - - src->hosts = g_new0(virStorageNetHostDef, 1); - src->nhosts = 1; - - if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts, - server) < 0) - return -1; - - return 0; -} - - -static int -virStorageSourceParseBackingJSONNFS(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - virJSONValuePtr server = virJSONValueObjectGetObject(json, "server"); - int uidStore = -1; - int gidStore = -1; - int gotUID = virJSONValueObjectGetNumberInt(json, "user", &uidStore); - int gotGID = virJSONValueObjectGetNumberInt(json, "group", &gidStore); - - if (!server) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'server' attribute in JSON backing definition for NFS volume")); - return -1; - } - - if (gotUID < 0 || gotGID < 0) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'user' or 'group' attribute in JSON backing definition for NFS volume")); - return -1; - } - - src->path = g_strdup(virJSONValueObjectGetString(json, "path")); - if (!src->path) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'path' attribute in JSON backing definition for NFS volume")); - return -1; - } - - src->nfs_user = g_strdup_printf("+%d", uidStore); - src->nfs_group = g_strdup_printf("+%d", gidStore); - - src->type = VIR_STORAGE_TYPE_NETWORK; - src->protocol = VIR_STORAGE_NET_PROTOCOL_NFS; - - src->hosts = g_new0(virStorageNetHostDef, 1); - src->nhosts = 1; - - if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts, - server) < 0) - return -1; - - return 0; -} - - -static int -virStorageSourceParseBackingJSONNVMe(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - g_autoptr(virStorageSourceNVMeDef) nvme = g_new0(virStorageSourceNVMeDef, 1); - const char *device = virJSONValueObjectGetString(json, "device"); - - if (!device || virPCIDeviceAddressParse((char *) device, &nvme->pciAddr) < 0) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing or malformed 'device' field of 'nvme' storage")); - return -1; - } - - if (virJSONValueObjectGetNumberUlong(json, "namespace", &nvme->namespc) < 0 || - nvme->namespc == 0) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing or malformed 'namespace' field of 'nvme' storage")); - return -1; - } - - src->type = VIR_STORAGE_TYPE_NVME; - src->nvme = g_steal_pointer(&nvme); - - return 0; -} - - -struct virStorageSourceJSONDriverParser { - const char *drvname; - bool formatdriver; - /** - * The callback gets a pre-allocated storage source @src and the JSON - * object to parse. The callback shall return -1 on error and report error - * 0 on success and 1 in cases when the configuration itself is valid, but - * can't be converted to libvirt's configuration (e.g. inline authentication - * credentials are present). - */ - int (*func)(virStorageSourcePtr src, virJSONValuePtr json, const char *jsonstr, int opaque); - int opaque; -}; - -static const struct virStorageSourceJSONDriverParser jsonParsers[] = { - {"file", false, virStorageSourceParseBackingJSONPath, VIR_STORAGE_TYPE_FILE}, - {"host_device", false, virStorageSourceParseBackingJSONPath, VIR_STORAGE_TYPE_BLOCK}, - {"host_cdrom", false, virStorageSourceParseBackingJSONPath, VIR_STORAGE_TYPE_BLOCK}, - {"http", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PROTOCOL_HTTP}, - {"https", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PROTOCOL_HTTPS}, - {"ftp", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PROTOCOL_FTP}, - {"ftps", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PROTOCOL_FTPS}, - {"tftp", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PROTOCOL_TFTP}, - {"gluster", false, virStorageSourceParseBackingJSONGluster, 0}, - {"iscsi", false, virStorageSourceParseBackingJSONiSCSI, 0}, - {"nbd", false, virStorageSourceParseBackingJSONNbd, 0}, - {"sheepdog", false, virStorageSourceParseBackingJSONSheepdog, 0}, - {"ssh", false, virStorageSourceParseBackingJSONSSH, 0}, - {"rbd", false, virStorageSourceParseBackingJSONRBD, 0}, - {"raw", true, virStorageSourceParseBackingJSONRaw, 0}, - {"nfs", false, virStorageSourceParseBackingJSONNFS, 0}, - {"vxhs", false, virStorageSourceParseBackingJSONVxHS, 0}, - {"nvme", false, virStorageSourceParseBackingJSONNVMe, 0}, -}; - - - -static int -virStorageSourceParseBackingJSONInternal(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr, - bool allowformat) -{ - const char *drvname; - size_t i; - - if (!(drvname = virJSONValueObjectGetString(json, "driver"))) { - virReportError(VIR_ERR_INVALID_ARG, - _("JSON backing volume definition '%s' lacks driver name"), - jsonstr); - return -1; - } - - for (i = 0; i < G_N_ELEMENTS(jsonParsers); i++) { - if (STRNEQ(drvname, jsonParsers[i].drvname)) - continue; - - if (jsonParsers[i].formatdriver && !allowformat) { - virReportError(VIR_ERR_INVALID_ARG, - _("JSON backing volume definition '%s' must not have nested format drivers"), - jsonstr); - return -1; - } - - return jsonParsers[i].func(src, json, jsonstr, jsonParsers[i].opaque); - } - - virReportError(VIR_ERR_INTERNAL_ERROR, - _("missing parser implementation for JSON backing volume " - "driver '%s'"), drvname); - return -1; -} - - -static int -virStorageSourceParseBackingJSON(virStorageSourcePtr src, - const char *json) -{ - g_autoptr(virJSONValue) root = NULL; - g_autoptr(virJSONValue) deflattened = NULL; - virJSONValuePtr file = NULL; - - if (!(root = virJSONValueFromString(json))) - return -1; - - if (!(deflattened = virJSONValueObjectDeflatten(root))) - return -1; - - /* There are 2 possible syntaxes: - * 1) json:{"file":{"driver":...}} - * 2) json:{"driver":...} - * Remove the 'file' wrapper object in case 1. - */ - if (!virJSONValueObjectHasKey(deflattened, "driver")) - file = virJSONValueObjectGetObject(deflattened, "file"); - - if (!file) - file = deflattened; - - return virStorageSourceParseBackingJSONInternal(src, file, json, true); -} - - /** * virStorageSourceNewFromBackingAbsolute * @path: string representing absolute location of a storage source diff --git a/src/storage_file/storage_source_backingstore.c b/src/storage_file/storage_source_backingstore.c new file mode 100644 index 0000000000..bbcc720af1 --- /dev/null +++ b/src/storage_file/storage_source_backingstore.c @@ -0,0 +1,1240 @@ +/* + * storage_source_backingstore.c: helpers for parsing backing store strings + * + * Copyright (C) 2007-2017 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * 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.1 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, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "internal.h" + +#include "storage_source_backingstore.h" + +#include "viruri.h" +#include "virstring.h" +#include "virjson.h" +#include "virlog.h" +#include "viralloc.h" + +#define VIR_FROM_THIS VIR_FROM_STORAGE + +VIR_LOG_INIT("storage_source_backingstore"); + + +int +virStorageSourceParseBackingURI(virStorageSourcePtr src, + const char *uristr) +{ + g_autoptr(virURI) uri = NULL; + const char *path = NULL; + g_auto(GStrv) scheme = NULL; + + if (!(uri = virURIParse(uristr))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to parse backing file location '%s'"), + uristr); + return -1; + } + + src->hosts = g_new0(virStorageNetHostDef, 1); + src->nhosts = 1; + + if (!(scheme = virStringSplit(uri->scheme, "+", 2))) + return -1; + + if (!scheme[0] || + (src->protocol = virStorageNetProtocolTypeFromString(scheme[0])) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid backing protocol '%s'"), + NULLSTR(scheme[0])); + return -1; + } + + if (scheme[1] && + (src->hosts->transport = virStorageNetHostTransportTypeFromString(scheme[1])) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid protocol transport type '%s'"), + scheme[1]); + return -1; + } + + if (uri->query) { + if (src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTP || + src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS) { + src->query = g_strdup(uri->query); + } else { + /* handle socket stored as a query */ + if (STRPREFIX(uri->query, "socket=")) + src->hosts->socket = g_strdup(STRSKIP(uri->query, "socket=")); + } + } + + /* uri->path is NULL if the URI does not contain slash after host: + * transport://host:port */ + if (uri->path) + path = uri->path; + else + path = ""; + + /* possibly skip the leading slash */ + if (path[0] == '/') + path++; + + /* NBD allows empty export name (path) */ + if (src->protocol == VIR_STORAGE_NET_PROTOCOL_NBD && + path[0] == '\0') + path = NULL; + + src->path = g_strdup(path); + + if (src->protocol == VIR_STORAGE_NET_PROTOCOL_GLUSTER) { + char *tmp; + + if (!src->path) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("missing volume name and path for gluster volume")); + return -1; + } + + if (!(tmp = strchr(src->path, '/')) || + tmp == src->path) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("missing volume name or file name in " + "gluster source path '%s'"), src->path); + return -1; + } + + src->volume = src->path; + + src->path = g_strdup(tmp + 1); + + tmp[0] = '\0'; + } + + src->hosts->port = uri->port; + + src->hosts->name = g_strdup(uri->server); + + /* Libvirt doesn't handle inline authentication. Make the caller aware. */ + if (uri->user) + return 1; + + return 0; +} + + +static int +virStorageSourceRBDAddHost(virStorageSourcePtr src, + char *hostport) +{ + char *port; + size_t skip; + g_auto(GStrv) parts = NULL; + + if (VIR_EXPAND_N(src->hosts, src->nhosts, 1) < 0) + return -1; + + if ((port = strchr(hostport, ']'))) { + /* ipv6, strip brackets */ + hostport += 1; + skip = 3; + } else { + port = strstr(hostport, "\\:"); + skip = 2; + } + + if (port) { + *port = '\0'; + port += skip; + if (virStringParsePort(port, &src->hosts[src->nhosts - 1].port) < 0) + goto error; + } + + parts = virStringSplit(hostport, "\\:", 0); + if (!parts) + goto error; + src->hosts[src->nhosts-1].name = virStringListJoin((const char **)parts, ":"); + if (!src->hosts[src->nhosts-1].name) + goto error; + + src->hosts[src->nhosts-1].transport = VIR_STORAGE_NET_HOST_TRANS_TCP; + src->hosts[src->nhosts-1].socket = NULL; + + return 0; + + error: + VIR_FREE(src->hosts[src->nhosts-1].name); + return -1; +} + + +int +virStorageSourceParseRBDColonString(const char *rbdstr, + virStorageSourcePtr src) +{ + char *p, *e, *next; + g_autofree char *options = NULL; + g_autoptr(virStorageAuthDef) authdef = NULL; + + /* optionally skip the "rbd:" prefix if provided */ + if (STRPREFIX(rbdstr, "rbd:")) + rbdstr += strlen("rbd:"); + + src->path = g_strdup(rbdstr); + + p = strchr(src->path, ':'); + if (p) { + options = g_strdup(p + 1); + *p = '\0'; + } + + /* snapshot name */ + if ((p = strchr(src->path, '@'))) { + src->snapshot = g_strdup(p + 1); + *p = '\0'; + } + + /* pool vs. image name */ + if ((p = strchr(src->path, '/'))) { + src->volume = g_steal_pointer(&src->path); + src->path = g_strdup(p + 1); + *p = '\0'; + } + + /* options */ + if (!options) + return 0; /* all done */ + + p = options; + while (*p) { + /* find : delimiter or end of string */ + for (e = p; *e && *e != ':'; ++e) { + if (*e == '\\') { + e++; + if (*e == '\0') + break; + } + } + if (*e == '\0') { + next = e; /* last kv pair */ + } else { + next = e + 1; + *e = '\0'; + } + + if (STRPREFIX(p, "id=")) { + /* formulate authdef for src->auth */ + if (src->auth) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("duplicate 'id' found in '%s'"), src->path); + return -1; + } + + authdef = g_new0(virStorageAuthDef, 1); + + authdef->username = g_strdup(p + strlen("id=")); + + authdef->secrettype = g_strdup(virSecretUsageTypeToString(VIR_SECRET_USAGE_TYPE_CEPH)); + src->auth = g_steal_pointer(&authdef); + + /* Cannot formulate a secretType (eg, usage or uuid) given + * what is provided. + */ + } + if (STRPREFIX(p, "mon_host=")) { + char *h, *sep; + + h = p + strlen("mon_host="); + while (h < e) { + for (sep = h; sep < e; ++sep) { + if (*sep == '\\' && (sep[1] == ',' || + sep[1] == ';' || + sep[1] == ' ')) { + *sep = '\0'; + sep += 2; + break; + } + } + + if (virStorageSourceRBDAddHost(src, h) < 0) + return -1; + + h = sep; + } + } + + if (STRPREFIX(p, "conf=")) + src->configFile = g_strdup(p + strlen("conf=")); + + p = next; + } + return 0; +} + + +static int +virStorageSourceParseNBDColonString(const char *nbdstr, + virStorageSourcePtr src) +{ + g_autofree char *nbd = g_strdup(nbdstr); + char *export_name; + char *host_spec; + char *unixpath; + char *port; + + src->hosts = g_new0(virStorageNetHostDef, 1); + src->nhosts = 1; + + /* We extract the parameters in a similar way qemu does it */ + + /* format: [] denotes optional sections, uppercase are variable strings + * nbd:unix:/PATH/TO/SOCKET[:exportname=EXPORTNAME] + * nbd:HOSTNAME:PORT[:exportname=EXPORTNAME] + */ + + /* first look for ':exportname=' and cut it off */ + if ((export_name = strstr(nbd, ":exportname="))) { + src->path = g_strdup(export_name + strlen(":exportname=")); + export_name[0] = '\0'; + } + + /* Verify the prefix and contents. Note that we require a + * "host_spec" part to be present. */ + 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; + + malformed: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("malformed nbd string '%s'"), nbdstr); + return -1; +} + + +int +virStorageSourceParseBackingColon(virStorageSourcePtr src, + const char *path) +{ + const char *p; + g_autofree char *protocol = NULL; + + if (!(p = strchr(path, ':'))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid backing protocol string '%s'"), + path); + return -1; + } + + protocol = g_strndup(path, p - path); + + if ((src->protocol = virStorageNetProtocolTypeFromString(protocol)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid backing protocol '%s'"), + protocol); + return -1; + } + + switch ((virStorageNetProtocol) src->protocol) { + case VIR_STORAGE_NET_PROTOCOL_NBD: + if (virStorageSourceParseNBDColonString(path, src) < 0) + return -1; + break; + + case VIR_STORAGE_NET_PROTOCOL_RBD: + if (virStorageSourceParseRBDColonString(path, src) < 0) + return -1; + break; + + case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: + case VIR_STORAGE_NET_PROTOCOL_LAST: + case VIR_STORAGE_NET_PROTOCOL_NONE: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("backing store parser is not implemented for protocol %s"), + protocol); + return -1; + + case VIR_STORAGE_NET_PROTOCOL_HTTP: + case VIR_STORAGE_NET_PROTOCOL_HTTPS: + case VIR_STORAGE_NET_PROTOCOL_FTP: + case VIR_STORAGE_NET_PROTOCOL_FTPS: + case VIR_STORAGE_NET_PROTOCOL_TFTP: + case VIR_STORAGE_NET_PROTOCOL_ISCSI: + case VIR_STORAGE_NET_PROTOCOL_GLUSTER: + case VIR_STORAGE_NET_PROTOCOL_SSH: + case VIR_STORAGE_NET_PROTOCOL_VXHS: + case VIR_STORAGE_NET_PROTOCOL_NFS: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("malformed backing store path for protocol %s"), + protocol); + return -1; + } + + return 0; +} + + +static int +virStorageSourceParseBackingJSONInternal(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr, + bool allowformat); + + +static int +virStorageSourceParseBackingJSONPath(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int type) +{ + const char *path; + + if (!(path = virJSONValueObjectGetString(json, "filename"))) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'filename' field in JSON backing volume " + "definition")); + return -1; + } + + src->path = g_strdup(path); + + src->type = type; + return 0; +} + + +static int +virStorageSourceParseBackingJSONUriStr(virStorageSourcePtr src, + const char *uri, + int protocol) +{ + int rc; + + if ((rc = virStorageSourceParseBackingURI(src, uri)) < 0) + return -1; + + if (src->protocol != protocol) { + virReportError(VIR_ERR_INVALID_ARG, + _("expected protocol '%s' but got '%s' in URI JSON volume " + "definition"), + virStorageNetProtocolTypeToString(protocol), + virStorageNetProtocolTypeToString(src->protocol)); + return -1; + } + + return rc; +} + + +static int +virStorageSourceParseBackingJSONUriCookies(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr) +{ + const char *cookiestr; + g_auto(GStrv) cookies = NULL; + size_t ncookies = 0; + size_t i; + + if (!virJSONValueObjectHasKey(json, "cookie")) + return 0; + + if (!(cookiestr = virJSONValueObjectGetString(json, "cookie"))) { + virReportError(VIR_ERR_INVALID_ARG, + _("wrong format of 'cookie' field in backing store definition '%s'"), + jsonstr); + return -1; + } + + if (!(cookies = virStringSplitCount(cookiestr, ";", 0, &ncookies))) + return -1; + + src->cookies = g_new0(virStorageNetCookieDefPtr, ncookies); + src->ncookies = ncookies; + + for (i = 0; i < ncookies; i++) { + char *cookiename = cookies[i]; + char *cookievalue; + + virSkipSpaces((const char **) &cookiename); + + if (!(cookievalue = strchr(cookiename, '='))) { + virReportError(VIR_ERR_INVALID_ARG, + _("malformed http cookie '%s' in backing store definition '%s'"), + cookies[i], jsonstr); + return -1; + } + + *cookievalue = '\0'; + cookievalue++; + + src->cookies[i] = g_new0(virStorageNetCookieDef, 1); + src->cookies[i]->name = g_strdup(cookiename); + src->cookies[i]->value = g_strdup(cookievalue); + } + + return 0; +} + + +static int +virStorageSourceParseBackingJSONUri(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr, + int protocol) +{ + const char *uri; + + if (!(uri = virJSONValueObjectGetString(json, "url"))) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'url' in JSON backing volume definition")); + return -1; + } + + if (protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS || + protocol == VIR_STORAGE_NET_PROTOCOL_FTPS) { + if (virJSONValueObjectHasKey(json, "sslverify")) { + const char *tmpstr; + bool tmp; + + /* libguestfs still uses undocumented legacy value of 'off' */ + if ((tmpstr = virJSONValueObjectGetString(json, "sslverify")) && + STREQ(tmpstr, "off")) { + src->sslverify = VIR_TRISTATE_BOOL_NO; + } else { + if (virJSONValueObjectGetBoolean(json, "sslverify", &tmp) < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("malformed 'sslverify' field in backing store definition '%s'"), + jsonstr); + return -1; + } + + src->sslverify = virTristateBoolFromBool(tmp); + } + } + } + + if (protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS || + protocol == VIR_STORAGE_NET_PROTOCOL_HTTP) { + if (virStorageSourceParseBackingJSONUriCookies(src, json, jsonstr) < 0) + return -1; + } + + if (virJSONValueObjectHasKey(json, "readahead") && + virJSONValueObjectGetNumberUlong(json, "readahead", &src->readahead) < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("malformed 'readahead' field in backing store definition '%s'"), + jsonstr); + return -1; + } + + if (virJSONValueObjectHasKey(json, "timeout") && + virJSONValueObjectGetNumberUlong(json, "timeout", &src->timeout) < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("malformed 'timeout' field in backing store definition '%s'"), + jsonstr); + return -1; + } + + return virStorageSourceParseBackingJSONUriStr(src, uri, protocol); +} + + +static int +virStorageSourceParseBackingJSONInetSocketAddress(virStorageNetHostDefPtr host, + virJSONValuePtr json) +{ + const char *hostname; + const char *port; + + if (!json) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing remote server specification in JSON " + "backing volume definition")); + return -1; + } + + hostname = virJSONValueObjectGetString(json, "host"); + port = virJSONValueObjectGetString(json, "port"); + + if (!hostname) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing hostname for tcp backing server in " + "JSON backing volume definition")); + return -1; + } + + host->transport = VIR_STORAGE_NET_HOST_TRANS_TCP; + host->name = g_strdup(hostname); + + if (virStringParsePort(port, &host->port) < 0) + return -1; + + return 0; +} + + +static int +virStorageSourceParseBackingJSONSocketAddress(virStorageNetHostDefPtr host, + virJSONValuePtr json) +{ + const char *type; + const char *socket; + + if (!json) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing remote server specification in JSON " + "backing volume definition")); + return -1; + } + + if (!(type = virJSONValueObjectGetString(json, "type"))) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing socket address type in " + "JSON backing volume definition")); + return -1; + } + + if (STREQ(type, "tcp") || STREQ(type, "inet")) { + return virStorageSourceParseBackingJSONInetSocketAddress(host, json); + + } else if (STREQ(type, "unix")) { + host->transport = VIR_STORAGE_NET_HOST_TRANS_UNIX; + + socket = virJSONValueObjectGetString(json, "path"); + + /* check for old spelling for gluster protocol */ + if (!socket) + socket = virJSONValueObjectGetString(json, "socket"); + + if (!socket) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing socket path for udp backing server in " + "JSON backing volume definition")); + return -1; + } + + host->socket = g_strdup(socket); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("backing store protocol '%s' is not yet supported"), + type); + return -1; + } + + return 0; +} + + +static int +virStorageSourceParseBackingJSONGluster(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + const char *uri = virJSONValueObjectGetString(json, "filename"); + const char *volume = virJSONValueObjectGetString(json, "volume"); + const char *path = virJSONValueObjectGetString(json, "path"); + virJSONValuePtr server = virJSONValueObjectGetArray(json, "server"); + size_t nservers; + size_t i; + + /* legacy URI based syntax passed via 'filename' option */ + if (uri) + return virStorageSourceParseBackingJSONUriStr(src, uri, + VIR_STORAGE_NET_PROTOCOL_GLUSTER); + + if (!volume || !path || !server) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'volume', 'path' or 'server' attribute in " + "JSON backing definition for gluster volume")); + return -1; + } + + src->type = VIR_STORAGE_TYPE_NETWORK; + src->protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER; + + src->volume = g_strdup(volume); + src->path = g_strdup(path); + + nservers = virJSONValueArraySize(server); + if (nservers == 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("at least 1 server is necessary in " + "JSON backing definition for gluster volume")); + + return -1; + } + + src->hosts = g_new0(virStorageNetHostDef, nservers); + src->nhosts = nservers; + + for (i = 0; i < nservers; i++) { + if (virStorageSourceParseBackingJSONSocketAddress(src->hosts + i, + virJSONValueArrayGet(server, i)) < 0) + return -1; + } + + return 0; +} + + +static int +virStorageSourceParseBackingJSONiSCSI(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + const char *transport = virJSONValueObjectGetString(json, "transport"); + const char *portal = virJSONValueObjectGetString(json, "portal"); + const char *target = virJSONValueObjectGetString(json, "target"); + const char *lun = virJSONValueObjectGetStringOrNumber(json, "lun"); + const char *uri; + char *port; + + /* legacy URI based syntax passed via 'filename' option */ + if ((uri = virJSONValueObjectGetString(json, "filename"))) + return virStorageSourceParseBackingJSONUriStr(src, uri, + VIR_STORAGE_NET_PROTOCOL_ISCSI); + + src->type = VIR_STORAGE_TYPE_NETWORK; + src->protocol = VIR_STORAGE_NET_PROTOCOL_ISCSI; + + if (!lun) + lun = "0"; + + src->hosts = g_new0(virStorageNetHostDef, 1); + src->nhosts = 1; + + if (STRNEQ_NULLABLE(transport, "tcp")) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("only TCP transport is supported for iSCSI volumes")); + return -1; + } + + src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP; + + if (!portal) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'portal' address in iSCSI backing definition")); + return -1; + } + + if (!target) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'target' in iSCSI backing definition")); + return -1; + } + + src->hosts->name = g_strdup(portal); + + if ((port = strrchr(src->hosts->name, ':')) && + !strchr(port, ']')) { + if (virStringParsePort(port + 1, &src->hosts->port) < 0) + return -1; + + *port = '\0'; + } + + src->path = g_strdup_printf("%s/%s", target, lun); + + /* Libvirt doesn't handle inline authentication. Make the caller aware. */ + if (virJSONValueObjectGetString(json, "user") || + virJSONValueObjectGetString(json, "password")) + return 1; + + return 0; +} + + +static int +virStorageSourceParseBackingJSONNbd(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + const char *path = virJSONValueObjectGetString(json, "path"); + const char *host = virJSONValueObjectGetString(json, "host"); + const char *port = virJSONValueObjectGetString(json, "port"); + const char *export = virJSONValueObjectGetString(json, "export"); + virJSONValuePtr server = virJSONValueObjectGetObject(json, "server"); + + if (!path && !host && !server) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing host specification of NBD server in JSON " + "backing volume definition")); + return -1; + } + + src->type = VIR_STORAGE_TYPE_NETWORK; + src->protocol = VIR_STORAGE_NET_PROTOCOL_NBD; + + src->path = g_strdup(export); + + src->hosts = g_new0(virStorageNetHostDef, 1); + src->nhosts = 1; + + if (server) { + if (virStorageSourceParseBackingJSONSocketAddress(src->hosts, server) < 0) + return -1; + } else { + if (path) { + src->hosts[0].transport = VIR_STORAGE_NET_HOST_TRANS_UNIX; + src->hosts[0].socket = g_strdup(path); + } else { + src->hosts[0].transport = VIR_STORAGE_NET_HOST_TRANS_TCP; + src->hosts[0].name = g_strdup(host); + + if (virStringParsePort(port, &src->hosts[0].port) < 0) + return -1; + } + } + + return 0; +} + + +static int +virStorageSourceParseBackingJSONSheepdog(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + const char *filename; + const char *vdi = virJSONValueObjectGetString(json, "vdi"); + virJSONValuePtr server = virJSONValueObjectGetObject(json, "server"); + + /* legacy URI based syntax passed via 'filename' option */ + if ((filename = virJSONValueObjectGetString(json, "filename"))) { + if (strstr(filename, "://")) + return virStorageSourceParseBackingJSONUriStr(src, filename, + VIR_STORAGE_NET_PROTOCOL_SHEEPDOG); + + /* libvirt doesn't implement a parser for the legacy non-URI syntax */ + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing sheepdog URI in JSON backing volume definition")); + return -1; + } + + src->type = VIR_STORAGE_TYPE_NETWORK; + src->protocol = VIR_STORAGE_NET_PROTOCOL_SHEEPDOG; + + if (!vdi) { + virReportError(VIR_ERR_INVALID_ARG, "%s", _("missing sheepdog vdi name")); + return -1; + } + + src->path = g_strdup(vdi); + + src->hosts = g_new0(virStorageNetHostDef, 1); + src->nhosts = 1; + + if (virStorageSourceParseBackingJSONSocketAddress(src->hosts, server) < 0) + return -1; + + return 0; +} + + +static int +virStorageSourceParseBackingJSONSSH(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + const char *path = virJSONValueObjectGetString(json, "path"); + const char *host = virJSONValueObjectGetString(json, "host"); + const char *port = virJSONValueObjectGetString(json, "port"); + const char *user = virJSONValueObjectGetString(json, "user"); + const char *host_key_check = virJSONValueObjectGetString(json, "host_key_check"); + virJSONValuePtr server = virJSONValueObjectGetObject(json, "server"); + + if (!(host || server) || !path) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing host/server or path of SSH JSON backing " + "volume definition")); + return -1; + } + + src->type = VIR_STORAGE_TYPE_NETWORK; + src->protocol = VIR_STORAGE_NET_PROTOCOL_SSH; + + src->path = g_strdup(path); + + src->hosts = g_new0(virStorageNetHostDef, 1); + src->nhosts = 1; + + if (server) { + if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts, + server) < 0) + return -1; + } else { + src->hosts[0].transport = VIR_STORAGE_NET_HOST_TRANS_TCP; + src->hosts[0].name = g_strdup(host); + + if (virStringParsePort(port, &src->hosts[0].port) < 0) + return -1; + } + + /* these two are parsed just to be passed back as we don't model them yet */ + src->ssh_user = g_strdup(user); + if (STREQ_NULLABLE(host_key_check, "no")) + src->ssh_host_key_check_disabled = true; + + return 0; +} + + +static int +virStorageSourceParseBackingJSONRBD(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + const char *filename; + const char *pool = virJSONValueObjectGetString(json, "pool"); + const char *image = virJSONValueObjectGetString(json, "image"); + const char *conf = virJSONValueObjectGetString(json, "conf"); + const char *snapshot = virJSONValueObjectGetString(json, "snapshot"); + virJSONValuePtr servers = virJSONValueObjectGetArray(json, "server"); + size_t nservers; + size_t i; + + src->type = VIR_STORAGE_TYPE_NETWORK; + src->protocol = VIR_STORAGE_NET_PROTOCOL_RBD; + + /* legacy syntax passed via 'filename' option */ + if ((filename = virJSONValueObjectGetString(json, "filename"))) + return virStorageSourceParseRBDColonString(filename, src); + + if (!pool || !image) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing pool or image name in ceph backing volume " + "JSON specification")); + return -1; + } + + src->volume = g_strdup(pool); + src->path = g_strdup(image); + src->snapshot = g_strdup(snapshot); + src->configFile = g_strdup(conf); + + if (servers) { + nservers = virJSONValueArraySize(servers); + + src->hosts = g_new0(virStorageNetHostDef, nservers); + src->nhosts = nservers; + + for (i = 0; i < nservers; i++) { + if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts + i, + virJSONValueArrayGet(servers, i)) < 0) + return -1; + } + } + + return 0; +} + +static int +virStorageSourceParseBackingJSONRaw(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr, + int opaque G_GNUC_UNUSED) +{ + bool has_offset = virJSONValueObjectHasKey(json, "offset"); + bool has_size = virJSONValueObjectHasKey(json, "size"); + virJSONValuePtr file; + + if (has_offset || has_size) { + src->sliceStorage = g_new0(virStorageSourceSlice, 1); + + if (has_offset && + virJSONValueObjectGetNumberUlong(json, "offset", &src->sliceStorage->offset) < 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("malformed 'offset' property of 'raw' driver")); + return -1; + } + + if (has_size && + virJSONValueObjectGetNumberUlong(json, "size", &src->sliceStorage->size) < 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("malformed 'size' property of 'raw' driver")); + return -1; + } + } + + /* 'raw' is a format driver so it can have protocol driver children */ + if (!(file = virJSONValueObjectGetObject(json, "file"))) { + virReportError(VIR_ERR_INVALID_ARG, + _("JSON backing volume definition '%s' lacks 'file' object"), + jsonstr); + return -1; + } + + return virStorageSourceParseBackingJSONInternal(src, file, jsonstr, false); +} + + +static int +virStorageSourceParseBackingJSONVxHS(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + const char *vdisk_id = virJSONValueObjectGetString(json, "vdisk-id"); + virJSONValuePtr server = virJSONValueObjectGetObject(json, "server"); + + if (!vdisk_id || !server) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'vdisk-id' or 'server' attribute in " + "JSON backing definition for VxHS volume")); + return -1; + } + + src->type = VIR_STORAGE_TYPE_NETWORK; + src->protocol = VIR_STORAGE_NET_PROTOCOL_VXHS; + + src->path = g_strdup(vdisk_id); + + src->hosts = g_new0(virStorageNetHostDef, 1); + src->nhosts = 1; + + if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts, + server) < 0) + return -1; + + return 0; +} + + +static int +virStorageSourceParseBackingJSONNFS(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + virJSONValuePtr server = virJSONValueObjectGetObject(json, "server"); + int uidStore = -1; + int gidStore = -1; + int gotUID = virJSONValueObjectGetNumberInt(json, "user", &uidStore); + int gotGID = virJSONValueObjectGetNumberInt(json, "group", &gidStore); + + if (!server) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'server' attribute in JSON backing definition for NFS volume")); + return -1; + } + + if (gotUID < 0 || gotGID < 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'user' or 'group' attribute in JSON backing definition for NFS volume")); + return -1; + } + + src->path = g_strdup(virJSONValueObjectGetString(json, "path")); + if (!src->path) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'path' attribute in JSON backing definition for NFS volume")); + return -1; + } + + src->nfs_user = g_strdup_printf("+%d", uidStore); + src->nfs_group = g_strdup_printf("+%d", gidStore); + + src->type = VIR_STORAGE_TYPE_NETWORK; + src->protocol = VIR_STORAGE_NET_PROTOCOL_NFS; + + src->hosts = g_new0(virStorageNetHostDef, 1); + src->nhosts = 1; + + if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts, + server) < 0) + return -1; + + return 0; +} + + +static int +virStorageSourceParseBackingJSONNVMe(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + g_autoptr(virStorageSourceNVMeDef) nvme = g_new0(virStorageSourceNVMeDef, 1); + const char *device = virJSONValueObjectGetString(json, "device"); + + if (!device || virPCIDeviceAddressParse((char *) device, &nvme->pciAddr) < 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing or malformed 'device' field of 'nvme' storage")); + return -1; + } + + if (virJSONValueObjectGetNumberUlong(json, "namespace", &nvme->namespc) < 0 || + nvme->namespc == 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing or malformed 'namespace' field of 'nvme' storage")); + return -1; + } + + src->type = VIR_STORAGE_TYPE_NVME; + src->nvme = g_steal_pointer(&nvme); + + return 0; +} + + +struct virStorageSourceJSONDriverParser { + const char *drvname; + bool formatdriver; + /** + * The callback gets a pre-allocated storage source @src and the JSON + * object to parse. The callback shall return -1 on error and report error + * 0 on success and 1 in cases when the configuration itself is valid, but + * can't be converted to libvirt's configuration (e.g. inline authentication + * credentials are present). + */ + int (*func)(virStorageSourcePtr src, virJSONValuePtr json, const char *jsonstr, int opaque); + int opaque; +}; + +static const struct virStorageSourceJSONDriverParser jsonParsers[] = { + {"file", false, virStorageSourceParseBackingJSONPath, VIR_STORAGE_TYPE_FILE}, + {"host_device", false, virStorageSourceParseBackingJSONPath, VIR_STORAGE_TYPE_BLOCK}, + {"host_cdrom", false, virStorageSourceParseBackingJSONPath, VIR_STORAGE_TYPE_BLOCK}, + {"http", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PROTOCOL_HTTP}, + {"https", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PROTOCOL_HTTPS}, + {"ftp", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PROTOCOL_FTP}, + {"ftps", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PROTOCOL_FTPS}, + {"tftp", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PROTOCOL_TFTP}, + {"gluster", false, virStorageSourceParseBackingJSONGluster, 0}, + {"iscsi", false, virStorageSourceParseBackingJSONiSCSI, 0}, + {"nbd", false, virStorageSourceParseBackingJSONNbd, 0}, + {"sheepdog", false, virStorageSourceParseBackingJSONSheepdog, 0}, + {"ssh", false, virStorageSourceParseBackingJSONSSH, 0}, + {"rbd", false, virStorageSourceParseBackingJSONRBD, 0}, + {"raw", true, virStorageSourceParseBackingJSONRaw, 0}, + {"nfs", false, virStorageSourceParseBackingJSONNFS, 0}, + {"vxhs", false, virStorageSourceParseBackingJSONVxHS, 0}, + {"nvme", false, virStorageSourceParseBackingJSONNVMe, 0}, +}; + + + +static int +virStorageSourceParseBackingJSONInternal(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr, + bool allowformat) +{ + const char *drvname; + size_t i; + + if (!(drvname = virJSONValueObjectGetString(json, "driver"))) { + virReportError(VIR_ERR_INVALID_ARG, + _("JSON backing volume definition '%s' lacks driver name"), + jsonstr); + return -1; + } + + for (i = 0; i < G_N_ELEMENTS(jsonParsers); i++) { + if (STRNEQ(drvname, jsonParsers[i].drvname)) + continue; + + if (jsonParsers[i].formatdriver && !allowformat) { + virReportError(VIR_ERR_INVALID_ARG, + _("JSON backing volume definition '%s' must not have nested format drivers"), + jsonstr); + return -1; + } + + return jsonParsers[i].func(src, json, jsonstr, jsonParsers[i].opaque); + } + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing parser implementation for JSON backing volume " + "driver '%s'"), drvname); + return -1; +} + + +int +virStorageSourceParseBackingJSON(virStorageSourcePtr src, + const char *json) +{ + g_autoptr(virJSONValue) root = NULL; + g_autoptr(virJSONValue) deflattened = NULL; + virJSONValuePtr file = NULL; + + if (!(root = virJSONValueFromString(json))) + return -1; + + if (!(deflattened = virJSONValueObjectDeflatten(root))) + return -1; + + /* There are 2 possible syntaxes: + * 1) json:{"file":{"driver":...}} + * 2) json:{"driver":...} + * Remove the 'file' wrapper object in case 1. + */ + if (!virJSONValueObjectHasKey(deflattened, "driver")) + file = virJSONValueObjectGetObject(deflattened, "file"); + + if (!file) + file = deflattened; + + return virStorageSourceParseBackingJSONInternal(src, file, json, true); +} diff --git a/src/storage_file/storage_source_backingstore.h b/src/storage_file/storage_source_backingstore.h new file mode 100644 index 0000000000..e51063e9e2 --- /dev/null +++ b/src/storage_file/storage_source_backingstore.h @@ -0,0 +1,40 @@ +/* + * storage_source_backingstore.h: helpers for parsing backing store strings + * + * Copyright (C) 2007-2009, 2012-2016 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * 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.1 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, see + * <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "storage_source_conf.h" + +int +virStorageSourceParseBackingURI(virStorageSourcePtr src, + const char *uristr); + +int +virStorageSourceParseRBDColonString(const char *rbdstr, + virStorageSourcePtr src); + +int +virStorageSourceParseBackingColon(virStorageSourcePtr src, + const char *path); + +int +virStorageSourceParseBackingJSON(virStorageSourcePtr src, + const char *json); -- 2.29.2

Use g_autoptr for the hash table and remove the 'ret' variable. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/storage_file/storage_source.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/storage_file/storage_source.c b/src/storage_file/storage_source.c index 9d3761f5bd..51f97b13ef 100644 --- a/src/storage_file/storage_source.c +++ b/src/storage_file/storage_source.c @@ -1421,17 +1421,13 @@ virStorageSourceGetMetadata(virStorageSourcePtr src, uid_t uid, gid_t gid, bool report_broken) { - GHashTable *cycle = NULL; + g_autoptr(GHashTable) cycle = virHashNew(NULL); virStorageType actualType = virStorageSourceGetActualType(src); - int ret = -1; VIR_DEBUG("path=%s format=%d uid=%u gid=%u report_broken=%d", src->path, src->format, (unsigned int)uid, (unsigned int)gid, report_broken); - if (!(cycle = virHashNew(NULL))) - return -1; - if (src->format <= VIR_STORAGE_FILE_NONE) { if (actualType == VIR_STORAGE_TYPE_DIR) src->format = VIR_STORAGE_FILE_DIR; @@ -1439,9 +1435,6 @@ virStorageSourceGetMetadata(virStorageSourcePtr src, src->format = VIR_STORAGE_FILE_RAW; } - ret = virStorageSourceGetMetadataRecurse(src, src, uid, gid, - report_broken, cycle, 1); - - virHashFree(cycle); - return ret; + return virStorageSourceGetMetadataRecurse(src, src, uid, gid, + report_broken, cycle, 1); } -- 2.29.2

'virDomainDiskGetSource' returns src->path effectively. Checking whether a disk is empty is done via 'virStorageSourceIsEmpty'. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/security/virt-aa-helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index f71fe6f23b..d3abe37a8c 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -933,7 +933,7 @@ get_files(vahControl * ctl) for (i = 0; i < ctl->def->ndisks; i++) { virDomainDiskDefPtr disk = ctl->def->disks[i]; - if (!virDomainDiskGetSource(disk)) + if (virStorageSourceIsEmpty(disk->src)) continue; /* XXX - if we knew the qemu user:group here we could send it in * so that the open could be re-tried as that user:group. -- 2.29.2

A terminated chain has a virStorageSource with type == VIR_STORAGE_TYPE_NONE at the end. Since virStorageSourceHasBacking is explicitly returning false in that case we'd probe the chain needlessly. Just check whether src->backingStore is non-NULL. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/security/virt-aa-helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index d3abe37a8c..d33bacde5d 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -938,7 +938,7 @@ get_files(vahControl * ctl) /* XXX - if we knew the qemu user:group here we could send it in * so that the open could be re-tried as that user:group. */ - if (!virStorageSourceHasBacking(disk->src)) + if (!disk->src->backingStore) virStorageSourceGetMetadata(disk->src, -1, -1, false); /* XXX should handle open errors more careful than just ignoring them. -- 2.29.2

Put them on one line for greppability. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/storage_file/storage_source.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/storage_file/storage_source.c b/src/storage_file/storage_source.c index 51f97b13ef..52edb91112 100644 --- a/src/storage_file/storage_source.c +++ b/src/storage_file/storage_source.c @@ -276,15 +276,13 @@ virStorageSourceChainLookup(virStorageSourcePtr chain, error: if (idx) { virReportError(VIR_ERR_INVALID_ARG, - _("could not find backing store index %u in chain " - "for '%s'"), + _("could not find backing store index '%u' in chain for '%s'"), idx, NULLSTR(start)); } else if (name) { if (startFrom) virReportError(VIR_ERR_INVALID_ARG, - _("could not find image '%s' beneath '%s' in " - "chain for '%s'"), name, NULLSTR(startFrom->path), - NULLSTR(start)); + _("could not find image '%s' beneath '%s' in chain for '%s'"), + name, NULLSTR(startFrom->path), NULLSTR(start)); else virReportError(VIR_ERR_INVALID_ARG, _("could not find image '%s' in chain for '%s'"), -- 2.29.2

The function attempts two calls to virStorageSourceChainLookup to see whether the function handles NULL correctly. This isn't very useful and additionally upcoming patch will remove the 'idx' parameter thus the test becomes obsolete. Remove it. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tests/virstoragetest.c | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c index 0e168ce730..99007dd662 100644 --- a/tests/virstoragetest.c +++ b/tests/virstoragetest.c @@ -365,36 +365,6 @@ testStorageLookup(const void *args) ret = -1; } - /* Test twice to ensure optional parameter doesn't cause NULL deref. */ - result = virStorageSourceChainLookup(data->chain, data->from, - idx ? NULL : data->name, - idx, NULL); - - if (!data->expResult) { - if (virGetLastErrorCode() == VIR_ERR_OK) { - fprintf(stderr, "call should have failed\n"); - ret = -1; - } - virResetLastError(); - } else { - if (virGetLastErrorCode()) { - fprintf(stderr, "call should not have warned\n"); - ret = -1; - } - } - - if (!result) { - if (data->expResult) { - fprintf(stderr, "result 1: expected %s, got NULL\n", - data->expResult); - ret = -1; - } - } else if (STRNEQ_NULLABLE(data->expResult, result->path)) { - fprintf(stderr, "result 1: expected %s, got %s\n", - NULLSTR(data->expResult), NULLSTR(result->path)); - ret = -1; - } - result = virStorageSourceChainLookup(data->chain, data->from, data->name, idx, &actualParent); if (!data->expResult) @@ -402,12 +372,12 @@ testStorageLookup(const void *args) if (!result) { if (data->expResult) { - fprintf(stderr, "result 2: expected %s, got NULL\n", + fprintf(stderr, "result: expected %s, got NULL\n", data->expResult); ret = -1; } } else if (STRNEQ_NULLABLE(data->expResult, result->path)) { - fprintf(stderr, "result 2: expected %s, got %s\n", + fprintf(stderr, "result: expected %s, got %s\n", NULLSTR(data->expResult), NULLSTR(result->path)); ret = -1; } -- 2.29.2

All callers of this function called virStorageFileParseChainIndex before. Internalize the logic of that function to prevent multiple calls and passing around unnecessary temporary variables. This is achieved by calling virStorageFileParseBackingStoreStr and using it to fill the values internally. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_driver.c | 19 ++++-------- src/storage_file/storage_source.c | 49 ++++++++++++++++++++++++------- src/storage_file/storage_source.h | 2 +- tests/virstoragetest.c | 2 +- 4 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ed966cf7e3..b3e663c9d5 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -14412,7 +14412,6 @@ qemuDomainBlockPullCommon(virDomainObjPtr vm, const char *jobname = NULL; virDomainDiskDefPtr disk; virStorageSourcePtr baseSource = NULL; - unsigned int baseIndex = 0; g_autofree char *basePath = NULL; g_autofree char *backingPath = NULL; unsigned long long speed = bandwidth; @@ -14448,9 +14447,8 @@ qemuDomainBlockPullCommon(virDomainObjPtr vm, goto endjob; if (base && - (virStorageFileParseChainIndex(disk->dst, base, &baseIndex) < 0 || - !(baseSource = virStorageSourceChainLookup(disk->src, disk->src, - base, baseIndex, NULL)))) + !(baseSource = virStorageSourceChainLookup(disk->src, disk->src, + base, disk->dst, NULL))) goto endjob; if (baseSource) { @@ -15465,9 +15463,7 @@ qemuDomainBlockCommit(virDomainPtr dom, int ret = -1; virDomainDiskDefPtr disk = NULL; virStorageSourcePtr topSource; - unsigned int topIndex = 0; virStorageSourcePtr baseSource = NULL; - unsigned int baseIndex = 0; virStorageSourcePtr top_parent = NULL; bool clean_access = false; g_autofree char *topPath = NULL; @@ -15540,10 +15536,8 @@ qemuDomainBlockCommit(virDomainPtr dom, if (!top || STREQ(top, disk->dst)) topSource = disk->src; - else if (virStorageFileParseChainIndex(disk->dst, top, &topIndex) < 0 || - !(topSource = virStorageSourceChainLookup(disk->src, NULL, - top, topIndex, - &top_parent))) + else if (!(topSource = virStorageSourceChainLookup(disk->src, NULL, top, + disk->dst, &top_parent))) goto endjob; if (topSource == disk->src) { @@ -15575,9 +15569,8 @@ qemuDomainBlockCommit(virDomainPtr dom, if (!base && (flags & VIR_DOMAIN_BLOCK_COMMIT_SHALLOW)) baseSource = topSource->backingStore; - else if (virStorageFileParseChainIndex(disk->dst, base, &baseIndex) < 0 || - !(baseSource = virStorageSourceChainLookup(disk->src, topSource, - base, baseIndex, NULL))) + else if (!(baseSource = virStorageSourceChainLookup(disk->src, topSource, + base, disk->dst, NULL))) goto endjob; if ((flags & VIR_DOMAIN_BLOCK_COMMIT_SHALLOW) && diff --git a/src/storage_file/storage_source.c b/src/storage_file/storage_source.c index 52edb91112..4b46cc4e84 100644 --- a/src/storage_file/storage_source.c +++ b/src/storage_file/storage_source.c @@ -198,32 +198,59 @@ virStorageSourceGetMetadataFromFD(const char *path, } -/* Given a @chain, look for the backing store @name that is a backing file - * of @startFrom (or any member of @chain if @startFrom is NULL) and return - * that location within the chain. @chain must always point to the top of - * the chain. Pass NULL for @name and 0 for @idx to find the base of the - * chain. Pass nonzero @idx to find the backing source according to its - * position in the backing chain. If @parent is not NULL, set *@parent to - * the preferred name of the parent (or to NULL if @name matches the start - * of the chain). Since the results point within @chain, they must not be - * independently freed. Reports an error and returns NULL if @name is not - * found. +/** + * virStorageSourceChainLookup: + * @chain: chain top to look in + * @startFrom: move the starting point of @chain if non-NULL + * @name: name of the file to look for in @chain + * @diskTarget: optional disk target to validate against + * @parent: Filled with parent virStorageSource of the returned value if non-NULL. + * + * Looks up a storage source definition corresponding to @name in @chain and + * returns the corresponding virStorageSource. If @startFrom is non-NULL, the + * lookup starts from that virStorageSource. + * + * @name can be: + * - NULL: the end of the source chain is returned + * - "vda[4]": Storage source with 'id' == 4 is returned. If @diskTarget is + * non-NULL it's also validated that the part before the square + * bracket matches the requested target + * - "/path/to/file": Literal path is matched. Symlink resolution is attempted + * if the filename doesn't string-match with the path. */ virStorageSourcePtr virStorageSourceChainLookup(virStorageSourcePtr chain, virStorageSourcePtr startFrom, const char *name, - unsigned int idx, + const char *diskTarget, virStorageSourcePtr *parent) { virStorageSourcePtr prev; const char *start = chain->path; bool nameIsFile = virStorageSourceBackinStoreStringIsFile(name); + g_autofree char *target = NULL; + unsigned int idx = 0; + + if (diskTarget) + start = diskTarget; if (!parent) parent = &prev; *parent = NULL; + /* parse the "vda[4]" type string */ + if (name && + virStorageFileParseBackingStoreStr(name, &target, &idx) == 0) { + if (diskTarget && + idx != 0 && + STRNEQ(diskTarget, target)) { + virReportError(VIR_ERR_INVALID_ARG, + _("requested target '%s' does not match target '%s'"), + target, diskTarget); + return NULL; + } + } + if (startFrom) { while (virStorageSourceIsBacking(chain) && chain != startFrom->backingStore) diff --git a/src/storage_file/storage_source.h b/src/storage_file/storage_source.h index 58f88a3c01..6eb795b301 100644 --- a/src/storage_file/storage_source.h +++ b/src/storage_file/storage_source.h @@ -43,7 +43,7 @@ virStorageSourcePtr virStorageSourceChainLookup(virStorageSourcePtr chain, virStorageSourcePtr startFrom, const char *name, - unsigned int idx, + const char *diskTarget, virStorageSourcePtr *parent) ATTRIBUTE_NONNULL(1); diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c index 99007dd662..c976a1c0d0 100644 --- a/tests/virstoragetest.c +++ b/tests/virstoragetest.c @@ -366,7 +366,7 @@ testStorageLookup(const void *args) } result = virStorageSourceChainLookup(data->chain, data->from, - data->name, idx, &actualParent); + data->name, data->target, &actualParent); if (!data->expResult) virResetLastError(); -- 2.29.2

Test the actual index in the returned virStorageSource rather than the parsed one. Some tests need to be adapted as they were on failed lookup. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tests/virstoragetest.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c index c976a1c0d0..762328df9a 100644 --- a/tests/virstoragetest.c +++ b/tests/virstoragetest.c @@ -353,17 +353,6 @@ testStorageLookup(const void *args) int ret = 0; virStorageSourcePtr result; virStorageSourcePtr actualParent; - unsigned int idx; - - if (virStorageFileParseChainIndex(data->target, data->name, &idx) < 0 && - data->expIndex) { - fprintf(stderr, "call should not have failed\n"); - ret = -1; - } - if (idx != data->expIndex) { - fprintf(stderr, "index: expected %u, got %u\n", data->expIndex, idx); - ret = -1; - } result = virStorageSourceChainLookup(data->chain, data->from, data->name, data->target, &actualParent); @@ -386,6 +375,17 @@ testStorageLookup(const void *args) data->expMeta, result); ret = -1; } + if (data->expIndex > 0) { + if (!result) { + fprintf(stderr, "index: resulting lookup is empty, can't match index\n"); + ret = -1; + } else { + if (result->id != data->expIndex) { + fprintf(stderr, "index: expected %u, got %u\n", data->expIndex, result->id); + ret = -1; + } + } + } if (data->expParent != actualParent) { fprintf(stderr, "parent: expected %s, got %s\n", NULLSTR(data->expParent ? data->expParent->path : NULL), @@ -1077,13 +1077,13 @@ mymain(void) TEST_LOOKUP_TARGET(72, "vda", NULL, "vda[0]", 0, NULL, NULL, NULL); TEST_LOOKUP_TARGET(73, "vda", NULL, "vda[1]", 1, chain2->path, chain2, chain); TEST_LOOKUP_TARGET(74, "vda", chain, "vda[1]", 1, chain2->path, chain2, chain); - TEST_LOOKUP_TARGET(75, "vda", chain2, "vda[1]", 1, NULL, NULL, NULL); - TEST_LOOKUP_TARGET(76, "vda", chain3, "vda[1]", 1, NULL, NULL, NULL); + TEST_LOOKUP_TARGET(75, "vda", chain2, "vda[1]", 0, NULL, NULL, NULL); + TEST_LOOKUP_TARGET(76, "vda", chain3, "vda[1]", 0, NULL, NULL, NULL); TEST_LOOKUP_TARGET(77, "vda", NULL, "vda[2]", 2, chain3->path, chain3, chain2); TEST_LOOKUP_TARGET(78, "vda", chain, "vda[2]", 2, chain3->path, chain3, chain2); TEST_LOOKUP_TARGET(79, "vda", chain2, "vda[2]", 2, chain3->path, chain3, chain2); - TEST_LOOKUP_TARGET(80, "vda", chain3, "vda[2]", 2, NULL, NULL, NULL); - TEST_LOOKUP_TARGET(81, "vda", NULL, "vda[3]", 3, NULL, NULL, NULL); + TEST_LOOKUP_TARGET(80, "vda", chain3, "vda[2]", 0, NULL, NULL, NULL); + TEST_LOOKUP_TARGET(81, "vda", NULL, "vda[3]", 0, NULL, NULL, NULL); #define TEST_PATH_CANONICALIZE(id, PATH, EXPECT) \ do { \ -- 2.29.2

Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/libvirt_private.syms | 1 - src/util/virstoragefile.c | 32 -------------------------------- src/util/virstoragefile.h | 5 ----- 3 files changed, 38 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0a2a54dfdf..4b6fca63d4 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3217,7 +3217,6 @@ virStorageFileCanonicalizePath; virStorageFileGetNPIVKey; virStorageFileGetSCSIKey; virStorageFileParseBackingStoreStr; -virStorageFileParseChainIndex; # util/virstring.h diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index d1e56db708..9df891b57c 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -216,38 +216,6 @@ virStorageFileParseBackingStoreStr(const char *str, } -int -virStorageFileParseChainIndex(const char *diskTarget, - const char *name, - unsigned int *chainIndex) -{ - unsigned int idx = 0; - g_autofree char *target = NULL; - - *chainIndex = 0; - - if (!name || !diskTarget) - return 0; - - if (virStorageFileParseBackingStoreStr(name, &target, &idx) < 0) - return 0; - - if (idx == 0) - return 0; - - if (STRNEQ(diskTarget, target)) { - virReportError(VIR_ERR_INVALID_ARG, - _("requested target '%s' does not match target '%s'"), - target, diskTarget); - return -1; - } - - *chainIndex = idx; - - return 0; -} - - static char * virStorageFileCanonicalizeFormatPath(char **components, size_t ncomponents, diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 455a978a8d..6b198858cc 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -23,11 +23,6 @@ #include "internal.h" -int virStorageFileParseChainIndex(const char *diskTarget, - const char *name, - unsigned int *chainIndex) - ATTRIBUTE_NONNULL(3); - int virStorageFileParseBackingStoreStr(const char *str, char **target, unsigned int *chainIndex) -- 2.29.2

On Mon, Jan 25, 2021 at 05:05:12PM +0100, Peter Krempa wrote:
Peter Krempa (12): virStorageSourceGetBackingStoreStr: Move the function earlier virStorageSourceGetBackingStoreStr: Return relative paths only util: virstoragefile: Move virStorageIs[File|Relative] to storage_source storage_source: Move backing store parsers into new file virStorageSourceGetMetadata: Refactor cleanup virt-aa-helper: Use proper check for empty disk in 'get_files' virt-aa-helper: Don't probe image metadata for terminated chains virStorageSourceChainLookup: Don't break error message strings test: storage: Remove double testing in testStorageLookup virStorageSourceChainLookup: Handle names like 'vda[4]' internally tests: storage: Replace index testing in testStorageLookup util: Remove unused 'virStorageFileParseChainIndex'
po/POTFILES.in | 1 + src/libvirt_private.syms | 5 +- src/qemu/qemu_block.c | 6 +- src/qemu/qemu_driver.c | 19 +- src/qemu/qemu_snapshot.c | 13 +- src/security/virt-aa-helper.c | 4 +- src/storage_file/meson.build | 1 + src/storage_file/storage_source.c | 1681 +++-------------- src/storage_file/storage_source.h | 6 +- .../storage_source_backingstore.c | 1240 ++++++++++++ .../storage_source_backingstore.h | 40 + src/util/virstoragefile.c | 66 - src/util/virstoragefile.h | 8 - tests/virstoragetest.c | 66 +- 14 files changed, 1582 insertions(+), 1574 deletions(-) create mode 100644 src/storage_file/storage_source_backingstore.c create mode 100644 src/storage_file/storage_source_backingstore.h
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
participants (2)
-
Pavel Hrdina
-
Peter Krempa