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(a)redhat.com>
---
po/POTFILES.in | 1 +
src/libvirt_private.syms | 57 +-
src/libxl/meson.build | 1 +
src/libxl/xen_xl.c | 2 +-
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/security/meson.build | 1 +
src/security/virt-aa-helper.c | 2 +-
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_source.c | 2614 +++++++++++++++++++++++++
src/storage_file/storage_source.h | 148 ++
src/util/virstoragefile.c | 2573 ------------------------
src/util/virstoragefile.h | 75 -
tests/meson.build | 4 +-
tests/qemublocktest.c | 1 +
tests/virstoragetest.c | 1 +
25 files changed, 2833 insertions(+), 2679 deletions(-)
create mode 100644 src/storage_file/storage_source.c
create mode 100644 src/storage_file/storage_source.h
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 19eb15ada0..1e47af987d 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -225,6 +225,7 @@
@SRCDIR(a)src/storage/storage_util.c
@SRCDIR(a)src/storage_file/storage_file_fs.c
@SRCDIR(a)src/storage_file/storage_file_gluster.c
+@SRCDIR(a)src/storage_file/storage_source.c
@SRCDIR(a)src/test/test_driver.c
@SRCDIR(a)src/util/iohelper.c
@SRCDIR(a)src/util/viralloc.c
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 2dfc7e32d5..67a863467d 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1618,6 +1618,36 @@ virSecurityManagerVerify;
virSecurityXATTRNamespaceDefined;
+# storage_file/storage_source.h
+virStorageFileAccess;
+virStorageFileChainLookup;
+virStorageFileChown;
+virStorageFileCreate;
+virStorageFileDeinit;
+virStorageFileGetBackingStoreStr;
+virStorageFileGetMetadata;
+virStorageFileGetMetadataFromBuf;
+virStorageFileGetMetadataFromFD;
+virStorageFileGetRelativeBackingPath;
+virStorageFileGetUniqueIdentifier;
+virStorageFileInit;
+virStorageFileInitAs;
+virStorageFileRead;
+virStorageFileReportBrokenChain;
+virStorageFileStat;
+virStorageFileSupportsAccess;
+virStorageFileSupportsBackingChainTraversal;
+virStorageFileSupportsCreate;
+virStorageFileSupportsSecurityDriver;
+virStorageFileUnlink;
+virStorageSourceNewFromBacking;
+virStorageSourceNewFromBackingAbsolute;
+virStorageSourceParseRBDColonString;
+virStorageSourceUpdateBackingSizes;
+virStorageSourceUpdateCapacity;
+virStorageSourceUpdatePhysicalSize;
+
+
# util/glibcompat.h
vir_g_canonicalize_filename;
vir_g_fsync;
@@ -3126,36 +3156,15 @@ 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;
-virStorageFileRead;
-virStorageFileReportBrokenChain;
-virStorageFileStat;
-virStorageFileSupportsAccess;
-virStorageFileSupportsBackingChainTraversal;
-virStorageFileSupportsCreate;
-virStorageFileSupportsSecurityDriver;
-virStorageFileUnlink;
virStorageIsFile;
virStorageIsRelative;
virStorageNetHostDefClear;
@@ -3191,18 +3200,12 @@ 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 17b93d0f5c..621ee63a99 100644
--- a/src/libxl/xen_xl.c
+++ b/src/libxl/xen_xl.c
@@ -29,7 +29,7 @@
#include "domain_conf.h"
#include "viralloc.h"
#include "virstring.h"
-#include "virstoragefile.h"
+#include "storage_source.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 c444f8aaba..72ca781d8b 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_source.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 c98094e746..ca23466b22 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_source.h"
#include "viralloc.h"
#include "virstoragefile.h"
#include "virstring.h"
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 966608882f..165321858e 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_source.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 34a8fbe233..a5c2488f2c 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -84,6 +84,7 @@
#include "virhook.h"
#include "virstoragefile.h"
#include "virstoragefileprobe.h"
+#include "storage_source.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 d016fe09a4..f1fa5986e1 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_source.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 202d867289..14e1f5d962 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_source.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
index c2fa54f5ab..994c7dc60b 100644
--- a/src/qemu/qemu_snapshot.c
+++ b/src/qemu/qemu_snapshot.c
@@ -42,6 +42,7 @@
#include "virstring.h"
#include "virdomainsnapshotobjlist.h"
#include "virqemu.h"
+#include "storage_source.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
diff --git a/src/security/meson.build b/src/security/meson.build
index 4f876c03c2..416fec7900 100644
--- a/src/security/meson.build
+++ b/src/security/meson.build
@@ -48,6 +48,7 @@ if conf.has('WITH_LIBVIRTD') and
conf.has('WITH_APPARMOR')
conf_inc_dir,
hypervisor_inc_dir,
include_directories('.'),
+ include_directories('../storage_file'),
],
}
endif
diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c
index 18af175655..6525baf193 100644
--- a/src/security/virt-aa-helper.c
+++ b/src/security/virt-aa-helper.c
@@ -37,6 +37,7 @@
#include "security_driver.h"
#include "security_apparmor.h"
+#include "storage_source.h"
#include "domain_conf.h"
#include "virxml.h"
#include "viruuid.h"
@@ -46,7 +47,6 @@
#include "virfile.h"
#include "configmake.h"
#include "virrandom.h"
-#include "virstoragefile.h"
#include "virstring.h"
#include "virgettext.h"
#include "virhostdev.h"
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 205a707a17..782f8eb611 100644
--- a/src/storage/storage_backend_gluster.c
+++ b/src/storage/storage_backend_gluster.c
@@ -32,6 +32,7 @@
#include "virstring.h"
#include "viruri.h"
#include "storage_util.h"
+#include "storage_source.h"
#define VIR_FROM_THIS VIR_FROM_STORAGE
diff --git a/src/storage/storage_util.c b/src/storage/storage_util.c
index 4117127d65..d51fa2b4c0 100644
--- a/src/storage/storage_util.c
+++ b/src/storage/storage_util.c
@@ -64,6 +64,7 @@
#include "virstoragefile.h"
#include "virstoragefileprobe.h"
#include "storage_util.h"
+#include "storage_source.h"
#include "virlog.h"
#include "virfile.h"
#include "virjson.h"
diff --git a/src/storage_file/meson.build b/src/storage_file/meson.build
index 20eb0176fc..bae018feac 100644
--- a/src/storage_file/meson.build
+++ b/src/storage_file/meson.build
@@ -1,3 +1,7 @@
+storage_file_sources = [
+ 'storage_source.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_source.c b/src/storage_file/storage_source.c
new file mode 100644
index 0000000000..b5e0bc5040
--- /dev/null
+++ b/src/storage_file/storage_source.c
@@ -0,0 +1,2614 @@
+/*
+ * storage_source.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 <sys/types.h>
+#include <unistd.h>
+
+#include "internal.h"
+#include "storage_source.h"
+#include "viralloc.h"
+#include "virerror.h"
+#include "virfile.h"
+#include "virhash.h"
+#include "virjson.h"
+#include "virlog.h"
+#include "virobject.h"
+#include "virstoragefile.h"
+#include "virstoragefilebackend.h"
+#include "virstoragefileprobe.h"
+#include "virstring.h"
+#include "viruri.h"
+#include "virutil.h"
+
+#define VIR_FROM_THIS VIR_FROM_STORAGE
+
+VIR_LOG_INIT("storage_source");
+
+
+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 (virStorageFileProbeGetMetadata(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 (virStorageFileProbeGetMetadata(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);
+}
+
+
+/* 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 = virStorageIsFile(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:
+ case VIR_STORAGE_NET_PROTOCOL_NFS:
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("malformed backing store path for protocol %s"),
+ protocol);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+virStorageSourceParseBackingJSONInternal(virStorageSourcePtr src,
+ virJSONValuePtr json,
+ const char *jsonstr,
+ bool allowformat);
+
+
+static int
+virStorageSourceParseBackingJSONPath(virStorageSourcePtr src,
+ virJSONValuePtr json,
+ const char *jsonstr G_GNUC_UNUSED,
+ int type)
+{
+ const char *path;
+
+ if (!(path = virJSONValueObjectGetString(json, "filename"))) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing 'filename' field in JSON backing volume
"
+ "definition"));
+ return -1;
+ }
+
+ src->path = g_strdup(path);
+
+ src->type = type;
+ return 0;
+}
+
+
+static int
+virStorageSourceParseBackingJSONUriStr(virStorageSourcePtr src,
+ const char *uri,
+ int protocol)
+{
+ int rc;
+
+ if ((rc = virStorageSourceParseBackingURI(src, uri)) < 0)
+ return -1;
+
+ if (src->protocol != protocol) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("expected protocol '%s' but got '%s' in URI
JSON volume "
+ "definition"),
+ virStorageNetProtocolTypeToString(protocol),
+ virStorageNetProtocolTypeToString(src->protocol));
+ return -1;
+ }
+
+ return rc;
+}
+
+
+static int
+virStorageSourceParseBackingJSONUriCookies(virStorageSourcePtr src,
+ virJSONValuePtr json,
+ const char *jsonstr)
+{
+ const char *cookiestr;
+ g_auto(GStrv) cookies = NULL;
+ size_t ncookies = 0;
+ size_t i;
+
+ if (!virJSONValueObjectHasKey(json, "cookie"))
+ return 0;
+
+ if (!(cookiestr = virJSONValueObjectGetString(json, "cookie"))) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("wrong format of 'cookie' field in backing store
definition '%s'"),
+ jsonstr);
+ return -1;
+ }
+
+ if (!(cookies = virStringSplitCount(cookiestr, ";", 0, &ncookies)))
+ return -1;
+
+ src->cookies = g_new0(virStorageNetCookieDefPtr, ncookies);
+ src->ncookies = ncookies;
+
+ for (i = 0; i < ncookies; i++) {
+ char *cookiename = cookies[i];
+ char *cookievalue;
+
+ virSkipSpaces((const char **) &cookiename);
+
+ if (!(cookievalue = strchr(cookiename, '='))) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("malformed http cookie '%s' in backing store
definition '%s'"),
+ cookies[i], jsonstr);
+ return -1;
+ }
+
+ *cookievalue = '\0';
+ cookievalue++;
+
+ src->cookies[i] = g_new0(virStorageNetCookieDef, 1);
+ src->cookies[i]->name = g_strdup(cookiename);
+ src->cookies[i]->value = g_strdup(cookievalue);
+ }
+
+ return 0;
+}
+
+
+static int
+virStorageSourceParseBackingJSONUri(virStorageSourcePtr src,
+ virJSONValuePtr json,
+ const char *jsonstr,
+ int protocol)
+{
+ const char *uri;
+
+ if (!(uri = virJSONValueObjectGetString(json, "url"))) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing 'url' in JSON backing volume
definition"));
+ return -1;
+ }
+
+ if (protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS ||
+ protocol == VIR_STORAGE_NET_PROTOCOL_FTPS) {
+ if (virJSONValueObjectHasKey(json, "sslverify")) {
+ const char *tmpstr;
+ bool tmp;
+
+ /* libguestfs still uses undocumented legacy value of 'off' */
+ if ((tmpstr = virJSONValueObjectGetString(json, "sslverify"))
&&
+ STREQ(tmpstr, "off")) {
+ src->sslverify = VIR_TRISTATE_BOOL_NO;
+ } else {
+ if (virJSONValueObjectGetBoolean(json, "sslverify", &tmp)
< 0) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("malformed 'sslverify' field in backing
store definition '%s'"),
+ jsonstr);
+ return -1;
+ }
+
+ src->sslverify = virTristateBoolFromBool(tmp);
+ }
+ }
+ }
+
+ if (protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS ||
+ protocol == VIR_STORAGE_NET_PROTOCOL_HTTP) {
+ if (virStorageSourceParseBackingJSONUriCookies(src, json, jsonstr) < 0)
+ return -1;
+ }
+
+ if (virJSONValueObjectHasKey(json, "readahead") &&
+ virJSONValueObjectGetNumberUlong(json, "readahead",
&src->readahead) < 0) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("malformed 'readahead' field in backing store
definition '%s'"),
+ jsonstr);
+ return -1;
+ }
+
+ if (virJSONValueObjectHasKey(json, "timeout") &&
+ virJSONValueObjectGetNumberUlong(json, "timeout", &src->timeout)
< 0) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("malformed 'timeout' field in backing store
definition '%s'"),
+ jsonstr);
+ return -1;
+ }
+
+ return virStorageSourceParseBackingJSONUriStr(src, uri, protocol);
+}
+
+
+static int
+virStorageSourceParseBackingJSONInetSocketAddress(virStorageNetHostDefPtr host,
+ virJSONValuePtr json)
+{
+ const char *hostname;
+ const char *port;
+
+ if (!json) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing remote server specification in JSON "
+ "backing volume definition"));
+ return -1;
+ }
+
+ hostname = virJSONValueObjectGetString(json, "host");
+ port = virJSONValueObjectGetString(json, "port");
+
+ if (!hostname) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing hostname for tcp backing server in "
+ "JSON backing volume definition"));
+ return -1;
+ }
+
+ host->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
+ host->name = g_strdup(hostname);
+
+ if (virStringParsePort(port, &host->port) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int
+virStorageSourceParseBackingJSONSocketAddress(virStorageNetHostDefPtr host,
+ virJSONValuePtr json)
+{
+ const char *type;
+ const char *socket;
+
+ if (!json) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing remote server specification in JSON "
+ "backing volume definition"));
+ return -1;
+ }
+
+ if (!(type = virJSONValueObjectGetString(json, "type"))) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing socket address type in "
+ "JSON backing volume definition"));
+ return -1;
+ }
+
+ if (STREQ(type, "tcp") || STREQ(type, "inet")) {
+ return virStorageSourceParseBackingJSONInetSocketAddress(host, json);
+
+ } else if (STREQ(type, "unix")) {
+ host->transport = VIR_STORAGE_NET_HOST_TRANS_UNIX;
+
+ socket = virJSONValueObjectGetString(json, "path");
+
+ /* check for old spelling for gluster protocol */
+ if (!socket)
+ socket = virJSONValueObjectGetString(json, "socket");
+
+ if (!socket) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing socket path for udp backing server in "
+ "JSON backing volume definition"));
+ return -1;
+ }
+
+ host->socket = g_strdup(socket);
+ } else {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("backing store protocol '%s' is not yet
supported"),
+ type);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+virStorageSourceParseBackingJSONGluster(virStorageSourcePtr src,
+ virJSONValuePtr json,
+ const char *jsonstr G_GNUC_UNUSED,
+ int opaque G_GNUC_UNUSED)
+{
+ const char *uri = virJSONValueObjectGetString(json, "filename");
+ const char *volume = virJSONValueObjectGetString(json, "volume");
+ const char *path = virJSONValueObjectGetString(json, "path");
+ virJSONValuePtr server = virJSONValueObjectGetArray(json, "server");
+ size_t nservers;
+ size_t i;
+
+ /* legacy URI based syntax passed via 'filename' option */
+ if (uri)
+ return virStorageSourceParseBackingJSONUriStr(src, uri,
+ VIR_STORAGE_NET_PROTOCOL_GLUSTER);
+
+ if (!volume || !path || !server) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing 'volume', 'path' or
'server' attribute in "
+ "JSON backing definition for gluster volume"));
+ return -1;
+ }
+
+ src->type = VIR_STORAGE_TYPE_NETWORK;
+ src->protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER;
+
+ src->volume = g_strdup(volume);
+ src->path = g_strdup(path);
+
+ nservers = virJSONValueArraySize(server);
+ if (nservers == 0) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("at least 1 server is necessary in "
+ "JSON backing definition for gluster volume"));
+
+ return -1;
+ }
+
+ src->hosts = g_new0(virStorageNetHostDef, nservers);
+ src->nhosts = nservers;
+
+ for (i = 0; i < nservers; i++) {
+ if (virStorageSourceParseBackingJSONSocketAddress(src->hosts + i,
+ virJSONValueArrayGet(server,
i)) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+virStorageSourceParseBackingJSONiSCSI(virStorageSourcePtr src,
+ virJSONValuePtr json,
+ const char *jsonstr G_GNUC_UNUSED,
+ int opaque G_GNUC_UNUSED)
+{
+ const char *transport = virJSONValueObjectGetString(json, "transport");
+ const char *portal = virJSONValueObjectGetString(json, "portal");
+ const char *target = virJSONValueObjectGetString(json, "target");
+ const char *lun = virJSONValueObjectGetStringOrNumber(json, "lun");
+ const char *uri;
+ char *port;
+
+ /* legacy URI based syntax passed via 'filename' option */
+ if ((uri = virJSONValueObjectGetString(json, "filename")))
+ return virStorageSourceParseBackingJSONUriStr(src, uri,
+ VIR_STORAGE_NET_PROTOCOL_ISCSI);
+
+ src->type = VIR_STORAGE_TYPE_NETWORK;
+ src->protocol = VIR_STORAGE_NET_PROTOCOL_ISCSI;
+
+ if (!lun)
+ lun = "0";
+
+ src->hosts = g_new0(virStorageNetHostDef, 1);
+ src->nhosts = 1;
+
+ if (STRNEQ_NULLABLE(transport, "tcp")) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("only TCP transport is supported for iSCSI
volumes"));
+ return -1;
+ }
+
+ src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
+
+ if (!portal) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing 'portal' address in iSCSI backing
definition"));
+ return -1;
+ }
+
+ if (!target) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing 'target' in iSCSI backing
definition"));
+ return -1;
+ }
+
+ src->hosts->name = g_strdup(portal);
+
+ if ((port = strrchr(src->hosts->name, ':')) &&
+ !strchr(port, ']')) {
+ if (virStringParsePort(port + 1, &src->hosts->port) < 0)
+ return -1;
+
+ *port = '\0';
+ }
+
+ src->path = g_strdup_printf("%s/%s", target, lun);
+
+ /* Libvirt doesn't handle inline authentication. Make the caller aware. */
+ if (virJSONValueObjectGetString(json, "user") ||
+ virJSONValueObjectGetString(json, "password"))
+ return 1;
+
+ return 0;
+}
+
+
+static int
+virStorageSourceParseBackingJSONNbd(virStorageSourcePtr src,
+ virJSONValuePtr json,
+ const char *jsonstr G_GNUC_UNUSED,
+ int opaque G_GNUC_UNUSED)
+{
+ const char *path = virJSONValueObjectGetString(json, "path");
+ const char *host = virJSONValueObjectGetString(json, "host");
+ const char *port = virJSONValueObjectGetString(json, "port");
+ const char *export = virJSONValueObjectGetString(json, "export");
+ virJSONValuePtr server = virJSONValueObjectGetObject(json, "server");
+
+ if (!path && !host && !server) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing host specification of NBD server in JSON "
+ "backing volume definition"));
+ return -1;
+ }
+
+ src->type = VIR_STORAGE_TYPE_NETWORK;
+ src->protocol = VIR_STORAGE_NET_PROTOCOL_NBD;
+
+ src->path = g_strdup(export);
+
+ src->hosts = g_new0(virStorageNetHostDef, 1);
+ src->nhosts = 1;
+
+ if (server) {
+ if (virStorageSourceParseBackingJSONSocketAddress(src->hosts, server) < 0)
+ return -1;
+ } else {
+ if (path) {
+ src->hosts[0].transport = VIR_STORAGE_NET_HOST_TRANS_UNIX;
+ src->hosts[0].socket = g_strdup(path);
+ } else {
+ src->hosts[0].transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
+ src->hosts[0].name = g_strdup(host);
+
+ if (virStringParsePort(port, &src->hosts[0].port) < 0)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+virStorageSourceParseBackingJSONSheepdog(virStorageSourcePtr src,
+ virJSONValuePtr json,
+ const char *jsonstr G_GNUC_UNUSED,
+ int opaque G_GNUC_UNUSED)
+{
+ const char *filename;
+ const char *vdi = virJSONValueObjectGetString(json, "vdi");
+ virJSONValuePtr server = virJSONValueObjectGetObject(json, "server");
+
+ /* legacy URI based syntax passed via 'filename' option */
+ if ((filename = virJSONValueObjectGetString(json, "filename"))) {
+ if (strstr(filename, "://"))
+ return virStorageSourceParseBackingJSONUriStr(src, filename,
+
VIR_STORAGE_NET_PROTOCOL_SHEEPDOG);
+
+ /* libvirt doesn't implement a parser for the legacy non-URI syntax */
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing sheepdog URI in JSON backing volume
definition"));
+ return -1;
+ }
+
+ src->type = VIR_STORAGE_TYPE_NETWORK;
+ src->protocol = VIR_STORAGE_NET_PROTOCOL_SHEEPDOG;
+
+ if (!vdi) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s", _("missing sheepdog vdi
name"));
+ return -1;
+ }
+
+ src->path = g_strdup(vdi);
+
+ src->hosts = g_new0(virStorageNetHostDef, 1);
+ src->nhosts = 1;
+
+ if (virStorageSourceParseBackingJSONSocketAddress(src->hosts, server) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int
+virStorageSourceParseBackingJSONSSH(virStorageSourcePtr src,
+ virJSONValuePtr json,
+ const char *jsonstr G_GNUC_UNUSED,
+ int opaque G_GNUC_UNUSED)
+{
+ const char *path = virJSONValueObjectGetString(json, "path");
+ const char *host = virJSONValueObjectGetString(json, "host");
+ const char *port = virJSONValueObjectGetString(json, "port");
+ const char *user = virJSONValueObjectGetString(json, "user");
+ const char *host_key_check = virJSONValueObjectGetString(json,
"host_key_check");
+ virJSONValuePtr server = virJSONValueObjectGetObject(json, "server");
+
+ if (!(host || server) || !path) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing host/server or path of SSH JSON backing "
+ "volume definition"));
+ return -1;
+ }
+
+ src->type = VIR_STORAGE_TYPE_NETWORK;
+ src->protocol = VIR_STORAGE_NET_PROTOCOL_SSH;
+
+ src->path = g_strdup(path);
+
+ src->hosts = g_new0(virStorageNetHostDef, 1);
+ src->nhosts = 1;
+
+ if (server) {
+ if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts,
+ server) < 0)
+ return -1;
+ } else {
+ src->hosts[0].transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
+ src->hosts[0].name = g_strdup(host);
+
+ if (virStringParsePort(port, &src->hosts[0].port) < 0)
+ return -1;
+ }
+
+ /* these two are parsed just to be passed back as we don't model them yet */
+ src->ssh_user = g_strdup(user);
+ if (STREQ_NULLABLE(host_key_check, "no"))
+ src->ssh_host_key_check_disabled = true;
+
+ return 0;
+}
+
+
+static int
+virStorageSourceParseBackingJSONRBD(virStorageSourcePtr src,
+ virJSONValuePtr json,
+ const char *jsonstr G_GNUC_UNUSED,
+ int opaque G_GNUC_UNUSED)
+{
+ const char *filename;
+ const char *pool = virJSONValueObjectGetString(json, "pool");
+ const char *image = virJSONValueObjectGetString(json, "image");
+ const char *conf = virJSONValueObjectGetString(json, "conf");
+ const char *snapshot = virJSONValueObjectGetString(json, "snapshot");
+ virJSONValuePtr servers = virJSONValueObjectGetArray(json, "server");
+ size_t nservers;
+ size_t i;
+
+ src->type = VIR_STORAGE_TYPE_NETWORK;
+ src->protocol = VIR_STORAGE_NET_PROTOCOL_RBD;
+
+ /* legacy syntax passed via 'filename' option */
+ if ((filename = virJSONValueObjectGetString(json, "filename")))
+ return virStorageSourceParseRBDColonString(filename, src);
+
+ if (!pool || !image) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing pool or image name in ceph backing volume "
+ "JSON specification"));
+ return -1;
+ }
+
+ src->volume = g_strdup(pool);
+ src->path = g_strdup(image);
+ src->snapshot = g_strdup(snapshot);
+ src->configFile = g_strdup(conf);
+
+ if (servers) {
+ nservers = virJSONValueArraySize(servers);
+
+ src->hosts = g_new0(virStorageNetHostDef, nservers);
+ src->nhosts = nservers;
+
+ for (i = 0; i < nservers; i++) {
+ if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts + i,
+
virJSONValueArrayGet(servers, i)) < 0)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+virStorageSourceParseBackingJSONRaw(virStorageSourcePtr src,
+ virJSONValuePtr json,
+ const char *jsonstr,
+ int opaque G_GNUC_UNUSED)
+{
+ bool has_offset = virJSONValueObjectHasKey(json, "offset");
+ bool has_size = virJSONValueObjectHasKey(json, "size");
+ virJSONValuePtr file;
+
+ if (has_offset || has_size) {
+ src->sliceStorage = g_new0(virStorageSourceSlice, 1);
+
+ if (has_offset &&
+ virJSONValueObjectGetNumberUlong(json, "offset",
&src->sliceStorage->offset) < 0) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("malformed 'offset' property of 'raw'
driver"));
+ return -1;
+ }
+
+ if (has_size &&
+ virJSONValueObjectGetNumberUlong(json, "size",
&src->sliceStorage->size) < 0) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("malformed 'size' property of 'raw'
driver"));
+ return -1;
+ }
+ }
+
+ /* 'raw' is a format driver so it can have protocol driver children */
+ if (!(file = virJSONValueObjectGetObject(json, "file"))) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("JSON backing volume definition '%s' lacks
'file' object"),
+ jsonstr);
+ return -1;
+ }
+
+ return virStorageSourceParseBackingJSONInternal(src, file, jsonstr, false);
+}
+
+
+static int
+virStorageSourceParseBackingJSONVxHS(virStorageSourcePtr src,
+ virJSONValuePtr json,
+ const char *jsonstr G_GNUC_UNUSED,
+ int opaque G_GNUC_UNUSED)
+{
+ const char *vdisk_id = virJSONValueObjectGetString(json, "vdisk-id");
+ virJSONValuePtr server = virJSONValueObjectGetObject(json, "server");
+
+ if (!vdisk_id || !server) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing 'vdisk-id' or 'server' attribute
in "
+ "JSON backing definition for VxHS volume"));
+ return -1;
+ }
+
+ src->type = VIR_STORAGE_TYPE_NETWORK;
+ src->protocol = VIR_STORAGE_NET_PROTOCOL_VXHS;
+
+ src->path = g_strdup(vdisk_id);
+
+ src->hosts = g_new0(virStorageNetHostDef, 1);
+ src->nhosts = 1;
+
+ if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts,
+ server) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int
+virStorageSourceParseBackingJSONNFS(virStorageSourcePtr src,
+ virJSONValuePtr json,
+ const char *jsonstr G_GNUC_UNUSED,
+ int opaque G_GNUC_UNUSED)
+{
+ virJSONValuePtr server = virJSONValueObjectGetObject(json, "server");
+ int uidStore = -1;
+ int gidStore = -1;
+ int gotUID = virJSONValueObjectGetNumberInt(json, "user", &uidStore);
+ int gotGID = virJSONValueObjectGetNumberInt(json, "group", &gidStore);
+
+ if (!server) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing 'server' attribute in JSON backing
definition for NFS volume"));
+ return -1;
+ }
+
+ if (gotUID < 0 || gotGID < 0) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing 'user' or 'group' attribute in
JSON backing definition for NFS volume"));
+ return -1;
+ }
+
+ src->path = g_strdup(virJSONValueObjectGetString(json, "path"));
+ if (!src->path) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing 'path' attribute in JSON backing
definition for NFS volume"));
+ return -1;
+ }
+
+ src->nfs_user = g_strdup_printf("+%d", uidStore);
+ src->nfs_group = g_strdup_printf("+%d", gidStore);
+
+ src->type = VIR_STORAGE_TYPE_NETWORK;
+ src->protocol = VIR_STORAGE_NET_PROTOCOL_NFS;
+
+ src->hosts = g_new0(virStorageNetHostDef, 1);
+ src->nhosts = 1;
+
+ if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts,
+ server) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int
+virStorageSourceParseBackingJSONNVMe(virStorageSourcePtr src,
+ virJSONValuePtr json,
+ const char *jsonstr G_GNUC_UNUSED,
+ int opaque G_GNUC_UNUSED)
+{
+ g_autoptr(virStorageSourceNVMeDef) nvme = g_new0(virStorageSourceNVMeDef, 1);
+ const char *device = virJSONValueObjectGetString(json, "device");
+
+ if (!device || virPCIDeviceAddressParse((char *) device, &nvme->pciAddr) <
0) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing or malformed 'device' field of
'nvme' storage"));
+ return -1;
+ }
+
+ if (virJSONValueObjectGetNumberUlong(json, "namespace",
&nvme->namespc) < 0 ||
+ nvme->namespc == 0) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing or malformed 'namespace' field of
'nvme' storage"));
+ return -1;
+ }
+
+ src->type = VIR_STORAGE_TYPE_NVME;
+ src->nvme = g_steal_pointer(&nvme);
+
+ return 0;
+}
+
+
+struct virStorageSourceJSONDriverParser {
+ const char *drvname;
+ bool formatdriver;
+ /**
+ * The callback gets a pre-allocated storage source @src and the JSON
+ * object to parse. The callback shall return -1 on error and report error
+ * 0 on success and 1 in cases when the configuration itself is valid, but
+ * can't be converted to libvirt's configuration (e.g. inline authentication
+ * credentials are present).
+ */
+ int (*func)(virStorageSourcePtr src, virJSONValuePtr json, const char *jsonstr, int
opaque);
+ int opaque;
+};
+
+static const struct virStorageSourceJSONDriverParser jsonParsers[] = {
+ {"file", false, virStorageSourceParseBackingJSONPath,
VIR_STORAGE_TYPE_FILE},
+ {"host_device", false, virStorageSourceParseBackingJSONPath,
VIR_STORAGE_TYPE_BLOCK},
+ {"host_cdrom", false, virStorageSourceParseBackingJSONPath,
VIR_STORAGE_TYPE_BLOCK},
+ {"http", false, virStorageSourceParseBackingJSONUri,
VIR_STORAGE_NET_PROTOCOL_HTTP},
+ {"https", false, virStorageSourceParseBackingJSONUri,
VIR_STORAGE_NET_PROTOCOL_HTTPS},
+ {"ftp", false, virStorageSourceParseBackingJSONUri,
VIR_STORAGE_NET_PROTOCOL_FTP},
+ {"ftps", false, virStorageSourceParseBackingJSONUri,
VIR_STORAGE_NET_PROTOCOL_FTPS},
+ {"tftp", false, virStorageSourceParseBackingJSONUri,
VIR_STORAGE_NET_PROTOCOL_TFTP},
+ {"gluster", false, virStorageSourceParseBackingJSONGluster, 0},
+ {"iscsi", false, virStorageSourceParseBackingJSONiSCSI, 0},
+ {"nbd", false, virStorageSourceParseBackingJSONNbd, 0},
+ {"sheepdog", false, virStorageSourceParseBackingJSONSheepdog, 0},
+ {"ssh", false, virStorageSourceParseBackingJSONSSH, 0},
+ {"rbd", false, virStorageSourceParseBackingJSONRBD, 0},
+ {"raw", true, virStorageSourceParseBackingJSONRaw, 0},
+ {"nfs", false, virStorageSourceParseBackingJSONNFS, 0},
+ {"vxhs", false, virStorageSourceParseBackingJSONVxHS, 0},
+ {"nvme", false, virStorageSourceParseBackingJSONNVMe, 0},
+};
+
+
+
+static int
+virStorageSourceParseBackingJSONInternal(virStorageSourcePtr src,
+ virJSONValuePtr json,
+ const char *jsonstr,
+ bool allowformat)
+{
+ const char *drvname;
+ size_t i;
+
+ if (!(drvname = virJSONValueObjectGetString(json, "driver"))) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("JSON backing volume definition '%s' lacks driver
name"),
+ jsonstr);
+ return -1;
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(jsonParsers); i++) {
+ if (STRNEQ(drvname, jsonParsers[i].drvname))
+ continue;
+
+ if (jsonParsers[i].formatdriver && !allowformat) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("JSON backing volume definition '%s' must not
have nested format drivers"),
+ jsonstr);
+ return -1;
+ }
+
+ return jsonParsers[i].func(src, json, jsonstr, jsonParsers[i].opaque);
+ }
+
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("missing parser implementation for JSON backing volume "
+ "driver '%s'"), drvname);
+ return -1;
+}
+
+
+static int
+virStorageSourceParseBackingJSON(virStorageSourcePtr src,
+ const char *json)
+{
+ g_autoptr(virJSONValue) root = NULL;
+ g_autoptr(virJSONValue) deflattened = NULL;
+ virJSONValuePtr file = NULL;
+
+ if (!(root = virJSONValueFromString(json)))
+ return -1;
+
+ if (!(deflattened = virJSONValueObjectDeflatten(root)))
+ return -1;
+
+ /* There are 2 possible syntaxes:
+ * 1) json:{"file":{"driver":...}}
+ * 2) json:{"driver":...}
+ * Remove the 'file' wrapper object in case 1.
+ */
+ if (!virJSONValueObjectHasKey(deflattened, "driver"))
+ file = virJSONValueObjectGetObject(deflattened, "file");
+
+ if (!file)
+ file = deflattened;
+
+ return virStorageSourceParseBackingJSONInternal(src, file, json, true);
+}
+
+
+/**
+ * virStorageSourceNewFromBackingAbsolute
+ * @path: string representing absolute location of a storage source
+ * @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 (virStorageIsFile(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 (virStorageIsRelative(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;
+}
+
+
+/**
+ * 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;
+}
+
+
+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 (virStorageFileProbeGetMetadata(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 (virStorageFileProbeGetMetadata(tmp, buf, headerLen) < 0)
+ return -1;
+
+ *backing = g_steal_pointer(&tmp->backingStoreRaw);
+ return 0;
+}
diff --git a/src/storage_file/storage_source.h b/src/storage_file/storage_source.h
new file mode 100644
index 0000000000..23f56417a8
--- /dev/null
+++ b/src/storage_file/storage_source.h
@@ -0,0 +1,148 @@
+/*
+ * storage_source.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"
+
+#ifndef DEV_BSIZE
+# define DEV_BSIZE 512
+#endif
+
+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);
+
+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);
+
+int
+virStorageFileGetRelativeBackingPath(virStorageSourcePtr top,
+ virStorageSourcePtr base,
+ char **relpath)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+
+int
+virStorageSourceNewFromBackingAbsolute(const char *path,
+ virStorageSourcePtr *src);
+
+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 *st);
+
+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/util/virstoragefile.c b/src/util/virstoragefile.c
index 98a3222d09..c70d5713de 100644
--- a/src/util/virstoragefile.c
+++ b/src/util/virstoragefile.c
@@ -31,14 +31,9 @@
#include "vircommand.h"
#include "virhash.h"
#include "virstring.h"
-#include "viruri.h"
#include "virbuffer.h"
-#include "virjson.h"
#include "virstorageencryption.h"
-#include "virstoragefilebackend.h"
-#include "virstoragefileprobe.h"
#include "virsecret.h"
-#include "virutil.h"
#define VIR_FROM_THIS VIR_FROM_STORAGE
@@ -146,126 +141,6 @@ virStorageIsRelative(const char *backing)
}
-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 (virStorageFileProbeGetMetadata(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 (virStorageFileProbeGetMetadata(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
* @path: Path to the SCSI device
@@ -503,107 +378,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 = virStorageIsFile(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)
{
@@ -1660,1550 +1434,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:
- case VIR_STORAGE_NET_PROTOCOL_NFS:
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("malformed backing store path for protocol %s"),
- protocol);
- return -1;
- }
-
- return 0;
-}
-
-
-static int
-virStorageSourceParseBackingJSONInternal(virStorageSourcePtr src,
- virJSONValuePtr json,
- const char *jsonstr,
- bool allowformat);
-
-
-static int
-virStorageSourceParseBackingJSONPath(virStorageSourcePtr src,
- virJSONValuePtr json,
- const char *jsonstr G_GNUC_UNUSED,
- int type)
-{
- const char *path;
-
- if (!(path = virJSONValueObjectGetString(json, "filename"))) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing 'filename' field in JSON backing volume
"
- "definition"));
- return -1;
- }
-
- src->path = g_strdup(path);
-
- src->type = type;
- return 0;
-}
-
-
-static int
-virStorageSourceParseBackingJSONUriStr(virStorageSourcePtr src,
- const char *uri,
- int protocol)
-{
- int rc;
-
- if ((rc = virStorageSourceParseBackingURI(src, uri)) < 0)
- return -1;
-
- if (src->protocol != protocol) {
- virReportError(VIR_ERR_INVALID_ARG,
- _("expected protocol '%s' but got '%s' in URI
JSON volume "
- "definition"),
- virStorageNetProtocolTypeToString(protocol),
- virStorageNetProtocolTypeToString(src->protocol));
- return -1;
- }
-
- return rc;
-}
-
-
-static int
-virStorageSourceParseBackingJSONUriCookies(virStorageSourcePtr src,
- virJSONValuePtr json,
- const char *jsonstr)
-{
- const char *cookiestr;
- g_auto(GStrv) cookies = NULL;
- size_t ncookies = 0;
- size_t i;
-
- if (!virJSONValueObjectHasKey(json, "cookie"))
- return 0;
-
- if (!(cookiestr = virJSONValueObjectGetString(json, "cookie"))) {
- virReportError(VIR_ERR_INVALID_ARG,
- _("wrong format of 'cookie' field in backing store
definition '%s'"),
- jsonstr);
- return -1;
- }
-
- if (!(cookies = virStringSplitCount(cookiestr, ";", 0, &ncookies)))
- return -1;
-
- src->cookies = g_new0(virStorageNetCookieDefPtr, ncookies);
- src->ncookies = ncookies;
-
- for (i = 0; i < ncookies; i++) {
- char *cookiename = cookies[i];
- char *cookievalue;
-
- virSkipSpaces((const char **) &cookiename);
-
- if (!(cookievalue = strchr(cookiename, '='))) {
- virReportError(VIR_ERR_INVALID_ARG,
- _("malformed http cookie '%s' in backing store
definition '%s'"),
- cookies[i], jsonstr);
- return -1;
- }
-
- *cookievalue = '\0';
- cookievalue++;
-
- src->cookies[i] = g_new0(virStorageNetCookieDef, 1);
- src->cookies[i]->name = g_strdup(cookiename);
- src->cookies[i]->value = g_strdup(cookievalue);
- }
-
- return 0;
-}
-
-
-static int
-virStorageSourceParseBackingJSONUri(virStorageSourcePtr src,
- virJSONValuePtr json,
- const char *jsonstr,
- int protocol)
-{
- const char *uri;
-
- if (!(uri = virJSONValueObjectGetString(json, "url"))) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing 'url' in JSON backing volume
definition"));
- return -1;
- }
-
- if (protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS ||
- protocol == VIR_STORAGE_NET_PROTOCOL_FTPS) {
- if (virJSONValueObjectHasKey(json, "sslverify")) {
- const char *tmpstr;
- bool tmp;
-
- /* libguestfs still uses undocumented legacy value of 'off' */
- if ((tmpstr = virJSONValueObjectGetString(json, "sslverify"))
&&
- STREQ(tmpstr, "off")) {
- src->sslverify = VIR_TRISTATE_BOOL_NO;
- } else {
- if (virJSONValueObjectGetBoolean(json, "sslverify", &tmp)
< 0) {
- virReportError(VIR_ERR_INVALID_ARG,
- _("malformed 'sslverify' field in backing
store definition '%s'"),
- jsonstr);
- return -1;
- }
-
- src->sslverify = virTristateBoolFromBool(tmp);
- }
- }
- }
-
- if (protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS ||
- protocol == VIR_STORAGE_NET_PROTOCOL_HTTP) {
- if (virStorageSourceParseBackingJSONUriCookies(src, json, jsonstr) < 0)
- return -1;
- }
-
- if (virJSONValueObjectHasKey(json, "readahead") &&
- virJSONValueObjectGetNumberUlong(json, "readahead",
&src->readahead) < 0) {
- virReportError(VIR_ERR_INVALID_ARG,
- _("malformed 'readahead' field in backing store
definition '%s'"),
- jsonstr);
- return -1;
- }
-
- if (virJSONValueObjectHasKey(json, "timeout") &&
- virJSONValueObjectGetNumberUlong(json, "timeout", &src->timeout)
< 0) {
- virReportError(VIR_ERR_INVALID_ARG,
- _("malformed 'timeout' field in backing store
definition '%s'"),
- jsonstr);
- return -1;
- }
-
- return virStorageSourceParseBackingJSONUriStr(src, uri, protocol);
-}
-
-
-static int
-virStorageSourceParseBackingJSONInetSocketAddress(virStorageNetHostDefPtr host,
- virJSONValuePtr json)
-{
- const char *hostname;
- const char *port;
-
- if (!json) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing remote server specification in JSON "
- "backing volume definition"));
- return -1;
- }
-
- hostname = virJSONValueObjectGetString(json, "host");
- port = virJSONValueObjectGetString(json, "port");
-
- if (!hostname) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing hostname for tcp backing server in "
- "JSON backing volume definition"));
- return -1;
- }
-
- host->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
- host->name = g_strdup(hostname);
-
- if (virStringParsePort(port, &host->port) < 0)
- return -1;
-
- return 0;
-}
-
-
-static int
-virStorageSourceParseBackingJSONSocketAddress(virStorageNetHostDefPtr host,
- virJSONValuePtr json)
-{
- const char *type;
- const char *socket;
-
- if (!json) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing remote server specification in JSON "
- "backing volume definition"));
- return -1;
- }
-
- if (!(type = virJSONValueObjectGetString(json, "type"))) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing socket address type in "
- "JSON backing volume definition"));
- return -1;
- }
-
- if (STREQ(type, "tcp") || STREQ(type, "inet")) {
- return virStorageSourceParseBackingJSONInetSocketAddress(host, json);
-
- } else if (STREQ(type, "unix")) {
- host->transport = VIR_STORAGE_NET_HOST_TRANS_UNIX;
-
- socket = virJSONValueObjectGetString(json, "path");
-
- /* check for old spelling for gluster protocol */
- if (!socket)
- socket = virJSONValueObjectGetString(json, "socket");
-
- if (!socket) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing socket path for udp backing server in "
- "JSON backing volume definition"));
- return -1;
- }
-
- host->socket = g_strdup(socket);
- } else {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("backing store protocol '%s' is not yet
supported"),
- type);
- return -1;
- }
-
- return 0;
-}
-
-
-static int
-virStorageSourceParseBackingJSONGluster(virStorageSourcePtr src,
- virJSONValuePtr json,
- const char *jsonstr G_GNUC_UNUSED,
- int opaque G_GNUC_UNUSED)
-{
- const char *uri = virJSONValueObjectGetString(json, "filename");
- const char *volume = virJSONValueObjectGetString(json, "volume");
- const char *path = virJSONValueObjectGetString(json, "path");
- virJSONValuePtr server = virJSONValueObjectGetArray(json, "server");
- size_t nservers;
- size_t i;
-
- /* legacy URI based syntax passed via 'filename' option */
- if (uri)
- return virStorageSourceParseBackingJSONUriStr(src, uri,
- VIR_STORAGE_NET_PROTOCOL_GLUSTER);
-
- if (!volume || !path || !server) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing 'volume', 'path' or
'server' attribute in "
- "JSON backing definition for gluster volume"));
- return -1;
- }
-
- src->type = VIR_STORAGE_TYPE_NETWORK;
- src->protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER;
-
- src->volume = g_strdup(volume);
- src->path = g_strdup(path);
-
- nservers = virJSONValueArraySize(server);
- if (nservers == 0) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("at least 1 server is necessary in "
- "JSON backing definition for gluster volume"));
-
- return -1;
- }
-
- src->hosts = g_new0(virStorageNetHostDef, nservers);
- src->nhosts = nservers;
-
- for (i = 0; i < nservers; i++) {
- if (virStorageSourceParseBackingJSONSocketAddress(src->hosts + i,
- virJSONValueArrayGet(server,
i)) < 0)
- return -1;
- }
-
- return 0;
-}
-
-
-static int
-virStorageSourceParseBackingJSONiSCSI(virStorageSourcePtr src,
- virJSONValuePtr json,
- const char *jsonstr G_GNUC_UNUSED,
- int opaque G_GNUC_UNUSED)
-{
- const char *transport = virJSONValueObjectGetString(json, "transport");
- const char *portal = virJSONValueObjectGetString(json, "portal");
- const char *target = virJSONValueObjectGetString(json, "target");
- const char *lun = virJSONValueObjectGetStringOrNumber(json, "lun");
- const char *uri;
- char *port;
-
- /* legacy URI based syntax passed via 'filename' option */
- if ((uri = virJSONValueObjectGetString(json, "filename")))
- return virStorageSourceParseBackingJSONUriStr(src, uri,
- VIR_STORAGE_NET_PROTOCOL_ISCSI);
-
- src->type = VIR_STORAGE_TYPE_NETWORK;
- src->protocol = VIR_STORAGE_NET_PROTOCOL_ISCSI;
-
- if (!lun)
- lun = "0";
-
- src->hosts = g_new0(virStorageNetHostDef, 1);
- src->nhosts = 1;
-
- if (STRNEQ_NULLABLE(transport, "tcp")) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("only TCP transport is supported for iSCSI
volumes"));
- return -1;
- }
-
- src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
-
- if (!portal) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing 'portal' address in iSCSI backing
definition"));
- return -1;
- }
-
- if (!target) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing 'target' in iSCSI backing
definition"));
- return -1;
- }
-
- src->hosts->name = g_strdup(portal);
-
- if ((port = strrchr(src->hosts->name, ':')) &&
- !strchr(port, ']')) {
- if (virStringParsePort(port + 1, &src->hosts->port) < 0)
- return -1;
-
- *port = '\0';
- }
-
- src->path = g_strdup_printf("%s/%s", target, lun);
-
- /* Libvirt doesn't handle inline authentication. Make the caller aware. */
- if (virJSONValueObjectGetString(json, "user") ||
- virJSONValueObjectGetString(json, "password"))
- return 1;
-
- return 0;
-}
-
-
-static int
-virStorageSourceParseBackingJSONNbd(virStorageSourcePtr src,
- virJSONValuePtr json,
- const char *jsonstr G_GNUC_UNUSED,
- int opaque G_GNUC_UNUSED)
-{
- const char *path = virJSONValueObjectGetString(json, "path");
- const char *host = virJSONValueObjectGetString(json, "host");
- const char *port = virJSONValueObjectGetString(json, "port");
- const char *export = virJSONValueObjectGetString(json, "export");
- virJSONValuePtr server = virJSONValueObjectGetObject(json, "server");
-
- if (!path && !host && !server) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing host specification of NBD server in JSON "
- "backing volume definition"));
- return -1;
- }
-
- src->type = VIR_STORAGE_TYPE_NETWORK;
- src->protocol = VIR_STORAGE_NET_PROTOCOL_NBD;
-
- src->path = g_strdup(export);
-
- src->hosts = g_new0(virStorageNetHostDef, 1);
- src->nhosts = 1;
-
- if (server) {
- if (virStorageSourceParseBackingJSONSocketAddress(src->hosts, server) < 0)
- return -1;
- } else {
- if (path) {
- src->hosts[0].transport = VIR_STORAGE_NET_HOST_TRANS_UNIX;
- src->hosts[0].socket = g_strdup(path);
- } else {
- src->hosts[0].transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
- src->hosts[0].name = g_strdup(host);
-
- if (virStringParsePort(port, &src->hosts[0].port) < 0)
- return -1;
- }
- }
-
- return 0;
-}
-
-
-static int
-virStorageSourceParseBackingJSONSheepdog(virStorageSourcePtr src,
- virJSONValuePtr json,
- const char *jsonstr G_GNUC_UNUSED,
- int opaque G_GNUC_UNUSED)
-{
- const char *filename;
- const char *vdi = virJSONValueObjectGetString(json, "vdi");
- virJSONValuePtr server = virJSONValueObjectGetObject(json, "server");
-
- /* legacy URI based syntax passed via 'filename' option */
- if ((filename = virJSONValueObjectGetString(json, "filename"))) {
- if (strstr(filename, "://"))
- return virStorageSourceParseBackingJSONUriStr(src, filename,
-
VIR_STORAGE_NET_PROTOCOL_SHEEPDOG);
-
- /* libvirt doesn't implement a parser for the legacy non-URI syntax */
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing sheepdog URI in JSON backing volume
definition"));
- return -1;
- }
-
- src->type = VIR_STORAGE_TYPE_NETWORK;
- src->protocol = VIR_STORAGE_NET_PROTOCOL_SHEEPDOG;
-
- if (!vdi) {
- virReportError(VIR_ERR_INVALID_ARG, "%s", _("missing sheepdog vdi
name"));
- return -1;
- }
-
- src->path = g_strdup(vdi);
-
- src->hosts = g_new0(virStorageNetHostDef, 1);
- src->nhosts = 1;
-
- if (virStorageSourceParseBackingJSONSocketAddress(src->hosts, server) < 0)
- return -1;
-
- return 0;
-}
-
-
-static int
-virStorageSourceParseBackingJSONSSH(virStorageSourcePtr src,
- virJSONValuePtr json,
- const char *jsonstr G_GNUC_UNUSED,
- int opaque G_GNUC_UNUSED)
-{
- const char *path = virJSONValueObjectGetString(json, "path");
- const char *host = virJSONValueObjectGetString(json, "host");
- const char *port = virJSONValueObjectGetString(json, "port");
- const char *user = virJSONValueObjectGetString(json, "user");
- const char *host_key_check = virJSONValueObjectGetString(json,
"host_key_check");
- virJSONValuePtr server = virJSONValueObjectGetObject(json, "server");
-
- if (!(host || server) || !path) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing host/server or path of SSH JSON backing "
- "volume definition"));
- return -1;
- }
-
- src->type = VIR_STORAGE_TYPE_NETWORK;
- src->protocol = VIR_STORAGE_NET_PROTOCOL_SSH;
-
- src->path = g_strdup(path);
-
- src->hosts = g_new0(virStorageNetHostDef, 1);
- src->nhosts = 1;
-
- if (server) {
- if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts,
- server) < 0)
- return -1;
- } else {
- src->hosts[0].transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
- src->hosts[0].name = g_strdup(host);
-
- if (virStringParsePort(port, &src->hosts[0].port) < 0)
- return -1;
- }
-
- /* these two are parsed just to be passed back as we don't model them yet */
- src->ssh_user = g_strdup(user);
- if (STREQ_NULLABLE(host_key_check, "no"))
- src->ssh_host_key_check_disabled = true;
-
- return 0;
-}
-
-
-static int
-virStorageSourceParseBackingJSONRBD(virStorageSourcePtr src,
- virJSONValuePtr json,
- const char *jsonstr G_GNUC_UNUSED,
- int opaque G_GNUC_UNUSED)
-{
- const char *filename;
- const char *pool = virJSONValueObjectGetString(json, "pool");
- const char *image = virJSONValueObjectGetString(json, "image");
- const char *conf = virJSONValueObjectGetString(json, "conf");
- const char *snapshot = virJSONValueObjectGetString(json, "snapshot");
- virJSONValuePtr servers = virJSONValueObjectGetArray(json, "server");
- size_t nservers;
- size_t i;
-
- src->type = VIR_STORAGE_TYPE_NETWORK;
- src->protocol = VIR_STORAGE_NET_PROTOCOL_RBD;
-
- /* legacy syntax passed via 'filename' option */
- if ((filename = virJSONValueObjectGetString(json, "filename")))
- return virStorageSourceParseRBDColonString(filename, src);
-
- if (!pool || !image) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing pool or image name in ceph backing volume "
- "JSON specification"));
- return -1;
- }
-
- src->volume = g_strdup(pool);
- src->path = g_strdup(image);
- src->snapshot = g_strdup(snapshot);
- src->configFile = g_strdup(conf);
-
- if (servers) {
- nservers = virJSONValueArraySize(servers);
-
- src->hosts = g_new0(virStorageNetHostDef, nservers);
- src->nhosts = nservers;
-
- for (i = 0; i < nservers; i++) {
- if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts + i,
-
virJSONValueArrayGet(servers, i)) < 0)
- return -1;
- }
- }
-
- return 0;
-}
-
-static int
-virStorageSourceParseBackingJSONRaw(virStorageSourcePtr src,
- virJSONValuePtr json,
- const char *jsonstr,
- int opaque G_GNUC_UNUSED)
-{
- bool has_offset = virJSONValueObjectHasKey(json, "offset");
- bool has_size = virJSONValueObjectHasKey(json, "size");
- virJSONValuePtr file;
-
- if (has_offset || has_size) {
- src->sliceStorage = g_new0(virStorageSourceSlice, 1);
-
- if (has_offset &&
- virJSONValueObjectGetNumberUlong(json, "offset",
&src->sliceStorage->offset) < 0) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("malformed 'offset' property of 'raw'
driver"));
- return -1;
- }
-
- if (has_size &&
- virJSONValueObjectGetNumberUlong(json, "size",
&src->sliceStorage->size) < 0) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("malformed 'size' property of 'raw'
driver"));
- return -1;
- }
- }
-
- /* 'raw' is a format driver so it can have protocol driver children */
- if (!(file = virJSONValueObjectGetObject(json, "file"))) {
- virReportError(VIR_ERR_INVALID_ARG,
- _("JSON backing volume definition '%s' lacks
'file' object"),
- jsonstr);
- return -1;
- }
-
- return virStorageSourceParseBackingJSONInternal(src, file, jsonstr, false);
-}
-
-
-static int
-virStorageSourceParseBackingJSONVxHS(virStorageSourcePtr src,
- virJSONValuePtr json,
- const char *jsonstr G_GNUC_UNUSED,
- int opaque G_GNUC_UNUSED)
-{
- const char *vdisk_id = virJSONValueObjectGetString(json, "vdisk-id");
- virJSONValuePtr server = virJSONValueObjectGetObject(json, "server");
-
- if (!vdisk_id || !server) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing 'vdisk-id' or 'server' attribute
in "
- "JSON backing definition for VxHS volume"));
- return -1;
- }
-
- src->type = VIR_STORAGE_TYPE_NETWORK;
- src->protocol = VIR_STORAGE_NET_PROTOCOL_VXHS;
-
- src->path = g_strdup(vdisk_id);
-
- src->hosts = g_new0(virStorageNetHostDef, 1);
- src->nhosts = 1;
-
- if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts,
- server) < 0)
- return -1;
-
- return 0;
-}
-
-
-static int
-virStorageSourceParseBackingJSONNFS(virStorageSourcePtr src,
- virJSONValuePtr json,
- const char *jsonstr G_GNUC_UNUSED,
- int opaque G_GNUC_UNUSED)
-{
- virJSONValuePtr server = virJSONValueObjectGetObject(json, "server");
- int uidStore = -1;
- int gidStore = -1;
- int gotUID = virJSONValueObjectGetNumberInt(json, "user", &uidStore);
- int gotGID = virJSONValueObjectGetNumberInt(json, "group", &gidStore);
-
- if (!server) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing 'server' attribute in JSON backing
definition for NFS volume"));
- return -1;
- }
-
- if (gotUID < 0 || gotGID < 0) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing 'user' or 'group' attribute in
JSON backing definition for NFS volume"));
- return -1;
- }
-
- src->path = g_strdup(virJSONValueObjectGetString(json, "path"));
- if (!src->path) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing 'path' attribute in JSON backing
definition for NFS volume"));
- return -1;
- }
-
- src->nfs_user = g_strdup_printf("+%d", uidStore);
- src->nfs_group = g_strdup_printf("+%d", gidStore);
-
- src->type = VIR_STORAGE_TYPE_NETWORK;
- src->protocol = VIR_STORAGE_NET_PROTOCOL_NFS;
-
- src->hosts = g_new0(virStorageNetHostDef, 1);
- src->nhosts = 1;
-
- if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts,
- server) < 0)
- return -1;
-
- return 0;
-}
-
-
-static int
-virStorageSourceParseBackingJSONNVMe(virStorageSourcePtr src,
- virJSONValuePtr json,
- const char *jsonstr G_GNUC_UNUSED,
- int opaque G_GNUC_UNUSED)
-{
- g_autoptr(virStorageSourceNVMeDef) nvme = g_new0(virStorageSourceNVMeDef, 1);
- const char *device = virJSONValueObjectGetString(json, "device");
-
- if (!device || virPCIDeviceAddressParse((char *) device, &nvme->pciAddr) <
0) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing or malformed 'device' field of
'nvme' storage"));
- return -1;
- }
-
- if (virJSONValueObjectGetNumberUlong(json, "namespace",
&nvme->namespc) < 0 ||
- nvme->namespc == 0) {
- virReportError(VIR_ERR_INVALID_ARG, "%s",
- _("missing or malformed 'namespace' field of
'nvme' storage"));
- return -1;
- }
-
- src->type = VIR_STORAGE_TYPE_NVME;
- src->nvme = g_steal_pointer(&nvme);
-
- return 0;
-}
-
-
-struct virStorageSourceJSONDriverParser {
- const char *drvname;
- bool formatdriver;
- /**
- * The callback gets a pre-allocated storage source @src and the JSON
- * object to parse. The callback shall return -1 on error and report error
- * 0 on success and 1 in cases when the configuration itself is valid, but
- * can't be converted to libvirt's configuration (e.g. inline authentication
- * credentials are present).
- */
- int (*func)(virStorageSourcePtr src, virJSONValuePtr json, const char *jsonstr, int
opaque);
- int opaque;
-};
-
-static const struct virStorageSourceJSONDriverParser jsonParsers[] = {
- {"file", false, virStorageSourceParseBackingJSONPath,
VIR_STORAGE_TYPE_FILE},
- {"host_device", false, virStorageSourceParseBackingJSONPath,
VIR_STORAGE_TYPE_BLOCK},
- {"host_cdrom", false, virStorageSourceParseBackingJSONPath,
VIR_STORAGE_TYPE_BLOCK},
- {"http", false, virStorageSourceParseBackingJSONUri,
VIR_STORAGE_NET_PROTOCOL_HTTP},
- {"https", false, virStorageSourceParseBackingJSONUri,
VIR_STORAGE_NET_PROTOCOL_HTTPS},
- {"ftp", false, virStorageSourceParseBackingJSONUri,
VIR_STORAGE_NET_PROTOCOL_FTP},
- {"ftps", false, virStorageSourceParseBackingJSONUri,
VIR_STORAGE_NET_PROTOCOL_FTPS},
- {"tftp", false, virStorageSourceParseBackingJSONUri,
VIR_STORAGE_NET_PROTOCOL_TFTP},
- {"gluster", false, virStorageSourceParseBackingJSONGluster, 0},
- {"iscsi", false, virStorageSourceParseBackingJSONiSCSI, 0},
- {"nbd", false, virStorageSourceParseBackingJSONNbd, 0},
- {"sheepdog", false, virStorageSourceParseBackingJSONSheepdog, 0},
- {"ssh", false, virStorageSourceParseBackingJSONSSH, 0},
- {"rbd", false, virStorageSourceParseBackingJSONRBD, 0},
- {"raw", true, virStorageSourceParseBackingJSONRaw, 0},
- {"nfs", false, virStorageSourceParseBackingJSONNFS, 0},
- {"vxhs", false, virStorageSourceParseBackingJSONVxHS, 0},
- {"nvme", false, virStorageSourceParseBackingJSONNVMe, 0},
-};
-
-
-
-static int
-virStorageSourceParseBackingJSONInternal(virStorageSourcePtr src,
- virJSONValuePtr json,
- const char *jsonstr,
- bool allowformat)
-{
- const char *drvname;
- size_t i;
-
- if (!(drvname = virJSONValueObjectGetString(json, "driver"))) {
- virReportError(VIR_ERR_INVALID_ARG,
- _("JSON backing volume definition '%s' lacks driver
name"),
- jsonstr);
- return -1;
- }
-
- for (i = 0; i < G_N_ELEMENTS(jsonParsers); i++) {
- if (STRNEQ(drvname, jsonParsers[i].drvname))
- continue;
-
- if (jsonParsers[i].formatdriver && !allowformat) {
- virReportError(VIR_ERR_INVALID_ARG,
- _("JSON backing volume definition '%s' must not
have nested format drivers"),
- jsonstr);
- return -1;
- }
-
- return jsonParsers[i].func(src, json, jsonstr, jsonParsers[i].opaque);
- }
-
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("missing parser implementation for JSON backing volume "
- "driver '%s'"), drvname);
- return -1;
-}
-
-
-static int
-virStorageSourceParseBackingJSON(virStorageSourcePtr src,
- const char *json)
-{
- g_autoptr(virJSONValue) root = NULL;
- g_autoptr(virJSONValue) deflattened = NULL;
- virJSONValuePtr file = NULL;
-
- if (!(root = virJSONValueFromString(json)))
- return -1;
-
- if (!(deflattened = virJSONValueObjectDeflatten(root)))
- return -1;
-
- /* There are 2 possible syntaxes:
- * 1) json:{"file":{"driver":...}}
- * 2) json:{"driver":...}
- * Remove the 'file' wrapper object in case 1.
- */
- if (!virJSONValueObjectHasKey(deflattened, "driver"))
- file = virJSONValueObjectGetObject(deflattened, "file");
-
- if (!file)
- file = deflattened;
-
- return virStorageSourceParseBackingJSONInternal(src, file, json, true);
-}
-
-
-/**
- * virStorageSourceNewFromBackingAbsolute
- * @path: string representing absolute location of a storage source
- * @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 (virStorageIsFile(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 (virStorageIsRelative(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,
@@ -3414,76 +1644,6 @@ virStorageFileCanonicalizePath(const char *path,
}
-/**
- * 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
@@ -3636,736 +1796,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 (virStorageFileProbeGetMetadata(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 (virStorageFileProbeGetMetadata(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 27ac6a493f..d6f194a7dd 100644
--- a/src/util/virstoragefile.h
+++ b/src/util/virstoragefile.h
@@ -388,18 +388,6 @@ struct _virStorageSource {
G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageSource, virObjectUnref);
-#ifndef DEV_BSIZE
-# define DEV_BSIZE 512
-#endif
-
-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)
@@ -410,13 +398,6 @@ int virStorageFileParseBackingStoreStr(const char *str,
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);
-
bool virStorageIsFile(const char *path);
bool virStorageIsRelative(const char *backing);
@@ -469,15 +450,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);
@@ -488,10 +460,6 @@ 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,
@@ -500,14 +468,6 @@ 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);
void
@@ -542,39 +502,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 23255dd62a..0de0783839 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..ddaf73359d 100644
--- a/tests/qemublocktest.c
+++ b/tests/qemublocktest.c
@@ -17,6 +17,7 @@
#include <config.h>
+#include "storage_source.h"
#include "testutils.h"
#include "testutilsqemu.h"
#include "testutilsqemuschema.h"
diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c
index 86c7cd910c..5c2f211557 100644
--- a/tests/virstoragetest.c
+++ b/tests/virstoragetest.c
@@ -20,6 +20,7 @@
#include <unistd.h>
+#include "storage_source.h"
#include "testutils.h"
#include "vircommand.h"
#include "virerror.h"
--
2.29.2