[PATCH 04/12] storage_source: Move backing store parsers into new file

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxx>
---
 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





[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux