[libvirt PATCH 13/17] util: extract virStorageFile code into storage_file

Pavel Hrdina phrdina at redhat.com
Mon Dec 14 15:55:33 UTC 2020


Up until now we had a runtime code and XML related code in the same
source file inside util directory.

This patch takes the runtime part and extracts it into the new
storage_file directory.

Signed-off-by: Pavel Hrdina <phrdina at redhat.com>
---
 po/POTFILES.in                          |    1 +
 src/libvirt_private.syms                |   71 +-
 src/libxl/meson.build                   |    1 +
 src/libxl/xen_xl.c                      |    1 +
 src/qemu/meson.build                    |    1 +
 src/qemu/qemu_backup.c                  |    1 +
 src/qemu/qemu_block.c                   |    1 +
 src/qemu/qemu_domain.c                  |    1 +
 src/qemu/qemu_driver.c                  |    1 +
 src/qemu/qemu_hotplug.c                 |    1 +
 src/qemu/qemu_process.c                 |    1 +
 src/qemu/qemu_snapshot.c                |    1 +
 src/storage/meson.build                 |    4 +
 src/storage/storage_backend_gluster.c   |    1 +
 src/storage/storage_util.c              |    1 +
 src/storage_file/meson.build            |   18 +
 src/storage_file/storage_file.c         | 3845 +++++++++++++++++++++++
 src/storage_file/storage_file.h         |  197 ++
 src/storage_file/storage_file_gluster.c |    1 +
 src/util/virstoragefile.c               | 3783 +---------------------
 src/util/virstoragefile.h               |  118 -
 tests/meson.build                       |    4 +-
 tests/qemublocktest.c                   |    1 +
 tests/virstoragetest.c                  |    1 +
 24 files changed, 4120 insertions(+), 3936 deletions(-)
 create mode 100644 src/storage_file/storage_file.c
 create mode 100644 src/storage_file/storage_file.h

diff --git a/po/POTFILES.in b/po/POTFILES.in
index e9fc3991f1..6d6678bd36 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -223,6 +223,7 @@
 @SRCDIR at src/storage/storage_backend_zfs.c
 @SRCDIR at src/storage/storage_driver.c
 @SRCDIR at src/storage/storage_util.c
+ at SRCDIR@src/storage_file/storage_file.c
 @SRCDIR at src/storage_file/storage_file_fs.c
 @SRCDIR at src/storage_file/storage_file_gluster.c
 @SRCDIR at src/test/test_driver.c
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 1bbf567847..43be8dd61a 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1618,6 +1618,43 @@ virSecurityManagerVerify;
 virSecurityXATTRNamespaceDefined;
 
 
+# storage_file/storage_file.h
+virStorageFileAccess;
+virStorageFileCanonicalizePath;
+virStorageFileChainLookup;
+virStorageFileChown;
+virStorageFileCreate;
+virStorageFileDeinit;
+virStorageFileGetBackingStoreStr;
+virStorageFileGetMetadata;
+virStorageFileGetMetadataFromBuf;
+virStorageFileGetMetadataFromFD;
+virStorageFileGetRelativeBackingPath;
+virStorageFileGetUniqueIdentifier;
+virStorageFileInit;
+virStorageFileInitAs;
+virStorageFileParseBackingStoreStr;
+virStorageFileParseChainIndex;
+virStorageFileProbeFormat;
+virStorageFileRead;
+virStorageFileReportBrokenChain;
+virStorageFileStat;
+virStorageFileSupportsAccess;
+virStorageFileSupportsBackingChainTraversal;
+virStorageFileSupportsCreate;
+virStorageFileSupportsSecurityDriver;
+virStorageFileUnlink;
+virStorageSourceFindByNodeName;
+virStorageSourceNewFromBacking;
+virStorageSourceNewFromBackingAbsolute;
+virStorageSourceParseRBDColonString;
+virStorageSourcePrivateDataFormatRelPath;
+virStorageSourcePrivateDataParseRelPath;
+virStorageSourceUpdateBackingSizes;
+virStorageSourceUpdateCapacity;
+virStorageSourceUpdatePhysicalSize;
+
+
 # util/glibcompat.h
 vir_g_canonicalize_filename;
 vir_g_fsync;
@@ -3127,37 +3164,12 @@ virStorageAuthDefCopy;
 virStorageAuthDefFormat;
 virStorageAuthDefFree;
 virStorageAuthDefParse;
-virStorageFileAccess;
-virStorageFileCanonicalizePath;
-virStorageFileChainLookup;
-virStorageFileChown;
-virStorageFileCreate;
-virStorageFileDeinit;
 virStorageFileFeatureTypeFromString;
 virStorageFileFeatureTypeToString;
 virStorageFileFormatTypeFromString;
 virStorageFileFormatTypeToString;
-virStorageFileGetBackingStoreStr;
-virStorageFileGetMetadata;
-virStorageFileGetMetadataFromBuf;
-virStorageFileGetMetadataFromFD;
 virStorageFileGetNPIVKey;
-virStorageFileGetRelativeBackingPath;
 virStorageFileGetSCSIKey;
-virStorageFileGetUniqueIdentifier;
-virStorageFileInit;
-virStorageFileInitAs;
-virStorageFileParseBackingStoreStr;
-virStorageFileParseChainIndex;
-virStorageFileProbeFormat;
-virStorageFileRead;
-virStorageFileReportBrokenChain;
-virStorageFileStat;
-virStorageFileSupportsAccess;
-virStorageFileSupportsBackingChainTraversal;
-virStorageFileSupportsCreate;
-virStorageFileSupportsSecurityDriver;
-virStorageFileUnlink;
 virStorageNetHostDefClear;
 virStorageNetHostDefCopy;
 virStorageNetHostDefFree;
@@ -3174,7 +3186,6 @@ virStorageSourceChainHasManagedPR;
 virStorageSourceChainHasNVMe;
 virStorageSourceClear;
 virStorageSourceCopy;
-virStorageSourceFindByNodeName;
 virStorageSourceGetActualType;
 virStorageSourceGetSecurityLabelDef;
 virStorageSourceHasBacking;
@@ -3192,18 +3203,10 @@ virStorageSourceIsSameLocation;
 virStorageSourceNetCookiesValidate;
 virStorageSourceNetworkAssignDefaultPorts;
 virStorageSourceNew;
-virStorageSourceNewFromBacking;
-virStorageSourceNewFromBackingAbsolute;
 virStorageSourceNVMeDefFree;
-virStorageSourceParseRBDColonString;
 virStorageSourcePoolDefFree;
 virStorageSourcePoolModeTypeFromString;
 virStorageSourcePoolModeTypeToString;
-virStorageSourcePrivateDataFormatRelPath;
-virStorageSourcePrivateDataParseRelPath;
-virStorageSourceUpdateBackingSizes;
-virStorageSourceUpdateCapacity;
-virStorageSourceUpdatePhysicalSize;
 virStorageTypeFromString;
 virStorageTypeToString;
 
diff --git a/src/libxl/meson.build b/src/libxl/meson.build
index 3bb6cc5f2e..783af6c667 100644
--- a/src/libxl/meson.build
+++ b/src/libxl/meson.build
@@ -27,6 +27,7 @@ if conf.has('WITH_LIBXL')
     include_directories: [
       conf_inc_dir,
       hypervisor_inc_dir,
+      storage_file_inc_dir,
     ],
   )
 
diff --git a/src/libxl/xen_xl.c b/src/libxl/xen_xl.c
index ba0942601f..1f97e6bdd4 100644
--- a/src/libxl/xen_xl.c
+++ b/src/libxl/xen_xl.c
@@ -30,6 +30,7 @@
 #include "viralloc.h"
 #include "virstring.h"
 #include "virstoragefile.h"
+#include "storage_file.h"
 #include "xen_xl.h"
 #include "libxl_capabilities.h"
 #include "libxl_conf.h"
diff --git a/src/qemu/meson.build b/src/qemu/meson.build
index 90640b03c6..7ab591d040 100644
--- a/src/qemu/meson.build
+++ b/src/qemu/meson.build
@@ -104,6 +104,7 @@ if conf.has('WITH_QEMU')
     include_directories: [
       conf_inc_dir,
       hypervisor_inc_dir,
+      storage_file_inc_dir,
     ],
   )
 
diff --git a/src/qemu/qemu_backup.c b/src/qemu/qemu_backup.c
index b2340eb1cf..bd699ad29f 100644
--- a/src/qemu/qemu_backup.c
+++ b/src/qemu/qemu_backup.c
@@ -29,6 +29,7 @@
 #include "qemu_checkpoint.h"
 #include "qemu_command.h"
 
+#include "storage_file.h"
 #include "virerror.h"
 #include "virlog.h"
 #include "virbuffer.h"
diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c
index 0743db4d2c..5444b5f4b7 100644
--- a/src/qemu/qemu_block.c
+++ b/src/qemu/qemu_block.c
@@ -24,6 +24,7 @@
 #include "qemu_alias.h"
 #include "qemu_security.h"
 
+#include "storage_file.h"
 #include "viralloc.h"
 #include "virstring.h"
 #include "virlog.h"
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index bfb6e23942..ef073e5395 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -52,6 +52,7 @@
 #include "virtime.h"
 #include "virnetdevopenvswitch.h"
 #include "virstoragefile.h"
+#include "storage_file.h"
 #include "virstring.h"
 #include "virthreadjob.h"
 #include "virprocess.h"
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 5f0fb0a55f..00ce56c6b9 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -83,6 +83,7 @@
 #include "domain_nwfilter.h"
 #include "virhook.h"
 #include "virstoragefile.h"
+#include "storage_file.h"
 #include "virfile.h"
 #include "virfdstream.h"
 #include "configmake.h"
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 9b93f256e8..a0727b8b90 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -53,6 +53,7 @@
 #include "virnetdevmidonet.h"
 #include "device_conf.h"
 #include "virstoragefile.h"
+#include "storage_file.h"
 #include "virstring.h"
 #include "virtime.h"
 #include "virqemu.h"
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index bb78967ca3..52f33985cf 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -95,6 +95,7 @@
 #include "viridentity.h"
 #include "virthreadjob.h"
 #include "virutil.h"
+#include "storage_file.h"
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
index 6f64111ca8..9633c8ab40 100644
--- a/src/qemu/qemu_snapshot.c
+++ b/src/qemu/qemu_snapshot.c
@@ -41,6 +41,7 @@
 #include "virstring.h"
 #include "virdomainsnapshotobjlist.h"
 #include "virqemu.h"
+#include "storage_file.h"
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
diff --git a/src/storage/meson.build b/src/storage/meson.build
index 8537359e93..153ff6f846 100644
--- a/src/storage/meson.build
+++ b/src/storage/meson.build
@@ -79,6 +79,7 @@ if conf.has('WITH_STORAGE')
     ],
     include_directories: [
       conf_inc_dir,
+      include_directories('../storage_file'),
     ],
   )
 
@@ -162,6 +163,9 @@ if conf.has('WITH_STORAGE_GLUSTER')
     'deps': [
       glusterfs_dep,
     ],
+    'include': [
+      include_directories('../storage_file'),
+    ],
     'install_dir': storage_backend_install_dir,
   }
 endif
diff --git a/src/storage/storage_backend_gluster.c b/src/storage/storage_backend_gluster.c
index 6c99c270da..671d62bf15 100644
--- a/src/storage/storage_backend_gluster.c
+++ b/src/storage/storage_backend_gluster.c
@@ -25,6 +25,7 @@
 
 #include "storage_backend_gluster.h"
 #include "storage_conf.h"
+#include "storage_file.h"
 #include "viralloc.h"
 #include "virerror.h"
 #include "virlog.h"
diff --git a/src/storage/storage_util.c b/src/storage/storage_util.c
index 652f65fce2..a1949a5a24 100644
--- a/src/storage/storage_util.c
+++ b/src/storage/storage_util.c
@@ -62,6 +62,7 @@
 #include "vircrypto.h"
 #include "viruuid.h"
 #include "virstoragefile.h"
+#include "storage_file.h"
 #include "storage_util.h"
 #include "virlog.h"
 #include "virfile.h"
diff --git a/src/storage_file/meson.build b/src/storage_file/meson.build
index 20eb0176fc..b25ca63b40 100644
--- a/src/storage_file/meson.build
+++ b/src/storage_file/meson.build
@@ -1,3 +1,7 @@
+storage_file_sources = [
+  'storage_file.c'
+]
+
 stoarge_file_fs_sources = [
   'storage_file_fs.c',
 ]
@@ -8,6 +12,18 @@ storage_file_gluster_sources = [
 
 storage_file_install_dir = libdir / 'libvirt' / 'storage-file'
 
+virt_storage_file_lib = static_library(
+  'virt_storage_file',
+  [
+    storage_file_sources,
+  ],
+  dependencies: [
+    src_dep,
+  ],
+)
+
+libvirt_libs += virt_storage_file_lib
+
 if conf.has('WITH_STORAGE')
   virt_modules += {
     'name': 'virt_storage_file_fs',
@@ -36,3 +52,5 @@ if conf.has('WITH_STORAGE_GLUSTER')
     'install_dir': storage_file_install_dir,
   }
 endif
+
+storage_file_inc_dir = include_directories('.')
diff --git a/src/storage_file/storage_file.c b/src/storage_file/storage_file.c
new file mode 100644
index 0000000000..6bfeb26233
--- /dev/null
+++ b/src/storage_file/storage_file.c
@@ -0,0 +1,3845 @@
+/*
+ * storage_file.c: file utility functions for FS storage backend
+ *
+ * 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 <fcntl.h>
+#include <unistd.h>
+
+#include "storage_file.h"
+#include "viralloc.h"
+#include "virendian.h"
+#include "virfile.h"
+#include "virhash.h"
+#include "virjson.h"
+#include "virlog.h"
+#include "virstoragefilebackend.h"
+#include "virstring.h"
+#include "viruri.h"
+
+#define VIR_FROM_THIS VIR_FROM_STORAGE
+
+VIR_LOG_INIT("storage_file");
+
+
+enum lv_endian {
+    LV_LITTLE_ENDIAN = 1, /* 1234 */
+    LV_BIG_ENDIAN         /* 4321 */
+};
+
+enum {
+    BACKING_STORE_OK,
+    BACKING_STORE_INVALID,
+    BACKING_STORE_ERROR,
+};
+
+#define FILE_TYPE_VERSIONS_LAST 3
+
+struct FileEncryptionInfo {
+    int format; /* Encryption format to assign */
+
+    int magicOffset; /* Byte offset of the magic */
+    const char *magic; /* Optional string of magic */
+
+    enum lv_endian endian; /* Endianness of file format */
+
+    int versionOffset;    /* Byte offset from start of file
+                           * where we find version number,
+                           * -1 to always fail the version test,
+                           * -2 to always pass the version test */
+    int versionSize;      /* Size in bytes of version data (0, 2, or 4) */
+    int versionNumbers[FILE_TYPE_VERSIONS_LAST];
+                          /* Version numbers to validate. Zeroes are ignored. */
+
+    int modeOffset; /* Byte offset of the format native encryption mode */
+    char modeValue; /* Value expected at offset */
+
+    int payloadOffset; /* start offset of the volume data (in 512 byte sectors) */
+};
+
+struct FileTypeInfo {
+    int magicOffset;    /* Byte offset of the magic */
+    const char *magic;  /* Optional string of file magic
+                         * to check at head of file */
+    enum lv_endian endian; /* Endianness of file format */
+
+    int versionOffset;    /* Byte offset from start of file
+                           * where we find version number,
+                           * -1 to always fail the version test,
+                           * -2 to always pass the version test */
+    int versionSize;      /* Size in bytes of version data (0, 2, or 4) */
+    int versionNumbers[FILE_TYPE_VERSIONS_LAST];
+                          /* Version numbers to validate. Zeroes are ignored. */
+    int sizeOffset;       /* Byte offset from start of file
+                           * where we find capacity info,
+                           * -1 to use st_size as capacity */
+    int sizeBytes;        /* Number of bytes for size field */
+    int sizeMultiplier;   /* A scaling factor if size is not in bytes */
+                          /* Store a COW base image path (possibly relative),
+                           * or NULL if there is no COW base image, to RES;
+                           * return BACKING_STORE_* */
+    const struct FileEncryptionInfo *cryptInfo; /* Encryption info */
+    int (*getBackingStore)(char **res, int *format,
+                           const char *buf, size_t buf_size);
+    int (*getFeatures)(virBitmapPtr *features, int format,
+                       char *buf, ssize_t len);
+};
+
+
+static int
+cowGetBackingStore(char **,
+                   int *,
+                   const char *,
+                   size_t);
+
+static int
+qcowXGetBackingStore(char **, int *,
+                     const char *,
+                     size_t);
+
+static int
+qcow2GetFeatures(virBitmapPtr *features,
+                 int format,
+                 char *buf,
+                 ssize_t len);
+
+static int
+vmdk4GetBackingStore(char **,
+                     int *,
+                     const char *,
+                     size_t);
+
+static int
+qedGetBackingStore(char **,
+                   int *,
+                   const char *,
+                   size_t);
+
+
+#define QCOWX_HDR_VERSION (4)
+#define QCOWX_HDR_BACKING_FILE_OFFSET (QCOWX_HDR_VERSION+4)
+#define QCOWX_HDR_BACKING_FILE_SIZE (QCOWX_HDR_BACKING_FILE_OFFSET+8)
+#define QCOWX_HDR_IMAGE_SIZE (QCOWX_HDR_BACKING_FILE_SIZE+4+4)
+
+#define QCOW1_HDR_CRYPT (QCOWX_HDR_IMAGE_SIZE+8+1+1+2)
+#define QCOW2_HDR_CRYPT (QCOWX_HDR_IMAGE_SIZE+8)
+
+#define QCOW1_HDR_TOTAL_SIZE (QCOW1_HDR_CRYPT+4+8)
+#define QCOW2_HDR_TOTAL_SIZE (QCOW2_HDR_CRYPT+4+4+8+8+4+4+8)
+
+#define QCOW2_HDR_EXTENSION_END 0
+#define QCOW2_HDR_EXTENSION_BACKING_FORMAT 0xE2792ACA
+
+#define QCOW2v3_HDR_FEATURES_INCOMPATIBLE (QCOW2_HDR_TOTAL_SIZE)
+#define QCOW2v3_HDR_FEATURES_COMPATIBLE (QCOW2v3_HDR_FEATURES_INCOMPATIBLE+8)
+#define QCOW2v3_HDR_FEATURES_AUTOCLEAR (QCOW2v3_HDR_FEATURES_COMPATIBLE+8)
+
+/* The location of the header size [4 bytes] */
+#define QCOW2v3_HDR_SIZE       (QCOW2_HDR_TOTAL_SIZE+8+8+8+4)
+
+#define QED_HDR_FEATURES_OFFSET (4+4+4+4)
+#define QED_HDR_IMAGE_SIZE (QED_HDR_FEATURES_OFFSET+8+8+8+8)
+#define QED_HDR_BACKING_FILE_OFFSET (QED_HDR_IMAGE_SIZE+8)
+#define QED_HDR_BACKING_FILE_SIZE (QED_HDR_BACKING_FILE_OFFSET+4)
+#define QED_F_BACKING_FILE 0x01
+#define QED_F_BACKING_FORMAT_NO_PROBE 0x04
+
+#define PLOOP_IMAGE_SIZE_OFFSET 36
+#define PLOOP_SIZE_MULTIPLIER 512
+
+#define LUKS_HDR_MAGIC_LEN 6
+#define LUKS_HDR_VERSION_LEN 2
+#define LUKS_HDR_CIPHER_NAME_LEN 32
+#define LUKS_HDR_CIPHER_MODE_LEN 32
+#define LUKS_HDR_HASH_SPEC_LEN 32
+#define LUKS_HDR_PAYLOAD_LEN 4
+
+/* Format described by qemu commit id '3e308f20e' */
+#define LUKS_HDR_VERSION_OFFSET LUKS_HDR_MAGIC_LEN
+#define LUKS_HDR_PAYLOAD_OFFSET (LUKS_HDR_MAGIC_LEN+\
+                                 LUKS_HDR_VERSION_LEN+\
+                                 LUKS_HDR_CIPHER_NAME_LEN+\
+                                 LUKS_HDR_CIPHER_MODE_LEN+\
+                                 LUKS_HDR_HASH_SPEC_LEN)
+
+
+static struct FileEncryptionInfo const luksEncryptionInfo[] = {
+    {
+        .format = VIR_STORAGE_ENCRYPTION_FORMAT_LUKS,
+
+        /* Magic is 'L','U','K','S', 0xBA, 0xBE */
+        .magicOffset = 0,
+        .magic = "\x4c\x55\x4b\x53\xba\xbe",
+        .endian = LV_BIG_ENDIAN,
+
+        .versionOffset  = LUKS_HDR_VERSION_OFFSET,
+        .versionSize = LUKS_HDR_VERSION_LEN,
+        .versionNumbers = {1},
+
+        .modeOffset = -1,
+        .modeValue = -1,
+
+        .payloadOffset = LUKS_HDR_PAYLOAD_OFFSET,
+    },
+    { 0 }
+};
+
+
+static struct FileEncryptionInfo const qcow1EncryptionInfo[] = {
+    {
+        .format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW,
+
+        .magicOffset = 0,
+        .magic = NULL,
+        .endian = LV_BIG_ENDIAN,
+
+        .versionOffset  = -1,
+        .versionSize = 0,
+        .versionNumbers = {},
+
+        .modeOffset = QCOW1_HDR_CRYPT,
+        .modeValue = 1,
+
+        .payloadOffset = -1,
+    },
+    { 0 }
+};
+
+
+static struct FileEncryptionInfo const qcow2EncryptionInfo[] = {
+    {
+        .format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW,
+
+        .magicOffset = 0,
+        .magic = NULL,
+        .endian = LV_BIG_ENDIAN,
+
+        .versionOffset  = -1,
+        .versionSize = 0,
+        .versionNumbers = {},
+
+        .modeOffset = QCOW2_HDR_CRYPT,
+        .modeValue = 1,
+
+        .payloadOffset = -1,
+    },
+    {
+        .format = VIR_STORAGE_ENCRYPTION_FORMAT_LUKS,
+
+        .magicOffset = 0,
+        .magic = NULL,
+        .endian = LV_BIG_ENDIAN,
+
+        .versionOffset  = -1,
+        .versionSize = 0,
+        .versionNumbers = {},
+
+        .modeOffset = QCOW2_HDR_CRYPT,
+        .modeValue = 2,
+
+        .payloadOffset = -1,
+    },
+    { 0 }
+};
+
+
+static struct FileTypeInfo const fileTypeInfo[] = {
+    [VIR_STORAGE_FILE_NONE] = { 0, NULL, LV_LITTLE_ENDIAN,
+                                -1, 0, {0}, 0, 0, 0, NULL, NULL, NULL },
+    [VIR_STORAGE_FILE_RAW] = { 0, NULL, LV_LITTLE_ENDIAN,
+                               -1, 0, {0}, 0, 0, 0,
+                               luksEncryptionInfo,
+                               NULL, NULL },
+    [VIR_STORAGE_FILE_DIR] = { 0, NULL, LV_LITTLE_ENDIAN,
+                               -1, 0, {0}, 0, 0, 0, NULL, NULL, NULL },
+    [VIR_STORAGE_FILE_BOCHS] = {
+        /*"Bochs Virtual HD Image", */ /* Untested */
+        0, NULL,
+        LV_LITTLE_ENDIAN, 64, 4, {0x20000},
+        32+16+16+4+4+4+4+4, 8, 1, NULL, NULL, NULL
+    },
+    [VIR_STORAGE_FILE_CLOOP] = {
+        /* #!/bin/sh
+           #V2.0 Format
+           modprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1
+        */ /* Untested */
+        0, NULL,
+        LV_LITTLE_ENDIAN, -1, 0, {0},
+        -1, 0, 0, NULL, NULL, NULL
+    },
+    [VIR_STORAGE_FILE_DMG] = {
+        /* XXX QEMU says there's no magic for dmg,
+         * /usr/share/misc/magic lists double magic (both offsets
+         * would have to match) but then disables that check. */
+        0, NULL,
+        0, -1, 0, {0},
+        -1, 0, 0, NULL, NULL, NULL
+    },
+    [VIR_STORAGE_FILE_ISO] = {
+        32769, "CD001",
+        LV_LITTLE_ENDIAN, -2, 0, {0},
+        -1, 0, 0, NULL, NULL, NULL
+    },
+    [VIR_STORAGE_FILE_VPC] = {
+        0, "conectix",
+        LV_BIG_ENDIAN, 12, 4, {0x10000},
+        8 + 4 + 4 + 8 + 4 + 4 + 2 + 2 + 4, 8, 1, NULL, NULL, NULL
+    },
+    /* TODO: add getBackingStore function */
+    [VIR_STORAGE_FILE_VDI] = {
+        64, "\x7f\x10\xda\xbe",
+        LV_LITTLE_ENDIAN, 68, 4, {0x00010001},
+        64 + 5 * 4 + 256 + 7 * 4, 8, 1, NULL, NULL, NULL},
+
+    /* Not direct file formats, but used for various drivers */
+    [VIR_STORAGE_FILE_FAT] = { 0, NULL, LV_LITTLE_ENDIAN,
+                               -1, 0, {0}, 0, 0, 0, NULL, NULL, NULL },
+    [VIR_STORAGE_FILE_VHD] = { 0, NULL, LV_LITTLE_ENDIAN,
+                               -1, 0, {0}, 0, 0, 0, NULL, NULL, NULL },
+    [VIR_STORAGE_FILE_PLOOP] = { 0, "WithouFreSpacExt", LV_LITTLE_ENDIAN,
+                                 -2, 0, {0}, PLOOP_IMAGE_SIZE_OFFSET, 0,
+                                 PLOOP_SIZE_MULTIPLIER, NULL, NULL, NULL },
+
+    /* All formats with a backing store probe below here */
+    [VIR_STORAGE_FILE_COW] = {
+        0, "OOOM",
+        LV_BIG_ENDIAN, 4, 4, {2},
+        4+4+1024+4, 8, 1, NULL, cowGetBackingStore, NULL
+    },
+    [VIR_STORAGE_FILE_QCOW] = {
+        0, "QFI",
+        LV_BIG_ENDIAN, 4, 4, {1},
+        QCOWX_HDR_IMAGE_SIZE, 8, 1,
+        qcow1EncryptionInfo,
+        qcowXGetBackingStore, NULL
+    },
+    [VIR_STORAGE_FILE_QCOW2] = {
+        0, "QFI",
+        LV_BIG_ENDIAN, 4, 4, {2, 3},
+        QCOWX_HDR_IMAGE_SIZE, 8, 1,
+        qcow2EncryptionInfo,
+        qcowXGetBackingStore,
+        qcow2GetFeatures
+    },
+    [VIR_STORAGE_FILE_QED] = {
+        /* https://wiki.qemu.org/Features/QED */
+        0, "QED",
+        LV_LITTLE_ENDIAN, -2, 0, {0},
+        QED_HDR_IMAGE_SIZE, 8, 1, NULL, qedGetBackingStore, NULL
+    },
+    [VIR_STORAGE_FILE_VMDK] = {
+        0, "KDMV",
+        LV_LITTLE_ENDIAN, 4, 4, {1, 2, 3},
+        4+4+4, 8, 512, NULL, vmdk4GetBackingStore, NULL
+    },
+};
+G_STATIC_ASSERT(G_N_ELEMENTS(fileTypeInfo) == VIR_STORAGE_FILE_LAST);
+
+
+/* qcow2 compatible features in the order they appear on-disk */
+enum qcow2CompatibleFeature {
+    QCOW2_COMPATIBLE_FEATURE_LAZY_REFCOUNTS = 0,
+
+    QCOW2_COMPATIBLE_FEATURE_LAST
+};
+
+
+/* conversion to virStorageFileFeature */
+static const int qcow2CompatibleFeatureArray[] = {
+    VIR_STORAGE_FILE_FEATURE_LAZY_REFCOUNTS,
+};
+G_STATIC_ASSERT(G_N_ELEMENTS(qcow2CompatibleFeatureArray) ==
+       QCOW2_COMPATIBLE_FEATURE_LAST);
+
+
+static int
+cowGetBackingStore(char **res,
+                   int *format,
+                   const char *buf,
+                   size_t buf_size)
+{
+#define COW_FILENAME_MAXLEN 1024
+    *res = NULL;
+    *format = VIR_STORAGE_FILE_AUTO;
+
+    if (buf_size < 4+4+ COW_FILENAME_MAXLEN)
+        return BACKING_STORE_INVALID;
+    if (buf[4+4] == '\0') { /* cow_header_v2.backing_file[0] */
+        *format = VIR_STORAGE_FILE_NONE;
+        return BACKING_STORE_OK;
+    }
+
+    *res = g_strndup((const char *)buf + 4 + 4, COW_FILENAME_MAXLEN);
+    return BACKING_STORE_OK;
+}
+
+
+static int
+qcow2GetExtensions(const char *buf,
+                   size_t buf_size,
+                   int *backingFormat)
+{
+    size_t offset;
+    size_t extension_start;
+    size_t extension_end;
+    int version = virReadBufInt32BE(buf + QCOWX_HDR_VERSION);
+
+    if (version < 2) {
+        /* QCow1 doesn't have the extensions capability
+         * used to store backing format */
+        return 0;
+    }
+
+    if (version == 2)
+        extension_start = QCOW2_HDR_TOTAL_SIZE;
+    else
+        extension_start = virReadBufInt32BE(buf + QCOW2v3_HDR_SIZE);
+
+    /*
+     * Traditionally QCow2 files had a layout of
+     *
+     * [header]
+     * [backingStoreName]
+     *
+     * Although the backingStoreName typically followed
+     * the header immediately, this was not required by
+     * the format. By specifying a higher byte offset for
+     * the backing file offset in the header, it was
+     * possible to leave space between the header and
+     * start of backingStore.
+     *
+     * This hack is now used to store extensions to the
+     * qcow2 format:
+     *
+     * [header]
+     * [extensions]
+     * [backingStoreName]
+     *
+     * Thus the file region to search for extensions is
+     * between the end of the header (QCOW2_HDR_TOTAL_SIZE)
+     * and the start of the backingStoreName (offset)
+     *
+     * for qcow2 v3 images, the length of the header
+     * is stored at QCOW2v3_HDR_SIZE
+     */
+    extension_end = virReadBufInt64BE(buf + QCOWX_HDR_BACKING_FILE_OFFSET);
+    if (extension_end > buf_size)
+        return -1;
+
+    /*
+     * The extensions take format of
+     *
+     * int32: magic
+     * int32: length
+     * byte[length]: payload
+     *
+     * Unknown extensions can be ignored by skipping
+     * over "length" bytes in the data stream.
+     */
+    offset = extension_start;
+    while (offset < (buf_size-8) &&
+           offset < (extension_end-8)) {
+        unsigned int magic = virReadBufInt32BE(buf + offset);
+        unsigned int len = virReadBufInt32BE(buf + offset + 4);
+
+        offset += 8;
+
+        if ((offset + len) < offset)
+            break;
+
+        if ((offset + len) > buf_size)
+            break;
+
+        switch (magic) {
+        case QCOW2_HDR_EXTENSION_BACKING_FORMAT: {
+            g_autofree char *tmp = NULL;
+            if (!backingFormat)
+                break;
+
+            tmp = g_new0(char, len + 1);
+            memcpy(tmp, buf + offset, len);
+            tmp[len] = '\0';
+
+            *backingFormat = virStorageFileFormatTypeFromString(tmp);
+            if (*backingFormat <= VIR_STORAGE_FILE_NONE)
+                return -1;
+            break;
+        }
+
+        case QCOW2_HDR_EXTENSION_END:
+            return 0;
+        }
+
+        offset += len;
+    }
+
+    return 0;
+}
+
+
+static int
+qcowXGetBackingStore(char **res,
+                     int *format,
+                     const char *buf,
+                     size_t buf_size)
+{
+    unsigned long long offset;
+    unsigned int size;
+
+    *res = NULL;
+    *format = VIR_STORAGE_FILE_AUTO;
+
+    if (buf_size < QCOWX_HDR_BACKING_FILE_OFFSET+8+4)
+        return BACKING_STORE_INVALID;
+
+    offset = virReadBufInt64BE(buf + QCOWX_HDR_BACKING_FILE_OFFSET);
+    if (offset > buf_size)
+        return BACKING_STORE_INVALID;
+
+    if (offset == 0) {
+        *format = VIR_STORAGE_FILE_NONE;
+        return BACKING_STORE_OK;
+    }
+
+    size = virReadBufInt32BE(buf + QCOWX_HDR_BACKING_FILE_SIZE);
+    if (size == 0) {
+        *format = VIR_STORAGE_FILE_NONE;
+        return BACKING_STORE_OK;
+    }
+    if (size > 1023)
+        return BACKING_STORE_INVALID;
+    if (offset + size > buf_size || offset + size < offset)
+        return BACKING_STORE_INVALID;
+    *res = g_new0(char, size + 1);
+    memcpy(*res, buf + offset, size);
+    (*res)[size] = '\0';
+
+    if (qcow2GetExtensions(buf, buf_size, format) < 0)
+        return BACKING_STORE_INVALID;
+
+    return BACKING_STORE_OK;
+}
+
+
+static int
+vmdk4GetBackingStore(char **res,
+                     int *format,
+                     const char *buf,
+                     size_t buf_size)
+{
+    static const char prefix[] = "parentFileNameHint=\"";
+    char *start, *end;
+    size_t len;
+    g_autofree char *desc = NULL;
+
+    desc = g_new0(char, VIR_STORAGE_MAX_HEADER);
+
+    *res = NULL;
+    /*
+     * Technically this should have been VMDK, since
+     * VMDK spec / VMware impl only support VMDK backed
+     * by VMDK. QEMU isn't following this though and
+     * does probing on VMDK backing files, hence we set
+     * AUTO
+     */
+    *format = VIR_STORAGE_FILE_AUTO;
+
+    if (buf_size <= 0x200)
+        return BACKING_STORE_INVALID;
+
+    len = buf_size - 0x200;
+    if (len > VIR_STORAGE_MAX_HEADER)
+        len = VIR_STORAGE_MAX_HEADER;
+    memcpy(desc, buf + 0x200, len);
+    desc[len] = '\0';
+    start = strstr(desc, prefix);
+    if (start == NULL) {
+        *format = VIR_STORAGE_FILE_NONE;
+        return BACKING_STORE_OK;
+    }
+    start += strlen(prefix);
+    end = strchr(start, '"');
+    if (end == NULL)
+        return BACKING_STORE_INVALID;
+
+    if (end == start) {
+        *format = VIR_STORAGE_FILE_NONE;
+        return BACKING_STORE_OK;
+    }
+    *end = '\0';
+    *res = g_strdup(start);
+
+    return BACKING_STORE_OK;
+}
+
+
+static int
+qedGetBackingStore(char **res,
+                   int *format,
+                   const char *buf,
+                   size_t buf_size)
+{
+    unsigned long long flags;
+    unsigned long offset, size;
+
+    *res = NULL;
+    /* Check if this image has a backing file */
+    if (buf_size < QED_HDR_FEATURES_OFFSET+8)
+        return BACKING_STORE_INVALID;
+    flags = virReadBufInt64LE(buf + QED_HDR_FEATURES_OFFSET);
+    if (!(flags & QED_F_BACKING_FILE)) {
+        *format = VIR_STORAGE_FILE_NONE;
+        return BACKING_STORE_OK;
+    }
+
+    /* Parse the backing file */
+    if (buf_size < QED_HDR_BACKING_FILE_OFFSET+8)
+        return BACKING_STORE_INVALID;
+    offset = virReadBufInt32LE(buf + QED_HDR_BACKING_FILE_OFFSET);
+    if (offset > buf_size)
+        return BACKING_STORE_INVALID;
+    size = virReadBufInt32LE(buf + QED_HDR_BACKING_FILE_SIZE);
+    if (size == 0)
+        return BACKING_STORE_OK;
+    if (offset + size > buf_size || offset + size < offset)
+        return BACKING_STORE_INVALID;
+    *res = g_new0(char, size + 1);
+    memcpy(*res, buf + offset, size);
+    (*res)[size] = '\0';
+
+    if (flags & QED_F_BACKING_FORMAT_NO_PROBE)
+        *format = VIR_STORAGE_FILE_RAW;
+    else
+        *format = VIR_STORAGE_FILE_AUTO_SAFE;
+
+    return BACKING_STORE_OK;
+}
+
+
+static bool
+virStorageFileMatchesMagic(int magicOffset,
+                           const char *magic,
+                           char *buf,
+                           size_t buflen)
+{
+    int mlen;
+
+    if (magic == NULL)
+        return false;
+
+    /* Validate magic data */
+    mlen = strlen(magic);
+    if (magicOffset + mlen > buflen)
+        return false;
+
+    if (memcmp(buf + magicOffset, magic, mlen) != 0)
+        return false;
+
+    return true;
+}
+
+
+static bool
+virStorageFileMatchesVersion(int versionOffset,
+                             int versionSize,
+                             const int *versionNumbers,
+                             int endian,
+                             char *buf,
+                             size_t buflen)
+{
+    int version;
+    size_t i;
+
+    /* Validate version number info */
+    if (versionOffset == -1)
+        return false;
+
+    /* -2 == non-versioned file format, so trivially match */
+    if (versionOffset == -2)
+        return true;
+
+    /* A positive versionOffset, requires using a valid versionSize */
+    if (versionSize != 2 && versionSize != 4)
+        return false;
+
+    if ((versionOffset + versionSize) > buflen)
+        return false;
+
+    if (endian == LV_LITTLE_ENDIAN) {
+        if (versionSize == 4)
+            version = virReadBufInt32LE(buf +
+                                        versionOffset);
+        else
+            version = virReadBufInt16LE(buf +
+                                        versionOffset);
+    } else {
+        if (versionSize == 4)
+            version = virReadBufInt32BE(buf +
+                                        versionOffset);
+        else
+            version = virReadBufInt16BE(buf +
+                                        versionOffset);
+    }
+
+    for (i = 0;
+         i < FILE_TYPE_VERSIONS_LAST && versionNumbers[i];
+         i++) {
+        VIR_DEBUG("Compare detected version %d vs one of the expected versions %d",
+                  version, versionNumbers[i]);
+        if (version == versionNumbers[i])
+            return true;
+    }
+
+    return false;
+}
+
+
+static int
+virStorageFileProbeFormatFromBuf(const char *path,
+                                 char *buf,
+                                 size_t buflen)
+{
+    int format = VIR_STORAGE_FILE_RAW;
+    size_t i;
+    int possibleFormat = VIR_STORAGE_FILE_RAW;
+    VIR_DEBUG("path=%s, buf=%p, buflen=%zu", path, buf, buflen);
+
+    /* First check file magic */
+    for (i = 0; i < VIR_STORAGE_FILE_LAST; i++) {
+        if (virStorageFileMatchesMagic(fileTypeInfo[i].magicOffset,
+                                       fileTypeInfo[i].magic,
+                                       buf, buflen)) {
+            if (!virStorageFileMatchesVersion(fileTypeInfo[i].versionOffset,
+                                              fileTypeInfo[i].versionSize,
+                                              fileTypeInfo[i].versionNumbers,
+                                              fileTypeInfo[i].endian,
+                                              buf, buflen)) {
+                possibleFormat = i;
+                continue;
+            }
+            format = i;
+            goto cleanup;
+        }
+    }
+
+    if (possibleFormat != VIR_STORAGE_FILE_RAW)
+        VIR_WARN("File %s matches %s magic, but version is wrong. "
+                 "Please report new version to libvir-list at redhat.com",
+                 path, virStorageFileFormatTypeToString(possibleFormat));
+
+ cleanup:
+    VIR_DEBUG("format=%d", format);
+    return format;
+}
+
+
+static int
+qcow2GetFeatures(virBitmapPtr *features,
+                 int format,
+                 char *buf,
+                 ssize_t len)
+{
+    int version = -1;
+    virBitmapPtr feat = NULL;
+    uint64_t bits;
+    size_t i;
+
+    version = virReadBufInt32BE(buf + fileTypeInfo[format].versionOffset);
+
+    if (version == 2)
+        return 0;
+
+    if (len < QCOW2v3_HDR_SIZE)
+        return -1;
+
+    feat = virBitmapNew(VIR_STORAGE_FILE_FEATURE_LAST);
+
+    /* todo: check for incompatible or autoclear features? */
+    bits = virReadBufInt64BE(buf + QCOW2v3_HDR_FEATURES_COMPATIBLE);
+    for (i = 0; i < QCOW2_COMPATIBLE_FEATURE_LAST; i++) {
+        if (bits & ((uint64_t) 1 << i))
+            ignore_value(virBitmapSetBit(feat, qcow2CompatibleFeatureArray[i]));
+    }
+
+    *features = feat;
+    return 0;
+}
+
+
+static bool
+virStorageFileHasEncryptionFormat(const struct FileEncryptionInfo *info,
+                                  char *buf,
+                                  size_t len)
+{
+    if (!info->magic && info->modeOffset == -1)
+        return false; /* Shouldn't happen - expect at least one */
+
+    if (info->magic) {
+        if (!virStorageFileMatchesMagic(info->magicOffset,
+                                        info->magic,
+                                        buf, len))
+            return false;
+
+        if (info->versionOffset != -1 &&
+            !virStorageFileMatchesVersion(info->versionOffset,
+                                          info->versionSize,
+                                          info->versionNumbers,
+                                          info->endian,
+                                          buf, len))
+            return false;
+
+        return true;
+    } else if (info->modeOffset != -1) {
+        int crypt_format;
+
+        if (info->modeOffset >= len)
+            return false;
+
+        crypt_format = virReadBufInt32BE(buf + info->modeOffset);
+        if (crypt_format != info->modeValue)
+            return false;
+
+        return true;
+    } else {
+        return false;
+    }
+}
+
+
+static int
+virStorageFileGetEncryptionPayloadOffset(const struct FileEncryptionInfo *info,
+                                         char *buf)
+{
+    int payload_offset = -1;
+
+    if (info->payloadOffset != -1) {
+        if (info->endian == LV_LITTLE_ENDIAN)
+            payload_offset = virReadBufInt32LE(buf + info->payloadOffset);
+        else
+            payload_offset = virReadBufInt32BE(buf + info->payloadOffset);
+    }
+
+    return payload_offset;
+}
+
+
+/* Given a header in BUF with length LEN, as parsed from the storage file
+ * assuming it has the given FORMAT, populate information into META
+ * with information about the file and its backing store. Return format
+ * of the backing store as BACKING_FORMAT. PATH and FORMAT have to be
+ * pre-populated in META.
+ *
+ * Note that this function may be called repeatedly on @meta, so it must
+ * clean up any existing allocated memory which would be overwritten.
+ */
+static int
+virStorageFileGetMetadataInternal(virStorageSourcePtr meta,
+                                  char *buf,
+                                  size_t len)
+{
+    int format;
+    size_t i;
+
+    VIR_DEBUG("path=%s, buf=%p, len=%zu, meta->format=%d",
+              meta->path, buf, len, meta->format);
+
+    if (meta->format == VIR_STORAGE_FILE_AUTO)
+        meta->format = virStorageFileProbeFormatFromBuf(meta->path, buf, len);
+
+    if (meta->format <= VIR_STORAGE_FILE_NONE ||
+        meta->format >= VIR_STORAGE_FILE_LAST) {
+        virReportSystemError(EINVAL, _("unknown storage file meta->format %d"),
+                             meta->format);
+        return -1;
+    }
+
+    if (fileTypeInfo[meta->format].cryptInfo != NULL) {
+        for (i = 0; fileTypeInfo[meta->format].cryptInfo[i].format != 0; i++) {
+            if (virStorageFileHasEncryptionFormat(&fileTypeInfo[meta->format].cryptInfo[i],
+                                                  buf, len)) {
+                int expt_fmt = fileTypeInfo[meta->format].cryptInfo[i].format;
+                if (!meta->encryption) {
+                    meta->encryption = g_new0(virStorageEncryption, 1);
+                    meta->encryption->format = expt_fmt;
+                } else {
+                    if (meta->encryption->format != expt_fmt) {
+                        virReportError(VIR_ERR_XML_ERROR,
+                                       _("encryption format %d doesn't match "
+                                         "expected format %d"),
+                                       meta->encryption->format, expt_fmt);
+                        return -1;
+                    }
+                }
+                meta->encryption->payload_offset =
+                    virStorageFileGetEncryptionPayloadOffset(&fileTypeInfo[meta->format].cryptInfo[i], buf);
+            }
+        }
+    }
+
+    /* XXX we should consider moving virStorageBackendUpdateVolInfo
+     * code into this method, for non-magic files
+     */
+    if (!fileTypeInfo[meta->format].magic)
+        return 0;
+
+    /* Optionally extract capacity from file */
+    if (fileTypeInfo[meta->format].sizeOffset != -1) {
+        if ((fileTypeInfo[meta->format].sizeOffset + 8) > len)
+            return 0;
+
+        if (fileTypeInfo[meta->format].endian == LV_LITTLE_ENDIAN)
+            meta->capacity = virReadBufInt64LE(buf +
+                                               fileTypeInfo[meta->format].sizeOffset);
+        else
+            meta->capacity = virReadBufInt64BE(buf +
+                                               fileTypeInfo[meta->format].sizeOffset);
+        /* Avoid unlikely, but theoretically possible overflow */
+        if (meta->capacity > (ULLONG_MAX /
+                              fileTypeInfo[meta->format].sizeMultiplier))
+            return 0;
+        meta->capacity *= fileTypeInfo[meta->format].sizeMultiplier;
+    }
+
+    VIR_FREE(meta->backingStoreRaw);
+    if (fileTypeInfo[meta->format].getBackingStore != NULL) {
+        int store = fileTypeInfo[meta->format].getBackingStore(&meta->backingStoreRaw,
+                                                               &format,
+                                                               buf, len);
+        meta->backingStoreRawFormat = format;
+
+        if (store == BACKING_STORE_INVALID)
+            return 0;
+
+        if (store == BACKING_STORE_ERROR)
+            return -1;
+    }
+
+    virBitmapFree(meta->features);
+    meta->features = NULL;
+    if (fileTypeInfo[meta->format].getFeatures != NULL &&
+        fileTypeInfo[meta->format].getFeatures(&meta->features, meta->format, buf, len) < 0)
+        return -1;
+
+    VIR_FREE(meta->compat);
+    if (meta->format == VIR_STORAGE_FILE_QCOW2 && meta->features)
+        meta->compat = g_strdup("1.1");
+
+    return 0;
+}
+
+
+/**
+ * virStorageFileProbeFormat:
+ *
+ * Probe for the format of 'path', returning the detected
+ * disk format.
+ *
+ * Callers are advised never to trust the returned 'format'
+ * unless it is listed as VIR_STORAGE_FILE_RAW, since a
+ * malicious guest can turn a raw file into any other non-raw
+ * format at will.
+ *
+ * Best option: Don't use this function
+ */
+int
+virStorageFileProbeFormat(const char *path,
+                          uid_t uid,
+                          gid_t gid)
+{
+    struct stat sb;
+    ssize_t len = VIR_STORAGE_MAX_HEADER;
+    VIR_AUTOCLOSE fd = -1;
+    g_autofree char *header = NULL;
+
+    if ((fd = virFileOpenAs(path, O_RDONLY, 0, uid, gid, 0)) < 0) {
+        virReportSystemError(-fd, _("Failed to open file '%s'"), path);
+        return -1;
+    }
+
+    if (fstat(fd, &sb) < 0) {
+        virReportSystemError(errno, _("cannot stat file '%s'"), path);
+        return -1;
+    }
+
+    /* No header to probe for directories */
+    if (S_ISDIR(sb.st_mode))
+        return VIR_STORAGE_FILE_DIR;
+
+    if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
+        virReportSystemError(errno, _("cannot set to start of '%s'"), path);
+        return -1;
+    }
+
+    if ((len = virFileReadHeaderFD(fd, len, &header)) < 0) {
+        virReportSystemError(errno, _("cannot read header '%s'"), path);
+        return -1;
+    }
+
+    return virStorageFileProbeFormatFromBuf(path, header, len);
+}
+
+
+static virStorageSourcePtr
+virStorageFileMetadataNew(const char *path,
+                          int format)
+{
+    g_autoptr(virStorageSource) def = virStorageSourceNew();
+
+    def->format = format;
+    def->type = VIR_STORAGE_TYPE_FILE;
+
+    def->path = g_strdup(path);
+
+    return g_steal_pointer(&def);
+}
+
+
+/**
+ * virStorageFileGetMetadataFromBuf:
+ * @path: name of file, for error messages
+ * @buf: header bytes from @path
+ * @len: length of @buf
+ * @format: format of the storage file
+ *
+ * Extract metadata about the storage volume with the specified image format.
+ * If image format is VIR_STORAGE_FILE_AUTO, it will probe to automatically
+ * identify the format.  Does not recurse.
+ *
+ * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a format on a file
+ * that might be raw if that file will then be passed to a guest, since a
+ * malicious guest can turn a raw file into any other non-raw format at will.
+ *
+ * If the 'backingStoreRawFormat' field of the returned structure is
+ * VIR_STORAGE_FILE_AUTO it indicates the image didn't specify an explicit
+ * format for its backing store. Callers are advised against probing for the
+ * backing store format in this case.
+ *
+ * Caller MUST free the result after use via virObjectUnref.
+ */
+virStorageSourcePtr
+virStorageFileGetMetadataFromBuf(const char *path,
+                                 char *buf,
+                                 size_t len,
+                                 int format)
+{
+    virStorageSourcePtr ret = NULL;
+
+    if (!(ret = virStorageFileMetadataNew(path, format)))
+        return NULL;
+
+    if (virStorageFileGetMetadataInternal(ret, buf, len) < 0) {
+        virObjectUnref(ret);
+        return NULL;
+    }
+
+    return ret;
+}
+
+
+/**
+ * virStorageFileGetMetadataFromFD:
+ *
+ * Extract metadata about the storage volume with the specified
+ * image format. If image format is VIR_STORAGE_FILE_AUTO, it
+ * will probe to automatically identify the format.  Does not recurse.
+ *
+ * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a
+ * format, since a malicious guest can turn a raw file into any
+ * other non-raw format at will.
+ *
+ * Caller MUST free the result after use via virObjectUnref.
+ */
+virStorageSourcePtr
+virStorageFileGetMetadataFromFD(const char *path,
+                                int fd,
+                                int format)
+
+{
+    ssize_t len = VIR_STORAGE_MAX_HEADER;
+    struct stat sb;
+    g_autofree char *buf = NULL;
+    g_autoptr(virStorageSource) meta = NULL;
+
+    if (fstat(fd, &sb) < 0) {
+        virReportSystemError(errno,
+                             _("cannot stat file '%s'"), path);
+        return NULL;
+    }
+
+    if (!(meta = virStorageFileMetadataNew(path, format)))
+        return NULL;
+
+    if (S_ISDIR(sb.st_mode)) {
+        /* No header to probe for directories, but also no backing file. Just
+         * update the metadata.*/
+        meta->type = VIR_STORAGE_TYPE_DIR;
+        meta->format = VIR_STORAGE_FILE_DIR;
+        return g_steal_pointer(&meta);
+    }
+
+    if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
+        virReportSystemError(errno, _("cannot seek to start of '%s'"), meta->path);
+        return NULL;
+    }
+
+    if ((len = virFileReadHeaderFD(fd, len, &buf)) < 0) {
+        virReportSystemError(errno, _("cannot read header '%s'"), meta->path);
+        return NULL;
+    }
+
+    if (virStorageFileGetMetadataInternal(meta, buf, len) < 0)
+        return NULL;
+
+    if (S_ISREG(sb.st_mode))
+        meta->type = VIR_STORAGE_TYPE_FILE;
+    else if (S_ISBLK(sb.st_mode))
+        meta->type = VIR_STORAGE_TYPE_BLOCK;
+
+    return g_steal_pointer(&meta);
+}
+
+
+/**
+ * virStorageFileParseBackingStoreStr:
+ * @str: backing store specifier string to parse
+ * @target: returns target device portion of the string
+ * @chainIndex: returns the backing store portion of the string
+ *
+ * Parses the backing store specifier string such as vda[1], or sda into
+ * components and returns them via arguments. If the string did not specify an
+ * index, 0 is assumed.
+ *
+ * Returns 0 on success -1 on error
+ */
+int
+virStorageFileParseBackingStoreStr(const char *str,
+                                   char **target,
+                                   unsigned int *chainIndex)
+{
+    size_t nstrings;
+    unsigned int idx = 0;
+    char *suffix;
+    g_auto(GStrv) strings = NULL;
+
+    *chainIndex = 0;
+
+    if (!(strings = virStringSplitCount(str, "[", 2, &nstrings)))
+        return -1;
+
+    if (nstrings == 2) {
+        if (virStrToLong_uip(strings[1], &suffix, 10, &idx) < 0 ||
+            STRNEQ(suffix, "]"))
+            return -1;
+    }
+
+    if (target)
+        *target = g_strdup(strings[0]);
+
+    *chainIndex = idx;
+    return 0;
+}
+
+
+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;
+}
+
+
+/* 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.
+ */
+virStorageSourcePtr
+virStorageFileChainLookup(virStorageSourcePtr chain,
+                          virStorageSourcePtr startFrom,
+                          const char *name,
+                          unsigned int idx,
+                          virStorageSourcePtr *parent)
+{
+    virStorageSourcePtr prev;
+    const char *start = chain->path;
+    bool nameIsFile = virFileIsFile(name);
+
+    if (!parent)
+        parent = &prev;
+    *parent = NULL;
+
+    if (startFrom) {
+        while (virStorageSourceIsBacking(chain) &&
+               chain != startFrom->backingStore)
+            chain = chain->backingStore;
+
+        *parent = startFrom;
+    }
+
+    while (virStorageSourceIsBacking(chain)) {
+        if (!name && !idx) {
+            if (!virStorageSourceHasBacking(chain))
+                break;
+        } else if (idx) {
+            VIR_DEBUG("%u: %s", chain->id, chain->path);
+            if (idx == chain->id)
+                break;
+        } else {
+            if (STREQ_NULLABLE(name, chain->relPath) ||
+                STREQ_NULLABLE(name, chain->path))
+                break;
+
+            if (nameIsFile && virStorageSourceIsLocalStorage(chain)) {
+                g_autofree char *parentDir = NULL;
+                int result;
+
+                if (*parent && virStorageSourceIsLocalStorage(*parent))
+                    parentDir = g_path_get_dirname((*parent)->path);
+                else
+                    parentDir = g_strdup(".");
+
+                result = virFileRelLinkPointsTo(parentDir, name,
+                                                chain->path);
+
+                if (result < 0)
+                    goto error;
+
+                if (result > 0)
+                    break;
+            }
+        }
+        *parent = chain;
+        chain = chain->backingStore;
+    }
+
+    if (!virStorageSourceIsBacking(chain))
+        goto error;
+
+    return chain;
+
+ error:
+    if (idx) {
+        virReportError(VIR_ERR_INVALID_ARG,
+                       _("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));
+        else
+            virReportError(VIR_ERR_INVALID_ARG,
+                           _("could not find image '%s' in chain for '%s'"),
+                           name, NULLSTR(start));
+    } else {
+        virReportError(VIR_ERR_INVALID_ARG,
+                       _("could not find base image in chain for '%s'"),
+                       NULLSTR(start));
+    }
+    *parent = NULL;
+    return NULL;
+}
+
+
+static virStorageSourcePtr
+virStorageSourceNewFromBackingRelative(virStorageSourcePtr parent,
+                                       const char *rel)
+{
+    g_autofree char *dirname = NULL;
+    g_autoptr(virStorageSource) def = virStorageSourceNew();
+
+    /* store relative name */
+    def->relPath = g_strdup(rel);
+
+    dirname = g_path_get_dirname(parent->path);
+
+    if (STRNEQ(dirname, "/")) {
+        def->path = g_strdup_printf("%s/%s", dirname, rel);
+    } else {
+        def->path = g_strdup_printf("/%s", rel);
+    }
+
+    if (virStorageSourceGetActualType(parent) == VIR_STORAGE_TYPE_NETWORK) {
+        def->type = VIR_STORAGE_TYPE_NETWORK;
+
+        /* copy the host network part */
+        def->protocol = parent->protocol;
+        if (parent->nhosts) {
+            if (!(def->hosts = virStorageNetHostDefCopy(parent->nhosts,
+                                                        parent->hosts)))
+                return NULL;
+
+            def->nhosts = parent->nhosts;
+        }
+
+        def->volume = g_strdup(parent->volume);
+    } else {
+        /* set the type to _FILE, the caller shall update it to the actual type */
+        def->type = VIR_STORAGE_TYPE_FILE;
+    }
+
+    return g_steal_pointer(&def);
+}
+
+
+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:
+        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
+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},
+    {"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
+ * @src: filled with virStorageSource object representing @path
+ *
+ * Returns 0 on success, 1 if we could parse all location data but @path
+ * specified other data unrepresentable by libvirt (e.g. inline authentication).
+ * In both cases @src is filled. On error -1 is returned @src is NULL and an
+ * error is reported.
+ */
+int
+virStorageSourceNewFromBackingAbsolute(const char *path,
+                                       virStorageSourcePtr *src)
+{
+    const char *json;
+    const char *dirpath;
+    int rc = 0;
+    g_autoptr(virStorageSource) def = virStorageSourceNew();
+
+    *src = NULL;
+
+    if (virFileIsFile(path)) {
+        def->type = VIR_STORAGE_TYPE_FILE;
+
+        def->path = g_strdup(path);
+    } else {
+        if ((dirpath = STRSKIP(path, "fat:"))) {
+            def->type = VIR_STORAGE_TYPE_DIR;
+            def->format = VIR_STORAGE_FILE_FAT;
+            def->path = g_strdup(dirpath);
+            *src = g_steal_pointer(&def);
+            return 0;
+        }
+
+        def->type = VIR_STORAGE_TYPE_NETWORK;
+
+        VIR_DEBUG("parsing backing store string: '%s'", path);
+
+        /* handle URI formatted backing stores */
+        if ((json = STRSKIP(path, "json:")))
+            rc = virStorageSourceParseBackingJSON(def, json);
+        else if (strstr(path, "://"))
+            rc = virStorageSourceParseBackingURI(def, path);
+        else
+            rc = virStorageSourceParseBackingColon(def, path);
+
+        if (rc < 0)
+            return -1;
+
+        virStorageSourceNetworkAssignDefaultPorts(def);
+
+        /* Some of the legacy parsers parse authentication data since they are
+         * also used in other places. For backing store detection the
+         * authentication data would be invalid anyways, so we clear it */
+        if (def->auth) {
+            virStorageAuthDefFree(def->auth);
+            def->auth = NULL;
+        }
+    }
+
+    *src = g_steal_pointer(&def);
+    return rc;
+}
+
+
+/**
+ * virStorageSourceNewFromChild:
+ * @parent: storage source parent
+ * @child: returned child/backing store definition
+ * @parentRaw: raw child string (backingStoreRaw)
+ *
+ * Creates a storage source which describes the backing image of @parent and
+ * fills it into @backing depending on the passed parentRaw (backingStoreRaw)
+ * and other data. Note that for local storage this function accesses the file
+ * to update the actual type of the child store.
+ *
+ * Returns 0 on success, 1 if we could parse all location data but the child
+ * store specification contained other data unrepresentable by libvirt (e.g.
+ * inline authentication).
+ * In both cases @src is filled. On error -1 is returned @src is NULL and an
+ * error is reported.
+ */
+static int
+virStorageSourceNewFromChild(virStorageSourcePtr parent,
+                             const char *parentRaw,
+                             virStorageSourcePtr *child)
+{
+    struct stat st;
+    g_autoptr(virStorageSource) def = NULL;
+    int rc = 0;
+
+    *child = NULL;
+
+    if (virFileIsRelative(parentRaw)) {
+        if (!(def = virStorageSourceNewFromBackingRelative(parent, parentRaw)))
+            return -1;
+    } else {
+        if ((rc = virStorageSourceNewFromBackingAbsolute(parentRaw, &def)) < 0)
+            return -1;
+    }
+
+    /* possibly update local type */
+    if (def->type == VIR_STORAGE_TYPE_FILE) {
+        if (stat(def->path, &st) == 0) {
+            if (S_ISDIR(st.st_mode)) {
+                def->type = VIR_STORAGE_TYPE_DIR;
+                def->format = VIR_STORAGE_FILE_DIR;
+            } else if (S_ISBLK(st.st_mode)) {
+                def->type = VIR_STORAGE_TYPE_BLOCK;
+            }
+        }
+    }
+
+    /* copy parent's labelling and other top level stuff */
+    if (virStorageSourceInitChainElement(def, parent, true) < 0)
+        return -1;
+
+    def->detected = true;
+
+    *child = g_steal_pointer(&def);
+    return rc;
+}
+
+
+int
+virStorageSourceNewFromBacking(virStorageSourcePtr parent,
+                               virStorageSourcePtr *backing)
+{
+    int rc;
+
+    if ((rc = virStorageSourceNewFromChild(parent,
+                                           parent->backingStoreRaw,
+                                           backing)) < 0)
+        return rc;
+
+    (*backing)->format = parent->backingStoreRawFormat;
+    (*backing)->readonly = true;
+    return rc;
+}
+
+
+/**
+ * @src: disk source definition structure
+ * @fd: file descriptor
+ * @sb: stat buffer
+ *
+ * Updates src->physical depending on the actual type of storage being used.
+ * To be called for domain storage source reporting as the volume code does
+ * not set/use the 'type' field for the voldef->source.target
+ *
+ * Returns 0 on success, -1 on error. No libvirt errors are reported.
+ */
+int
+virStorageSourceUpdatePhysicalSize(virStorageSourcePtr src,
+                                   int fd,
+                                   struct stat const *sb)
+{
+    off_t end;
+    virStorageType actual_type = virStorageSourceGetActualType(src);
+
+    switch (actual_type) {
+    case VIR_STORAGE_TYPE_FILE:
+    case VIR_STORAGE_TYPE_NETWORK:
+        src->physical = sb->st_size;
+        break;
+
+    case VIR_STORAGE_TYPE_BLOCK:
+        if ((end = lseek(fd, 0, SEEK_END)) == (off_t) -1)
+            return -1;
+
+        src->physical = end;
+        break;
+
+    case VIR_STORAGE_TYPE_DIR:
+        src->physical = 0;
+        break;
+
+    /* We shouldn't get VOLUME, but the switch requires all cases */
+    case VIR_STORAGE_TYPE_VOLUME:
+    case VIR_STORAGE_TYPE_NVME:
+    case VIR_STORAGE_TYPE_NONE:
+    case VIR_STORAGE_TYPE_LAST:
+        return -1;
+    }
+
+    return 0;
+}
+
+
+/**
+ * @src: disk source definition structure
+ * @fd: file descriptor
+ * @sb: stat buffer
+ *
+ * Update the capacity, allocation, physical values for the storage @src
+ * Shared between the domain storage source for an inactive domain and the
+ * voldef source target as the result is not affected by the 'type' field.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int
+virStorageSourceUpdateBackingSizes(virStorageSourcePtr src,
+                                   int fd,
+                                   struct stat const *sb)
+{
+    /* Get info for normal formats */
+    if (S_ISREG(sb->st_mode) || fd == -1) {
+#ifndef WIN32
+        src->allocation = (unsigned long long)sb->st_blocks *
+            (unsigned long long)DEV_BSIZE;
+#else
+        src->allocation = sb->st_size;
+#endif
+        /* Regular files may be sparse, so logical size (capacity) is not same
+         * as actual allocation above
+         */
+        src->capacity = sb->st_size;
+
+        /* Allocation tracks when the file is sparse, physical is the
+         * last offset of the file. */
+        src->physical = sb->st_size;
+    } else if (S_ISDIR(sb->st_mode)) {
+        src->allocation = 0;
+        src->capacity = 0;
+        src->physical = 0;
+    } else if (fd >= 0) {
+        off_t end;
+
+        /* XXX this is POSIX compliant, but doesn't work for CHAR files,
+         * only BLOCK. There is a Linux specific ioctl() for getting
+         * size of both CHAR / BLOCK devices we should check for in
+         * configure
+         *
+         * NB. Because we configure with AC_SYS_LARGEFILE, off_t
+         * should be 64 bits on all platforms.  For block devices, we
+         * have to seek (safe even if someone else is writing) to
+         * determine physical size, and assume that allocation is the
+         * same as physical (but can refine that assumption later if
+         * qemu is still running).
+         */
+        if ((end = lseek(fd, 0, SEEK_END)) == (off_t)-1) {
+            virReportSystemError(errno,
+                                 _("failed to seek to end of %s"), src->path);
+            return -1;
+        }
+        src->physical = end;
+        src->allocation = end;
+        src->capacity = end;
+    }
+
+    return 0;
+}
+
+
+/**
+ * @src: disk source definition structure
+ * @buf: buffer to the storage file header
+ * @len: length of the storage file header
+ *
+ * Update the storage @src capacity.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int
+virStorageSourceUpdateCapacity(virStorageSourcePtr src,
+                               char *buf,
+                               ssize_t len)
+{
+    int format = src->format;
+    g_autoptr(virStorageSource) meta = NULL;
+
+    /* Raw files: capacity is physical size.  For all other files: if
+     * the metadata has a capacity, use that, otherwise fall back to
+     * physical size.  */
+    if (format == VIR_STORAGE_FILE_NONE) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("no disk format for %s was specified"),
+                       src->path);
+        return -1;
+    }
+
+    if (format == VIR_STORAGE_FILE_RAW && !src->encryption) {
+        src->capacity = src->physical;
+    } else if ((meta = virStorageFileGetMetadataFromBuf(src->path, buf,
+                                                        len, format))) {
+        src->capacity = meta->capacity ? meta->capacity : src->physical;
+        if (src->encryption && meta->encryption)
+            src->encryption->payload_offset = meta->encryption->payload_offset;
+    } else {
+        return -1;
+    }
+
+    if (src->encryption && src->encryption->payload_offset != -1)
+        src->capacity -= src->encryption->payload_offset * 512;
+
+    return 0;
+}
+
+
+static char *
+virStorageFileCanonicalizeFormatPath(char **components,
+                                     size_t ncomponents,
+                                     bool beginSlash,
+                                     bool beginDoubleSlash)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+    size_t i;
+    char *ret = NULL;
+
+    if (beginSlash)
+        virBufferAddLit(&buf, "/");
+
+    if (beginDoubleSlash)
+        virBufferAddLit(&buf, "/");
+
+    for (i = 0; i < ncomponents; i++) {
+        if (i != 0)
+            virBufferAddLit(&buf, "/");
+
+        virBufferAdd(&buf, components[i], -1);
+    }
+
+    /* if the output string is empty just return an empty string */
+    if (!(ret = virBufferContentAndReset(&buf)))
+        ret = g_strdup("");
+
+    return ret;
+}
+
+
+static int
+virStorageFileCanonicalizeInjectSymlink(const char *path,
+                                        size_t at,
+                                        char ***components,
+                                        size_t *ncomponents)
+{
+    char **tmp = NULL;
+    char **next;
+    size_t ntmp = 0;
+    int ret = -1;
+
+    if (!(tmp = virStringSplitCount(path, "/", 0, &ntmp)))
+        goto cleanup;
+
+    /* prepend */
+    for (next = tmp; *next; next++) {
+        if (VIR_INSERT_ELEMENT(*components, at, *ncomponents, *next) < 0)
+            goto cleanup;
+
+        at++;
+    }
+
+    ret = 0;
+
+ cleanup:
+    virStringListFreeCount(tmp, ntmp);
+    return ret;
+}
+
+
+char *
+virStorageFileCanonicalizePath(const char *path,
+                               virStorageFileSimplifyPathReadlinkCallback cb,
+                               void *cbdata)
+{
+    GHashTable *cycle = NULL;
+    bool beginSlash = false;
+    bool beginDoubleSlash = false;
+    char **components = NULL;
+    size_t ncomponents = 0;
+    size_t i = 0;
+    size_t j = 0;
+    int rc;
+    char *ret = NULL;
+    g_autofree char *linkpath = NULL;
+    g_autofree char *currentpath = NULL;
+
+    if (path[0] == '/') {
+        beginSlash = true;
+
+        if (path[1] == '/' && path[2] != '/')
+            beginDoubleSlash = true;
+    }
+
+    if (!(cycle = virHashNew(NULL)))
+        goto cleanup;
+
+    if (!(components = virStringSplitCount(path, "/", 0, &ncomponents)))
+        goto cleanup;
+
+    j = 0;
+    while (j < ncomponents) {
+        /* skip slashes */
+        if (STREQ(components[j], "")) {
+            VIR_FREE(components[j]);
+            VIR_DELETE_ELEMENT(components, j, ncomponents);
+            continue;
+        }
+        j++;
+    }
+
+    while (i < ncomponents) {
+        /* skip '.'s unless it's the last one remaining */
+        if (STREQ(components[i], ".") &&
+            (beginSlash || ncomponents  > 1)) {
+            VIR_FREE(components[i]);
+            VIR_DELETE_ELEMENT(components, i, ncomponents);
+            continue;
+        }
+
+        /* resolve changes to parent directory */
+        if (STREQ(components[i], "..")) {
+            if (!beginSlash &&
+                (i == 0 || STREQ(components[i - 1], ".."))) {
+                i++;
+                continue;
+            }
+
+            VIR_FREE(components[i]);
+            VIR_DELETE_ELEMENT(components, i, ncomponents);
+
+            if (i != 0) {
+                VIR_FREE(components[i - 1]);
+                VIR_DELETE_ELEMENT(components, i - 1, ncomponents);
+                i--;
+            }
+
+            continue;
+        }
+
+        /* check if the actual path isn't resulting into a symlink */
+        if (!(currentpath = virStorageFileCanonicalizeFormatPath(components,
+                                                                 i + 1,
+                                                                 beginSlash,
+                                                                 beginDoubleSlash)))
+            goto cleanup;
+
+        if ((rc = cb(currentpath, &linkpath, cbdata)) < 0)
+            goto cleanup;
+
+        if (rc == 0) {
+            if (virHashLookup(cycle, currentpath)) {
+                virReportSystemError(ELOOP,
+                                     _("Failed to canonicalize path '%s'"), path);
+                goto cleanup;
+            }
+
+            if (virHashAddEntry(cycle, currentpath, (void *) 1) < 0)
+                goto cleanup;
+
+            if (linkpath[0] == '/') {
+                /* kill everything from the beginning including the actual component */
+                i++;
+                while (i--) {
+                    VIR_FREE(components[0]);
+                    VIR_DELETE_ELEMENT(components, 0, ncomponents);
+                }
+                beginSlash = true;
+
+                if (linkpath[1] == '/' && linkpath[2] != '/')
+                    beginDoubleSlash = true;
+                else
+                    beginDoubleSlash = false;
+
+                i = 0;
+            } else {
+                VIR_FREE(components[i]);
+                VIR_DELETE_ELEMENT(components, i, ncomponents);
+            }
+
+            if (virStorageFileCanonicalizeInjectSymlink(linkpath,
+                                                        i,
+                                                        &components,
+                                                        &ncomponents) < 0)
+                goto cleanup;
+
+            j = 0;
+            while (j < ncomponents) {
+                /* skip slashes */
+                if (STREQ(components[j], "")) {
+                    VIR_FREE(components[j]);
+                    VIR_DELETE_ELEMENT(components, j, ncomponents);
+                    continue;
+                }
+                j++;
+            }
+
+            VIR_FREE(linkpath);
+            VIR_FREE(currentpath);
+
+            continue;
+        }
+
+        VIR_FREE(currentpath);
+
+        i++;
+    }
+
+    ret = virStorageFileCanonicalizeFormatPath(components, ncomponents,
+                                               beginSlash, beginDoubleSlash);
+
+ cleanup:
+    virHashFree(cycle);
+    virStringListFreeCount(components, ncomponents);
+
+    return ret;
+}
+
+
+/**
+ * virStorageFileRemoveLastPathComponent:
+ *
+ * @path: Path string to remove the last component from
+ *
+ * Removes the last path component of a path. This function is designed to be
+ * called on file paths only (no trailing slashes in @path). Caller is
+ * responsible to free the returned string.
+ */
+static char *
+virStorageFileRemoveLastPathComponent(const char *path)
+{
+    char *ret;
+
+    ret = g_strdup(NULLSTR_EMPTY(path));
+
+    virFileRemoveLastComponent(ret);
+
+    return ret;
+}
+
+
+/*
+ * virStorageFileGetRelativeBackingPath:
+ *
+ * Resolve relative path to be written to the overlay of @top image when
+ * collapsing the backing chain between @top and @base.
+ *
+ * Returns 0 on success; 1 if backing chain isn't relative and -1 on error.
+ */
+int
+virStorageFileGetRelativeBackingPath(virStorageSourcePtr top,
+                                     virStorageSourcePtr base,
+                                     char **relpath)
+{
+    virStorageSourcePtr next;
+    g_autofree char *tmp = NULL;
+    g_autofree char *path = NULL;
+
+    *relpath = NULL;
+
+    for (next = top; virStorageSourceIsBacking(next); next = next->backingStore) {
+        if (!next->relPath)
+            return 1;
+
+        if (!(tmp = virStorageFileRemoveLastPathComponent(path)))
+            return -1;
+
+        VIR_FREE(path);
+
+        path = g_strdup_printf("%s%s", tmp, next->relPath);
+
+        VIR_FREE(tmp);
+
+        if (next == base)
+            break;
+    }
+
+    if (next != base) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("failed to resolve relative backing name: "
+                         "base image is not in backing chain"));
+        return -1;
+    }
+
+    *relpath = g_steal_pointer(&path);
+    return 0;
+}
+
+
+/**
+ * virStorageSourceFindByNodeName:
+ * @top: backing chain top
+ * @nodeName: node name to find in backing chain
+ *
+ * Looks up the given storage source in the backing chain and returns the
+ * pointer to it.
+ * On failure NULL is returned and no error is reported.
+ */
+virStorageSourcePtr
+virStorageSourceFindByNodeName(virStorageSourcePtr top,
+                               const char *nodeName)
+{
+    virStorageSourcePtr tmp;
+
+    for (tmp = top; virStorageSourceIsBacking(tmp); tmp = tmp->backingStore) {
+        if ((tmp->nodeformat && STREQ(tmp->nodeformat, nodeName)) ||
+            (tmp->nodestorage && STREQ(tmp->nodestorage, nodeName)))
+            return tmp;
+    }
+
+    return NULL;
+}
+
+
+int
+virStorageSourcePrivateDataParseRelPath(xmlXPathContextPtr ctxt,
+                                        virStorageSourcePtr src)
+{
+    src->relPath = virXPathString("string(./relPath)", ctxt);
+    return 0;
+}
+
+
+int
+virStorageSourcePrivateDataFormatRelPath(virStorageSourcePtr src,
+                                         virBufferPtr buf)
+{
+    if (src->relPath)
+        virBufferEscapeString(buf, "<relPath>%s</relPath>\n", src->relPath);
+
+    return 0;
+}
+
+
+static bool
+virStorageFileIsInitialized(const virStorageSource *src)
+{
+    return src && src->drv;
+}
+
+
+/**
+ * virStorageFileGetBackendForSupportCheck:
+ * @src: storage source to check support for
+ * @backend: pointer to the storage backend for @src if it's supported
+ *
+ * Returns 0 if @src is not supported by any storage backend currently linked
+ * 1 if it is supported and -1 on error with an error reported.
+ */
+static int
+virStorageFileGetBackendForSupportCheck(const virStorageSource *src,
+                                        virStorageFileBackendPtr *backend)
+{
+    int actualType;
+
+
+    if (!src) {
+        *backend = NULL;
+        return 0;
+    }
+
+    if (src->drv) {
+        virStorageDriverDataPtr drv = src->drv;
+        *backend = drv->backend;
+        return 1;
+    }
+
+    actualType = virStorageSourceGetActualType(src);
+
+    if (virStorageFileBackendForType(actualType, src->protocol, false, backend) < 0)
+        return -1;
+
+    if (!*backend)
+        return 0;
+
+    return 1;
+}
+
+
+int
+virStorageFileSupportsBackingChainTraversal(const virStorageSource *src)
+{
+    virStorageFileBackendPtr backend;
+    int rv;
+
+    if ((rv = virStorageFileGetBackendForSupportCheck(src, &backend)) < 1)
+        return rv;
+
+    return backend->storageFileGetUniqueIdentifier &&
+           backend->storageFileRead &&
+           backend->storageFileAccess ? 1 : 0;
+}
+
+
+/**
+ * virStorageFileSupportsSecurityDriver:
+ *
+ * @src: a storage file structure
+ *
+ * Check if a storage file supports operations needed by the security
+ * driver to perform labelling
+ */
+int
+virStorageFileSupportsSecurityDriver(const virStorageSource *src)
+{
+    virStorageFileBackendPtr backend;
+    int rv;
+
+    if ((rv = virStorageFileGetBackendForSupportCheck(src, &backend)) < 1)
+        return rv;
+
+    return backend->storageFileChown ? 1 : 0;
+}
+
+
+/**
+ * virStorageFileSupportsAccess:
+ *
+ * @src: a storage file structure
+ *
+ * Check if a storage file supports checking if the storage source is accessible
+ * for the given vm.
+ */
+int
+virStorageFileSupportsAccess(const virStorageSource *src)
+{
+    virStorageFileBackendPtr backend;
+    int rv;
+
+    if ((rv = virStorageFileGetBackendForSupportCheck(src, &backend)) < 1)
+        return rv;
+
+    return backend->storageFileAccess ? 1 : 0;
+}
+
+
+/**
+ * virStorageFileSupportsCreate:
+ * @src: a storage file structure
+ *
+ * Check if the storage driver supports creating storage described by @src
+ * via virStorageFileCreate.
+ */
+int
+virStorageFileSupportsCreate(const virStorageSource *src)
+{
+    virStorageFileBackendPtr backend;
+    int rv;
+
+    if ((rv = virStorageFileGetBackendForSupportCheck(src, &backend)) < 1)
+        return rv;
+
+    return backend->storageFileCreate ? 1 : 0;
+}
+
+
+void
+virStorageFileDeinit(virStorageSourcePtr src)
+{
+    virStorageDriverDataPtr drv = NULL;
+
+    if (!virStorageFileIsInitialized(src))
+        return;
+
+    drv = src->drv;
+
+    if (drv->backend &&
+        drv->backend->backendDeinit)
+        drv->backend->backendDeinit(src);
+
+    VIR_FREE(src->drv);
+}
+
+
+/**
+ * virStorageFileInitAs:
+ *
+ * @src: storage source definition
+ * @uid: uid used to access the file, or -1 for current uid
+ * @gid: gid used to access the file, or -1 for current gid
+ *
+ * Initialize a storage source to be used with storage driver. Use the provided
+ * uid and gid if possible for the operations.
+ *
+ * Returns 0 if the storage file was successfully initialized, -1 if the
+ * initialization failed. Libvirt error is reported.
+ */
+int
+virStorageFileInitAs(virStorageSourcePtr src,
+                     uid_t uid,
+                     gid_t gid)
+{
+    int actualType = virStorageSourceGetActualType(src);
+    virStorageDriverDataPtr drv = g_new0(virStorageDriverData, 1);
+
+    src->drv = drv;
+
+    if (uid == (uid_t) -1)
+        drv->uid = geteuid();
+    else
+        drv->uid = uid;
+
+    if (gid == (gid_t) -1)
+        drv->gid = getegid();
+    else
+        drv->gid = gid;
+
+    if (virStorageFileBackendForType(actualType,
+                                     src->protocol,
+                                     true,
+                                     &drv->backend) < 0)
+        goto error;
+
+    if (drv->backend->backendInit &&
+        drv->backend->backendInit(src) < 0)
+        goto error;
+
+    return 0;
+
+ error:
+    VIR_FREE(src->drv);
+    return -1;
+}
+
+
+/**
+ * virStorageFileInit:
+ *
+ * See virStorageFileInitAs. The file is initialized to be accessed by the
+ * current user.
+ */
+int
+virStorageFileInit(virStorageSourcePtr src)
+{
+    return virStorageFileInitAs(src, -1, -1);
+}
+
+
+/**
+ * virStorageFileCreate: Creates an empty storage file via storage driver
+ *
+ * @src: file structure pointing to the file
+ *
+ * Returns 0 on success, -2 if the function isn't supported by the backend,
+ * -1 on other failure. Errno is set in case of failure.
+ */
+int
+virStorageFileCreate(virStorageSourcePtr src)
+{
+    virStorageDriverDataPtr drv = NULL;
+    int ret;
+
+    if (!virStorageFileIsInitialized(src)) {
+        errno = ENOSYS;
+        return -2;
+    }
+
+    drv = src->drv;
+
+    if (!drv->backend->storageFileCreate) {
+        errno = ENOSYS;
+        return -2;
+    }
+
+    ret = drv->backend->storageFileCreate(src);
+
+    VIR_DEBUG("created storage file %p: ret=%d, errno=%d",
+              src, ret, errno);
+
+    return ret;
+}
+
+
+/**
+ * virStorageFileUnlink: Unlink storage file via storage driver
+ *
+ * @src: file structure pointing to the file
+ *
+ * Unlinks the file described by the @file structure.
+ *
+ * Returns 0 on success, -2 if the function isn't supported by the backend,
+ * -1 on other failure. Errno is set in case of failure.
+ */
+int
+virStorageFileUnlink(virStorageSourcePtr src)
+{
+    virStorageDriverDataPtr drv = NULL;
+    int ret;
+
+    if (!virStorageFileIsInitialized(src)) {
+        errno = ENOSYS;
+        return -2;
+    }
+
+    drv = src->drv;
+
+    if (!drv->backend->storageFileUnlink) {
+        errno = ENOSYS;
+        return -2;
+    }
+
+    ret = drv->backend->storageFileUnlink(src);
+
+    VIR_DEBUG("unlinked storage file %p: ret=%d, errno=%d",
+              src, ret, errno);
+
+    return ret;
+}
+
+
+/**
+ * virStorageFileStat: returns stat struct of a file via storage driver
+ *
+ * @src: file structure pointing to the file
+ * @stat: stat structure to return data
+ *
+ * Returns 0 on success, -2 if the function isn't supported by the backend,
+ * -1 on other failure. Errno is set in case of failure.
+*/
+int
+virStorageFileStat(virStorageSourcePtr src,
+                   struct stat *st)
+{
+    virStorageDriverDataPtr drv = NULL;
+    int ret;
+
+    if (!virStorageFileIsInitialized(src)) {
+        errno = ENOSYS;
+        return -2;
+    }
+
+    drv = src->drv;
+
+    if (!drv->backend->storageFileStat) {
+        errno = ENOSYS;
+        return -2;
+    }
+
+    ret = drv->backend->storageFileStat(src, st);
+
+    VIR_DEBUG("stat of storage file %p: ret=%d, errno=%d",
+              src, ret, errno);
+
+    return ret;
+}
+
+
+/**
+ * virStorageFileRead: read bytes from a file into a buffer
+ *
+ * @src: file structure pointing to the file
+ * @offset: number of bytes to skip in the storage file
+ * @len: maximum number of bytes read from the storage file
+ * @buf: buffer to read the data into. (buffer shall be freed by caller)
+ *
+ * Returns the count of bytes read on success and -1 on failure, -2 if the
+ * function isn't supported by the backend.
+ * Libvirt error is reported on failure.
+ */
+ssize_t
+virStorageFileRead(virStorageSourcePtr src,
+                   size_t offset,
+                   size_t len,
+                   char **buf)
+{
+    virStorageDriverDataPtr drv = NULL;
+    ssize_t ret;
+
+    if (!virStorageFileIsInitialized(src)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("storage file backend not initialized"));
+        return -1;
+    }
+
+    drv = src->drv;
+
+    if (!drv->backend->storageFileRead)
+        return -2;
+
+    ret = drv->backend->storageFileRead(src, offset, len, buf);
+
+    VIR_DEBUG("read '%zd' bytes from storage '%p' starting at offset '%zu'",
+              ret, src, offset);
+
+    return ret;
+}
+
+
+/*
+ * virStorageFileGetUniqueIdentifier: Get a unique string describing the volume
+ *
+ * @src: file structure pointing to the file
+ *
+ * Returns a string uniquely describing a single volume (canonical path).
+ * The string shall not be freed and is valid until the storage file is
+ * deinitialized. Returns NULL on error and sets a libvirt error code */
+const char *
+virStorageFileGetUniqueIdentifier(virStorageSourcePtr src)
+{
+    virStorageDriverDataPtr drv = NULL;
+
+    if (!virStorageFileIsInitialized(src)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("storage file backend not initialized"));
+        return NULL;
+    }
+
+    drv = src->drv;
+
+    if (!drv->backend->storageFileGetUniqueIdentifier) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("unique storage file identifier not implemented for "
+                         "storage type %s (protocol: %s)'"),
+                       virStorageTypeToString(src->type),
+                       virStorageNetProtocolTypeToString(src->protocol));
+        return NULL;
+    }
+
+    return drv->backend->storageFileGetUniqueIdentifier(src);
+}
+
+
+/**
+ * virStorageFileAccess: Check accessibility of a storage file
+ *
+ * @src: storage file to check access permissions
+ * @mode: accessibility check options (see man 2 access)
+ *
+ * Returns 0 on success, -1 on error and sets errno. No libvirt
+ * error is reported. Returns -2 if the operation isn't supported
+ * by libvirt storage backend.
+ */
+int
+virStorageFileAccess(virStorageSourcePtr src,
+                     int mode)
+{
+    virStorageDriverDataPtr drv = NULL;
+
+    if (!virStorageFileIsInitialized(src)) {
+        errno = ENOSYS;
+        return -2;
+    }
+
+    drv = src->drv;
+
+    if (!drv->backend->storageFileAccess) {
+        errno = ENOSYS;
+        return -2;
+    }
+
+    return drv->backend->storageFileAccess(src, mode);
+}
+
+
+/**
+ * virStorageFileChown: Change owner of a storage file
+ *
+ * @src: storage file to change owner of
+ * @uid: new owner id
+ * @gid: new group id
+ *
+ * Returns 0 on success, -1 on error and sets errno. No libvirt
+ * error is reported. Returns -2 if the operation isn't supported
+ * by libvirt storage backend.
+ */
+int
+virStorageFileChown(const virStorageSource *src,
+                    uid_t uid,
+                    gid_t gid)
+{
+    virStorageDriverDataPtr drv = NULL;
+
+    if (!virStorageFileIsInitialized(src)) {
+        errno = ENOSYS;
+        return -2;
+    }
+
+    drv = src->drv;
+
+    if (!drv->backend->storageFileChown) {
+        errno = ENOSYS;
+        return -2;
+    }
+
+    VIR_DEBUG("chown of storage file %p to %u:%u",
+              src, (unsigned int)uid, (unsigned int)gid);
+
+    return drv->backend->storageFileChown(src, uid, gid);
+}
+
+
+/**
+ * virStorageFileReportBrokenChain:
+ *
+ * @errcode: errno when accessing @src
+ * @src: inaccessible file in the backing chain of @parent
+ * @parent: root virStorageSource being checked
+ *
+ * Reports the correct error message if @src is missing in the backing chain
+ * for @parent.
+ */
+void
+virStorageFileReportBrokenChain(int errcode,
+                                virStorageSourcePtr src,
+                                virStorageSourcePtr parent)
+{
+    if (src->drv) {
+        virStorageDriverDataPtr drv = src->drv;
+        unsigned int access_user = drv->uid;
+        unsigned int access_group = drv->gid;
+
+        if (src == parent) {
+            virReportSystemError(errcode,
+                                 _("Cannot access storage file '%s' "
+                                   "(as uid:%u, gid:%u)"),
+                                 src->path, access_user, access_group);
+        } else {
+            virReportSystemError(errcode,
+                                 _("Cannot access backing file '%s' "
+                                   "of storage file '%s' (as uid:%u, gid:%u)"),
+                                 src->path, parent->path, access_user, access_group);
+        }
+    } else {
+        if (src == parent) {
+            virReportSystemError(errcode,
+                                 _("Cannot access storage file '%s'"),
+                                 src->path);
+        } else {
+            virReportSystemError(errcode,
+                                 _("Cannot access backing file '%s' "
+                                   "of storage file '%s'"),
+                                 src->path, parent->path);
+        }
+    }
+}
+
+
+static int
+virStorageFileGetMetadataRecurseReadHeader(virStorageSourcePtr src,
+                                           virStorageSourcePtr parent,
+                                           uid_t uid,
+                                           gid_t gid,
+                                           char **buf,
+                                           size_t *headerLen,
+                                           GHashTable *cycle)
+{
+    int ret = -1;
+    const char *uniqueName;
+    ssize_t len;
+
+    if (virStorageFileInitAs(src, uid, gid) < 0)
+        return -1;
+
+    if (virStorageFileAccess(src, F_OK) < 0) {
+        virStorageFileReportBrokenChain(errno, src, parent);
+        goto cleanup;
+    }
+
+    if (!(uniqueName = virStorageFileGetUniqueIdentifier(src)))
+        goto cleanup;
+
+    if (virHashHasEntry(cycle, uniqueName)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("backing store for %s (%s) is self-referential"),
+                       NULLSTR(src->path), uniqueName);
+        goto cleanup;
+    }
+
+    if (virHashAddEntry(cycle, uniqueName, NULL) < 0)
+        goto cleanup;
+
+    if ((len = virStorageFileRead(src, 0, VIR_STORAGE_MAX_HEADER, buf)) < 0)
+        goto cleanup;
+
+    *headerLen = len;
+    ret = 0;
+
+ cleanup:
+    virStorageFileDeinit(src);
+    return ret;
+}
+
+
+/* Recursive workhorse for virStorageFileGetMetadata.  */
+static int
+virStorageFileGetMetadataRecurse(virStorageSourcePtr src,
+                                 virStorageSourcePtr parent,
+                                 uid_t uid, gid_t gid,
+                                 bool report_broken,
+                                 GHashTable *cycle,
+                                 unsigned int depth)
+{
+    virStorageFileFormat orig_format = src->format;
+    size_t headerLen;
+    int rv;
+    g_autofree char *buf = NULL;
+    g_autoptr(virStorageSource) backingStore = NULL;
+
+    VIR_DEBUG("path=%s format=%d uid=%u gid=%u",
+              NULLSTR(src->path), src->format,
+              (unsigned int)uid, (unsigned int)gid);
+
+    if (src->format == VIR_STORAGE_FILE_AUTO_SAFE)
+        src->format = VIR_STORAGE_FILE_AUTO;
+
+    /* exit if we can't load information about the current image */
+    rv = virStorageFileSupportsBackingChainTraversal(src);
+    if (rv <= 0) {
+        if (orig_format == VIR_STORAGE_FILE_AUTO)
+            return -2;
+
+        return rv;
+    }
+
+    if (virStorageFileGetMetadataRecurseReadHeader(src, parent, uid, gid,
+                                                   &buf, &headerLen, cycle) < 0)
+        return -1;
+
+    if (virStorageFileGetMetadataInternal(src, buf, headerLen) < 0)
+        return -1;
+
+    /* If we probed the format we MUST ensure that nothing else than the current
+     * image is considered for security labelling and/or recursion. */
+    if (orig_format == VIR_STORAGE_FILE_AUTO) {
+        if (src->backingStoreRaw) {
+            src->format = VIR_STORAGE_FILE_RAW;
+            VIR_FREE(src->backingStoreRaw);
+            return -2;
+        }
+    }
+
+    if (src->backingStoreRaw) {
+        if ((rv = virStorageSourceNewFromBacking(src, &backingStore)) < 0)
+            return -1;
+
+        /* the backing file would not be usable for VM usage */
+        if (rv == 1)
+            return 0;
+
+        if ((rv = virStorageFileGetMetadataRecurse(backingStore, parent,
+                                                   uid, gid,
+                                                   report_broken,
+                                                   cycle, depth + 1)) < 0) {
+            if (!report_broken)
+                return 0;
+
+            if (rv == -2) {
+                virReportError(VIR_ERR_OPERATION_INVALID,
+                               _("format of backing image '%s' of image '%s' was not specified in the image metadata "
+                                 "(See https://libvirt.org/kbase/backing_chains.html for troubleshooting)"),
+                               src->backingStoreRaw, NULLSTR(src->path));
+            }
+
+            return -1;
+        }
+
+        backingStore->id = depth;
+        src->backingStore = g_steal_pointer(&backingStore);
+    } else {
+        /* add terminator */
+        src->backingStore = virStorageSourceNew();
+    }
+
+    return 0;
+}
+
+
+/**
+ * virStorageFileGetMetadata:
+ *
+ * Extract metadata about the storage volume with the specified
+ * image format. If image format is VIR_STORAGE_FILE_AUTO, it
+ * will probe to automatically identify the format.  Recurses through
+ * the entire chain.
+ *
+ * Open files using UID and GID (or pass -1 for the current user/group).
+ * Treat any backing files without explicit type as raw, unless ALLOW_PROBE.
+ *
+ * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a
+ * format, since a malicious guest can turn a raw file into any
+ * other non-raw format at will.
+ *
+ * If @report_broken is true, the whole function fails with a possibly sane
+ * error instead of just returning a broken chain. Note that the inability for
+ * libvirt to traverse a given source is not considered an error.
+ *
+ * Caller MUST free result after use via virObjectUnref.
+ */
+int
+virStorageFileGetMetadata(virStorageSourcePtr src,
+                          uid_t uid, gid_t gid,
+                          bool report_broken)
+{
+    GHashTable *cycle = 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;
+        else
+            src->format = VIR_STORAGE_FILE_RAW;
+    }
+
+    ret = virStorageFileGetMetadataRecurse(src, src, uid, gid,
+                                           report_broken, cycle, 1);
+
+    virHashFree(cycle);
+    return ret;
+}
+
+
+/**
+ * virStorageFileGetBackingStoreStr:
+ * @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
+virStorageFileGetBackingStoreStr(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 (!virStorageFileSupportsBackingChainTraversal(src))
+        return 0;
+
+    rv = virStorageFileAccess(src, F_OK);
+    if (rv == -2)
+        return 0;
+    if (rv < 0) {
+        virStorageFileReportBrokenChain(errno, src, src);
+        return -1;
+    }
+
+    if ((headerLen = virStorageFileRead(src, 0, VIR_STORAGE_MAX_HEADER,
+                                        &buf)) < 0) {
+        if (headerLen == -2)
+            return 0;
+        return -1;
+    }
+
+    if (!(tmp = virStorageSourceCopy(src, false)))
+        return -1;
+
+    if (virStorageFileGetMetadataInternal(tmp, buf, headerLen) < 0)
+        return -1;
+
+    *backing = g_steal_pointer(&tmp->backingStoreRaw);
+    return 0;
+}
diff --git a/src/storage_file/storage_file.h b/src/storage_file/storage_file.h
new file mode 100644
index 0000000000..8afedc35b3
--- /dev/null
+++ b/src/storage_file/storage_file.h
@@ -0,0 +1,197 @@
+/*
+ * storage_file.h: file utility functions for FS storage backend
+ *
+ * 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 "virstoragefile.h"
+
+
+/* Minimum header size required to probe all known formats with
+ * virStorageFileProbeFormat, or obtain metadata from a known format.
+ * Rounded to multiple of 512 (ISO has a 5-byte magic at offset
+ * 32769).  Some formats can be probed with fewer bytes.  Although
+ * some formats theoretically permit metadata that can rely on offsets
+ * beyond this size, in practice that doesn't matter.  */
+#define VIR_STORAGE_MAX_HEADER 0x8200
+
+#ifndef DEV_BSIZE
+# define DEV_BSIZE 512
+#endif
+
+int
+virStorageFileProbeFormat(const char *path,
+                          uid_t uid,
+                          gid_t gid);
+
+virStorageSourcePtr
+virStorageFileGetMetadataFromFD(const char *path,
+                                int fd,
+                                int format);
+
+virStorageSourcePtr
+virStorageFileGetMetadataFromBuf(const char *path,
+                                 char *buf,
+                                 size_t len,
+                                 int format)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+
+int
+virStorageFileParseChainIndex(const char *diskTarget,
+                              const char *name,
+                              unsigned int *chainIndex)
+    ATTRIBUTE_NONNULL(3);
+
+int
+virStorageFileParseBackingStoreStr(const char *str,
+                                   char **target,
+                                   unsigned int *chainIndex)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
+
+virStorageSourcePtr
+virStorageFileChainLookup(virStorageSourcePtr chain,
+                          virStorageSourcePtr startFrom,
+                          const char *name,
+                          unsigned int idx,
+                          virStorageSourcePtr *parent)
+    ATTRIBUTE_NONNULL(1);
+
+int
+virStorageSourceUpdatePhysicalSize(virStorageSourcePtr src,
+                                   int fd,
+                                   struct stat const *sb);
+int
+virStorageSourceUpdateBackingSizes(virStorageSourcePtr src,
+                                   int fd,
+                                   struct stat const *sb);
+
+int
+virStorageSourceUpdateCapacity(virStorageSourcePtr src,
+                               char *buf,
+                               ssize_t len);
+
+int
+virStorageSourceNewFromBacking(virStorageSourcePtr parent,
+                               virStorageSourcePtr *backing);
+
+int
+virStorageSourceParseRBDColonString(const char *rbdstr,
+                                    virStorageSourcePtr src)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+
+typedef int
+(*virStorageFileSimplifyPathReadlinkCallback)(const char *path,
+                                              char **link,
+                                              void *data);
+
+char *
+virStorageFileCanonicalizePath(const char *path,
+                               virStorageFileSimplifyPathReadlinkCallback cb,
+                               void *cbdata);
+
+int
+virStorageFileGetRelativeBackingPath(virStorageSourcePtr from,
+                                     virStorageSourcePtr to,
+                                     char **relpath)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+
+int
+virStorageSourceNewFromBackingAbsolute(const char *path,
+                                       virStorageSourcePtr *src);
+
+virStorageSourcePtr
+virStorageSourceFindByNodeName(virStorageSourcePtr top,
+                               const char *nodeName)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+
+int
+virStorageSourcePrivateDataParseRelPath(xmlXPathContextPtr ctxt,
+                                        virStorageSourcePtr src);
+
+int
+virStorageSourcePrivateDataFormatRelPath(virStorageSourcePtr src,
+                                         virBufferPtr buf);
+
+int
+virStorageFileInit(virStorageSourcePtr src);
+
+int
+virStorageFileInitAs(virStorageSourcePtr src,
+                     uid_t uid,
+                     gid_t gid);
+
+void
+virStorageFileDeinit(virStorageSourcePtr src);
+
+int
+virStorageFileCreate(virStorageSourcePtr src);
+
+int
+virStorageFileUnlink(virStorageSourcePtr src);
+
+int
+virStorageFileStat(virStorageSourcePtr src,
+                   struct stat *stat);
+
+ssize_t
+virStorageFileRead(virStorageSourcePtr src,
+                   size_t offset,
+                   size_t len,
+                   char **buf);
+
+const char *
+virStorageFileGetUniqueIdentifier(virStorageSourcePtr src);
+
+int
+virStorageFileAccess(virStorageSourcePtr src,
+                     int mode);
+
+int
+virStorageFileChown(const virStorageSource *src,
+                    uid_t uid,
+                    gid_t gid);
+
+int
+virStorageFileSupportsSecurityDriver(const virStorageSource *src);
+
+int
+virStorageFileSupportsAccess(const virStorageSource *src);
+
+int
+virStorageFileSupportsCreate(const virStorageSource *src);
+
+int
+virStorageFileSupportsBackingChainTraversal(const virStorageSource *src);
+
+int
+virStorageFileGetMetadata(virStorageSourcePtr src,
+                          uid_t uid, gid_t gid,
+                          bool report_broken)
+    ATTRIBUTE_NONNULL(1);
+
+int
+virStorageFileGetBackingStoreStr(virStorageSourcePtr src,
+                                 char **backing)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+
+void
+virStorageFileReportBrokenChain(int errcode,
+                                virStorageSourcePtr src,
+                                virStorageSourcePtr parent);
diff --git a/src/storage_file/storage_file_gluster.c b/src/storage_file/storage_file_gluster.c
index e26dfdfd99..599afada2c 100644
--- a/src/storage_file/storage_file_gluster.c
+++ b/src/storage_file/storage_file_gluster.c
@@ -23,6 +23,7 @@
 
 #include <glusterfs/api/glfs.h>
 
+#include "storage_file.h"
 #include "storage_file_gluster.h"
 #include "viralloc.h"
 #include "virerror.h"
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
index 524204a56c..3489934546 100644
--- a/src/util/virstoragefile.c
+++ b/src/util/virstoragefile.c
@@ -20,26 +20,17 @@
  */
 
 #include <config.h>
-#include "virstoragefilebackend.h"
+#include "virstoragefile.h"
 
-#include <unistd.h>
-#include <fcntl.h>
 #include "viralloc.h"
 #include "virxml.h"
-#include "viruuid.h"
 #include "virerror.h"
 #include "virlog.h"
-#include "virfile.h"
 #include "vircommand.h"
-#include "virhash.h"
-#include "virendian.h"
 #include "virstring.h"
-#include "viruri.h"
 #include "virbuffer.h"
-#include "virjson.h"
 #include "virstorageencryption.h"
 #include "virsecret.h"
-#include "virutil.h"
 
 #define VIR_FROM_THIS VIR_FROM_STORAGE
 
@@ -111,1054 +102,6 @@ VIR_ENUM_IMPL(virStorageAuth,
               "none", "chap", "ceph",
 );
 
-enum lv_endian {
-    LV_LITTLE_ENDIAN = 1, /* 1234 */
-    LV_BIG_ENDIAN         /* 4321 */
-};
-
-enum {
-    BACKING_STORE_OK,
-    BACKING_STORE_INVALID,
-    BACKING_STORE_ERROR,
-};
-
-#define FILE_TYPE_VERSIONS_LAST 3
-
-struct FileEncryptionInfo {
-    int format; /* Encryption format to assign */
-
-    int magicOffset; /* Byte offset of the magic */
-    const char *magic; /* Optional string of magic */
-
-    enum lv_endian endian; /* Endianness of file format */
-
-    int versionOffset;    /* Byte offset from start of file
-                           * where we find version number,
-                           * -1 to always fail the version test,
-                           * -2 to always pass the version test */
-    int versionSize;      /* Size in bytes of version data (0, 2, or 4) */
-    int versionNumbers[FILE_TYPE_VERSIONS_LAST];
-                          /* Version numbers to validate. Zeroes are ignored. */
-
-    int modeOffset; /* Byte offset of the format native encryption mode */
-    char modeValue; /* Value expected at offset */
-
-    int payloadOffset; /* start offset of the volume data (in 512 byte sectors) */
-};
-
-struct FileTypeInfo {
-    int magicOffset;    /* Byte offset of the magic */
-    const char *magic;  /* Optional string of file magic
-                         * to check at head of file */
-    enum lv_endian endian; /* Endianness of file format */
-
-    int versionOffset;    /* Byte offset from start of file
-                           * where we find version number,
-                           * -1 to always fail the version test,
-                           * -2 to always pass the version test */
-    int versionSize;      /* Size in bytes of version data (0, 2, or 4) */
-    int versionNumbers[FILE_TYPE_VERSIONS_LAST];
-                          /* Version numbers to validate. Zeroes are ignored. */
-    int sizeOffset;       /* Byte offset from start of file
-                           * where we find capacity info,
-                           * -1 to use st_size as capacity */
-    int sizeBytes;        /* Number of bytes for size field */
-    int sizeMultiplier;   /* A scaling factor if size is not in bytes */
-                          /* Store a COW base image path (possibly relative),
-                           * or NULL if there is no COW base image, to RES;
-                           * return BACKING_STORE_* */
-    const struct FileEncryptionInfo *cryptInfo; /* Encryption info */
-    int (*getBackingStore)(char **res, int *format,
-                           const char *buf, size_t buf_size);
-    int (*getFeatures)(virBitmapPtr *features, int format,
-                       char *buf, ssize_t len);
-};
-
-
-static int cowGetBackingStore(char **, int *,
-                              const char *, size_t);
-static int qcowXGetBackingStore(char **, int *,
-                                const char *, size_t);
-static int qcow2GetFeatures(virBitmapPtr *features, int format,
-                            char *buf, ssize_t len);
-static int vmdk4GetBackingStore(char **, int *,
-                                const char *, size_t);
-static int
-qedGetBackingStore(char **, int *, const char *, size_t);
-
-#define QCOWX_HDR_VERSION (4)
-#define QCOWX_HDR_BACKING_FILE_OFFSET (QCOWX_HDR_VERSION+4)
-#define QCOWX_HDR_BACKING_FILE_SIZE (QCOWX_HDR_BACKING_FILE_OFFSET+8)
-#define QCOWX_HDR_IMAGE_SIZE (QCOWX_HDR_BACKING_FILE_SIZE+4+4)
-
-#define QCOW1_HDR_CRYPT (QCOWX_HDR_IMAGE_SIZE+8+1+1+2)
-#define QCOW2_HDR_CRYPT (QCOWX_HDR_IMAGE_SIZE+8)
-
-#define QCOW1_HDR_TOTAL_SIZE (QCOW1_HDR_CRYPT+4+8)
-#define QCOW2_HDR_TOTAL_SIZE (QCOW2_HDR_CRYPT+4+4+8+8+4+4+8)
-
-#define QCOW2_HDR_EXTENSION_END 0
-#define QCOW2_HDR_EXTENSION_BACKING_FORMAT 0xE2792ACA
-
-#define QCOW2v3_HDR_FEATURES_INCOMPATIBLE (QCOW2_HDR_TOTAL_SIZE)
-#define QCOW2v3_HDR_FEATURES_COMPATIBLE (QCOW2v3_HDR_FEATURES_INCOMPATIBLE+8)
-#define QCOW2v3_HDR_FEATURES_AUTOCLEAR (QCOW2v3_HDR_FEATURES_COMPATIBLE+8)
-
-/* The location of the header size [4 bytes] */
-#define QCOW2v3_HDR_SIZE       (QCOW2_HDR_TOTAL_SIZE+8+8+8+4)
-
-#define QED_HDR_FEATURES_OFFSET (4+4+4+4)
-#define QED_HDR_IMAGE_SIZE (QED_HDR_FEATURES_OFFSET+8+8+8+8)
-#define QED_HDR_BACKING_FILE_OFFSET (QED_HDR_IMAGE_SIZE+8)
-#define QED_HDR_BACKING_FILE_SIZE (QED_HDR_BACKING_FILE_OFFSET+4)
-#define QED_F_BACKING_FILE 0x01
-#define QED_F_BACKING_FORMAT_NO_PROBE 0x04
-
-#define PLOOP_IMAGE_SIZE_OFFSET 36
-#define PLOOP_SIZE_MULTIPLIER 512
-
-#define LUKS_HDR_MAGIC_LEN 6
-#define LUKS_HDR_VERSION_LEN 2
-#define LUKS_HDR_CIPHER_NAME_LEN 32
-#define LUKS_HDR_CIPHER_MODE_LEN 32
-#define LUKS_HDR_HASH_SPEC_LEN 32
-#define LUKS_HDR_PAYLOAD_LEN 4
-
-/* Format described by qemu commit id '3e308f20e' */
-#define LUKS_HDR_VERSION_OFFSET LUKS_HDR_MAGIC_LEN
-#define LUKS_HDR_PAYLOAD_OFFSET (LUKS_HDR_MAGIC_LEN+\
-                                 LUKS_HDR_VERSION_LEN+\
-                                 LUKS_HDR_CIPHER_NAME_LEN+\
-                                 LUKS_HDR_CIPHER_MODE_LEN+\
-                                 LUKS_HDR_HASH_SPEC_LEN)
-
-static struct FileEncryptionInfo const luksEncryptionInfo[] = {
-    {
-        .format = VIR_STORAGE_ENCRYPTION_FORMAT_LUKS,
-
-        /* Magic is 'L','U','K','S', 0xBA, 0xBE */
-        .magicOffset = 0,
-        .magic = "\x4c\x55\x4b\x53\xba\xbe",
-        .endian = LV_BIG_ENDIAN,
-
-        .versionOffset  = LUKS_HDR_VERSION_OFFSET,
-        .versionSize = LUKS_HDR_VERSION_LEN,
-        .versionNumbers = {1},
-
-        .modeOffset = -1,
-        .modeValue = -1,
-
-        .payloadOffset = LUKS_HDR_PAYLOAD_OFFSET,
-    },
-    { 0 }
-};
-
-static struct FileEncryptionInfo const qcow1EncryptionInfo[] = {
-    {
-        .format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW,
-
-        .magicOffset = 0,
-        .magic = NULL,
-        .endian = LV_BIG_ENDIAN,
-
-        .versionOffset  = -1,
-        .versionSize = 0,
-        .versionNumbers = {},
-
-        .modeOffset = QCOW1_HDR_CRYPT,
-        .modeValue = 1,
-
-        .payloadOffset = -1,
-    },
-    { 0 }
-};
-
-static struct FileEncryptionInfo const qcow2EncryptionInfo[] = {
-    {
-        .format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW,
-
-        .magicOffset = 0,
-        .magic = NULL,
-        .endian = LV_BIG_ENDIAN,
-
-        .versionOffset  = -1,
-        .versionSize = 0,
-        .versionNumbers = {},
-
-        .modeOffset = QCOW2_HDR_CRYPT,
-        .modeValue = 1,
-
-        .payloadOffset = -1,
-    },
-    {
-        .format = VIR_STORAGE_ENCRYPTION_FORMAT_LUKS,
-
-        .magicOffset = 0,
-        .magic = NULL,
-        .endian = LV_BIG_ENDIAN,
-
-        .versionOffset  = -1,
-        .versionSize = 0,
-        .versionNumbers = {},
-
-        .modeOffset = QCOW2_HDR_CRYPT,
-        .modeValue = 2,
-
-        .payloadOffset = -1,
-    },
-    { 0 }
-};
-
-static struct FileTypeInfo const fileTypeInfo[] = {
-    [VIR_STORAGE_FILE_NONE] = { 0, NULL, LV_LITTLE_ENDIAN,
-                                -1, 0, {0}, 0, 0, 0, NULL, NULL, NULL },
-    [VIR_STORAGE_FILE_RAW] = { 0, NULL, LV_LITTLE_ENDIAN,
-                               -1, 0, {0}, 0, 0, 0,
-                               luksEncryptionInfo,
-                               NULL, NULL },
-    [VIR_STORAGE_FILE_DIR] = { 0, NULL, LV_LITTLE_ENDIAN,
-                               -1, 0, {0}, 0, 0, 0, NULL, NULL, NULL },
-    [VIR_STORAGE_FILE_BOCHS] = {
-        /*"Bochs Virtual HD Image", */ /* Untested */
-        0, NULL,
-        LV_LITTLE_ENDIAN, 64, 4, {0x20000},
-        32+16+16+4+4+4+4+4, 8, 1, NULL, NULL, NULL
-    },
-    [VIR_STORAGE_FILE_CLOOP] = {
-        /* #!/bin/sh
-           #V2.0 Format
-           modprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1
-        */ /* Untested */
-        0, NULL,
-        LV_LITTLE_ENDIAN, -1, 0, {0},
-        -1, 0, 0, NULL, NULL, NULL
-    },
-    [VIR_STORAGE_FILE_DMG] = {
-        /* XXX QEMU says there's no magic for dmg,
-         * /usr/share/misc/magic lists double magic (both offsets
-         * would have to match) but then disables that check. */
-        0, NULL,
-        0, -1, 0, {0},
-        -1, 0, 0, NULL, NULL, NULL
-    },
-    [VIR_STORAGE_FILE_ISO] = {
-        32769, "CD001",
-        LV_LITTLE_ENDIAN, -2, 0, {0},
-        -1, 0, 0, NULL, NULL, NULL
-    },
-    [VIR_STORAGE_FILE_VPC] = {
-        0, "conectix",
-        LV_BIG_ENDIAN, 12, 4, {0x10000},
-        8 + 4 + 4 + 8 + 4 + 4 + 2 + 2 + 4, 8, 1, NULL, NULL, NULL
-    },
-    /* TODO: add getBackingStore function */
-    [VIR_STORAGE_FILE_VDI] = {
-        64, "\x7f\x10\xda\xbe",
-        LV_LITTLE_ENDIAN, 68, 4, {0x00010001},
-        64 + 5 * 4 + 256 + 7 * 4, 8, 1, NULL, NULL, NULL},
-
-    /* Not direct file formats, but used for various drivers */
-    [VIR_STORAGE_FILE_FAT] = { 0, NULL, LV_LITTLE_ENDIAN,
-                               -1, 0, {0}, 0, 0, 0, NULL, NULL, NULL },
-    [VIR_STORAGE_FILE_VHD] = { 0, NULL, LV_LITTLE_ENDIAN,
-                               -1, 0, {0}, 0, 0, 0, NULL, NULL, NULL },
-    [VIR_STORAGE_FILE_PLOOP] = { 0, "WithouFreSpacExt", LV_LITTLE_ENDIAN,
-                                 -2, 0, {0}, PLOOP_IMAGE_SIZE_OFFSET, 0,
-                                 PLOOP_SIZE_MULTIPLIER, NULL, NULL, NULL },
-
-    /* All formats with a backing store probe below here */
-    [VIR_STORAGE_FILE_COW] = {
-        0, "OOOM",
-        LV_BIG_ENDIAN, 4, 4, {2},
-        4+4+1024+4, 8, 1, NULL, cowGetBackingStore, NULL
-    },
-    [VIR_STORAGE_FILE_QCOW] = {
-        0, "QFI",
-        LV_BIG_ENDIAN, 4, 4, {1},
-        QCOWX_HDR_IMAGE_SIZE, 8, 1,
-        qcow1EncryptionInfo,
-        qcowXGetBackingStore, NULL
-    },
-    [VIR_STORAGE_FILE_QCOW2] = {
-        0, "QFI",
-        LV_BIG_ENDIAN, 4, 4, {2, 3},
-        QCOWX_HDR_IMAGE_SIZE, 8, 1,
-        qcow2EncryptionInfo,
-        qcowXGetBackingStore,
-        qcow2GetFeatures
-    },
-    [VIR_STORAGE_FILE_QED] = {
-        /* https://wiki.qemu.org/Features/QED */
-        0, "QED",
-        LV_LITTLE_ENDIAN, -2, 0, {0},
-        QED_HDR_IMAGE_SIZE, 8, 1, NULL, qedGetBackingStore, NULL
-    },
-    [VIR_STORAGE_FILE_VMDK] = {
-        0, "KDMV",
-        LV_LITTLE_ENDIAN, 4, 4, {1, 2, 3},
-        4+4+4, 8, 512, NULL, vmdk4GetBackingStore, NULL
-    },
-};
-G_STATIC_ASSERT(G_N_ELEMENTS(fileTypeInfo) == VIR_STORAGE_FILE_LAST);
-
-
-/* qcow2 compatible features in the order they appear on-disk */
-enum qcow2CompatibleFeature {
-    QCOW2_COMPATIBLE_FEATURE_LAZY_REFCOUNTS = 0,
-
-    QCOW2_COMPATIBLE_FEATURE_LAST
-};
-
-/* conversion to virStorageFileFeature */
-static const int qcow2CompatibleFeatureArray[] = {
-    VIR_STORAGE_FILE_FEATURE_LAZY_REFCOUNTS,
-};
-G_STATIC_ASSERT(G_N_ELEMENTS(qcow2CompatibleFeatureArray) ==
-       QCOW2_COMPATIBLE_FEATURE_LAST);
-
-static int
-cowGetBackingStore(char **res,
-                   int *format,
-                   const char *buf,
-                   size_t buf_size)
-{
-#define COW_FILENAME_MAXLEN 1024
-    *res = NULL;
-    *format = VIR_STORAGE_FILE_AUTO;
-
-    if (buf_size < 4+4+ COW_FILENAME_MAXLEN)
-        return BACKING_STORE_INVALID;
-    if (buf[4+4] == '\0') { /* cow_header_v2.backing_file[0] */
-        *format = VIR_STORAGE_FILE_NONE;
-        return BACKING_STORE_OK;
-    }
-
-    *res = g_strndup((const char *)buf + 4 + 4, COW_FILENAME_MAXLEN);
-    return BACKING_STORE_OK;
-}
-
-
-static int
-qcow2GetExtensions(const char *buf,
-                   size_t buf_size,
-                   int *backingFormat)
-{
-    size_t offset;
-    size_t extension_start;
-    size_t extension_end;
-    int version = virReadBufInt32BE(buf + QCOWX_HDR_VERSION);
-
-    if (version < 2) {
-        /* QCow1 doesn't have the extensions capability
-         * used to store backing format */
-        return 0;
-    }
-
-    if (version == 2)
-        extension_start = QCOW2_HDR_TOTAL_SIZE;
-    else
-        extension_start = virReadBufInt32BE(buf + QCOW2v3_HDR_SIZE);
-
-    /*
-     * Traditionally QCow2 files had a layout of
-     *
-     * [header]
-     * [backingStoreName]
-     *
-     * Although the backingStoreName typically followed
-     * the header immediately, this was not required by
-     * the format. By specifying a higher byte offset for
-     * the backing file offset in the header, it was
-     * possible to leave space between the header and
-     * start of backingStore.
-     *
-     * This hack is now used to store extensions to the
-     * qcow2 format:
-     *
-     * [header]
-     * [extensions]
-     * [backingStoreName]
-     *
-     * Thus the file region to search for extensions is
-     * between the end of the header (QCOW2_HDR_TOTAL_SIZE)
-     * and the start of the backingStoreName (offset)
-     *
-     * for qcow2 v3 images, the length of the header
-     * is stored at QCOW2v3_HDR_SIZE
-     */
-    extension_end = virReadBufInt64BE(buf + QCOWX_HDR_BACKING_FILE_OFFSET);
-    if (extension_end > buf_size)
-        return -1;
-
-    /*
-     * The extensions take format of
-     *
-     * int32: magic
-     * int32: length
-     * byte[length]: payload
-     *
-     * Unknown extensions can be ignored by skipping
-     * over "length" bytes in the data stream.
-     */
-    offset = extension_start;
-    while (offset < (buf_size-8) &&
-           offset < (extension_end-8)) {
-        unsigned int magic = virReadBufInt32BE(buf + offset);
-        unsigned int len = virReadBufInt32BE(buf + offset + 4);
-
-        offset += 8;
-
-        if ((offset + len) < offset)
-            break;
-
-        if ((offset + len) > buf_size)
-            break;
-
-        switch (magic) {
-        case QCOW2_HDR_EXTENSION_BACKING_FORMAT: {
-            g_autofree char *tmp = NULL;
-            if (!backingFormat)
-                break;
-
-            tmp = g_new0(char, len + 1);
-            memcpy(tmp, buf + offset, len);
-            tmp[len] = '\0';
-
-            *backingFormat = virStorageFileFormatTypeFromString(tmp);
-            if (*backingFormat <= VIR_STORAGE_FILE_NONE)
-                return -1;
-            break;
-        }
-
-        case QCOW2_HDR_EXTENSION_END:
-            return 0;
-        }
-
-        offset += len;
-    }
-
-    return 0;
-}
-
-
-static int
-qcowXGetBackingStore(char **res,
-                     int *format,
-                     const char *buf,
-                     size_t buf_size)
-{
-    unsigned long long offset;
-    unsigned int size;
-
-    *res = NULL;
-    *format = VIR_STORAGE_FILE_AUTO;
-
-    if (buf_size < QCOWX_HDR_BACKING_FILE_OFFSET+8+4)
-        return BACKING_STORE_INVALID;
-
-    offset = virReadBufInt64BE(buf + QCOWX_HDR_BACKING_FILE_OFFSET);
-    if (offset > buf_size)
-        return BACKING_STORE_INVALID;
-
-    if (offset == 0) {
-        *format = VIR_STORAGE_FILE_NONE;
-        return BACKING_STORE_OK;
-    }
-
-    size = virReadBufInt32BE(buf + QCOWX_HDR_BACKING_FILE_SIZE);
-    if (size == 0) {
-        *format = VIR_STORAGE_FILE_NONE;
-        return BACKING_STORE_OK;
-    }
-    if (size > 1023)
-        return BACKING_STORE_INVALID;
-    if (offset + size > buf_size || offset + size < offset)
-        return BACKING_STORE_INVALID;
-    *res = g_new0(char, size + 1);
-    memcpy(*res, buf + offset, size);
-    (*res)[size] = '\0';
-
-    if (qcow2GetExtensions(buf, buf_size, format) < 0)
-        return BACKING_STORE_INVALID;
-
-    return BACKING_STORE_OK;
-}
-
-
-static int
-vmdk4GetBackingStore(char **res,
-                     int *format,
-                     const char *buf,
-                     size_t buf_size)
-{
-    static const char prefix[] = "parentFileNameHint=\"";
-    char *start, *end;
-    size_t len;
-    g_autofree char *desc = NULL;
-
-    desc = g_new0(char, VIR_STORAGE_MAX_HEADER);
-
-    *res = NULL;
-    /*
-     * Technically this should have been VMDK, since
-     * VMDK spec / VMware impl only support VMDK backed
-     * by VMDK. QEMU isn't following this though and
-     * does probing on VMDK backing files, hence we set
-     * AUTO
-     */
-    *format = VIR_STORAGE_FILE_AUTO;
-
-    if (buf_size <= 0x200)
-        return BACKING_STORE_INVALID;
-
-    len = buf_size - 0x200;
-    if (len > VIR_STORAGE_MAX_HEADER)
-        len = VIR_STORAGE_MAX_HEADER;
-    memcpy(desc, buf + 0x200, len);
-    desc[len] = '\0';
-    start = strstr(desc, prefix);
-    if (start == NULL) {
-        *format = VIR_STORAGE_FILE_NONE;
-        return BACKING_STORE_OK;
-    }
-    start += strlen(prefix);
-    end = strchr(start, '"');
-    if (end == NULL)
-        return BACKING_STORE_INVALID;
-
-    if (end == start) {
-        *format = VIR_STORAGE_FILE_NONE;
-        return BACKING_STORE_OK;
-    }
-    *end = '\0';
-    *res = g_strdup(start);
-
-    return BACKING_STORE_OK;
-}
-
-static int
-qedGetBackingStore(char **res,
-                   int *format,
-                   const char *buf,
-                   size_t buf_size)
-{
-    unsigned long long flags;
-    unsigned long offset, size;
-
-    *res = NULL;
-    /* Check if this image has a backing file */
-    if (buf_size < QED_HDR_FEATURES_OFFSET+8)
-        return BACKING_STORE_INVALID;
-    flags = virReadBufInt64LE(buf + QED_HDR_FEATURES_OFFSET);
-    if (!(flags & QED_F_BACKING_FILE)) {
-        *format = VIR_STORAGE_FILE_NONE;
-        return BACKING_STORE_OK;
-    }
-
-    /* Parse the backing file */
-    if (buf_size < QED_HDR_BACKING_FILE_OFFSET+8)
-        return BACKING_STORE_INVALID;
-    offset = virReadBufInt32LE(buf + QED_HDR_BACKING_FILE_OFFSET);
-    if (offset > buf_size)
-        return BACKING_STORE_INVALID;
-    size = virReadBufInt32LE(buf + QED_HDR_BACKING_FILE_SIZE);
-    if (size == 0)
-        return BACKING_STORE_OK;
-    if (offset + size > buf_size || offset + size < offset)
-        return BACKING_STORE_INVALID;
-    *res = g_new0(char, size + 1);
-    memcpy(*res, buf + offset, size);
-    (*res)[size] = '\0';
-
-    if (flags & QED_F_BACKING_FORMAT_NO_PROBE)
-        *format = VIR_STORAGE_FILE_RAW;
-    else
-        *format = VIR_STORAGE_FILE_AUTO_SAFE;
-
-    return BACKING_STORE_OK;
-}
-
-
-static bool
-virStorageFileMatchesMagic(int magicOffset,
-                           const char *magic,
-                           char *buf,
-                           size_t buflen)
-{
-    int mlen;
-
-    if (magic == NULL)
-        return false;
-
-    /* Validate magic data */
-    mlen = strlen(magic);
-    if (magicOffset + mlen > buflen)
-        return false;
-
-    if (memcmp(buf + magicOffset, magic, mlen) != 0)
-        return false;
-
-    return true;
-}
-
-
-static bool
-virStorageFileMatchesVersion(int versionOffset,
-                             int versionSize,
-                             const int *versionNumbers,
-                             int endian,
-                             char *buf,
-                             size_t buflen)
-{
-    int version;
-    size_t i;
-
-    /* Validate version number info */
-    if (versionOffset == -1)
-        return false;
-
-    /* -2 == non-versioned file format, so trivially match */
-    if (versionOffset == -2)
-        return true;
-
-    /* A positive versionOffset, requires using a valid versionSize */
-    if (versionSize != 2 && versionSize != 4)
-        return false;
-
-    if ((versionOffset + versionSize) > buflen)
-        return false;
-
-    if (endian == LV_LITTLE_ENDIAN) {
-        if (versionSize == 4)
-            version = virReadBufInt32LE(buf +
-                                        versionOffset);
-        else
-            version = virReadBufInt16LE(buf +
-                                        versionOffset);
-    } else {
-        if (versionSize == 4)
-            version = virReadBufInt32BE(buf +
-                                        versionOffset);
-        else
-            version = virReadBufInt16BE(buf +
-                                        versionOffset);
-    }
-
-    for (i = 0;
-         i < FILE_TYPE_VERSIONS_LAST && versionNumbers[i];
-         i++) {
-        VIR_DEBUG("Compare detected version %d vs one of the expected versions %d",
-                  version, versionNumbers[i]);
-        if (version == versionNumbers[i])
-            return true;
-    }
-
-    return false;
-}
-
-
-static int
-virStorageFileProbeFormatFromBuf(const char *path,
-                                 char *buf,
-                                 size_t buflen)
-{
-    int format = VIR_STORAGE_FILE_RAW;
-    size_t i;
-    int possibleFormat = VIR_STORAGE_FILE_RAW;
-    VIR_DEBUG("path=%s, buf=%p, buflen=%zu", path, buf, buflen);
-
-    /* First check file magic */
-    for (i = 0; i < VIR_STORAGE_FILE_LAST; i++) {
-        if (virStorageFileMatchesMagic(fileTypeInfo[i].magicOffset,
-                                       fileTypeInfo[i].magic,
-                                       buf, buflen)) {
-            if (!virStorageFileMatchesVersion(fileTypeInfo[i].versionOffset,
-                                              fileTypeInfo[i].versionSize,
-                                              fileTypeInfo[i].versionNumbers,
-                                              fileTypeInfo[i].endian,
-                                              buf, buflen)) {
-                possibleFormat = i;
-                continue;
-            }
-            format = i;
-            goto cleanup;
-        }
-    }
-
-    if (possibleFormat != VIR_STORAGE_FILE_RAW)
-        VIR_WARN("File %s matches %s magic, but version is wrong. "
-                 "Please report new version to libvir-list at redhat.com",
-                 path, virStorageFileFormatTypeToString(possibleFormat));
-
- cleanup:
-    VIR_DEBUG("format=%d", format);
-    return format;
-}
-
-
-static int
-qcow2GetFeatures(virBitmapPtr *features,
-                 int format,
-                 char *buf,
-                 ssize_t len)
-{
-    int version = -1;
-    virBitmapPtr feat = NULL;
-    uint64_t bits;
-    size_t i;
-
-    version = virReadBufInt32BE(buf + fileTypeInfo[format].versionOffset);
-
-    if (version == 2)
-        return 0;
-
-    if (len < QCOW2v3_HDR_SIZE)
-        return -1;
-
-    feat = virBitmapNew(VIR_STORAGE_FILE_FEATURE_LAST);
-
-    /* todo: check for incompatible or autoclear features? */
-    bits = virReadBufInt64BE(buf + QCOW2v3_HDR_FEATURES_COMPATIBLE);
-    for (i = 0; i < QCOW2_COMPATIBLE_FEATURE_LAST; i++) {
-        if (bits & ((uint64_t) 1 << i))
-            ignore_value(virBitmapSetBit(feat, qcow2CompatibleFeatureArray[i]));
-    }
-
-    *features = feat;
-    return 0;
-}
-
-
-static bool
-virStorageFileHasEncryptionFormat(const struct FileEncryptionInfo *info,
-                                  char *buf,
-                                  size_t len)
-{
-    if (!info->magic && info->modeOffset == -1)
-        return false; /* Shouldn't happen - expect at least one */
-
-    if (info->magic) {
-        if (!virStorageFileMatchesMagic(info->magicOffset,
-                                        info->magic,
-                                        buf, len))
-            return false;
-
-        if (info->versionOffset != -1 &&
-            !virStorageFileMatchesVersion(info->versionOffset,
-                                          info->versionSize,
-                                          info->versionNumbers,
-                                          info->endian,
-                                          buf, len))
-            return false;
-
-        return true;
-    } else if (info->modeOffset != -1) {
-        int crypt_format;
-
-        if (info->modeOffset >= len)
-            return false;
-
-        crypt_format = virReadBufInt32BE(buf + info->modeOffset);
-        if (crypt_format != info->modeValue)
-            return false;
-
-        return true;
-    } else {
-        return false;
-    }
-}
-
-
-static int
-virStorageFileGetEncryptionPayloadOffset(const struct FileEncryptionInfo *info,
-                                         char *buf)
-{
-    int payload_offset = -1;
-
-    if (info->payloadOffset != -1) {
-        if (info->endian == LV_LITTLE_ENDIAN)
-            payload_offset = virReadBufInt32LE(buf + info->payloadOffset);
-        else
-            payload_offset = virReadBufInt32BE(buf + info->payloadOffset);
-    }
-
-    return payload_offset;
-}
-
-
-/* Given a header in BUF with length LEN, as parsed from the storage file
- * assuming it has the given FORMAT, populate information into META
- * with information about the file and its backing store. Return format
- * of the backing store as BACKING_FORMAT. PATH and FORMAT have to be
- * pre-populated in META.
- *
- * Note that this function may be called repeatedly on @meta, so it must
- * clean up any existing allocated memory which would be overwritten.
- */
-static int
-virStorageFileGetMetadataInternal(virStorageSourcePtr meta,
-                                  char *buf,
-                                  size_t len)
-{
-    int format;
-    size_t i;
-
-    VIR_DEBUG("path=%s, buf=%p, len=%zu, meta->format=%d",
-              meta->path, buf, len, meta->format);
-
-    if (meta->format == VIR_STORAGE_FILE_AUTO)
-        meta->format = virStorageFileProbeFormatFromBuf(meta->path, buf, len);
-
-    if (meta->format <= VIR_STORAGE_FILE_NONE ||
-        meta->format >= VIR_STORAGE_FILE_LAST) {
-        virReportSystemError(EINVAL, _("unknown storage file meta->format %d"),
-                             meta->format);
-        return -1;
-    }
-
-    if (fileTypeInfo[meta->format].cryptInfo != NULL) {
-        for (i = 0; fileTypeInfo[meta->format].cryptInfo[i].format != 0; i++) {
-            if (virStorageFileHasEncryptionFormat(&fileTypeInfo[meta->format].cryptInfo[i],
-                                                  buf, len)) {
-                int expt_fmt = fileTypeInfo[meta->format].cryptInfo[i].format;
-                if (!meta->encryption) {
-                    meta->encryption = g_new0(virStorageEncryption, 1);
-                    meta->encryption->format = expt_fmt;
-                } else {
-                    if (meta->encryption->format != expt_fmt) {
-                        virReportError(VIR_ERR_XML_ERROR,
-                                       _("encryption format %d doesn't match "
-                                         "expected format %d"),
-                                       meta->encryption->format, expt_fmt);
-                        return -1;
-                    }
-                }
-                meta->encryption->payload_offset =
-                    virStorageFileGetEncryptionPayloadOffset(&fileTypeInfo[meta->format].cryptInfo[i], buf);
-            }
-        }
-    }
-
-    /* XXX we should consider moving virStorageBackendUpdateVolInfo
-     * code into this method, for non-magic files
-     */
-    if (!fileTypeInfo[meta->format].magic)
-        return 0;
-
-    /* Optionally extract capacity from file */
-    if (fileTypeInfo[meta->format].sizeOffset != -1) {
-        if ((fileTypeInfo[meta->format].sizeOffset + 8) > len)
-            return 0;
-
-        if (fileTypeInfo[meta->format].endian == LV_LITTLE_ENDIAN)
-            meta->capacity = virReadBufInt64LE(buf +
-                                               fileTypeInfo[meta->format].sizeOffset);
-        else
-            meta->capacity = virReadBufInt64BE(buf +
-                                               fileTypeInfo[meta->format].sizeOffset);
-        /* Avoid unlikely, but theoretically possible overflow */
-        if (meta->capacity > (ULLONG_MAX /
-                              fileTypeInfo[meta->format].sizeMultiplier))
-            return 0;
-        meta->capacity *= fileTypeInfo[meta->format].sizeMultiplier;
-    }
-
-    VIR_FREE(meta->backingStoreRaw);
-    if (fileTypeInfo[meta->format].getBackingStore != NULL) {
-        int store = fileTypeInfo[meta->format].getBackingStore(&meta->backingStoreRaw,
-                                                               &format,
-                                                               buf, len);
-        meta->backingStoreRawFormat = format;
-
-        if (store == BACKING_STORE_INVALID)
-            return 0;
-
-        if (store == BACKING_STORE_ERROR)
-            return -1;
-    }
-
-    virBitmapFree(meta->features);
-    meta->features = NULL;
-    if (fileTypeInfo[meta->format].getFeatures != NULL &&
-        fileTypeInfo[meta->format].getFeatures(&meta->features, meta->format, buf, len) < 0)
-        return -1;
-
-    VIR_FREE(meta->compat);
-    if (meta->format == VIR_STORAGE_FILE_QCOW2 && meta->features)
-        meta->compat = g_strdup("1.1");
-
-    return 0;
-}
-
-
-/**
- * virStorageFileProbeFormat:
- *
- * Probe for the format of 'path', returning the detected
- * disk format.
- *
- * Callers are advised never to trust the returned 'format'
- * unless it is listed as VIR_STORAGE_FILE_RAW, since a
- * malicious guest can turn a raw file into any other non-raw
- * format at will.
- *
- * Best option: Don't use this function
- */
-int
-virStorageFileProbeFormat(const char *path, uid_t uid, gid_t gid)
-{
-    struct stat sb;
-    ssize_t len = VIR_STORAGE_MAX_HEADER;
-    VIR_AUTOCLOSE fd = -1;
-    g_autofree char *header = NULL;
-
-    if ((fd = virFileOpenAs(path, O_RDONLY, 0, uid, gid, 0)) < 0) {
-        virReportSystemError(-fd, _("Failed to open file '%s'"), path);
-        return -1;
-    }
-
-    if (fstat(fd, &sb) < 0) {
-        virReportSystemError(errno, _("cannot stat file '%s'"), path);
-        return -1;
-    }
-
-    /* No header to probe for directories */
-    if (S_ISDIR(sb.st_mode))
-        return VIR_STORAGE_FILE_DIR;
-
-    if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
-        virReportSystemError(errno, _("cannot set to start of '%s'"), path);
-        return -1;
-    }
-
-    if ((len = virFileReadHeaderFD(fd, len, &header)) < 0) {
-        virReportSystemError(errno, _("cannot read header '%s'"), path);
-        return -1;
-    }
-
-    return virStorageFileProbeFormatFromBuf(path, header, len);
-}
-
-
-static virStorageSourcePtr
-virStorageFileMetadataNew(const char *path,
-                          int format)
-{
-    g_autoptr(virStorageSource) def = virStorageSourceNew();
-
-    def->format = format;
-    def->type = VIR_STORAGE_TYPE_FILE;
-
-    def->path = g_strdup(path);
-
-    return g_steal_pointer(&def);
-}
-
-
-/**
- * virStorageFileGetMetadataFromBuf:
- * @path: name of file, for error messages
- * @buf: header bytes from @path
- * @len: length of @buf
- * @format: format of the storage file
- *
- * Extract metadata about the storage volume with the specified image format.
- * If image format is VIR_STORAGE_FILE_AUTO, it will probe to automatically
- * identify the format.  Does not recurse.
- *
- * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a format on a file
- * that might be raw if that file will then be passed to a guest, since a
- * malicious guest can turn a raw file into any other non-raw format at will.
- *
- * If the 'backingStoreRawFormat' field of the returned structure is
- * VIR_STORAGE_FILE_AUTO it indicates the image didn't specify an explicit
- * format for its backing store. Callers are advised against probing for the
- * backing store format in this case.
- *
- * Caller MUST free the result after use via virObjectUnref.
- */
-virStorageSourcePtr
-virStorageFileGetMetadataFromBuf(const char *path,
-                                 char *buf,
-                                 size_t len,
-                                 int format)
-{
-    virStorageSourcePtr ret = NULL;
-
-    if (!(ret = virStorageFileMetadataNew(path, format)))
-        return NULL;
-
-    if (virStorageFileGetMetadataInternal(ret, buf, len) < 0) {
-        virObjectUnref(ret);
-        return NULL;
-    }
-
-    return ret;
-}
-
-
-/**
- * virStorageFileGetMetadataFromFD:
- *
- * Extract metadata about the storage volume with the specified
- * image format. If image format is VIR_STORAGE_FILE_AUTO, it
- * will probe to automatically identify the format.  Does not recurse.
- *
- * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a
- * format, since a malicious guest can turn a raw file into any
- * other non-raw format at will.
- *
- * Caller MUST free the result after use via virObjectUnref.
- */
-virStorageSourcePtr
-virStorageFileGetMetadataFromFD(const char *path,
-                                int fd,
-                                int format)
-
-{
-    ssize_t len = VIR_STORAGE_MAX_HEADER;
-    struct stat sb;
-    g_autofree char *buf = NULL;
-    g_autoptr(virStorageSource) meta = NULL;
-
-    if (fstat(fd, &sb) < 0) {
-        virReportSystemError(errno,
-                             _("cannot stat file '%s'"), path);
-        return NULL;
-    }
-
-    if (!(meta = virStorageFileMetadataNew(path, format)))
-        return NULL;
-
-    if (S_ISDIR(sb.st_mode)) {
-        /* No header to probe for directories, but also no backing file. Just
-         * update the metadata.*/
-        meta->type = VIR_STORAGE_TYPE_DIR;
-        meta->format = VIR_STORAGE_FILE_DIR;
-        return g_steal_pointer(&meta);
-    }
-
-    if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
-        virReportSystemError(errno, _("cannot seek to start of '%s'"), meta->path);
-        return NULL;
-    }
-
-    if ((len = virFileReadHeaderFD(fd, len, &buf)) < 0) {
-        virReportSystemError(errno, _("cannot read header '%s'"), meta->path);
-        return NULL;
-    }
-
-    if (virStorageFileGetMetadataInternal(meta, buf, len) < 0)
-        return NULL;
-
-    if (S_ISREG(sb.st_mode))
-        meta->type = VIR_STORAGE_TYPE_FILE;
-    else if (S_ISBLK(sb.st_mode))
-        meta->type = VIR_STORAGE_TYPE_BLOCK;
-
-    return g_steal_pointer(&meta);
-}
-
 
 #ifdef WITH_UDEV
 /* virStorageFileGetSCSIKey
@@ -1297,78 +240,6 @@ int virStorageFileGetNPIVKey(const char *path G_GNUC_UNUSED,
 }
 #endif
 
-/**
- * virStorageFileParseBackingStoreStr:
- * @str: backing store specifier string to parse
- * @target: returns target device portion of the string
- * @chainIndex: returns the backing store portion of the string
- *
- * Parses the backing store specifier string such as vda[1], or sda into
- * components and returns them via arguments. If the string did not specify an
- * index, 0 is assumed.
- *
- * Returns 0 on success -1 on error
- */
-int
-virStorageFileParseBackingStoreStr(const char *str,
-                                   char **target,
-                                   unsigned int *chainIndex)
-{
-    size_t nstrings;
-    unsigned int idx = 0;
-    char *suffix;
-    g_auto(GStrv) strings = NULL;
-
-    *chainIndex = 0;
-
-    if (!(strings = virStringSplitCount(str, "[", 2, &nstrings)))
-        return -1;
-
-    if (nstrings == 2) {
-        if (virStrToLong_uip(strings[1], &suffix, 10, &idx) < 0 ||
-            STRNEQ(suffix, "]"))
-            return -1;
-    }
-
-    if (target)
-        *target = g_strdup(strings[0]);
-
-    *chainIndex = idx;
-    return 0;
-}
-
-
-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;
-}
-
 
 /**
  * virStorageSourceIsBacking:
@@ -1397,107 +268,6 @@ virStorageSourceHasBacking(const virStorageSource *src)
 }
 
 
-/* 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.
- */
-virStorageSourcePtr
-virStorageFileChainLookup(virStorageSourcePtr chain,
-                          virStorageSourcePtr startFrom,
-                          const char *name,
-                          unsigned int idx,
-                          virStorageSourcePtr *parent)
-{
-    virStorageSourcePtr prev;
-    const char *start = chain->path;
-    bool nameIsFile = virFileIsFile(name);
-
-    if (!parent)
-        parent = &prev;
-    *parent = NULL;
-
-    if (startFrom) {
-        while (virStorageSourceIsBacking(chain) &&
-               chain != startFrom->backingStore)
-            chain = chain->backingStore;
-
-        *parent = startFrom;
-    }
-
-    while (virStorageSourceIsBacking(chain)) {
-        if (!name && !idx) {
-            if (!virStorageSourceHasBacking(chain))
-                break;
-        } else if (idx) {
-            VIR_DEBUG("%u: %s", chain->id, chain->path);
-            if (idx == chain->id)
-                break;
-        } else {
-            if (STREQ_NULLABLE(name, chain->relPath) ||
-                STREQ_NULLABLE(name, chain->path))
-                break;
-
-            if (nameIsFile && virStorageSourceIsLocalStorage(chain)) {
-                g_autofree char *parentDir = NULL;
-                int result;
-
-                if (*parent && virStorageSourceIsLocalStorage(*parent))
-                    parentDir = g_path_get_dirname((*parent)->path);
-                else
-                    parentDir = g_strdup(".");
-
-                result = virFileRelLinkPointsTo(parentDir, name,
-                                                chain->path);
-
-                if (result < 0)
-                    goto error;
-
-                if (result > 0)
-                    break;
-            }
-        }
-        *parent = chain;
-        chain = chain->backingStore;
-    }
-
-    if (!virStorageSourceIsBacking(chain))
-        goto error;
-
-    return chain;
-
- error:
-    if (idx) {
-        virReportError(VIR_ERR_INVALID_ARG,
-                       _("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));
-        else
-            virReportError(VIR_ERR_INVALID_ARG,
-                           _("could not find image '%s' in chain for '%s'"),
-                           name, NULLSTR(start));
-    } else {
-        virReportError(VIR_ERR_INVALID_ARG,
-                       _("could not find base image in chain for '%s'"),
-                       NULLSTR(start));
-    }
-    *parent = NULL;
-    return NULL;
-}
-
-
 void
 virStorageNetHostDefClear(virStorageNetHostDefPtr def)
 {
@@ -2545,1780 +1315,6 @@ virStorageSourceNew(void)
 }
 
 
-static virStorageSourcePtr
-virStorageSourceNewFromBackingRelative(virStorageSourcePtr parent,
-                                       const char *rel)
-{
-    g_autofree char *dirname = NULL;
-    g_autoptr(virStorageSource) def = virStorageSourceNew();
-
-    /* store relative name */
-    def->relPath = g_strdup(rel);
-
-    dirname = g_path_get_dirname(parent->path);
-
-    if (STRNEQ(dirname, "/")) {
-        def->path = g_strdup_printf("%s/%s", dirname, rel);
-    } else {
-        def->path = g_strdup_printf("/%s", rel);
-    }
-
-    if (virStorageSourceGetActualType(parent) == VIR_STORAGE_TYPE_NETWORK) {
-        def->type = VIR_STORAGE_TYPE_NETWORK;
-
-        /* copy the host network part */
-        def->protocol = parent->protocol;
-        if (parent->nhosts) {
-            if (!(def->hosts = virStorageNetHostDefCopy(parent->nhosts,
-                                                        parent->hosts)))
-                return NULL;
-
-            def->nhosts = parent->nhosts;
-        }
-
-        def->volume = g_strdup(parent->volume);
-    } else {
-        /* set the type to _FILE, the caller shall update it to the actual type */
-        def->type = VIR_STORAGE_TYPE_FILE;
-    }
-
-    return g_steal_pointer(&def);
-}
-
-
-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:
-        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
-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},
-    {"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
- * @src: filled with virStorageSource object representing @path
- *
- * Returns 0 on success, 1 if we could parse all location data but @path
- * specified other data unrepresentable by libvirt (e.g. inline authentication).
- * In both cases @src is filled. On error -1 is returned @src is NULL and an
- * error is reported.
- */
-int
-virStorageSourceNewFromBackingAbsolute(const char *path,
-                                       virStorageSourcePtr *src)
-{
-    const char *json;
-    const char *dirpath;
-    int rc = 0;
-    g_autoptr(virStorageSource) def = virStorageSourceNew();
-
-    *src = NULL;
-
-    if (virFileIsFile(path)) {
-        def->type = VIR_STORAGE_TYPE_FILE;
-
-        def->path = g_strdup(path);
-    } else {
-        if ((dirpath = STRSKIP(path, "fat:"))) {
-            def->type = VIR_STORAGE_TYPE_DIR;
-            def->format = VIR_STORAGE_FILE_FAT;
-            def->path = g_strdup(dirpath);
-            *src = g_steal_pointer(&def);
-            return 0;
-        }
-
-        def->type = VIR_STORAGE_TYPE_NETWORK;
-
-        VIR_DEBUG("parsing backing store string: '%s'", path);
-
-        /* handle URI formatted backing stores */
-        if ((json = STRSKIP(path, "json:")))
-            rc = virStorageSourceParseBackingJSON(def, json);
-        else if (strstr(path, "://"))
-            rc = virStorageSourceParseBackingURI(def, path);
-        else
-            rc = virStorageSourceParseBackingColon(def, path);
-
-        if (rc < 0)
-            return -1;
-
-        virStorageSourceNetworkAssignDefaultPorts(def);
-
-        /* Some of the legacy parsers parse authentication data since they are
-         * also used in other places. For backing store detection the
-         * authentication data would be invalid anyways, so we clear it */
-        if (def->auth) {
-            virStorageAuthDefFree(def->auth);
-            def->auth = NULL;
-        }
-    }
-
-    *src = g_steal_pointer(&def);
-    return rc;
-}
-
-
-/**
- * virStorageSourceNewFromChild:
- * @parent: storage source parent
- * @child: returned child/backing store definition
- * @parentRaw: raw child string (backingStoreRaw)
- *
- * Creates a storage source which describes the backing image of @parent and
- * fills it into @backing depending on the passed parentRaw (backingStoreRaw)
- * and other data. Note that for local storage this function accesses the file
- * to update the actual type of the child store.
- *
- * Returns 0 on success, 1 if we could parse all location data but the child
- * store specification contained other data unrepresentable by libvirt (e.g.
- * inline authentication).
- * In both cases @src is filled. On error -1 is returned @src is NULL and an
- * error is reported.
- */
-static int
-virStorageSourceNewFromChild(virStorageSourcePtr parent,
-                             const char *parentRaw,
-                             virStorageSourcePtr *child)
-{
-    struct stat st;
-    g_autoptr(virStorageSource) def = NULL;
-    int rc = 0;
-
-    *child = NULL;
-
-    if (virFileIsRelative(parentRaw)) {
-        if (!(def = virStorageSourceNewFromBackingRelative(parent, parentRaw)))
-            return -1;
-    } else {
-        if ((rc = virStorageSourceNewFromBackingAbsolute(parentRaw, &def)) < 0)
-            return -1;
-    }
-
-    /* possibly update local type */
-    if (def->type == VIR_STORAGE_TYPE_FILE) {
-        if (stat(def->path, &st) == 0) {
-            if (S_ISDIR(st.st_mode)) {
-                def->type = VIR_STORAGE_TYPE_DIR;
-                def->format = VIR_STORAGE_FILE_DIR;
-            } else if (S_ISBLK(st.st_mode)) {
-                def->type = VIR_STORAGE_TYPE_BLOCK;
-            }
-        }
-    }
-
-    /* copy parent's labelling and other top level stuff */
-    if (virStorageSourceInitChainElement(def, parent, true) < 0)
-        return -1;
-
-    def->detected = true;
-
-    *child = g_steal_pointer(&def);
-    return rc;
-}
-
-
-int
-virStorageSourceNewFromBacking(virStorageSourcePtr parent,
-                               virStorageSourcePtr *backing)
-{
-    int rc;
-
-    if ((rc = virStorageSourceNewFromChild(parent,
-                                           parent->backingStoreRaw,
-                                           backing)) < 0)
-        return rc;
-
-    (*backing)->format = parent->backingStoreRawFormat;
-    (*backing)->readonly = true;
-    return rc;
-}
-
-
-/**
- * @src: disk source definition structure
- * @fd: file descriptor
- * @sb: stat buffer
- *
- * Updates src->physical depending on the actual type of storage being used.
- * To be called for domain storage source reporting as the volume code does
- * not set/use the 'type' field for the voldef->source.target
- *
- * Returns 0 on success, -1 on error. No libvirt errors are reported.
- */
-int
-virStorageSourceUpdatePhysicalSize(virStorageSourcePtr src,
-                                   int fd,
-                                   struct stat const *sb)
-{
-    off_t end;
-    virStorageType actual_type = virStorageSourceGetActualType(src);
-
-    switch (actual_type) {
-    case VIR_STORAGE_TYPE_FILE:
-    case VIR_STORAGE_TYPE_NETWORK:
-        src->physical = sb->st_size;
-        break;
-
-    case VIR_STORAGE_TYPE_BLOCK:
-        if ((end = lseek(fd, 0, SEEK_END)) == (off_t) -1)
-            return -1;
-
-        src->physical = end;
-        break;
-
-    case VIR_STORAGE_TYPE_DIR:
-        src->physical = 0;
-        break;
-
-    /* We shouldn't get VOLUME, but the switch requires all cases */
-    case VIR_STORAGE_TYPE_VOLUME:
-    case VIR_STORAGE_TYPE_NVME:
-    case VIR_STORAGE_TYPE_NONE:
-    case VIR_STORAGE_TYPE_LAST:
-        return -1;
-    }
-
-    return 0;
-}
-
-
-/**
- * @src: disk source definition structure
- * @fd: file descriptor
- * @sb: stat buffer
- *
- * Update the capacity, allocation, physical values for the storage @src
- * Shared between the domain storage source for an inactive domain and the
- * voldef source target as the result is not affected by the 'type' field.
- *
- * Returns 0 on success, -1 on error.
- */
-int
-virStorageSourceUpdateBackingSizes(virStorageSourcePtr src,
-                                   int fd,
-                                   struct stat const *sb)
-{
-    /* Get info for normal formats */
-    if (S_ISREG(sb->st_mode) || fd == -1) {
-#ifndef WIN32
-        src->allocation = (unsigned long long)sb->st_blocks *
-            (unsigned long long)DEV_BSIZE;
-#else
-        src->allocation = sb->st_size;
-#endif
-        /* Regular files may be sparse, so logical size (capacity) is not same
-         * as actual allocation above
-         */
-        src->capacity = sb->st_size;
-
-        /* Allocation tracks when the file is sparse, physical is the
-         * last offset of the file. */
-        src->physical = sb->st_size;
-    } else if (S_ISDIR(sb->st_mode)) {
-        src->allocation = 0;
-        src->capacity = 0;
-        src->physical = 0;
-    } else if (fd >= 0) {
-        off_t end;
-
-        /* XXX this is POSIX compliant, but doesn't work for CHAR files,
-         * only BLOCK. There is a Linux specific ioctl() for getting
-         * size of both CHAR / BLOCK devices we should check for in
-         * configure
-         *
-         * NB. Because we configure with AC_SYS_LARGEFILE, off_t
-         * should be 64 bits on all platforms.  For block devices, we
-         * have to seek (safe even if someone else is writing) to
-         * determine physical size, and assume that allocation is the
-         * same as physical (but can refine that assumption later if
-         * qemu is still running).
-         */
-        if ((end = lseek(fd, 0, SEEK_END)) == (off_t)-1) {
-            virReportSystemError(errno,
-                                 _("failed to seek to end of %s"), src->path);
-            return -1;
-        }
-        src->physical = end;
-        src->allocation = end;
-        src->capacity = end;
-    }
-
-    return 0;
-}
-
-
-/**
- * @src: disk source definition structure
- * @buf: buffer to the storage file header
- * @len: length of the storage file header
- *
- * Update the storage @src capacity.
- *
- * Returns 0 on success, -1 on error.
- */
-int
-virStorageSourceUpdateCapacity(virStorageSourcePtr src,
-                               char *buf,
-                               ssize_t len)
-{
-    int format = src->format;
-    g_autoptr(virStorageSource) meta = NULL;
-
-    /* Raw files: capacity is physical size.  For all other files: if
-     * the metadata has a capacity, use that, otherwise fall back to
-     * physical size.  */
-    if (format == VIR_STORAGE_FILE_NONE) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("no disk format for %s was specified"),
-                       src->path);
-        return -1;
-    }
-
-    if (format == VIR_STORAGE_FILE_RAW && !src->encryption) {
-        src->capacity = src->physical;
-    } else if ((meta = virStorageFileGetMetadataFromBuf(src->path, buf,
-                                                        len, format))) {
-        src->capacity = meta->capacity ? meta->capacity : src->physical;
-        if (src->encryption && meta->encryption)
-            src->encryption->payload_offset = meta->encryption->payload_offset;
-    } else {
-        return -1;
-    }
-
-    if (src->encryption && src->encryption->payload_offset != -1)
-        src->capacity -= src->encryption->payload_offset * 512;
-
-    return 0;
-}
-
-
-static char *
-virStorageFileCanonicalizeFormatPath(char **components,
-                                     size_t ncomponents,
-                                     bool beginSlash,
-                                     bool beginDoubleSlash)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-    size_t i;
-    char *ret = NULL;
-
-    if (beginSlash)
-        virBufferAddLit(&buf, "/");
-
-    if (beginDoubleSlash)
-        virBufferAddLit(&buf, "/");
-
-    for (i = 0; i < ncomponents; i++) {
-        if (i != 0)
-            virBufferAddLit(&buf, "/");
-
-        virBufferAdd(&buf, components[i], -1);
-    }
-
-    /* if the output string is empty just return an empty string */
-    if (!(ret = virBufferContentAndReset(&buf)))
-        ret = g_strdup("");
-
-    return ret;
-}
-
-
-static int
-virStorageFileCanonicalizeInjectSymlink(const char *path,
-                                        size_t at,
-                                        char ***components,
-                                        size_t *ncomponents)
-{
-    char **tmp = NULL;
-    char **next;
-    size_t ntmp = 0;
-    int ret = -1;
-
-    if (!(tmp = virStringSplitCount(path, "/", 0, &ntmp)))
-        goto cleanup;
-
-    /* prepend */
-    for (next = tmp; *next; next++) {
-        if (VIR_INSERT_ELEMENT(*components, at, *ncomponents, *next) < 0)
-            goto cleanup;
-
-        at++;
-    }
-
-    ret = 0;
-
- cleanup:
-    virStringListFreeCount(tmp, ntmp);
-    return ret;
-}
-
-
-char *
-virStorageFileCanonicalizePath(const char *path,
-                               virStorageFileSimplifyPathReadlinkCallback cb,
-                               void *cbdata)
-{
-    GHashTable *cycle = NULL;
-    bool beginSlash = false;
-    bool beginDoubleSlash = false;
-    char **components = NULL;
-    size_t ncomponents = 0;
-    size_t i = 0;
-    size_t j = 0;
-    int rc;
-    char *ret = NULL;
-    g_autofree char *linkpath = NULL;
-    g_autofree char *currentpath = NULL;
-
-    if (path[0] == '/') {
-        beginSlash = true;
-
-        if (path[1] == '/' && path[2] != '/')
-            beginDoubleSlash = true;
-    }
-
-    if (!(cycle = virHashNew(NULL)))
-        goto cleanup;
-
-    if (!(components = virStringSplitCount(path, "/", 0, &ncomponents)))
-        goto cleanup;
-
-    j = 0;
-    while (j < ncomponents) {
-        /* skip slashes */
-        if (STREQ(components[j], "")) {
-            VIR_FREE(components[j]);
-            VIR_DELETE_ELEMENT(components, j, ncomponents);
-            continue;
-        }
-        j++;
-    }
-
-    while (i < ncomponents) {
-        /* skip '.'s unless it's the last one remaining */
-        if (STREQ(components[i], ".") &&
-            (beginSlash || ncomponents  > 1)) {
-            VIR_FREE(components[i]);
-            VIR_DELETE_ELEMENT(components, i, ncomponents);
-            continue;
-        }
-
-        /* resolve changes to parent directory */
-        if (STREQ(components[i], "..")) {
-            if (!beginSlash &&
-                (i == 0 || STREQ(components[i - 1], ".."))) {
-                i++;
-                continue;
-            }
-
-            VIR_FREE(components[i]);
-            VIR_DELETE_ELEMENT(components, i, ncomponents);
-
-            if (i != 0) {
-                VIR_FREE(components[i - 1]);
-                VIR_DELETE_ELEMENT(components, i - 1, ncomponents);
-                i--;
-            }
-
-            continue;
-        }
-
-        /* check if the actual path isn't resulting into a symlink */
-        if (!(currentpath = virStorageFileCanonicalizeFormatPath(components,
-                                                                 i + 1,
-                                                                 beginSlash,
-                                                                 beginDoubleSlash)))
-            goto cleanup;
-
-        if ((rc = cb(currentpath, &linkpath, cbdata)) < 0)
-            goto cleanup;
-
-        if (rc == 0) {
-            if (virHashLookup(cycle, currentpath)) {
-                virReportSystemError(ELOOP,
-                                     _("Failed to canonicalize path '%s'"), path);
-                goto cleanup;
-            }
-
-            if (virHashAddEntry(cycle, currentpath, (void *) 1) < 0)
-                goto cleanup;
-
-            if (linkpath[0] == '/') {
-                /* kill everything from the beginning including the actual component */
-                i++;
-                while (i--) {
-                    VIR_FREE(components[0]);
-                    VIR_DELETE_ELEMENT(components, 0, ncomponents);
-                }
-                beginSlash = true;
-
-                if (linkpath[1] == '/' && linkpath[2] != '/')
-                    beginDoubleSlash = true;
-                else
-                    beginDoubleSlash = false;
-
-                i = 0;
-            } else {
-                VIR_FREE(components[i]);
-                VIR_DELETE_ELEMENT(components, i, ncomponents);
-            }
-
-            if (virStorageFileCanonicalizeInjectSymlink(linkpath,
-                                                        i,
-                                                        &components,
-                                                        &ncomponents) < 0)
-                goto cleanup;
-
-            j = 0;
-            while (j < ncomponents) {
-                /* skip slashes */
-                if (STREQ(components[j], "")) {
-                    VIR_FREE(components[j]);
-                    VIR_DELETE_ELEMENT(components, j, ncomponents);
-                    continue;
-                }
-                j++;
-            }
-
-            VIR_FREE(linkpath);
-            VIR_FREE(currentpath);
-
-            continue;
-        }
-
-        VIR_FREE(currentpath);
-
-        i++;
-    }
-
-    ret = virStorageFileCanonicalizeFormatPath(components, ncomponents,
-                                               beginSlash, beginDoubleSlash);
-
- cleanup:
-    virHashFree(cycle);
-    virStringListFreeCount(components, ncomponents);
-
-    return ret;
-}
-
-
-/**
- * virStorageFileRemoveLastPathComponent:
- *
- * @path: Path string to remove the last component from
- *
- * Removes the last path component of a path. This function is designed to be
- * called on file paths only (no trailing slashes in @path). Caller is
- * responsible to free the returned string.
- */
-static char *
-virStorageFileRemoveLastPathComponent(const char *path)
-{
-    char *ret;
-
-    ret = g_strdup(NULLSTR_EMPTY(path));
-
-    virFileRemoveLastComponent(ret);
-
-    return ret;
-}
-
-
-/*
- * virStorageFileGetRelativeBackingPath:
- *
- * Resolve relative path to be written to the overlay of @top image when
- * collapsing the backing chain between @top and @base.
- *
- * Returns 0 on success; 1 if backing chain isn't relative and -1 on error.
- */
-int
-virStorageFileGetRelativeBackingPath(virStorageSourcePtr top,
-                                     virStorageSourcePtr base,
-                                     char **relpath)
-{
-    virStorageSourcePtr next;
-    g_autofree char *tmp = NULL;
-    g_autofree char *path = NULL;
-
-    *relpath = NULL;
-
-    for (next = top; virStorageSourceIsBacking(next); next = next->backingStore) {
-        if (!next->relPath)
-            return 1;
-
-        if (!(tmp = virStorageFileRemoveLastPathComponent(path)))
-            return -1;
-
-        VIR_FREE(path);
-
-        path = g_strdup_printf("%s%s", tmp, next->relPath);
-
-        VIR_FREE(tmp);
-
-        if (next == base)
-            break;
-    }
-
-    if (next != base) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("failed to resolve relative backing name: "
-                         "base image is not in backing chain"));
-        return -1;
-    }
-
-    *relpath = g_steal_pointer(&path);
-    return 0;
-}
-
-
 /**
  * virStorageSourceIsRelative:
  * @src: storage source to check
@@ -4351,31 +1347,6 @@ virStorageSourceIsRelative(virStorageSourcePtr src)
 }
 
 
-/**
- * virStorageSourceFindByNodeName:
- * @top: backing chain top
- * @nodeName: node name to find in backing chain
- *
- * Looks up the given storage source in the backing chain and returns the
- * pointer to it.
- * On failure NULL is returned and no error is reported.
- */
-virStorageSourcePtr
-virStorageSourceFindByNodeName(virStorageSourcePtr top,
-                               const char *nodeName)
-{
-    virStorageSourcePtr tmp;
-
-    for (tmp = top; virStorageSourceIsBacking(tmp); tmp = tmp->backingStore) {
-        if ((tmp->nodeformat && STREQ(tmp->nodeformat, nodeName)) ||
-            (tmp->nodestorage && STREQ(tmp->nodestorage, nodeName)))
-            return tmp;
-    }
-
-    return NULL;
-}
-
-
 static unsigned int
 virStorageSourceNetworkDefaultPort(virStorageNetProtocol protocol)
 {
@@ -4439,25 +1410,6 @@ virStorageSourceNetworkAssignDefaultPorts(virStorageSourcePtr src)
 }
 
 
-int
-virStorageSourcePrivateDataParseRelPath(xmlXPathContextPtr ctxt,
-                                        virStorageSourcePtr src)
-{
-    src->relPath = virXPathString("string(./relPath)", ctxt);
-    return 0;
-}
-
-
-int
-virStorageSourcePrivateDataFormatRelPath(virStorageSourcePtr src,
-                                         virBufferPtr buf)
-{
-    if (src->relPath)
-        virBufferEscapeString(buf, "<relPath>%s</relPath>\n", src->relPath);
-
-    return 0;
-}
-
 void
 virStorageSourceInitiatorParseXML(xmlXPathContextPtr ctxt,
                                   virStorageSourceInitiatorDefPtr initiator)
@@ -4492,736 +1444,3 @@ virStorageSourceInitiatorClear(virStorageSourceInitiatorDefPtr initiator)
 {
     VIR_FREE(initiator->iqn);
 }
-
-static bool
-virStorageFileIsInitialized(const virStorageSource *src)
-{
-    return src && src->drv;
-}
-
-
-/**
- * virStorageFileGetBackendForSupportCheck:
- * @src: storage source to check support for
- * @backend: pointer to the storage backend for @src if it's supported
- *
- * Returns 0 if @src is not supported by any storage backend currently linked
- * 1 if it is supported and -1 on error with an error reported.
- */
-static int
-virStorageFileGetBackendForSupportCheck(const virStorageSource *src,
-                                        virStorageFileBackendPtr *backend)
-{
-    int actualType;
-
-
-    if (!src) {
-        *backend = NULL;
-        return 0;
-    }
-
-    if (src->drv) {
-        virStorageDriverDataPtr drv = src->drv;
-        *backend = drv->backend;
-        return 1;
-    }
-
-    actualType = virStorageSourceGetActualType(src);
-
-    if (virStorageFileBackendForType(actualType, src->protocol, false, backend) < 0)
-        return -1;
-
-    if (!*backend)
-        return 0;
-
-    return 1;
-}
-
-
-int
-virStorageFileSupportsBackingChainTraversal(const virStorageSource *src)
-{
-    virStorageFileBackendPtr backend;
-    int rv;
-
-    if ((rv = virStorageFileGetBackendForSupportCheck(src, &backend)) < 1)
-        return rv;
-
-    return backend->storageFileGetUniqueIdentifier &&
-           backend->storageFileRead &&
-           backend->storageFileAccess ? 1 : 0;
-}
-
-
-/**
- * virStorageFileSupportsSecurityDriver:
- *
- * @src: a storage file structure
- *
- * Check if a storage file supports operations needed by the security
- * driver to perform labelling
- */
-int
-virStorageFileSupportsSecurityDriver(const virStorageSource *src)
-{
-    virStorageFileBackendPtr backend;
-    int rv;
-
-    if ((rv = virStorageFileGetBackendForSupportCheck(src, &backend)) < 1)
-        return rv;
-
-    return backend->storageFileChown ? 1 : 0;
-}
-
-
-/**
- * virStorageFileSupportsAccess:
- *
- * @src: a storage file structure
- *
- * Check if a storage file supports checking if the storage source is accessible
- * for the given vm.
- */
-int
-virStorageFileSupportsAccess(const virStorageSource *src)
-{
-    virStorageFileBackendPtr backend;
-    int rv;
-
-    if ((rv = virStorageFileGetBackendForSupportCheck(src, &backend)) < 1)
-        return rv;
-
-    return backend->storageFileAccess ? 1 : 0;
-}
-
-
-/**
- * virStorageFileSupportsCreate:
- * @src: a storage file structure
- *
- * Check if the storage driver supports creating storage described by @src
- * via virStorageFileCreate.
- */
-int
-virStorageFileSupportsCreate(const virStorageSource *src)
-{
-    virStorageFileBackendPtr backend;
-    int rv;
-
-    if ((rv = virStorageFileGetBackendForSupportCheck(src, &backend)) < 1)
-        return rv;
-
-    return backend->storageFileCreate ? 1 : 0;
-}
-
-
-void
-virStorageFileDeinit(virStorageSourcePtr src)
-{
-    virStorageDriverDataPtr drv = NULL;
-
-    if (!virStorageFileIsInitialized(src))
-        return;
-
-    drv = src->drv;
-
-    if (drv->backend &&
-        drv->backend->backendDeinit)
-        drv->backend->backendDeinit(src);
-
-    VIR_FREE(src->drv);
-}
-
-
-/**
- * virStorageFileInitAs:
- *
- * @src: storage source definition
- * @uid: uid used to access the file, or -1 for current uid
- * @gid: gid used to access the file, or -1 for current gid
- *
- * Initialize a storage source to be used with storage driver. Use the provided
- * uid and gid if possible for the operations.
- *
- * Returns 0 if the storage file was successfully initialized, -1 if the
- * initialization failed. Libvirt error is reported.
- */
-int
-virStorageFileInitAs(virStorageSourcePtr src,
-                     uid_t uid, gid_t gid)
-{
-    int actualType = virStorageSourceGetActualType(src);
-    virStorageDriverDataPtr drv = g_new0(virStorageDriverData, 1);
-
-    src->drv = drv;
-
-    if (uid == (uid_t) -1)
-        drv->uid = geteuid();
-    else
-        drv->uid = uid;
-
-    if (gid == (gid_t) -1)
-        drv->gid = getegid();
-    else
-        drv->gid = gid;
-
-    if (virStorageFileBackendForType(actualType,
-                                     src->protocol,
-                                     true,
-                                     &drv->backend) < 0)
-        goto error;
-
-    if (drv->backend->backendInit &&
-        drv->backend->backendInit(src) < 0)
-        goto error;
-
-    return 0;
-
- error:
-    VIR_FREE(src->drv);
-    return -1;
-}
-
-
-/**
- * virStorageFileInit:
- *
- * See virStorageFileInitAs. The file is initialized to be accessed by the
- * current user.
- */
-int
-virStorageFileInit(virStorageSourcePtr src)
-{
-    return virStorageFileInitAs(src, -1, -1);
-}
-
-
-/**
- * virStorageFileCreate: Creates an empty storage file via storage driver
- *
- * @src: file structure pointing to the file
- *
- * Returns 0 on success, -2 if the function isn't supported by the backend,
- * -1 on other failure. Errno is set in case of failure.
- */
-int
-virStorageFileCreate(virStorageSourcePtr src)
-{
-    virStorageDriverDataPtr drv = NULL;
-    int ret;
-
-    if (!virStorageFileIsInitialized(src)) {
-        errno = ENOSYS;
-        return -2;
-    }
-
-    drv = src->drv;
-
-    if (!drv->backend->storageFileCreate) {
-        errno = ENOSYS;
-        return -2;
-    }
-
-    ret = drv->backend->storageFileCreate(src);
-
-    VIR_DEBUG("created storage file %p: ret=%d, errno=%d",
-              src, ret, errno);
-
-    return ret;
-}
-
-
-/**
- * virStorageFileUnlink: Unlink storage file via storage driver
- *
- * @src: file structure pointing to the file
- *
- * Unlinks the file described by the @file structure.
- *
- * Returns 0 on success, -2 if the function isn't supported by the backend,
- * -1 on other failure. Errno is set in case of failure.
- */
-int
-virStorageFileUnlink(virStorageSourcePtr src)
-{
-    virStorageDriverDataPtr drv = NULL;
-    int ret;
-
-    if (!virStorageFileIsInitialized(src)) {
-        errno = ENOSYS;
-        return -2;
-    }
-
-    drv = src->drv;
-
-    if (!drv->backend->storageFileUnlink) {
-        errno = ENOSYS;
-        return -2;
-    }
-
-    ret = drv->backend->storageFileUnlink(src);
-
-    VIR_DEBUG("unlinked storage file %p: ret=%d, errno=%d",
-              src, ret, errno);
-
-    return ret;
-}
-
-
-/**
- * virStorageFileStat: returns stat struct of a file via storage driver
- *
- * @src: file structure pointing to the file
- * @stat: stat structure to return data
- *
- * Returns 0 on success, -2 if the function isn't supported by the backend,
- * -1 on other failure. Errno is set in case of failure.
-*/
-int
-virStorageFileStat(virStorageSourcePtr src,
-                   struct stat *st)
-{
-    virStorageDriverDataPtr drv = NULL;
-    int ret;
-
-    if (!virStorageFileIsInitialized(src)) {
-        errno = ENOSYS;
-        return -2;
-    }
-
-    drv = src->drv;
-
-    if (!drv->backend->storageFileStat) {
-        errno = ENOSYS;
-        return -2;
-    }
-
-    ret = drv->backend->storageFileStat(src, st);
-
-    VIR_DEBUG("stat of storage file %p: ret=%d, errno=%d",
-              src, ret, errno);
-
-    return ret;
-}
-
-
-/**
- * virStorageFileRead: read bytes from a file into a buffer
- *
- * @src: file structure pointing to the file
- * @offset: number of bytes to skip in the storage file
- * @len: maximum number of bytes read from the storage file
- * @buf: buffer to read the data into. (buffer shall be freed by caller)
- *
- * Returns the count of bytes read on success and -1 on failure, -2 if the
- * function isn't supported by the backend.
- * Libvirt error is reported on failure.
- */
-ssize_t
-virStorageFileRead(virStorageSourcePtr src,
-                   size_t offset,
-                   size_t len,
-                   char **buf)
-{
-    virStorageDriverDataPtr drv = NULL;
-    ssize_t ret;
-
-    if (!virStorageFileIsInitialized(src)) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("storage file backend not initialized"));
-        return -1;
-    }
-
-    drv = src->drv;
-
-    if (!drv->backend->storageFileRead)
-        return -2;
-
-    ret = drv->backend->storageFileRead(src, offset, len, buf);
-
-    VIR_DEBUG("read '%zd' bytes from storage '%p' starting at offset '%zu'",
-              ret, src, offset);
-
-    return ret;
-}
-
-
-/*
- * virStorageFileGetUniqueIdentifier: Get a unique string describing the volume
- *
- * @src: file structure pointing to the file
- *
- * Returns a string uniquely describing a single volume (canonical path).
- * The string shall not be freed and is valid until the storage file is
- * deinitialized. Returns NULL on error and sets a libvirt error code */
-const char *
-virStorageFileGetUniqueIdentifier(virStorageSourcePtr src)
-{
-    virStorageDriverDataPtr drv = NULL;
-
-    if (!virStorageFileIsInitialized(src)) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("storage file backend not initialized"));
-        return NULL;
-    }
-
-    drv = src->drv;
-
-    if (!drv->backend->storageFileGetUniqueIdentifier) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("unique storage file identifier not implemented for "
-                         "storage type %s (protocol: %s)'"),
-                       virStorageTypeToString(src->type),
-                       virStorageNetProtocolTypeToString(src->protocol));
-        return NULL;
-    }
-
-    return drv->backend->storageFileGetUniqueIdentifier(src);
-}
-
-
-/**
- * virStorageFileAccess: Check accessibility of a storage file
- *
- * @src: storage file to check access permissions
- * @mode: accessibility check options (see man 2 access)
- *
- * Returns 0 on success, -1 on error and sets errno. No libvirt
- * error is reported. Returns -2 if the operation isn't supported
- * by libvirt storage backend.
- */
-int
-virStorageFileAccess(virStorageSourcePtr src,
-                     int mode)
-{
-    virStorageDriverDataPtr drv = NULL;
-
-    if (!virStorageFileIsInitialized(src)) {
-        errno = ENOSYS;
-        return -2;
-    }
-
-    drv = src->drv;
-
-    if (!drv->backend->storageFileAccess) {
-        errno = ENOSYS;
-        return -2;
-    }
-
-    return drv->backend->storageFileAccess(src, mode);
-}
-
-
-/**
- * virStorageFileChown: Change owner of a storage file
- *
- * @src: storage file to change owner of
- * @uid: new owner id
- * @gid: new group id
- *
- * Returns 0 on success, -1 on error and sets errno. No libvirt
- * error is reported. Returns -2 if the operation isn't supported
- * by libvirt storage backend.
- */
-int
-virStorageFileChown(const virStorageSource *src,
-                    uid_t uid,
-                    gid_t gid)
-{
-    virStorageDriverDataPtr drv = NULL;
-
-    if (!virStorageFileIsInitialized(src)) {
-        errno = ENOSYS;
-        return -2;
-    }
-
-    drv = src->drv;
-
-    if (!drv->backend->storageFileChown) {
-        errno = ENOSYS;
-        return -2;
-    }
-
-    VIR_DEBUG("chown of storage file %p to %u:%u",
-              src, (unsigned int)uid, (unsigned int)gid);
-
-    return drv->backend->storageFileChown(src, uid, gid);
-}
-
-
-/**
- * virStorageFileReportBrokenChain:
- *
- * @errcode: errno when accessing @src
- * @src: inaccessible file in the backing chain of @parent
- * @parent: root virStorageSource being checked
- *
- * Reports the correct error message if @src is missing in the backing chain
- * for @parent.
- */
-void
-virStorageFileReportBrokenChain(int errcode,
-                                virStorageSourcePtr src,
-                                virStorageSourcePtr parent)
-{
-    if (src->drv) {
-        virStorageDriverDataPtr drv = src->drv;
-        unsigned int access_user = drv->uid;
-        unsigned int access_group = drv->gid;
-
-        if (src == parent) {
-            virReportSystemError(errcode,
-                                 _("Cannot access storage file '%s' "
-                                   "(as uid:%u, gid:%u)"),
-                                 src->path, access_user, access_group);
-        } else {
-            virReportSystemError(errcode,
-                                 _("Cannot access backing file '%s' "
-                                   "of storage file '%s' (as uid:%u, gid:%u)"),
-                                 src->path, parent->path, access_user, access_group);
-        }
-    } else {
-        if (src == parent) {
-            virReportSystemError(errcode,
-                                 _("Cannot access storage file '%s'"),
-                                 src->path);
-        } else {
-            virReportSystemError(errcode,
-                                 _("Cannot access backing file '%s' "
-                                   "of storage file '%s'"),
-                                 src->path, parent->path);
-        }
-    }
-}
-
-
-static int
-virStorageFileGetMetadataRecurseReadHeader(virStorageSourcePtr src,
-                                           virStorageSourcePtr parent,
-                                           uid_t uid,
-                                           gid_t gid,
-                                           char **buf,
-                                           size_t *headerLen,
-                                           GHashTable *cycle)
-{
-    int ret = -1;
-    const char *uniqueName;
-    ssize_t len;
-
-    if (virStorageFileInitAs(src, uid, gid) < 0)
-        return -1;
-
-    if (virStorageFileAccess(src, F_OK) < 0) {
-        virStorageFileReportBrokenChain(errno, src, parent);
-        goto cleanup;
-    }
-
-    if (!(uniqueName = virStorageFileGetUniqueIdentifier(src)))
-        goto cleanup;
-
-    if (virHashHasEntry(cycle, uniqueName)) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("backing store for %s (%s) is self-referential"),
-                       NULLSTR(src->path), uniqueName);
-        goto cleanup;
-    }
-
-    if (virHashAddEntry(cycle, uniqueName, NULL) < 0)
-        goto cleanup;
-
-    if ((len = virStorageFileRead(src, 0, VIR_STORAGE_MAX_HEADER, buf)) < 0)
-        goto cleanup;
-
-    *headerLen = len;
-    ret = 0;
-
- cleanup:
-    virStorageFileDeinit(src);
-    return ret;
-}
-
-
-/* Recursive workhorse for virStorageFileGetMetadata.  */
-static int
-virStorageFileGetMetadataRecurse(virStorageSourcePtr src,
-                                 virStorageSourcePtr parent,
-                                 uid_t uid, gid_t gid,
-                                 bool report_broken,
-                                 GHashTable *cycle,
-                                 unsigned int depth)
-{
-    virStorageFileFormat orig_format = src->format;
-    size_t headerLen;
-    int rv;
-    g_autofree char *buf = NULL;
-    g_autoptr(virStorageSource) backingStore = NULL;
-
-    VIR_DEBUG("path=%s format=%d uid=%u gid=%u",
-              NULLSTR(src->path), src->format,
-              (unsigned int)uid, (unsigned int)gid);
-
-    if (src->format == VIR_STORAGE_FILE_AUTO_SAFE)
-        src->format = VIR_STORAGE_FILE_AUTO;
-
-    /* exit if we can't load information about the current image */
-    rv = virStorageFileSupportsBackingChainTraversal(src);
-    if (rv <= 0) {
-        if (orig_format == VIR_STORAGE_FILE_AUTO)
-            return -2;
-
-        return rv;
-    }
-
-    if (virStorageFileGetMetadataRecurseReadHeader(src, parent, uid, gid,
-                                                   &buf, &headerLen, cycle) < 0)
-        return -1;
-
-    if (virStorageFileGetMetadataInternal(src, buf, headerLen) < 0)
-        return -1;
-
-    /* If we probed the format we MUST ensure that nothing else than the current
-     * image is considered for security labelling and/or recursion. */
-    if (orig_format == VIR_STORAGE_FILE_AUTO) {
-        if (src->backingStoreRaw) {
-            src->format = VIR_STORAGE_FILE_RAW;
-            VIR_FREE(src->backingStoreRaw);
-            return -2;
-        }
-    }
-
-    if (src->backingStoreRaw) {
-        if ((rv = virStorageSourceNewFromBacking(src, &backingStore)) < 0)
-            return -1;
-
-        /* the backing file would not be usable for VM usage */
-        if (rv == 1)
-            return 0;
-
-        if ((rv = virStorageFileGetMetadataRecurse(backingStore, parent,
-                                                   uid, gid,
-                                                   report_broken,
-                                                   cycle, depth + 1)) < 0) {
-            if (!report_broken)
-                return 0;
-
-            if (rv == -2) {
-                virReportError(VIR_ERR_OPERATION_INVALID,
-                               _("format of backing image '%s' of image '%s' was not specified in the image metadata "
-                                 "(See https://libvirt.org/kbase/backing_chains.html for troubleshooting)"),
-                               src->backingStoreRaw, NULLSTR(src->path));
-            }
-
-            return -1;
-        }
-
-        backingStore->id = depth;
-        src->backingStore = g_steal_pointer(&backingStore);
-    } else {
-        /* add terminator */
-        src->backingStore = virStorageSourceNew();
-    }
-
-    return 0;
-}
-
-
-/**
- * virStorageFileGetMetadata:
- *
- * Extract metadata about the storage volume with the specified
- * image format. If image format is VIR_STORAGE_FILE_AUTO, it
- * will probe to automatically identify the format.  Recurses through
- * the entire chain.
- *
- * Open files using UID and GID (or pass -1 for the current user/group).
- * Treat any backing files without explicit type as raw, unless ALLOW_PROBE.
- *
- * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a
- * format, since a malicious guest can turn a raw file into any
- * other non-raw format at will.
- *
- * If @report_broken is true, the whole function fails with a possibly sane
- * error instead of just returning a broken chain. Note that the inability for
- * libvirt to traverse a given source is not considered an error.
- *
- * Caller MUST free result after use via virObjectUnref.
- */
-int
-virStorageFileGetMetadata(virStorageSourcePtr src,
-                          uid_t uid, gid_t gid,
-                          bool report_broken)
-{
-    GHashTable *cycle = 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;
-        else
-            src->format = VIR_STORAGE_FILE_RAW;
-    }
-
-    ret = virStorageFileGetMetadataRecurse(src, src, uid, gid,
-                                           report_broken, cycle, 1);
-
-    virHashFree(cycle);
-    return ret;
-}
-
-
-/**
- * virStorageFileGetBackingStoreStr:
- * @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
-virStorageFileGetBackingStoreStr(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 (!virStorageFileSupportsBackingChainTraversal(src))
-        return 0;
-
-    rv = virStorageFileAccess(src, F_OK);
-    if (rv == -2)
-        return 0;
-    if (rv < 0) {
-        virStorageFileReportBrokenChain(errno, src, src);
-        return -1;
-    }
-
-    if ((headerLen = virStorageFileRead(src, 0, VIR_STORAGE_MAX_HEADER,
-                                        &buf)) < 0) {
-        if (headerLen == -2)
-            return 0;
-        return -1;
-    }
-
-    if (!(tmp = virStorageSourceCopy(src, false)))
-        return -1;
-
-    if (virStorageFileGetMetadataInternal(tmp, buf, headerLen) < 0)
-        return -1;
-
-    *backing = g_steal_pointer(&tmp->backingStoreRaw);
-    return 0;
-}
diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h
index a1e0afb28f..557e7da81e 100644
--- a/src/util/virstoragefile.h
+++ b/src/util/virstoragefile.h
@@ -21,8 +21,6 @@
 
 #pragma once
 
-#include <sys/stat.h>
-
 #include "virbitmap.h"
 #include "virobject.h"
 #include "virseclabel.h"
@@ -31,14 +29,6 @@
 #include "virenum.h"
 #include "virpci.h"
 
-/* Minimum header size required to probe all known formats with
- * virStorageFileProbeFormat, or obtain metadata from a known format.
- * Rounded to multiple of 512 (ISO has a 5-byte magic at offset
- * 32769).  Some formats can be probed with fewer bytes.  Although
- * some formats theoretically permit metadata that can rely on offsets
- * beyond this size, in practice that doesn't matter.  */
-#define VIR_STORAGE_MAX_HEADER 0x8200
-
 
 /* Types of disk backends (host resource).  Comparable to the public
  * virStorageVolType, except we have an undetermined state, don't have
@@ -386,37 +376,6 @@ struct _virStorageSource {
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageSource, virObjectUnref);
 
 
-#ifndef DEV_BSIZE
-# define DEV_BSIZE 512
-#endif
-
-int virStorageFileProbeFormat(const char *path, uid_t uid, gid_t gid);
-
-virStorageSourcePtr virStorageFileGetMetadataFromFD(const char *path,
-                                                    int fd,
-                                                    int format);
-virStorageSourcePtr virStorageFileGetMetadataFromBuf(const char *path,
-                                                     char *buf,
-                                                     size_t len,
-                                                     int format)
-    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
-int virStorageFileParseChainIndex(const char *diskTarget,
-                                  const char *name,
-                                  unsigned int *chainIndex)
-    ATTRIBUTE_NONNULL(3);
-
-int virStorageFileParseBackingStoreStr(const char *str,
-                                       char **target,
-                                       unsigned int *chainIndex)
-    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
-
-virStorageSourcePtr virStorageFileChainLookup(virStorageSourcePtr chain,
-                                              virStorageSourcePtr startFrom,
-                                              const char *name,
-                                              unsigned int idx,
-                                              virStorageSourcePtr *parent)
-    ATTRIBUTE_NONNULL(1);
-
 int virStorageFileGetSCSIKey(const char *path,
                              char **key,
                              bool ignoreError);
@@ -454,7 +413,6 @@ void virStorageNetHostDefClear(virStorageNetHostDefPtr def);
 void virStorageNetHostDefFree(size_t nhosts, virStorageNetHostDefPtr hosts);
 virStorageNetHostDefPtr virStorageNetHostDefCopy(size_t nhosts,
                                                  virStorageNetHostDefPtr hosts);
-
 int virStorageSourceInitChainElement(virStorageSourcePtr newelem,
                                      virStorageSourcePtr old,
                                      bool force);
@@ -466,15 +424,6 @@ bool virStorageSourceIsEmpty(virStorageSourcePtr src);
 bool virStorageSourceIsBlockLocal(const virStorageSource *src);
 virStorageSourcePtr virStorageSourceNew(void);
 void virStorageSourceBackingStoreClear(virStorageSourcePtr def);
-int virStorageSourceUpdatePhysicalSize(virStorageSourcePtr src,
-                                       int fd, struct stat const *sb);
-int virStorageSourceUpdateBackingSizes(virStorageSourcePtr src,
-                                       int fd, struct stat const *sb);
-int virStorageSourceUpdateCapacity(virStorageSourcePtr src,
-                                   char *buf, ssize_t len);
-
-int virStorageSourceNewFromBacking(virStorageSourcePtr parent,
-                                   virStorageSourcePtr *backing);
 
 int virStorageSourceNetCookiesValidate(virStorageSourcePtr src);
 
@@ -485,33 +434,8 @@ bool virStorageSourceIsSameLocation(virStorageSourcePtr a,
                                     virStorageSourcePtr b)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
 
-int virStorageSourceParseRBDColonString(const char *rbdstr,
-                                        virStorageSourcePtr src)
-    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
-
-typedef int
-(*virStorageFileSimplifyPathReadlinkCallback)(const char *path,
-                                              char **link,
-                                              void *data);
-char *virStorageFileCanonicalizePath(const char *path,
-                                     virStorageFileSimplifyPathReadlinkCallback cb,
-                                     void *cbdata);
-
-int virStorageFileGetRelativeBackingPath(virStorageSourcePtr from,
-                                         virStorageSourcePtr to,
-                                         char **relpath)
-    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
-
-int virStorageSourceNewFromBackingAbsolute(const char *path,
-                                           virStorageSourcePtr *src);
-
 bool virStorageSourceIsRelative(virStorageSourcePtr src);
 
-virStorageSourcePtr
-virStorageSourceFindByNodeName(virStorageSourcePtr top,
-                               const char *nodeName)
-    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
-
 void
 virStorageSourceNetworkAssignDefaultPorts(virStorageSourcePtr src)
     ATTRIBUTE_NONNULL(1);
@@ -522,13 +446,6 @@ bool
 virStorageSourceHasBacking(const virStorageSource *src);
 
 
-int
-virStorageSourcePrivateDataParseRelPath(xmlXPathContextPtr ctxt,
-                                        virStorageSourcePtr src);
-int
-virStorageSourcePrivateDataFormatRelPath(virStorageSourcePtr src,
-                                         virBufferPtr buf);
-
 void
 virStorageSourceInitiatorParseXML(xmlXPathContextPtr ctxt,
                                   virStorageSourceInitiatorDefPtr initiator);
@@ -544,39 +461,4 @@ virStorageSourceInitiatorCopy(virStorageSourceInitiatorDefPtr dest,
 void
 virStorageSourceInitiatorClear(virStorageSourceInitiatorDefPtr initiator);
 
-int virStorageFileInit(virStorageSourcePtr src);
-int virStorageFileInitAs(virStorageSourcePtr src,
-                         uid_t uid, gid_t gid);
-void virStorageFileDeinit(virStorageSourcePtr src);
-
-int virStorageFileCreate(virStorageSourcePtr src);
-int virStorageFileUnlink(virStorageSourcePtr src);
-int virStorageFileStat(virStorageSourcePtr src,
-                       struct stat *stat);
-ssize_t virStorageFileRead(virStorageSourcePtr src,
-                           size_t offset,
-                           size_t len,
-                           char **buf);
-const char *virStorageFileGetUniqueIdentifier(virStorageSourcePtr src);
-int virStorageFileAccess(virStorageSourcePtr src, int mode);
-int virStorageFileChown(const virStorageSource *src, uid_t uid, gid_t gid);
-
-int virStorageFileSupportsSecurityDriver(const virStorageSource *src);
-int virStorageFileSupportsAccess(const virStorageSource *src);
-int virStorageFileSupportsCreate(const virStorageSource *src);
-int virStorageFileSupportsBackingChainTraversal(const virStorageSource *src);
-
-int virStorageFileGetMetadata(virStorageSourcePtr src,
-                              uid_t uid, gid_t gid,
-                              bool report_broken)
-    ATTRIBUTE_NONNULL(1);
-
-int virStorageFileGetBackingStoreStr(virStorageSourcePtr src,
-                                     char **backing)
-    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
-
-void virStorageFileReportBrokenChain(int errcode,
-                                     virStorageSourcePtr src,
-                                     virStorageSourcePtr parent);
-
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageAuthDef, virStorageAuthDefFree);
diff --git a/tests/meson.build b/tests/meson.build
index f1d91ca50d..ce5b625b43 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -442,7 +442,7 @@ endif
 if conf.has('WITH_QEMU')
   tests += [
     { 'name': 'qemuagenttest', 'link_with': [ test_qemu_driver_lib, test_utils_qemu_monitor_lib ], 'link_whole': [ test_utils_qemu_lib ] },
-    { 'name': 'qemublocktest', 'link_with': [ test_qemu_driver_lib, test_utils_qemu_monitor_lib ], 'link_whole': [ test_utils_qemu_lib ] },
+    { 'name': 'qemublocktest', 'include': [ storage_file_inc_dir ], 'link_with': [ test_qemu_driver_lib, test_utils_qemu_monitor_lib ], 'link_whole': [ test_utils_qemu_lib ] },
     { 'name': 'qemucapabilitiestest', 'link_with': [ test_qemu_driver_lib, test_utils_qemu_monitor_lib ], 'link_whole': [ test_utils_qemu_lib ] },
     { 'name': 'qemucaps2xmltest', 'link_with': [ test_qemu_driver_lib ], 'link_whole': [ test_utils_qemu_lib ] },
     { 'name': 'qemucommandutiltest', 'link_with': [ test_qemu_driver_lib, test_utils_qemu_monitor_lib ], 'link_whole': [ test_utils_qemu_lib ] },
@@ -509,7 +509,7 @@ endif
 
 if conf.has('WITH_STORAGE_FS')
   tests += [
-    { 'name': 'virstoragetest', 'link_with': [ storage_driver_impl_lib ] },
+    { 'name': 'virstoragetest', 'include': [ storage_file_inc_dir ], 'link_with': [ storage_driver_impl_lib ] },
   ]
 endif
 
diff --git a/tests/qemublocktest.c b/tests/qemublocktest.c
index f86e9fd552..ee57449868 100644
--- a/tests/qemublocktest.c
+++ b/tests/qemublocktest.c
@@ -21,6 +21,7 @@
 #include "testutilsqemu.h"
 #include "testutilsqemuschema.h"
 #include "virstoragefile.h"
+#include "storage_file.h"
 #include "virstring.h"
 #include "virlog.h"
 #include "qemu/qemu_block.h"
diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c
index a376154def..59c03255d4 100644
--- a/tests/virstoragetest.c
+++ b/tests/virstoragetest.c
@@ -20,6 +20,7 @@
 
 #include <unistd.h>
 
+#include "storage_file.h"
 #include "testutils.h"
 #include "vircommand.h"
 #include "virerror.h"
-- 
2.28.0




More information about the libvir-list mailing list