[libvirt PATCH 00/17] split and cleanup virStorageSource code

The series is a prerequisite for implementing vhost-user-blk-pci. I wanted to use already existing code to parse and format virDomainChrSourceDefPtr as the vhostuser <disk> source similarly like we do for <interface> but it was not possible because the virStorageSource definition was living in util directory where we don't allow including anything from conf directory. Pavel Hrdina (17): util: remove unused virStorageGenerateQcowPassphrase virstoragefile: remove unused virStorageFileChainCheckBroken util: move virQEMUBuildQemuImgKeySecretOpts into storage util: move virStorageFileGetLVMKey to locking util: move virStorageFileCheckCompat into conf virfile: refactor virFileNBDDeviceAssociate virstoragefile: move virStorageFileResize into virfile virstoragefile: move virStorageFileIsClusterFS into virfile virstoragefile: move virStorageIsFile into virfile virstoragefile: move virStorageIsRelative into virfile virstoragefile: change virStorageSource->drv to void pointer storage: move storage file sources to separate directory util: extract virStorageFile code into storage_file util: move virStorageFileBackend code into storage_file util: move virStorageSource code into conf util: move virStorageEncryption code into conf storage_file: use virStorageFile prefix for all functions po/POTFILES.in | 10 +- src/conf/backup_conf.c | 2 +- src/conf/checkpoint_conf.c | 2 +- src/conf/domain_conf.c | 2 +- src/conf/domain_conf.h | 6 +- src/conf/meson.build | 2 + src/conf/snapshot_conf.c | 2 +- src/conf/storage_conf.c | 25 +- src/conf/storage_conf.h | 4 +- .../storage_encryption.c} | 38 +- .../storage_encryption.h} | 4 +- src/conf/storage_source.c | 1316 +++++ src/conf/storage_source.h | 518 ++ src/driver.c | 1 + src/esx/esx_storage_backend_iscsi.c | 2 +- src/esx/esx_storage_backend_vmfs.c | 2 +- src/libvirt_private.syms | 200 +- src/libxl/meson.build | 1 + src/libxl/xen_xl.c | 3 +- src/locking/lock_driver_lockd.c | 67 +- src/lxc/lxc_controller.c | 4 +- src/meson.build | 1 + src/qemu/meson.build | 1 + src/qemu/qemu_backup.c | 3 +- src/qemu/qemu_block.c | 3 +- src/qemu/qemu_blockjob.c | 2 +- src/qemu/qemu_command.c | 2 +- src/qemu/qemu_domain.c | 19 +- src/qemu/qemu_driver.c | 9 +- src/qemu/qemu_hotplug.c | 3 +- src/qemu/qemu_interop_config.c | 1 + src/qemu/qemu_migration.c | 4 +- src/qemu/qemu_process.c | 1 + src/qemu/qemu_process.h | 2 +- src/qemu/qemu_shim.c | 1 + src/qemu/qemu_snapshot.c | 3 +- src/storage/meson.build | 34 +- src/storage/parthelper.c | 1 + src/storage/storage_backend.c | 2 +- src/storage/storage_backend_gluster.c | 1 + src/storage/storage_util.c | 84 +- src/storage_file/meson.build | 60 + src/storage_file/storage_file.c | 3845 ++++++++++++ src/storage_file/storage_file.h | 197 + .../storage_file_backend.c} | 4 +- .../storage_file_backend.h} | 6 +- .../storage_file_fs.c | 23 +- .../storage_file_fs.h | 0 .../storage_file_gluster.c | 34 +- .../storage_file_gluster.h | 0 src/util/meson.build | 2 - src/util/virarptable.c | 1 + src/util/vircgroupv1.c | 1 + src/util/vircgroupv2devices.c | 1 + src/util/virfile.c | 101 +- src/util/virfile.h | 12 +- src/util/virpidfile.c | 1 + src/util/virqemu.c | 69 - src/util/virqemu.h | 6 - src/util/virresctrl.c | 1 + src/util/virstoragefile.c | 5222 +---------------- src/util/virstoragefile.h | 572 +- src/util/virsysinfo.c | 1 + src/util/virtpm.c | 1 + tests/meson.build | 4 +- tests/qemublocktest.c | 7 +- tests/virstoragetest.c | 9 +- tools/virsh-console.c | 1 + tools/virsh-util.c | 1 + 69 files changed, 6441 insertions(+), 6129 deletions(-) rename src/{util/virstorageencryption.c => conf/storage_encryption.c} (90%) rename src/{util/virstorageencryption.h => conf/storage_encryption.h} (96%) create mode 100644 src/conf/storage_source.c create mode 100644 src/conf/storage_source.h create mode 100644 src/storage_file/meson.build create mode 100644 src/storage_file/storage_file.c create mode 100644 src/storage_file/storage_file.h rename src/{util/virstoragefilebackend.c => storage_file/storage_file_backend.c} (97%) rename src/{util/virstoragefilebackend.h => storage_file/storage_file_backend.h} (93%) rename src/{storage => storage_file}/storage_file_fs.c (90%) rename src/{storage => storage_file}/storage_file_fs.h (100%) rename src/{storage => storage_file}/storage_file_gluster.c (89%) rename src/{storage => storage_file}/storage_file_gluster.h (100%) -- 2.28.0

The last user was removed by commit <40f0e0348dfc84f28a500e262c4953b0d3b44fa0>. Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 1 - src/util/virstorageencryption.c | 34 --------------------------------- src/util/virstorageencryption.h | 2 -- 3 files changed, 37 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index d0e548b0a3..8e1d101a97 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3117,7 +3117,6 @@ virSocketAddrSetPort; virStorageEncryptionFormat; virStorageEncryptionFree; virStorageEncryptionParseNode; -virStorageGenerateQcowPassphrase; # util/virstoragefile.h diff --git a/src/util/virstorageencryption.c b/src/util/virstorageencryption.c index a330b79637..c893f0babe 100644 --- a/src/util/virstorageencryption.c +++ b/src/util/virstorageencryption.c @@ -364,37 +364,3 @@ virStorageEncryptionFormat(virBufferPtr buf, return 0; } - -int -virStorageGenerateQcowPassphrase(unsigned char *dest) -{ - int fd; - size_t i; - - /* A qcow passphrase is up to 16 bytes, with any data following a NUL - ignored. Prohibit control and non-ASCII characters to avoid possible - unpleasant surprises with the qemu monitor input mechanism. */ - fd = open("/dev/urandom", O_RDONLY); - if (fd < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Cannot open /dev/urandom")); - return -1; - } - i = 0; - while (i < VIR_STORAGE_QCOW_PASSPHRASE_SIZE) { - ssize_t r; - - while ((r = read(fd, dest + i, 1)) == -1 && errno == EINTR) - ; - if (r <= 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Cannot read from /dev/urandom")); - VIR_FORCE_CLOSE(fd); - return -1; - } - if (dest[i] >= 0x20 && dest[i] <= 0x7E) - i++; /* Got an acceptable character */ - } - VIR_FORCE_CLOSE(fd); - return 0; -} diff --git a/src/util/virstorageencryption.h b/src/util/virstorageencryption.h index 05a7bffdfc..352dd373d6 100644 --- a/src/util/virstorageencryption.h +++ b/src/util/virstorageencryption.h @@ -90,5 +90,3 @@ int virStorageEncryptionFormat(virBufferPtr buf, enum { VIR_STORAGE_QCOW_PASSPHRASE_SIZE = 16 }; - -int virStorageGenerateQcowPassphrase(unsigned char *dest); -- 2.28.0

On Mon, Dec 14, 2020 at 16:55:21 +0100, Pavel Hrdina wrote:
The last user was removed by commit <40f0e0348dfc84f28a500e262c4953b0d3b44fa0>.
Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 1 - src/util/virstorageencryption.c | 34 --------------------------------- src/util/virstorageencryption.h | 2 -- 3 files changed, 37 deletions(-)
Reviewed-by: Peter Krempa <pkrempa@redhat.com>

The last usage outside of tests was removed by commit <780f8c94ca8b3dee7eb59c1bfbc32f672f965df8>. Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 1 - src/util/virstoragefile.c | 33 --------------------------------- src/util/virstoragefile.h | 3 --- tests/virstoragetest.c | 6 ------ 4 files changed, 43 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 8e1d101a97..8f55ba515c 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3126,7 +3126,6 @@ virStorageAuthDefFree; virStorageAuthDefParse; virStorageFileAccess; virStorageFileCanonicalizePath; -virStorageFileChainGetBroken; virStorageFileChainLookup; virStorageFileChown; virStorageFileCreate; diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index fac93118fd..119342d296 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -1193,39 +1193,6 @@ virStorageFileGetMetadataFromFD(const char *path, } -/** - * virStorageFileChainCheckBroken - * - * If CHAIN is broken, set *brokenFile to the broken file name, - * otherwise set it to NULL. Caller MUST free *brokenFile after use. - * Return 0 on success (including when brokenFile is set), negative on - * error (allocation failure). - */ -int -virStorageFileChainGetBroken(virStorageSourcePtr chain, - char **brokenFile) -{ - virStorageSourcePtr tmp; - - *brokenFile = NULL; - - if (!chain) - return 0; - - for (tmp = chain; virStorageSourceIsBacking(tmp); tmp = tmp->backingStore) { - /* Break when we hit end of chain; report error if we detected - * a missing backing file, infinite loop, or other error */ - if (!tmp->backingStore && tmp->backingStoreRaw) { - *brokenFile = g_strdup(tmp->backingStoreRaw); - - return 0; - } - } - - return 0; -} - - /** * virStorageFileResize: * diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 87763cf389..cc9bd4875f 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -403,9 +403,6 @@ virStorageSourcePtr virStorageFileGetMetadataFromBuf(const char *path, size_t len, int format) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); -int virStorageFileChainGetBroken(virStorageSourcePtr chain, - char **broken_file); - int virStorageFileParseChainIndex(const char *diskTarget, const char *name, unsigned int *chainIndex) diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c index 2e466ecb99..a376154def 100644 --- a/tests/virstoragetest.c +++ b/tests/virstoragetest.c @@ -270,7 +270,6 @@ testStorageChain(const void *args) virStorageSourcePtr elt; size_t i = 0; g_autoptr(virStorageSource) meta = NULL; - g_autofree char *broken = NULL; meta = testStorageFileGetMetadata(data->start, data->format, -1, -1); if (!meta) { @@ -289,11 +288,6 @@ testStorageChain(const void *args) return -1; } - if (virStorageFileChainGetBroken(meta, &broken) || broken) { - fprintf(stderr, "chain should not be identified as broken\n"); - return -1; - } - elt = meta; while (virStorageSourceIsBacking(elt)) { g_autofree char *expect = NULL; -- 2.28.0

On Mon, Dec 14, 2020 at 16:55:22 +0100, Pavel Hrdina wrote:
The last usage outside of tests was removed by commit <780f8c94ca8b3dee7eb59c1bfbc32f672f965df8>.
Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 1 - src/util/virstoragefile.c | 33 --------------------------------- src/util/virstoragefile.h | 3 --- tests/virstoragetest.c | 6 ------ 4 files changed, 43 deletions(-)
Reviewed-by: Peter Krempa <pkrempa@redhat.com>

Function virQEMUBuildQemuImgKeySecretOpts is not used anywhere else so there is no need to have it in util. Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 1 - src/storage/storage_util.c | 74 ++++++++++++++++++++++++++++++++++++-- src/util/virqemu.c | 69 ----------------------------------- src/util/virqemu.h | 6 ---- 4 files changed, 72 insertions(+), 78 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 8f55ba515c..aecc5cd654 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2953,7 +2953,6 @@ virQEMUBuildCommandLineJSONArrayNumbered; virQEMUBuildDriveCommandlineFromJSON; virQEMUBuildNetdevCommandlineFromJSON; virQEMUBuildObjectCommandlineFromJSON; -virQEMUBuildQemuImgKeySecretOpts; # util/virrandom.h diff --git a/src/storage/storage_util.c b/src/storage/storage_util.c index cf1f33f177..9b9f8b4f13 100644 --- a/src/storage/storage_util.c +++ b/src/storage/storage_util.c @@ -693,6 +693,74 @@ struct _virStorageBackendQemuImgInfo { }; +/** + * storageBackendBuildQemuImgEncriptionOpts: + * @buf: buffer to build the string into + * @encinfo: pointer to encryption info + * @alias: alias to use + * + * Generate the string for id=$alias and any encryption options for + * into the buffer. + * + * Important note, a trailing comma (",") is built into the return since + * it's expected other arguments are appended after the id=$alias string. + * So either turn something like: + * + * "key-secret=$alias," + * + * or + * "key-secret=$alias,cipher-alg=twofish-256,cipher-mode=cbc, + * hash-alg=sha256,ivgen-alg=plain64,igven-hash-alg=sha256," + * + */ +static void +storageBackendBuildQemuImgEncriptionOpts(virBufferPtr buf, + int format, + virStorageEncryptionInfoDefPtr encinfo, + const char *alias) +{ + const char *encprefix; + + if (format == VIR_STORAGE_FILE_QCOW2) { + virBufferAddLit(buf, "encrypt.format=luks,"); + encprefix = "encrypt."; + } else { + encprefix = ""; + } + + virBufferAsprintf(buf, "%skey-secret=%s,", encprefix, alias); + + if (!encinfo->cipher_name) + return; + + virBufferAsprintf(buf, "%scipher-alg=", encprefix); + virQEMUBuildBufferEscapeComma(buf, encinfo->cipher_name); + virBufferAsprintf(buf, "-%u,", encinfo->cipher_size); + if (encinfo->cipher_mode) { + virBufferAsprintf(buf, "%scipher-mode=", encprefix); + virQEMUBuildBufferEscapeComma(buf, encinfo->cipher_mode); + virBufferAddLit(buf, ","); + } + if (encinfo->cipher_hash) { + virBufferAsprintf(buf, "%shash-alg=", encprefix); + virQEMUBuildBufferEscapeComma(buf, encinfo->cipher_hash); + virBufferAddLit(buf, ","); + } + if (!encinfo->ivgen_name) + return; + + virBufferAsprintf(buf, "%sivgen-alg=", encprefix); + virQEMUBuildBufferEscapeComma(buf, encinfo->ivgen_name); + virBufferAddLit(buf, ","); + + if (encinfo->ivgen_hash) { + virBufferAsprintf(buf, "%sivgen-hash-alg=", encprefix); + virQEMUBuildBufferEscapeComma(buf, encinfo->ivgen_hash); + virBufferAddLit(buf, ","); + } +} + + static int storageBackendCreateQemuImgOpts(virStorageEncryptionInfoDefPtr encinfo, char **opts, @@ -704,8 +772,10 @@ storageBackendCreateQemuImgOpts(virStorageEncryptionInfoDefPtr encinfo, virBufferAsprintf(&buf, "backing_fmt=%s,", virStorageFileFormatTypeToString(info->backingFormat)); - if (encinfo) - virQEMUBuildQemuImgKeySecretOpts(&buf, info->format, encinfo, info->secretAlias); + if (encinfo) { + storageBackendBuildQemuImgEncriptionOpts(&buf, info->format, encinfo, + info->secretAlias); + } if (info->preallocate) { if (info->size_arg > info->allocation) diff --git a/src/util/virqemu.c b/src/util/virqemu.c index 5405c9eac9..c4b6e8b3db 100644 --- a/src/util/virqemu.c +++ b/src/util/virqemu.c @@ -28,7 +28,6 @@ #include "virqemu.h" #include "virstring.h" #include "viralloc.h" -#include "virstoragefile.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -384,71 +383,3 @@ virQEMUBuildBufferEscapeComma(virBufferPtr buf, const char *str) { virBufferEscape(buf, ',', ",", "%s", str); } - - -/** - * virQEMUBuildQemuImgKeySecretOpts: - * @buf: buffer to build the string into - * @encinfo: pointer to encryption info - * @alias: alias to use - * - * Generate the string for id=$alias and any encryption options for - * into the buffer. - * - * Important note, a trailing comma (",") is built into the return since - * it's expected other arguments are appended after the id=$alias string. - * So either turn something like: - * - * "key-secret=$alias," - * - * or - * "key-secret=$alias,cipher-alg=twofish-256,cipher-mode=cbc, - * hash-alg=sha256,ivgen-alg=plain64,igven-hash-alg=sha256," - * - */ -void -virQEMUBuildQemuImgKeySecretOpts(virBufferPtr buf, - int format, - virStorageEncryptionInfoDefPtr encinfo, - const char *alias) -{ - const char *encprefix; - - if (format == VIR_STORAGE_FILE_QCOW2) { - virBufferAddLit(buf, "encrypt.format=luks,"); - encprefix = "encrypt."; - } else { - encprefix = ""; - } - - virBufferAsprintf(buf, "%skey-secret=%s,", encprefix, alias); - - if (!encinfo->cipher_name) - return; - - virBufferAsprintf(buf, "%scipher-alg=", encprefix); - virQEMUBuildBufferEscapeComma(buf, encinfo->cipher_name); - virBufferAsprintf(buf, "-%u,", encinfo->cipher_size); - if (encinfo->cipher_mode) { - virBufferAsprintf(buf, "%scipher-mode=", encprefix); - virQEMUBuildBufferEscapeComma(buf, encinfo->cipher_mode); - virBufferAddLit(buf, ","); - } - if (encinfo->cipher_hash) { - virBufferAsprintf(buf, "%shash-alg=", encprefix); - virQEMUBuildBufferEscapeComma(buf, encinfo->cipher_hash); - virBufferAddLit(buf, ","); - } - if (!encinfo->ivgen_name) - return; - - virBufferAsprintf(buf, "%sivgen-alg=", encprefix); - virQEMUBuildBufferEscapeComma(buf, encinfo->ivgen_name); - virBufferAddLit(buf, ","); - - if (encinfo->ivgen_hash) { - virBufferAsprintf(buf, "%sivgen-hash-alg=", encprefix); - virQEMUBuildBufferEscapeComma(buf, encinfo->ivgen_hash); - virBufferAddLit(buf, ","); - } -} diff --git a/src/util/virqemu.h b/src/util/virqemu.h index 2b33968158..b81efc765f 100644 --- a/src/util/virqemu.h +++ b/src/util/virqemu.h @@ -25,7 +25,6 @@ #include "internal.h" #include "virbuffer.h" #include "virjson.h" -#include "virstorageencryption.h" typedef int (*virQEMUBuildCommandLineJSONArrayFormatFunc)(const char *key, virJSONValuePtr array, @@ -59,8 +58,3 @@ int virQEMUBuildObjectCommandlineFromJSON(virBufferPtr buf, char *virQEMUBuildDriveCommandlineFromJSON(virJSONValuePtr src); void virQEMUBuildBufferEscapeComma(virBufferPtr buf, const char *str); -void virQEMUBuildQemuImgKeySecretOpts(virBufferPtr buf, - int format, - virStorageEncryptionInfoDefPtr enc, - const char *alias) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4); -- 2.28.0

On Mon, Dec 14, 2020 at 16:55:23 +0100, Pavel Hrdina wrote:
Function virQEMUBuildQemuImgKeySecretOpts is not used anywhere else so there is no need to have it in util.
Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 1 - src/storage/storage_util.c | 74 ++++++++++++++++++++++++++++++++++++-- src/util/virqemu.c | 69 ----------------------------------- src/util/virqemu.h | 6 ---- 4 files changed, 72 insertions(+), 78 deletions(-)
Additionally, with blockdev-create in qemu it will not be used for any image creation while the VM is running. Reviewed-by: Peter Krempa <pkrempa@redhat.com>

The function doesn't take virStorageSource as argument and has nothing in common with virStorageSource or storage file. Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 1 - src/locking/lock_driver_lockd.c | 66 ++++++++++++++++++++++++++++++++- src/util/virstoragefile.c | 62 ------------------------------- src/util/virstoragefile.h | 2 - 4 files changed, 65 insertions(+), 66 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index aecc5cd654..d04b8f5e0c 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3134,7 +3134,6 @@ virStorageFileFeatureTypeToString; virStorageFileFormatTypeFromString; virStorageFileFormatTypeToString; virStorageFileGetBackingStoreStr; -virStorageFileGetLVMKey; virStorageFileGetMetadata; virStorageFileGetMetadataFromBuf; virStorageFileGetMetadataFromFD; diff --git a/src/locking/lock_driver_lockd.c b/src/locking/lock_driver_lockd.c index 15d9e5f076..b8c2121e78 100644 --- a/src/locking/lock_driver_lockd.c +++ b/src/locking/lock_driver_lockd.c @@ -24,6 +24,7 @@ #include "lock_driver.h" #include "virconf.h" #include "viralloc.h" +#include "vircommand.h" #include "vircrypto.h" #include "virlog.h" #include "viruuid.h" @@ -455,6 +456,69 @@ static int virLockManagerLockDaemonNew(virLockManagerPtr lock, } +#ifdef LVS +static int virLockManagerGetLVMKey(const char *path, + char **key) +{ + /* + * # lvs --noheadings --unbuffered --nosuffix --options "uuid" LVNAME + * 06UgP5-2rhb-w3Bo-3mdR-WeoL-pytO-SAa2ky + */ + int status; + int ret = -1; + g_autoptr(virCommand) cmd = NULL; + + cmd = virCommandNewArgList(LVS, "--noheadings", + "--unbuffered", "--nosuffix", + "--options", "uuid", path, + NULL + ); + *key = NULL; + + /* Run the program and capture its output */ + virCommandSetOutputBuffer(cmd, key); + if (virCommandRun(cmd, &status) < 0) + goto cleanup; + + /* Explicitly check status == 0, rather than passing NULL + * to virCommandRun because we don't want to raise an actual + * error in this scenario, just return a NULL key. + */ + + if (status == 0 && *key) { + char *nl; + char *tmp = *key; + + /* Find first non-space character */ + while (*tmp && g_ascii_isspace(*tmp)) + tmp++; + /* Kill leading spaces */ + if (tmp != *key) + memmove(*key, tmp, strlen(tmp)+1); + + /* Kill trailing newline */ + if ((nl = strchr(*key, '\n'))) + *nl = '\0'; + } + + ret = 0; + + cleanup: + if (*key && STREQ(*key, "")) + VIR_FREE(*key); + + return ret; +} +#else +static int virLockManagerGetLVMKey(const char *path, + char **key G_GNUC_UNUSED) +{ + virReportSystemError(ENOSYS, _("Unable to get LVM key for %s"), path); + return -1; +} +#endif + + static int virLockManagerLockDaemonAddResource(virLockManagerPtr lock, unsigned int type, const char *name, @@ -494,7 +558,7 @@ static int virLockManagerLockDaemonAddResource(virLockManagerPtr lock, if (STRPREFIX(name, "/dev") && driver->lvmLockSpaceDir) { VIR_DEBUG("Trying to find an LVM UUID for %s", name); - if (virStorageFileGetLVMKey(name, &newName) < 0) + if (virLockManagerGetLVMKey(name, &newName) < 0) goto cleanup; if (newName) { diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 119342d296..bc342cabe3 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -1251,68 +1251,6 @@ int virStorageFileIsClusterFS(const char *path) VIR_FILE_SHFS_CEPH); } -#ifdef LVS -int virStorageFileGetLVMKey(const char *path, - char **key) -{ - /* - * # lvs --noheadings --unbuffered --nosuffix --options "uuid" LVNAME - * 06UgP5-2rhb-w3Bo-3mdR-WeoL-pytO-SAa2ky - */ - int status; - int ret = -1; - g_autoptr(virCommand) cmd = NULL; - - cmd = virCommandNewArgList(LVS, "--noheadings", - "--unbuffered", "--nosuffix", - "--options", "uuid", path, - NULL - ); - *key = NULL; - - /* Run the program and capture its output */ - virCommandSetOutputBuffer(cmd, key); - if (virCommandRun(cmd, &status) < 0) - goto cleanup; - - /* Explicitly check status == 0, rather than passing NULL - * to virCommandRun because we don't want to raise an actual - * error in this scenario, just return a NULL key. - */ - - if (status == 0 && *key) { - char *nl; - char *tmp = *key; - - /* Find first non-space character */ - while (*tmp && g_ascii_isspace(*tmp)) - tmp++; - /* Kill leading spaces */ - if (tmp != *key) - memmove(*key, tmp, strlen(tmp)+1); - - /* Kill trailing newline */ - if ((nl = strchr(*key, '\n'))) - *nl = '\0'; - } - - ret = 0; - - cleanup: - if (*key && STREQ(*key, "")) - VIR_FREE(*key); - - return ret; -} -#else -int virStorageFileGetLVMKey(const char *path, - char **key G_GNUC_UNUSED) -{ - virReportSystemError(ENOSYS, _("Unable to get LVM key for %s"), path); - return -1; -} -#endif - #ifdef WITH_UDEV /* virStorageFileGetSCSIKey * @path: Path to the SCSI device diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index cc9bd4875f..31feb22f26 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -428,8 +428,6 @@ int virStorageFileIsClusterFS(const char *path); bool virStorageIsFile(const char *path); bool virStorageIsRelative(const char *backing); -int virStorageFileGetLVMKey(const char *path, - char **key); int virStorageFileGetSCSIKey(const char *path, char **key, bool ignoreError); -- 2.28.0

On Mon, Dec 14, 2020 at 16:55:24 +0100, Pavel Hrdina wrote:
The function doesn't take virStorageSource as argument and has nothing in common with virStorageSource or storage file.
Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 1 - src/locking/lock_driver_lockd.c | 66 ++++++++++++++++++++++++++++++++- src/util/virstoragefile.c | 62 ------------------------------- src/util/virstoragefile.h | 2 - 4 files changed, 65 insertions(+), 66 deletions(-)
[...]
+#ifdef LVS +static int virLockManagerGetLVMKey(const char *path, + char **key) +{
Preferably convert the header to the modern function header style where the function name starts on a new line after the type. (also in the #else section)
+ /* + * # lvs --noheadings --unbuffered --nosuffix --options "uuid" LVNAME
Reviewed-by: Peter Krempa <pkrempa@redhat.com>

On Mon, Jan 04, 2021 at 04:30:03PM +0100, Peter Krempa wrote:
On Mon, Dec 14, 2020 at 16:55:24 +0100, Pavel Hrdina wrote:
The function doesn't take virStorageSource as argument and has nothing in common with virStorageSource or storage file.
Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 1 - src/locking/lock_driver_lockd.c | 66 ++++++++++++++++++++++++++++++++- src/util/virstoragefile.c | 62 ------------------------------- src/util/virstoragefile.h | 2 - 4 files changed, 65 insertions(+), 66 deletions(-)
[...]
+#ifdef LVS +static int virLockManagerGetLVMKey(const char *path, + char **key) +{
Preferably convert the header to the modern function header style where the function name starts on a new line after the type. (also in the #else section)
I opted to the old style because the majority of that file uses but I guess there is no harm of using the modern style. I'll fix it before pushing.
+ /* + * # lvs --noheadings --unbuffered --nosuffix --options "uuid" LVNAME
Reviewed-by: Peter Krempa <pkrempa@redhat.com>

It is not used anywhere else. Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/conf/storage_conf.c | 23 ++++++++++++++++++++++- src/util/virstoragefile.c | 24 ------------------------ src/util/virstoragefile.h | 2 -- 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c index 09769e6c79..0c50529ace 100644 --- a/src/conf/storage_conf.c +++ b/src/conf/storage_conf.c @@ -1296,6 +1296,27 @@ virStorageSize(const char *unit, } +static int +virStorageCheckCompat(const char *compat) +{ + unsigned int result; + g_auto(GStrv) version = NULL; + + if (!compat) + return 0; + + version = virStringSplit(compat, ".", 2); + if (!version || !version[1] || + virStrToLong_ui(version[0], NULL, 10, &result) < 0 || + virStrToLong_ui(version[1], NULL, 10, &result) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("forbidden characters in 'compat' attribute")); + return -1; + } + return 0; +} + + static virStorageVolDefPtr virStorageVolDefParseXML(virStoragePoolDefPtr pool, xmlXPathContextPtr ctxt, @@ -1424,7 +1445,7 @@ virStorageVolDefParseXML(virStoragePoolDefPtr pool, } def->target.compat = virXPathString("string(./target/compat)", ctxt); - if (virStorageFileCheckCompat(def->target.compat) < 0) + if (virStorageCheckCompat(def->target.compat) < 0) return NULL; if (virXPathNode("./target/nocow", ctxt)) diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index bc342cabe3..d6ff47e4a7 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -4410,30 +4410,6 @@ virStorageFileGetRelativeBackingPath(virStorageSourcePtr top, } -/* - * virStorageFileCheckCompat - */ -int -virStorageFileCheckCompat(const char *compat) -{ - unsigned int result; - g_auto(GStrv) version = NULL; - - if (!compat) - return 0; - - version = virStringSplit(compat, ".", 2); - if (!version || !version[1] || - virStrToLong_ui(version[0], NULL, 10, &result) < 0 || - virStrToLong_ui(version[1], NULL, 10, &result) < 0) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("forbidden characters in 'compat' attribute")); - return -1; - } - return 0; -} - - /** * virStorageSourceIsRelative: * @src: storage source to check diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 31feb22f26..ec34f2d899 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -513,8 +513,6 @@ int virStorageFileGetRelativeBackingPath(virStorageSourcePtr from, char **relpath) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); -int virStorageFileCheckCompat(const char *compat); - int virStorageSourceNewFromBackingAbsolute(const char *path, virStorageSourcePtr *src); -- 2.28.0

On Mon, Dec 14, 2020 at 16:55:25 +0100, Pavel Hrdina wrote:
It is not used anywhere else.
Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/conf/storage_conf.c | 23 ++++++++++++++++++++++- src/util/virstoragefile.c | 24 ------------------------ src/util/virstoragefile.h | 2 -- 3 files changed, 22 insertions(+), 27 deletions(-)
Reviewed-by: Peter Krempa <pkrempa@redhat.com>

The only reason why virstoragefile.h needs to be included in virfile.h is that virFileNBDDeviceAssociate() takes virStorageFileFormat argument. The function doesn't need the enum value as it converts the value to string and uses only that. Change the argument to string which will allow us to remove that include. The extra seemingly unrelated include changes is because all of the added includes where indirectly provided by virfile.h having virstoragefile.h. Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/driver.c | 1 + src/lxc/lxc_controller.c | 4 ++-- src/qemu/qemu_interop_config.c | 1 + src/qemu/qemu_shim.c | 1 + src/storage/parthelper.c | 1 + src/util/virarptable.c | 1 + src/util/vircgroupv1.c | 1 + src/util/vircgroupv2devices.c | 1 + src/util/virfile.c | 8 ++------ src/util/virfile.h | 4 ++-- src/util/virpidfile.c | 1 + src/util/virresctrl.c | 1 + src/util/virsysinfo.c | 1 + src/util/virtpm.c | 1 + tools/virsh-console.c | 1 + tools/virsh-util.c | 1 + 16 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/driver.c b/src/driver.c index 1cacec24ff..e005a89d57 100644 --- a/src/driver.c +++ b/src/driver.c @@ -29,6 +29,7 @@ #include "virfile.h" #include "virlog.h" #include "virmodule.h" +#include "virobject.h" #include "virstring.h" #include "virthread.h" #include "virutil.h" diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c index 67e5e63d00..8f166a436a 100644 --- a/src/lxc/lxc_controller.c +++ b/src/lxc/lxc_controller.c @@ -509,7 +509,7 @@ static int virLXCControllerSetupNBDDeviceFS(virDomainFSDefPtr fs) } if (virFileNBDDeviceAssociate(fs->src->path, - fs->format, + virStorageFileFormatTypeToString(fs->format), fs->readonly, &dev) < 0) return -1; @@ -541,7 +541,7 @@ static int virLXCControllerSetupNBDDeviceDisk(virDomainDiskDefPtr disk) } if (virFileNBDDeviceAssociate(src, - format, + virStorageFileFormatTypeToString(format), disk->src->readonly, &dev) < 0) return -1; diff --git a/src/qemu/qemu_interop_config.c b/src/qemu/qemu_interop_config.c index c5a5a9c6ec..bcaddda446 100644 --- a/src/qemu/qemu_interop_config.c +++ b/src/qemu/qemu_interop_config.c @@ -24,6 +24,7 @@ #include "configmake.h" #include "viralloc.h" #include "virenum.h" +#include "virerror.h" #include "virfile.h" #include "virhash.h" #include "virlog.h" diff --git a/src/qemu/qemu_shim.c b/src/qemu/qemu_shim.c index ef0ba086b5..18bdc99256 100644 --- a/src/qemu/qemu_shim.c +++ b/src/qemu/qemu_shim.c @@ -27,6 +27,7 @@ #include "virfile.h" #include "virstring.h" #include "virgettext.h" +#include "virthread.h" #define VIR_FROM_THIS VIR_FROM_QEMU diff --git a/src/storage/parthelper.c b/src/storage/parthelper.c index caa2e8fa62..94d588615f 100644 --- a/src/storage/parthelper.c +++ b/src/storage/parthelper.c @@ -40,6 +40,7 @@ #include "virstring.h" #include "virgettext.h" #include "virdevmapper.h" +#include "virerror.h" /* we don't need to include the full internal.h just for this */ #define STREQ(a, b) (strcmp(a, b) == 0) diff --git a/src/util/virarptable.c b/src/util/virarptable.c index 01a27c0093..d62de5e3dd 100644 --- a/src/util/virarptable.c +++ b/src/util/virarptable.c @@ -26,6 +26,7 @@ #include "viralloc.h" #include "virarptable.h" +#include "virerror.h" #include "virfile.h" #include "virlog.h" #include "virnetlink.h" diff --git a/src/util/vircgroupv1.c b/src/util/vircgroupv1.c index 06849efd38..2b4d625c35 100644 --- a/src/util/vircgroupv1.c +++ b/src/util/vircgroupv1.c @@ -41,6 +41,7 @@ #include "virsystemd.h" #include "virerror.h" #include "viralloc.h" +#include "virthread.h" VIR_LOG_INIT("util.cgroup"); diff --git a/src/util/vircgroupv2devices.c b/src/util/vircgroupv2devices.c index f31244f3c1..71591be4c4 100644 --- a/src/util/vircgroupv2devices.c +++ b/src/util/vircgroupv2devices.c @@ -34,6 +34,7 @@ #include "virbpf.h" #include "vircgroup.h" #include "vircgroupv2devices.h" +#include "virerror.h" #include "virfile.h" #include "virlog.h" diff --git a/src/util/virfile.c b/src/util/virfile.c index f7283fa72f..3f4c6d1d0a 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -879,14 +879,13 @@ virFileNBDLoadDriver(void) } int virFileNBDDeviceAssociate(const char *file, - virStorageFileFormat fmt, + const char *fmtstr, bool readonly, char **dev) { g_autofree char *nbddev = NULL; g_autofree char *qemunbd = NULL; g_autoptr(virCommand) cmd = NULL; - const char *fmtstr = NULL; if (!virFileNBDLoadDriver()) return -1; @@ -900,9 +899,6 @@ int virFileNBDDeviceAssociate(const char *file, return -1; } - if (fmt > 0) - fmtstr = virStorageFileFormatTypeToString(fmt); - cmd = virCommandNew(qemunbd); /* Explicitly not trying to cope with old qemu-nbd which @@ -945,7 +941,7 @@ int virFileLoopDeviceAssociate(const char *file, } int virFileNBDDeviceAssociate(const char *file, - virStorageFileFormat fmt G_GNUC_UNUSED, + const char *fmtstr G_GNUC_UNUSED, bool readonly G_GNUC_UNUSED, char **dev G_GNUC_UNUSED) { diff --git a/src/util/virfile.h b/src/util/virfile.h index 32505826ee..28dfe86445 100644 --- a/src/util/virfile.h +++ b/src/util/virfile.h @@ -28,7 +28,7 @@ #include "internal.h" #include "virbitmap.h" -#include "virstoragefile.h" +#include "virenum.h" typedef enum { VIR_FILE_CLOSE_PRESERVE_ERRNO = 1 << 0, @@ -143,7 +143,7 @@ int virFileLoopDeviceAssociate(const char *file, char **dev); int virFileNBDDeviceAssociate(const char *file, - virStorageFileFormat fmt, + const char *fmtstr, bool readonly, char **dev); diff --git a/src/util/virpidfile.c b/src/util/virpidfile.c index 895eb83ea9..b4d0059405 100644 --- a/src/util/virpidfile.c +++ b/src/util/virpidfile.c @@ -30,6 +30,7 @@ #include "virpidfile.h" #include "virfile.h" #include "viralloc.h" +#include "virbuffer.h" #include "virutil.h" #include "virlog.h" #include "virerror.h" diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c index d3087b98c1..67a921b44b 100644 --- a/src/util/virresctrl.c +++ b/src/util/virresctrl.c @@ -27,6 +27,7 @@ #define LIBVIRT_VIRRESCTRLPRIV_H_ALLOW #include "virresctrlpriv.h" #include "viralloc.h" +#include "virbuffer.h" #include "virfile.h" #include "virlog.h" #include "virobject.h" diff --git a/src/util/virsysinfo.c b/src/util/virsysinfo.c index 217f842a37..28f7104145 100644 --- a/src/util/virsysinfo.c +++ b/src/util/virsysinfo.c @@ -32,6 +32,7 @@ #include "virlog.h" #include "virfile.h" #include "virstring.h" +#include "virxml.h" #define LIBVIRT_VIRSYSINFOPRIV_H_ALLOW #include "virsysinfopriv.h" diff --git a/src/util/virtpm.c b/src/util/virtpm.c index 0e11674a3c..b41eb00619 100644 --- a/src/util/virtpm.c +++ b/src/util/virtpm.c @@ -31,6 +31,7 @@ #include "virbitmap.h" #include "virjson.h" #include "virlog.h" +#include "virthread.h" #define VIR_FROM_THIS VIR_FROM_TPM diff --git a/tools/virsh-console.c b/tools/virsh-console.c index 2e498a6903..4e9bdb67e1 100644 --- a/tools/virsh-console.c +++ b/tools/virsh-console.c @@ -38,6 +38,7 @@ # include "viralloc.h" # include "virthread.h" # include "virerror.h" +# include "virobject.h" VIR_LOG_INIT("tools.virsh-console"); diff --git a/tools/virsh-util.c b/tools/virsh-util.c index af7ed55348..9f69bad8cc 100644 --- a/tools/virsh-util.c +++ b/tools/virsh-util.c @@ -23,6 +23,7 @@ #include "virfile.h" #include "virstring.h" #include "viralloc.h" +#include "virxml.h" static virDomainPtr virshLookupDomainInternal(vshControl *ctl, -- 2.28.0

On Mon, Dec 14, 2020 at 16:55:26 +0100, Pavel Hrdina wrote:
The only reason why virstoragefile.h needs to be included in virfile.h is that virFileNBDDeviceAssociate() takes virStorageFileFormat argument. The function doesn't need the enum value as it converts the value to string and uses only that.
Change the argument to string which will allow us to remove that include.
The extra seemingly unrelated include changes is because all of the added includes where indirectly provided by virfile.h having virstoragefile.h.
Please split all the #include additions into a separate patch, since that can be commited before and is a justifiable change. This will cut-down the noise in this commit.
Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/driver.c | 1 + src/lxc/lxc_controller.c | 4 ++-- src/qemu/qemu_interop_config.c | 1 + src/qemu/qemu_shim.c | 1 + src/storage/parthelper.c | 1 + src/util/virarptable.c | 1 + src/util/vircgroupv1.c | 1 + src/util/vircgroupv2devices.c | 1 + src/util/virfile.c | 8 ++------ src/util/virfile.h | 4 ++-- src/util/virpidfile.c | 1 + src/util/virresctrl.c | 1 + src/util/virsysinfo.c | 1 + src/util/virtpm.c | 1 + tools/virsh-console.c | 1 + tools/virsh-util.c | 1 + 16 files changed, 19 insertions(+), 10 deletions(-)
[...]
diff --git a/src/util/virfile.c b/src/util/virfile.c index f7283fa72f..3f4c6d1d0a 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -879,14 +879,13 @@ virFileNBDLoadDriver(void) }
int virFileNBDDeviceAssociate(const char *file, - virStorageFileFormat fmt, + const char *fmtstr, bool readonly, char **dev) { g_autofree char *nbddev = NULL; g_autofree char *qemunbd = NULL; g_autoptr(virCommand) cmd = NULL; - const char *fmtstr = NULL;
if (!virFileNBDLoadDriver()) return -1;
You can use my: Reviewed-by: Peter Krempa <pkrempa@redhat.com> on the patch(es) adding the headers as well as this with the header additions stripped (just don't forget to send them to the list before pushing :) )

Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 2 +- src/storage/storage_util.c | 2 +- src/util/virfile.c | 47 ++++++++++++++++++++++++++++++++++++++ src/util/virfile.h | 4 ++++ src/util/virstoragefile.c | 47 -------------------------------------- src/util/virstoragefile.h | 4 ---- 6 files changed, 53 insertions(+), 53 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index d04b8f5e0c..2d1cb7786d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2121,6 +2121,7 @@ virFileRelLinkPointsTo; virFileRemove; virFileRemoveLastComponent; virFileRemoveXAttr; +virFileResize; virFileResolveAllLinks; virFileResolveLink; virFileRewrite; @@ -3149,7 +3150,6 @@ virStorageFileParseChainIndex; virStorageFileProbeFormat; virStorageFileRead; virStorageFileReportBrokenChain; -virStorageFileResize; virStorageFileStat; virStorageFileSupportsAccess; virStorageFileSupportsBackingChainTraversal; diff --git a/src/storage/storage_util.c b/src/storage/storage_util.c index 9b9f8b4f13..652f65fce2 100644 --- a/src/storage/storage_util.c +++ b/src/storage/storage_util.c @@ -2408,7 +2408,7 @@ virStorageBackendVolResizeLocal(virStoragePoolObjPtr pool, VIR_STORAGE_VOL_RESIZE_SHRINK, -1); if (vol->target.format == VIR_STORAGE_FILE_RAW && !vol->target.encryption) { - return virStorageFileResize(vol->target.path, capacity, pre_allocate); + return virFileResize(vol->target.path, capacity, pre_allocate); } else if (vol->target.format == VIR_STORAGE_FILE_RAW && vol->target.encryption) { if (pre_allocate) { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", diff --git a/src/util/virfile.c b/src/util/virfile.c index 3f4c6d1d0a..6e16780e30 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -557,6 +557,53 @@ virFileRewriteStr(const char *path, } +/** + * virFileResize: + * + * Change the capacity of the raw storage file at 'path'. + */ +int +virFileResize(const char *path, + unsigned long long capacity, + bool pre_allocate) +{ + int rc; + VIR_AUTOCLOSE fd = -1; + + if ((fd = open(path, O_RDWR)) < 0) { + virReportSystemError(errno, _("Unable to open '%s'"), path); + return -1; + } + + if (pre_allocate) { + if ((rc = virFileAllocate(fd, 0, capacity)) != 0) { + if (rc == -2) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("preallocate is not supported on this platform")); + } else { + virReportSystemError(errno, + _("Failed to pre-allocate space for " + "file '%s'"), path); + } + return -1; + } + } + + if (ftruncate(fd, capacity) < 0) { + virReportSystemError(errno, + _("Failed to truncate file '%s'"), path); + return -1; + } + + if (VIR_CLOSE(fd) < 0) { + virReportSystemError(errno, _("Unable to save '%s'"), path); + return -1; + } + + return 0; +} + + int virFileTouch(const char *path, mode_t mode) { int fd = -1; diff --git a/src/util/virfile.h b/src/util/virfile.h index 28dfe86445..45b75a059e 100644 --- a/src/util/virfile.h +++ b/src/util/virfile.h @@ -133,6 +133,10 @@ int virFileRewriteStr(const char *path, mode_t mode, const char *str); +int virFileResize(const char *path, + unsigned long long capacity, + bool pre_allocate); + int virFileTouch(const char *path, mode_t mode); int virFileUpdatePerm(const char *path, diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index d6ff47e4a7..66694bfcc0 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -1193,53 +1193,6 @@ virStorageFileGetMetadataFromFD(const char *path, } -/** - * virStorageFileResize: - * - * Change the capacity of the raw storage file at 'path'. - */ -int -virStorageFileResize(const char *path, - unsigned long long capacity, - bool pre_allocate) -{ - int rc; - VIR_AUTOCLOSE fd = -1; - - if ((fd = open(path, O_RDWR)) < 0) { - virReportSystemError(errno, _("Unable to open '%s'"), path); - return -1; - } - - if (pre_allocate) { - if ((rc = virFileAllocate(fd, 0, capacity)) != 0) { - if (rc == -2) { - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", - _("preallocate is not supported on this platform")); - } else { - virReportSystemError(errno, - _("Failed to pre-allocate space for " - "file '%s'"), path); - } - return -1; - } - } - - if (ftruncate(fd, capacity) < 0) { - virReportSystemError(errno, - _("Failed to truncate file '%s'"), path); - return -1; - } - - if (VIR_CLOSE(fd) < 0) { - virReportSystemError(errno, _("Unable to save '%s'"), path); - return -1; - } - - return 0; -} - - int virStorageFileIsClusterFS(const char *path) { /* These are coherent cluster filesystems known to be safe for diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index ec34f2d899..94bb889d84 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -420,10 +420,6 @@ virStorageSourcePtr virStorageFileChainLookup(virStorageSourcePtr chain, virStorageSourcePtr *parent) ATTRIBUTE_NONNULL(1); -int virStorageFileResize(const char *path, - unsigned long long capacity, - bool pre_allocate); - int virStorageFileIsClusterFS(const char *path); bool virStorageIsFile(const char *path); bool virStorageIsRelative(const char *backing); -- 2.28.0

On Mon, Dec 14, 2020 at 16:55:27 +0100, Pavel Hrdina wrote:
Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 2 +- src/storage/storage_util.c | 2 +- src/util/virfile.c | 47 ++++++++++++++++++++++++++++++++++++++ src/util/virfile.h | 4 ++++ src/util/virstoragefile.c | 47 -------------------------------------- src/util/virstoragefile.h | 4 ---- 6 files changed, 53 insertions(+), 53 deletions(-)
Yup, that has nothing to to with virStorageSource. Reviewed-by: Peter Krempa <pkrempa@redhat.com>

Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 2 +- src/qemu/qemu_migration.c | 2 +- src/util/virfile.c | 12 ++++++++++++ src/util/virfile.h | 1 + src/util/virstoragefile.c | 11 ----------- src/util/virstoragefile.h | 1 - 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2d1cb7786d..57f3b12000 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2087,6 +2087,7 @@ virFileGetXAttr; virFileGetXAttrQuiet; virFileInData; virFileIsCDROM; +virFileIsClusterFS; virFileIsDir; virFileIsExecutable; virFileIsLink; @@ -3144,7 +3145,6 @@ virStorageFileGetSCSIKey; virStorageFileGetUniqueIdentifier; virStorageFileInit; virStorageFileInitAs; -virStorageFileIsClusterFS; virStorageFileParseBackingStoreStr; virStorageFileParseChainIndex; virStorageFileProbeFormat; diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index fcb33d0364..16c9f18e0a 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1461,7 +1461,7 @@ qemuMigrationSrcIsSafe(virDomainDefPtr def, } else if (rc == 0) { unsafe = true; } - if ((rc = virStorageFileIsClusterFS(src)) < 0) + if ((rc = virFileIsClusterFS(src)) < 0) return false; else if (rc == 1) continue; diff --git a/src/util/virfile.c b/src/util/virfile.c index 6e16780e30..3f58b98248 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -3709,6 +3709,18 @@ int virFileIsSharedFS(const char *path) } +int virFileIsClusterFS(const char *path) +{ + /* These are coherent cluster filesystems known to be safe for + * migration with cache != none + */ + return virFileIsSharedFSType(path, + VIR_FILE_SHFS_GFS2 | + VIR_FILE_SHFS_OCFS | + VIR_FILE_SHFS_CEPH); +} + + #if defined(__linux__) && defined(WITH_SYS_MOUNT_H) int virFileSetupDev(const char *path, diff --git a/src/util/virfile.h b/src/util/virfile.h index 45b75a059e..733d652ac9 100644 --- a/src/util/virfile.h +++ b/src/util/virfile.h @@ -223,6 +223,7 @@ enum { int virFileIsSharedFSType(const char *path, int fstypes) ATTRIBUTE_NONNULL(1); int virFileIsSharedFS(const char *path) ATTRIBUTE_NONNULL(1); +int virFileIsClusterFS(const char *path) ATTRIBUTE_NONNULL(1); int virFileIsMountPoint(const char *file) ATTRIBUTE_NONNULL(1); int virFileIsCDROM(const char *path) ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT; diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 66694bfcc0..3db85d8b89 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -1193,17 +1193,6 @@ virStorageFileGetMetadataFromFD(const char *path, } -int virStorageFileIsClusterFS(const char *path) -{ - /* These are coherent cluster filesystems known to be safe for - * migration with cache != none - */ - return virFileIsSharedFSType(path, - VIR_FILE_SHFS_GFS2 | - VIR_FILE_SHFS_OCFS | - VIR_FILE_SHFS_CEPH); -} - #ifdef WITH_UDEV /* virStorageFileGetSCSIKey * @path: Path to the SCSI device diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 94bb889d84..2452b967b2 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -420,7 +420,6 @@ virStorageSourcePtr virStorageFileChainLookup(virStorageSourcePtr chain, virStorageSourcePtr *parent) ATTRIBUTE_NONNULL(1); -int virStorageFileIsClusterFS(const char *path); bool virStorageIsFile(const char *path); bool virStorageIsRelative(const char *backing); -- 2.28.0

On Mon, Dec 14, 2020 at 16:55:28 +0100, Pavel Hrdina wrote:
Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 2 +- src/qemu/qemu_migration.c | 2 +- src/util/virfile.c | 12 ++++++++++++ src/util/virfile.h | 1 + src/util/virstoragefile.c | 11 ----------- src/util/virstoragefile.h | 1 - 6 files changed, 15 insertions(+), 14 deletions(-)
[...]
diff --git a/src/util/virfile.c b/src/util/virfile.c index 6e16780e30..3f58b98248 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -3709,6 +3709,18 @@ int virFileIsSharedFS(const char *path) }
+int virFileIsClusterFS(const char *path)
Please use the modern function header format.
+{ + /* These are coherent cluster filesystems known to be safe for + * migration with cache != none + */ + return virFileIsSharedFSType(path, + VIR_FILE_SHFS_GFS2 | + VIR_FILE_SHFS_OCFS | + VIR_FILE_SHFS_CEPH); +} + + #if defined(__linux__) && defined(WITH_SYS_MOUNT_H) int virFileSetupDev(const char *path,
Reviewed-by: Peter Krempa <pkrempa@redhat.com>

Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 2 +- src/util/virfile.c | 21 +++++++++++++++++++++ src/util/virfile.h | 1 + src/util/virstoragefile.c | 26 +++----------------------- src/util/virstoragefile.h | 1 - 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 57f3b12000..0b560dfe45 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2090,6 +2090,7 @@ virFileIsCDROM; virFileIsClusterFS; virFileIsDir; virFileIsExecutable; +virFileIsFile; virFileIsLink; virFileIsMountPoint; virFileIsRegular; @@ -3156,7 +3157,6 @@ virStorageFileSupportsBackingChainTraversal; virStorageFileSupportsCreate; virStorageFileSupportsSecurityDriver; virStorageFileUnlink; -virStorageIsFile; virStorageIsRelative; virStorageNetHostDefClear; virStorageNetHostDefCopy; diff --git a/src/util/virfile.c b/src/util/virfile.c index 3f58b98248..7a7468b202 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -1995,6 +1995,27 @@ virFileIsCDROM(const char *path) #endif /* defined(__linux__) */ +bool +virFileIsFile(const char *backing) +{ + char *colon; + char *slash; + + if (!backing) + return false; + + colon = strchr(backing, ':'); + slash = strchr(backing, '/'); + + /* Reject anything that looks like a protocol (such as nbd: or + * rbd:); if someone really does want a relative file name that + * includes ':', they can always prefix './'. */ + if (colon && (!slash || colon < slash)) + return false; + return true; +} + + #if defined WITH_MNTENT_H && defined WITH_GETMNTENT_R static int virFileGetMountSubtreeImpl(const char *mtabpath, diff --git a/src/util/virfile.h b/src/util/virfile.h index 733d652ac9..dfd3188ff2 100644 --- a/src/util/virfile.h +++ b/src/util/virfile.h @@ -227,6 +227,7 @@ int virFileIsClusterFS(const char *path) ATTRIBUTE_NONNULL(1); int virFileIsMountPoint(const char *file) ATTRIBUTE_NONNULL(1); int virFileIsCDROM(const char *path) ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT; +bool virFileIsFile(const char *path); int virFileGetMountSubtree(const char *mtabpath, const char *prefix, diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 3db85d8b89..f37802260b 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -756,26 +756,6 @@ virStorageFileMatchesVersion(int versionOffset, return false; } -bool -virStorageIsFile(const char *backing) -{ - char *colon; - char *slash; - - if (!backing) - return false; - - colon = strchr(backing, ':'); - slash = strchr(backing, '/'); - - /* Reject anything that looks like a protocol (such as nbd: or - * rbd:); if someone really does want a relative file name that - * includes ':', they can always prefix './'. */ - if (colon && (!slash || colon < slash)) - return false; - return true; -} - bool virStorageIsRelative(const char *backing) @@ -783,7 +763,7 @@ virStorageIsRelative(const char *backing) if (backing[0] == '/') return false; - if (!virStorageIsFile(backing)) + if (!virFileIsFile(backing)) return false; return true; @@ -1450,7 +1430,7 @@ virStorageFileChainLookup(virStorageSourcePtr chain, { virStorageSourcePtr prev; const char *start = chain->path; - bool nameIsFile = virStorageIsFile(name); + bool nameIsFile = virFileIsFile(name); if (!parent) parent = &prev; @@ -3794,7 +3774,7 @@ virStorageSourceNewFromBackingAbsolute(const char *path, *src = NULL; - if (virStorageIsFile(path)) { + if (virFileIsFile(path)) { def->type = VIR_STORAGE_TYPE_FILE; def->path = g_strdup(path); diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 2452b967b2..2a44a535a1 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -420,7 +420,6 @@ virStorageSourcePtr virStorageFileChainLookup(virStorageSourcePtr chain, virStorageSourcePtr *parent) ATTRIBUTE_NONNULL(1); -bool virStorageIsFile(const char *path); bool virStorageIsRelative(const char *backing); int virStorageFileGetSCSIKey(const char *path, -- 2.28.0

On Mon, Dec 14, 2020 at 16:55:29 +0100, Pavel Hrdina wrote:
Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 2 +- src/util/virfile.c | 21 +++++++++++++++++++++ src/util/virfile.h | 1 + src/util/virstoragefile.c | 26 +++----------------------- src/util/virstoragefile.h | 1 - 5 files changed, 26 insertions(+), 25 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 57f3b12000..0b560dfe45 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2090,6 +2090,7 @@ virFileIsCDROM; virFileIsClusterFS; virFileIsDir; virFileIsExecutable; +virFileIsFile;
This doesn't really belong here. virFileIs* checks an actual file, while the function checks only the filename to conform to some rules. Additionally ...
virFileIsLink; virFileIsMountPoint; virFileIsRegular; @@ -3156,7 +3157,6 @@ virStorageFileSupportsBackingChainTraversal; virStorageFileSupportsCreate; virStorageFileSupportsSecurityDriver; virStorageFileUnlink; -virStorageIsFile; virStorageIsRelative; virStorageNetHostDefClear; virStorageNetHostDefCopy;
[...]
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 3db85d8b89..f37802260b 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c
[...]
@@ -783,7 +763,7 @@ virStorageIsRelative(const char *backing) if (backing[0] == '/') return false;
- if (!virStorageIsFile(backing)) + if (!virFileIsFile(backing))
... all uses ...
return false;
return true; @@ -1450,7 +1430,7 @@ virStorageFileChainLookup(virStorageSourcePtr chain, { virStorageSourcePtr prev; const char *start = chain->path; - bool nameIsFile = virStorageIsFile(name); + bool nameIsFile = virFileIsFile(name);
... are within ...
if (!parent) parent = &prev; @@ -3794,7 +3774,7 @@ virStorageSourceNewFromBackingAbsolute(const char *path,
*src = NULL;
- if (virStorageIsFile(path)) { + if (virFileIsFile(path)) {
... this one file. Just unexport it and keep it here, since it's only relevant to backing chain traversal.
def->type = VIR_STORAGE_TYPE_FILE;
def->path = g_strdup(path);
You can use my R-b on the patch which unexports it. NACK to this movement.

Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 2 +- src/qemu/qemu_block.c | 2 +- src/qemu/qemu_snapshot.c | 2 +- src/util/virfile.c | 13 +++++++++++++ src/util/virfile.h | 2 ++ src/util/virstoragefile.c | 15 +-------------- src/util/virstoragefile.h | 2 -- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0b560dfe45..1bbf567847 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2094,6 +2094,7 @@ virFileIsFile; virFileIsLink; virFileIsMountPoint; virFileIsRegular; +virFileIsRelative; virFileIsSharedFS; virFileIsSharedFSType; virFileLength; @@ -3157,7 +3158,6 @@ virStorageFileSupportsBackingChainTraversal; virStorageFileSupportsCreate; virStorageFileSupportsSecurityDriver; virStorageFileUnlink; -virStorageIsRelative; virStorageNetHostDefClear; virStorageNetHostDefCopy; virStorageNetHostDefFree; diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index 4640e339c0..0743db4d2c 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -3357,7 +3357,7 @@ qemuBlockUpdateRelativeBacking(virDomainObjPtr vm, if (rc < 0) return rc; - if (backingStoreStr && virStorageIsRelative(backingStoreStr)) + if (backingStoreStr && virFileIsRelative(backingStoreStr)) n->backingStore->relPath = g_steal_pointer(&backingStoreStr); } diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index 15494c3415..6f64111ca8 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -1075,7 +1075,7 @@ qemuSnapshotDiskPrepareOne(virDomainObjPtr vm, if (virStorageFileGetBackingStoreStr(dd->src, &backingStoreStr) < 0) return -1; if (backingStoreStr != NULL) { - if (virStorageIsRelative(backingStoreStr)) + if (virFileIsRelative(backingStoreStr)) dd->relPath = g_steal_pointer(&backingStoreStr); } } diff --git a/src/util/virfile.c b/src/util/virfile.c index 7a7468b202..c3f0e7cabc 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -2016,6 +2016,19 @@ virFileIsFile(const char *backing) } +bool +virFileIsRelative(const char *path) +{ + if (path[0] == '/') + return false; + + if (!virFileIsFile(path)) + return false; + + return true; +} + + #if defined WITH_MNTENT_H && defined WITH_GETMNTENT_R static int virFileGetMountSubtreeImpl(const char *mtabpath, diff --git a/src/util/virfile.h b/src/util/virfile.h index dfd3188ff2..06d9cffd88 100644 --- a/src/util/virfile.h +++ b/src/util/virfile.h @@ -228,6 +228,8 @@ int virFileIsMountPoint(const char *file) ATTRIBUTE_NONNULL(1); int virFileIsCDROM(const char *path) ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT; bool virFileIsFile(const char *path); +bool virFileIsRelative(const char *path); + int virFileGetMountSubtree(const char *mtabpath, const char *prefix, diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index f37802260b..6ced28762a 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -757,19 +757,6 @@ virStorageFileMatchesVersion(int versionOffset, } -bool -virStorageIsRelative(const char *backing) -{ - if (backing[0] == '/') - return false; - - if (!virFileIsFile(backing)) - return false; - - return true; -} - - static int virStorageFileProbeFormatFromBuf(const char *path, char *buf, @@ -3846,7 +3833,7 @@ virStorageSourceNewFromChild(virStorageSourcePtr parent, *child = NULL; - if (virStorageIsRelative(parentRaw)) { + if (virFileIsRelative(parentRaw)) { if (!(def = virStorageSourceNewFromBackingRelative(parent, parentRaw))) return -1; } else { diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 2a44a535a1..d39486ad59 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -420,8 +420,6 @@ virStorageSourcePtr virStorageFileChainLookup(virStorageSourcePtr chain, virStorageSourcePtr *parent) ATTRIBUTE_NONNULL(1); -bool virStorageIsRelative(const char *backing); - int virStorageFileGetSCSIKey(const char *path, char **key, bool ignoreError); -- 2.28.0

On Mon, Dec 14, 2020 at 16:55:30 +0100, Pavel Hrdina wrote:
Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 2 +- src/qemu/qemu_block.c | 2 +- src/qemu/qemu_snapshot.c | 2 +- src/util/virfile.c | 13 +++++++++++++ src/util/virfile.h | 2 ++ src/util/virstoragefile.c | 15 +-------------- src/util/virstoragefile.h | 2 -- 7 files changed, 19 insertions(+), 19 deletions(-)
NACK based on the same grounds as previous patch. This function is not checking whether a file is relative, but just the filename. This is relevant just to backing chain manipulation.

This will allow following patches to move virStorageSource into conf directory and virStorageDriverData into a new storage_file directory. Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/storage/storage_file_fs.c | 21 +++-- src/storage/storage_file_gluster.c | 31 +++++--- src/util/virstoragefile.c | 118 +++++++++++++++++++++-------- src/util/virstoragefile.h | 5 +- src/util/virstoragefilebackend.h | 2 + 5 files changed, 122 insertions(+), 55 deletions(-) diff --git a/src/storage/storage_file_fs.c b/src/storage/storage_file_fs.c index b379a8ca8e..5a44ef8c2d 100644 --- a/src/storage/storage_file_fs.c +++ b/src/storage/storage_file_fs.c @@ -51,7 +51,8 @@ struct _virStorageFileBackendFsPriv { static void virStorageFileBackendFileDeinit(virStorageSourcePtr src) { - virStorageFileBackendFsPrivPtr priv = src->drv->priv; + virStorageDriverDataPtr drv = src->drv; + virStorageFileBackendFsPrivPtr priv = drv->priv; VIR_DEBUG("deinitializing FS storage file %p (%s:%s)", src, virStorageTypeToString(virStorageSourceGetActualType(src)), @@ -66,16 +67,17 @@ virStorageFileBackendFileDeinit(virStorageSourcePtr src) static int virStorageFileBackendFileInit(virStorageSourcePtr src) { + virStorageDriverDataPtr drv = src->drv; virStorageFileBackendFsPrivPtr priv = NULL; VIR_DEBUG("initializing FS storage file %p (%s:%s)[%u:%u]", src, virStorageTypeToString(virStorageSourceGetActualType(src)), src->path, - (unsigned int)src->drv->uid, (unsigned int)src->drv->gid); + (unsigned int)drv->uid, (unsigned int)drv->gid); priv = g_new0(virStorageFileBackendFsPriv, 1); - src->drv->priv = priv; + drv->priv = priv; return 0; } @@ -84,10 +86,11 @@ virStorageFileBackendFileInit(virStorageSourcePtr src) static int virStorageFileBackendFileCreate(virStorageSourcePtr src) { + virStorageDriverDataPtr drv = src->drv; VIR_AUTOCLOSE fd = -1; if ((fd = virFileOpenAs(src->path, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR, - src->drv->uid, src->drv->gid, 0)) < 0) { + drv->uid, drv->gid, 0)) < 0) { errno = -fd; return -1; } @@ -117,11 +120,12 @@ virStorageFileBackendFileRead(virStorageSourcePtr src, size_t len, char **buf) { + virStorageDriverDataPtr drv = src->drv; ssize_t ret = -1; VIR_AUTOCLOSE fd = -1; if ((fd = virFileOpenAs(src->path, O_RDONLY, 0, - src->drv->uid, src->drv->gid, 0)) < 0) { + drv->uid, drv->gid, 0)) < 0) { virReportSystemError(-fd, _("Failed to open file '%s'"), src->path); return -1; @@ -146,7 +150,8 @@ virStorageFileBackendFileRead(virStorageSourcePtr src, static const char * virStorageFileBackendFileGetUniqueIdentifier(virStorageSourcePtr src) { - virStorageFileBackendFsPrivPtr priv = src->drv->priv; + virStorageDriverDataPtr drv = src->drv; + virStorageFileBackendFsPrivPtr priv = drv->priv; if (!priv->canonpath) { if (!(priv->canonpath = virFileCanonicalizePath(src->path))) { @@ -164,8 +169,10 @@ static int virStorageFileBackendFileAccess(virStorageSourcePtr src, int mode) { + virStorageDriverDataPtr drv = src->drv; + return virFileAccessibleAs(src->path, mode, - src->drv->uid, src->drv->gid); + drv->uid, drv->gid); } diff --git a/src/storage/storage_file_gluster.c b/src/storage/storage_file_gluster.c index c84af8a4e8..e26dfdfd99 100644 --- a/src/storage/storage_file_gluster.c +++ b/src/storage/storage_file_gluster.c @@ -46,7 +46,8 @@ struct _virStorageFileBackendGlusterPriv { static void virStorageFileBackendGlusterDeinit(virStorageSourcePtr src) { - virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; + virStorageDriverDataPtr drv = src->drv; + virStorageFileBackendGlusterPrivPtr priv = drv->priv; VIR_DEBUG("deinitializing gluster storage file %p (gluster://%s:%u/%s%s)", src, src->hosts->name, src->hosts->port, src->volume, src->path); @@ -56,7 +57,7 @@ virStorageFileBackendGlusterDeinit(virStorageSourcePtr src) VIR_FREE(priv->canonpath); VIR_FREE(priv); - src->drv->priv = NULL; + drv->priv = NULL; } static int @@ -99,6 +100,7 @@ virStorageFileBackendGlusterInitServer(virStorageFileBackendGlusterPrivPtr priv, static int virStorageFileBackendGlusterInit(virStorageSourcePtr src) { + virStorageDriverDataPtr drv = src->drv; virStorageFileBackendGlusterPrivPtr priv = NULL; size_t i; @@ -114,7 +116,7 @@ virStorageFileBackendGlusterInit(virStorageSourcePtr src) VIR_DEBUG("initializing gluster storage file %p " "(priv='%p' volume='%s' path='%s') as [%u:%u]", src, priv, src->volume, src->path, - (unsigned int)src->drv->uid, (unsigned int)src->drv->gid); + (unsigned int)drv->uid, (unsigned int)drv->gid); if (!(priv->vol = glfs_new(src->volume))) { virReportOOMError(); @@ -133,7 +135,7 @@ virStorageFileBackendGlusterInit(virStorageSourcePtr src) goto error; } - src->drv->priv = priv; + drv->priv = priv; return 0; @@ -149,7 +151,8 @@ virStorageFileBackendGlusterInit(virStorageSourcePtr src) static int virStorageFileBackendGlusterCreate(virStorageSourcePtr src) { - virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; + virStorageDriverDataPtr drv = src->drv; + virStorageFileBackendGlusterPrivPtr priv = drv->priv; glfs_fd_t *fd = NULL; if (!(fd = glfs_creat(priv->vol, src->path, @@ -164,7 +167,8 @@ virStorageFileBackendGlusterCreate(virStorageSourcePtr src) static int virStorageFileBackendGlusterUnlink(virStorageSourcePtr src) { - virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; + virStorageDriverDataPtr drv = src->drv; + virStorageFileBackendGlusterPrivPtr priv = drv->priv; return glfs_unlink(priv->vol, src->path); } @@ -174,7 +178,8 @@ static int virStorageFileBackendGlusterStat(virStorageSourcePtr src, struct stat *st) { - virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; + virStorageDriverDataPtr drv = src->drv; + virStorageFileBackendGlusterPrivPtr priv = drv->priv; return glfs_stat(priv->vol, src->path, st); } @@ -186,7 +191,8 @@ virStorageFileBackendGlusterRead(virStorageSourcePtr src, size_t len, char **buf) { - virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; + virStorageDriverDataPtr drv = src->drv; + virStorageFileBackendGlusterPrivPtr priv = drv->priv; glfs_fd_t *fd = NULL; ssize_t ret = -1; char *s; @@ -241,7 +247,8 @@ static int virStorageFileBackendGlusterAccess(virStorageSourcePtr src, int mode) { - virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; + virStorageDriverDataPtr drv = src->drv; + virStorageFileBackendGlusterPrivPtr priv = drv->priv; return glfs_access(priv->vol, src->path, mode); } @@ -294,7 +301,8 @@ virStorageFileBackendGlusterReadlinkCallback(const char *path, static const char * virStorageFileBackendGlusterGetUniqueIdentifier(virStorageSourcePtr src) { - virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; + virStorageDriverDataPtr drv = src->drv; + virStorageFileBackendGlusterPrivPtr priv = drv->priv; g_autofree char *filePath = NULL; if (priv->canonpath) @@ -320,7 +328,8 @@ virStorageFileBackendGlusterChown(const virStorageSource *src, uid_t uid, gid_t gid) { - virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; + virStorageDriverDataPtr drv = src->drv; + virStorageFileBackendGlusterPrivPtr priv = drv->priv; return glfs_chown(priv->vol, src->path, uid, gid); } diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 6ced28762a..524204a56c 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -4521,7 +4521,8 @@ virStorageFileGetBackendForSupportCheck(const virStorageSource *src, } if (src->drv) { - *backend = src->drv->backend; + virStorageDriverDataPtr drv = src->drv; + *backend = drv->backend; return 1; } @@ -4617,12 +4618,16 @@ virStorageFileSupportsCreate(const virStorageSource *src) void virStorageFileDeinit(virStorageSourcePtr src) { + virStorageDriverDataPtr drv = NULL; + if (!virStorageFileIsInitialized(src)) return; - if (src->drv->backend && - src->drv->backend->backendDeinit) - src->drv->backend->backendDeinit(src); + drv = src->drv; + + if (drv->backend && + drv->backend->backendDeinit) + drv->backend->backendDeinit(src); VIR_FREE(src->drv); } @@ -4646,26 +4651,28 @@ virStorageFileInitAs(virStorageSourcePtr src, uid_t uid, gid_t gid) { int actualType = virStorageSourceGetActualType(src); - src->drv = g_new0(virStorageDriverData, 1); + virStorageDriverDataPtr drv = g_new0(virStorageDriverData, 1); + + src->drv = drv; if (uid == (uid_t) -1) - src->drv->uid = geteuid(); + drv->uid = geteuid(); else - src->drv->uid = uid; + drv->uid = uid; if (gid == (gid_t) -1) - src->drv->gid = getegid(); + drv->gid = getegid(); else - src->drv->gid = gid; + drv->gid = gid; if (virStorageFileBackendForType(actualType, src->protocol, true, - &src->drv->backend) < 0) + &drv->backend) < 0) goto error; - if (src->drv->backend->backendInit && - src->drv->backend->backendInit(src) < 0) + if (drv->backend->backendInit && + drv->backend->backendInit(src) < 0) goto error; return 0; @@ -4700,15 +4707,22 @@ virStorageFileInit(virStorageSourcePtr src) int virStorageFileCreate(virStorageSourcePtr src) { + virStorageDriverDataPtr drv = NULL; int ret; - if (!virStorageFileIsInitialized(src) || - !src->drv->backend->storageFileCreate) { + if (!virStorageFileIsInitialized(src)) { errno = ENOSYS; return -2; } - ret = src->drv->backend->storageFileCreate(src); + 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); @@ -4730,15 +4744,22 @@ virStorageFileCreate(virStorageSourcePtr src) int virStorageFileUnlink(virStorageSourcePtr src) { + virStorageDriverDataPtr drv = NULL; int ret; - if (!virStorageFileIsInitialized(src) || - !src->drv->backend->storageFileUnlink) { + if (!virStorageFileIsInitialized(src)) { errno = ENOSYS; return -2; } - ret = src->drv->backend->storageFileUnlink(src); + 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); @@ -4760,15 +4781,22 @@ int virStorageFileStat(virStorageSourcePtr src, struct stat *st) { + virStorageDriverDataPtr drv = NULL; int ret; - if (!virStorageFileIsInitialized(src) || - !src->drv->backend->storageFileStat) { + if (!virStorageFileIsInitialized(src)) { errno = ENOSYS; return -2; } - ret = src->drv->backend->storageFileStat(src, st); + 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); @@ -4795,6 +4823,7 @@ virStorageFileRead(virStorageSourcePtr src, size_t len, char **buf) { + virStorageDriverDataPtr drv = NULL; ssize_t ret; if (!virStorageFileIsInitialized(src)) { @@ -4803,10 +4832,12 @@ virStorageFileRead(virStorageSourcePtr src, return -1; } - if (!src->drv->backend->storageFileRead) + drv = src->drv; + + if (!drv->backend->storageFileRead) return -2; - ret = src->drv->backend->storageFileRead(src, offset, len, buf); + ret = drv->backend->storageFileRead(src, offset, len, buf); VIR_DEBUG("read '%zd' bytes from storage '%p' starting at offset '%zu'", ret, src, offset); @@ -4826,13 +4857,17 @@ virStorageFileRead(virStorageSourcePtr src, const char * virStorageFileGetUniqueIdentifier(virStorageSourcePtr src) { + virStorageDriverDataPtr drv = NULL; + if (!virStorageFileIsInitialized(src)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("storage file backend not initialized")); return NULL; } - if (!src->drv->backend->storageFileGetUniqueIdentifier) { + drv = src->drv; + + if (!drv->backend->storageFileGetUniqueIdentifier) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unique storage file identifier not implemented for " "storage type %s (protocol: %s)'"), @@ -4841,7 +4876,7 @@ virStorageFileGetUniqueIdentifier(virStorageSourcePtr src) return NULL; } - return src->drv->backend->storageFileGetUniqueIdentifier(src); + return drv->backend->storageFileGetUniqueIdentifier(src); } @@ -4859,13 +4894,21 @@ int virStorageFileAccess(virStorageSourcePtr src, int mode) { - if (!virStorageFileIsInitialized(src) || - !src->drv->backend->storageFileAccess) { + virStorageDriverDataPtr drv = NULL; + + if (!virStorageFileIsInitialized(src)) { + errno = ENOSYS; + return -2; + } + + drv = src->drv; + + if (!drv->backend->storageFileAccess) { errno = ENOSYS; return -2; } - return src->drv->backend->storageFileAccess(src, mode); + return drv->backend->storageFileAccess(src, mode); } @@ -4885,8 +4928,16 @@ virStorageFileChown(const virStorageSource *src, uid_t uid, gid_t gid) { - if (!virStorageFileIsInitialized(src) || - !src->drv->backend->storageFileChown) { + virStorageDriverDataPtr drv = NULL; + + if (!virStorageFileIsInitialized(src)) { + errno = ENOSYS; + return -2; + } + + drv = src->drv; + + if (!drv->backend->storageFileChown) { errno = ENOSYS; return -2; } @@ -4894,7 +4945,7 @@ virStorageFileChown(const virStorageSource *src, VIR_DEBUG("chown of storage file %p to %u:%u", src, (unsigned int)uid, (unsigned int)gid); - return src->drv->backend->storageFileChown(src, uid, gid); + return drv->backend->storageFileChown(src, uid, gid); } @@ -4914,8 +4965,9 @@ virStorageFileReportBrokenChain(int errcode, virStorageSourcePtr parent) { if (src->drv) { - unsigned int access_user = src->drv->uid; - unsigned int access_group = src->drv->gid; + virStorageDriverDataPtr drv = src->drv; + unsigned int access_user = drv->uid; + unsigned int access_group = drv->gid; if (src == parent) { virReportSystemError(errcode, diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index d39486ad59..a1e0afb28f 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -262,9 +262,6 @@ struct _virStorageSourceSlice { }; -typedef struct _virStorageDriverData virStorageDriverData; -typedef virStorageDriverData *virStorageDriverDataPtr; - typedef struct _virStorageSource virStorageSource; typedef virStorageSource *virStorageSourcePtr; @@ -334,7 +331,7 @@ struct _virStorageSource { virStorageSourcePtr backingStore; /* metadata for storage driver access to remote and local volumes */ - virStorageDriverDataPtr drv; + void *drv; /* metadata about storage image which need separate fields */ /* Relative name by which this image was opened from its parent, or NULL diff --git a/src/util/virstoragefilebackend.h b/src/util/virstoragefilebackend.h index c7af77bf8f..43b36e95bc 100644 --- a/src/util/virstoragefilebackend.h +++ b/src/util/virstoragefilebackend.h @@ -28,6 +28,8 @@ typedef struct _virStorageFileBackend virStorageFileBackend; typedef virStorageFileBackend *virStorageFileBackendPtr; +typedef struct _virStorageDriverData virStorageDriverData; +typedef virStorageDriverData *virStorageDriverDataPtr; struct _virStorageDriverData { virStorageFileBackendPtr backend; void *priv; -- 2.28.0

On Mon, Dec 14, 2020 at 16:55:31 +0100, Pavel Hrdina wrote:
This will allow following patches to move virStorageSource into conf directory and virStorageDriverData into a new storage_file directory.
Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/storage/storage_file_fs.c | 21 +++-- src/storage/storage_file_gluster.c | 31 +++++--- src/util/virstoragefile.c | 118 +++++++++++++++++++++-------- src/util/virstoragefile.h | 5 +- src/util/virstoragefilebackend.h | 2 + 5 files changed, 122 insertions(+), 55 deletions(-)
Reviewed-by: Peter Krempa <pkrempa@redhat.com>

Introduce a new storage_file directory where we will keep storage file related code. Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- po/POTFILES.in | 4 +- src/meson.build | 1 + src/storage/meson.build | 30 +-------------- src/storage_file/meson.build | 38 +++++++++++++++++++ .../storage_file_fs.c | 0 .../storage_file_fs.h | 0 .../storage_file_gluster.c | 0 .../storage_file_gluster.h | 0 8 files changed, 43 insertions(+), 30 deletions(-) create mode 100644 src/storage_file/meson.build rename src/{storage => storage_file}/storage_file_fs.c (100%) rename src/{storage => storage_file}/storage_file_fs.h (100%) rename src/{storage => storage_file}/storage_file_gluster.c (100%) rename src/{storage => storage_file}/storage_file_gluster.h (100%) diff --git a/po/POTFILES.in b/po/POTFILES.in index 14636d4b93..e9fc3991f1 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -222,9 +222,9 @@ @SRCDIR@src/storage/storage_backend_vstorage.c @SRCDIR@src/storage/storage_backend_zfs.c @SRCDIR@src/storage/storage_driver.c -@SRCDIR@src/storage/storage_file_fs.c -@SRCDIR@src/storage/storage_file_gluster.c @SRCDIR@src/storage/storage_util.c +@SRCDIR@src/storage_file/storage_file_fs.c +@SRCDIR@src/storage_file/storage_file_gluster.c @SRCDIR@src/test/test_driver.c @SRCDIR@src/util/iohelper.c @SRCDIR@src/util/viralloc.c diff --git a/src/meson.build b/src/meson.build index 7c478219d6..3aa9498b98 100644 --- a/src/meson.build +++ b/src/meson.build @@ -266,6 +266,7 @@ subdir('nwfilter') subdir('secret') subdir('security') subdir('storage') +subdir('storage_file') subdir('bhyve') subdir('esx') diff --git a/src/storage/meson.build b/src/storage/meson.build index b4cefe9a89..8537359e93 100644 --- a/src/storage/meson.build +++ b/src/storage/meson.build @@ -15,10 +15,6 @@ storage_backend_fs_sources = [ 'storage_backend_fs.c', ] -stoarge_file_fs_sources = [ - 'storage_file_fs.c', -] - storage_backend_disk_sources = [ 'storage_backend_disk.c', ] @@ -31,10 +27,6 @@ storage_backend_gluster_sources = [ 'storage_backend_gluster.c', ] -storage_file_gluster_sources = [ - 'storage_file_gluster.c', -] - storage_backend_iscsi_sources = [ 'storage_backend_iscsi.c', ] @@ -72,7 +64,6 @@ storage_backend_zfs_sources = [ ] storage_backend_install_dir = libdir / 'libvirt' / 'storage-backend' -storage_file_install_dir = libdir / 'libvirt' / 'storage-file' if conf.has('WITH_STORAGE') storage_driver_impl_lib = static_library( @@ -109,14 +100,6 @@ if conf.has('WITH_STORAGE') 'install_dir': storage_backend_install_dir, } - virt_modules += { - 'name': 'virt_storage_file_fs', - 'sources': [ - files(stoarge_file_fs_sources), - ], - 'install_dir': storage_file_install_dir, - } - virt_daemons += { 'name': 'virtstoraged', 'c_args': [ @@ -181,17 +164,6 @@ if conf.has('WITH_STORAGE_GLUSTER') ], 'install_dir': storage_backend_install_dir, } - - virt_modules += { - 'name': 'virt_storage_file_gluster', - 'sources': [ - files(storage_file_gluster_sources), - ], - 'deps': [ - glusterfs_dep, - ], - 'install_dir': storage_file_install_dir, - } endif if conf.has('WITH_STORAGE_ISCSI') @@ -303,3 +275,5 @@ if conf.has('WITH_STORAGE_ZFS') 'install_dir': storage_backend_install_dir, } endif + +storage_inc_dir = include_directories('.') diff --git a/src/storage_file/meson.build b/src/storage_file/meson.build new file mode 100644 index 0000000000..20eb0176fc --- /dev/null +++ b/src/storage_file/meson.build @@ -0,0 +1,38 @@ +stoarge_file_fs_sources = [ + 'storage_file_fs.c', +] + +storage_file_gluster_sources = [ + 'storage_file_gluster.c', +] + +storage_file_install_dir = libdir / 'libvirt' / 'storage-file' + +if conf.has('WITH_STORAGE') + virt_modules += { + 'name': 'virt_storage_file_fs', + 'sources': [ + files(stoarge_file_fs_sources), + ], + 'include': [ + storage_inc_dir, + ], + 'install_dir': storage_file_install_dir, + } +endif + +if conf.has('WITH_STORAGE_GLUSTER') + virt_modules += { + 'name': 'virt_storage_file_gluster', + 'sources': [ + files(storage_file_gluster_sources), + ], + 'include': [ + storage_inc_dir, + ], + 'deps': [ + glusterfs_dep, + ], + 'install_dir': storage_file_install_dir, + } +endif diff --git a/src/storage/storage_file_fs.c b/src/storage_file/storage_file_fs.c similarity index 100% rename from src/storage/storage_file_fs.c rename to src/storage_file/storage_file_fs.c diff --git a/src/storage/storage_file_fs.h b/src/storage_file/storage_file_fs.h similarity index 100% rename from src/storage/storage_file_fs.h rename to src/storage_file/storage_file_fs.h diff --git a/src/storage/storage_file_gluster.c b/src/storage_file/storage_file_gluster.c similarity index 100% rename from src/storage/storage_file_gluster.c rename to src/storage_file/storage_file_gluster.c diff --git a/src/storage/storage_file_gluster.h b/src/storage_file/storage_file_gluster.h similarity index 100% rename from src/storage/storage_file_gluster.h rename to src/storage_file/storage_file_gluster.h -- 2.28.0

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

On Mon, Dec 14, 2020 at 16:55:33 +0100, Pavel Hrdina wrote:
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.
I like this idea but I'm not a fan of the direction of the rename that happens along with it. Specifically rename of virStorageSource* to virStorageFile. All the functions here basically work on a virStorageSource so preferably we keep the name in both places the code for it exists, thus src/conf/storage_source.[ch] and src/storage_source/storage_source.[ch]. I'm aware that it might be confusing at first to have two files which can take functions for a single type, unfortunately the distinction seems necessary, since we have two sources which can create configuration based on virStorageSource. Alternatively when we want to avoid files with same name we can use storage_source_conf.c and storage_source.c
Signed-off-by: Pavel Hrdina <phrdina@redhat.com> ---
[...]
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 1bbf567847..43be8dd61a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1618,6 +1618,43 @@ virSecurityManagerVerify; virSecurityXATTRNamespaceDefined;
Having the name duality then also brings up the question how you picked the functions to move here, since ... The following ones actually access the storage directly:
+# storage_file/storage_file.h +virStorageFileAccess; +virStorageFileCanonicalizePath; +virStorageFileChown; +virStorageFileCreate; +virStorageFileGetBackingStoreStr; +virStorageFileGetUniqueIdentifier; +virStorageFileProbeFormat; +virStorageFileRead; +virStorageFileStat; +virStorageFileUnlink; +virStorageSourceUpdateBackingSizes; +virStorageSourceUpdateCapacity; +virStorageSourceUpdatePhysicalSize;
These are helper function to the backend access:
+virStorageFileInit; +virStorageFileInitAs; +virStorageFileDeinit; +virStorageFileSupportsAccess; +virStorageFileSupportsBackingChainTraversal; +virStorageFileSupportsCreate; +virStorageFileSupportsSecurityDriver;
These access storage indirectly:
+virStorageFileGetMetadata; +virStorageFileGetMetadataFromBuf; +virStorageFileGetMetadataFromFD; +virStorageFileReportBrokenChain;
This one is a qemuism and should be rather moved to src/qemu/qemu_block.c or qemu_domain.c
+virStorageSourceFindByNodeName;
These just allocate a virStorageSource based on parsing a string:
+virStorageSourceNewFromBacking; +virStorageSourceNewFromBackingAbsolute;
This is almost same as the above (actually it could be replaced by virStorageSourceNewFromBackingAbsolute + a check that the result is _PROTOCOL_RBD)
+virStorageSourceParseRBDColonString;
These are just helpers which look through a chain of virStorageSources or are not really accessing anything
+virStorageFileChainLookup; +virStorageFileGetRelativeBackingPath; +virStorageFileParseBackingStoreStr; +virStorageFileParseChainIndex;
These are actually private data XML formatter/parser
+virStorageSourcePrivateDataFormatRelPath; +virStorageSourcePrivateDataParseRelPath;
The naming mismatch comes from the years this code was gradually modified and I'd really prefer if everything unifies on virStorageSource since everything is actually related to virStorageSource. For the backends which do the actual access of storage we could rename it to virStorageSourceBackend.
diff --git a/src/storage_file/storage_file.c b/src/storage_file/storage_file.c new file mode 100644 index 0000000000..6bfeb26233 --- /dev/null +++ b/src/storage_file/storage_file.c @@ -0,0 +1,3845 @@ +/* + * storage_file.c: file utility functions for FS storage backend + * + * Copyright (C) 2007-2017 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <fcntl.h> +#include <unistd.h> + +#include "storage_file.h" +#include "viralloc.h" +#include "virendian.h" +#include "virfile.h" +#include "virhash.h" +#include "virjson.h" +#include "virlog.h" +#include "virstoragefilebackend.h" +#include "virstring.h" +#include "viruri.h" + +#define VIR_FROM_THIS VIR_FROM_STORAGE + +VIR_LOG_INIT("storage_file"); + + +enum lv_endian { + LV_LITTLE_ENDIAN = 1, /* 1234 */ + LV_BIG_ENDIAN /* 4321 */ +}; + +enum { + BACKING_STORE_OK, + BACKING_STORE_INVALID, + BACKING_STORE_ERROR, +}; + +#define FILE_TYPE_VERSIONS_LAST 3 + +struct FileEncryptionInfo { + int format; /* Encryption format to assign */ + + int magicOffset; /* Byte offset of the magic */ + const char *magic; /* Optional string of magic */ + + enum lv_endian endian; /* Endianness of file format */ + + int versionOffset; /* Byte offset from start of file + * where we find version number, + * -1 to always fail the version test, + * -2 to always pass the version test */ + int versionSize; /* Size in bytes of version data (0, 2, or 4) */ + int versionNumbers[FILE_TYPE_VERSIONS_LAST]; + /* Version numbers to validate. Zeroes are ignored. */ + + int modeOffset; /* Byte offset of the format native encryption mode */ + char modeValue; /* Value expected at offset */ + + int payloadOffset; /* start offset of the volume data (in 512 byte sectors) */ +}; + +struct FileTypeInfo { + int magicOffset; /* Byte offset of the magic */ + const char *magic; /* Optional string of file magic + * to check at head of file */ + enum lv_endian endian; /* Endianness of file format */ + + int versionOffset; /* Byte offset from start of file + * where we find version number, + * -1 to always fail the version test, + * -2 to always pass the version test */ + int versionSize; /* Size in bytes of version data (0, 2, or 4) */ + int versionNumbers[FILE_TYPE_VERSIONS_LAST]; + /* Version numbers to validate. Zeroes are ignored. */ + int sizeOffset; /* Byte offset from start of file + * where we find capacity info, + * -1 to use st_size as capacity */ + int sizeBytes; /* Number of bytes for size field */ + int sizeMultiplier; /* A scaling factor if size is not in bytes */ + /* Store a COW base image path (possibly relative), + * or NULL if there is no COW base image, to RES; + * return BACKING_STORE_* */ + const struct FileEncryptionInfo *cryptInfo; /* Encryption info */ + int (*getBackingStore)(char **res, int *format, + const char *buf, size_t buf_size); + int (*getFeatures)(virBitmapPtr *features, int format, + char *buf, ssize_t len); +}; + + +static int +cowGetBackingStore(char **, + int *, + const char *, + size_t); + +static int +qcowXGetBackingStore(char **, int *, + const char *, + size_t); + +static int +qcow2GetFeatures(virBitmapPtr *features, + int format, + char *buf, + ssize_t len); + +static int +vmdk4GetBackingStore(char **, + int *, + const char *, + size_t); + +static int +qedGetBackingStore(char **, + int *, + const char *, + size_t);
I'd also strongly prefer if this image detection code is stashed into a separate file so that I don't have to see it any time I deal with virStorageSource. I would not worry with a rename except for maybe the exported functions. [...]

On Mon, Jan 04, 2021 at 05:43:32PM +0100, Peter Krempa wrote:
On Mon, Dec 14, 2020 at 16:55:33 +0100, Pavel Hrdina wrote:
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.
I like this idea but I'm not a fan of the direction of the rename that happens along with it. Specifically rename of virStorageSource* to virStorageFile.
All the functions here basically work on a virStorageSource so preferably we keep the name in both places the code for it exists, thus src/conf/storage_source.[ch] and src/storage_source/storage_source.[ch].
I'm aware that it might be confusing at first to have two files which can take functions for a single type, unfortunately the distinction seems necessary, since we have two sources which can create configuration based on virStorageSource.
Alternatively when we want to avoid files with same name we can use storage_source_conf.c and storage_source.c
Sounds good, I'll use storage_source_conf and storage_source for the file names.
Signed-off-by: Pavel Hrdina <phrdina@redhat.com> ---
[...]
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 1bbf567847..43be8dd61a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1618,6 +1618,43 @@ virSecurityManagerVerify; virSecurityXATTRNamespaceDefined;
Having the name duality then also brings up the question how you picked the functions to move here, since ...
Basically I checked where they are used. Every function called from src/conf was moved to src/conf/storage_source and the rest to the new storage_file.
The following ones actually access the storage directly:
+# storage_file/storage_file.h +virStorageFileAccess; +virStorageFileCanonicalizePath; +virStorageFileChown; +virStorageFileCreate; +virStorageFileGetBackingStoreStr; +virStorageFileGetUniqueIdentifier; +virStorageFileProbeFormat; +virStorageFileRead; +virStorageFileStat; +virStorageFileUnlink; +virStorageSourceUpdateBackingSizes; +virStorageSourceUpdateCapacity; +virStorageSourceUpdatePhysicalSize;
These are helper function to the backend access:
+virStorageFileInit; +virStorageFileInitAs; +virStorageFileDeinit; +virStorageFileSupportsAccess; +virStorageFileSupportsBackingChainTraversal; +virStorageFileSupportsCreate; +virStorageFileSupportsSecurityDriver;
These access storage indirectly:
+virStorageFileGetMetadata; +virStorageFileGetMetadataFromBuf; +virStorageFileGetMetadataFromFD; +virStorageFileReportBrokenChain;
This one is a qemuism and should be rather moved to src/qemu/qemu_block.c or qemu_domain.c
+virStorageSourceFindByNodeName;
These just allocate a virStorageSource based on parsing a string:
+virStorageSourceNewFromBacking; +virStorageSourceNewFromBackingAbsolute;
This is almost same as the above (actually it could be replaced by virStorageSourceNewFromBackingAbsolute + a check that the result is _PROTOCOL_RBD)
+virStorageSourceParseRBDColonString;
These are just helpers which look through a chain of virStorageSources or are not really accessing anything
+virStorageFileChainLookup; +virStorageFileGetRelativeBackingPath; +virStorageFileParseBackingStoreStr; +virStorageFileParseChainIndex;
These are actually private data XML formatter/parser
+virStorageSourcePrivateDataFormatRelPath; +virStorageSourcePrivateDataParseRelPath;
Thanks for the explanation for what the functions are used but it doesn't make it clear to me where they should be placed.
The naming mismatch comes from the years this code was gradually modified and I'd really prefer if everything unifies on virStorageSource since everything is actually related to virStorageSource.
For the backends which do the actual access of storage we could rename it to virStorageSourceBackend.
Sounds good to match the naming of functions to the structure that it operates with but I'm afraid that it will make more confusion in the future if someone tries to add a new functionality into the virStorageSource code, especially if the conf or storage_file place should be used. I guess we should eventually somehow split the structure into two separate structures.
diff --git a/src/storage_file/storage_file.c b/src/storage_file/storage_file.c new file mode 100644 index 0000000000..6bfeb26233 --- /dev/null +++ b/src/storage_file/storage_file.c @@ -0,0 +1,3845 @@ +/* + * storage_file.c: file utility functions for FS storage backend + * + * Copyright (C) 2007-2017 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <fcntl.h> +#include <unistd.h> + +#include "storage_file.h" +#include "viralloc.h" +#include "virendian.h" +#include "virfile.h" +#include "virhash.h" +#include "virjson.h" +#include "virlog.h" +#include "virstoragefilebackend.h" +#include "virstring.h" +#include "viruri.h" + +#define VIR_FROM_THIS VIR_FROM_STORAGE + +VIR_LOG_INIT("storage_file"); + + +enum lv_endian { + LV_LITTLE_ENDIAN = 1, /* 1234 */ + LV_BIG_ENDIAN /* 4321 */ +}; + +enum { + BACKING_STORE_OK, + BACKING_STORE_INVALID, + BACKING_STORE_ERROR, +}; + +#define FILE_TYPE_VERSIONS_LAST 3 + +struct FileEncryptionInfo { + int format; /* Encryption format to assign */ + + int magicOffset; /* Byte offset of the magic */ + const char *magic; /* Optional string of magic */ + + enum lv_endian endian; /* Endianness of file format */ + + int versionOffset; /* Byte offset from start of file + * where we find version number, + * -1 to always fail the version test, + * -2 to always pass the version test */ + int versionSize; /* Size in bytes of version data (0, 2, or 4) */ + int versionNumbers[FILE_TYPE_VERSIONS_LAST]; + /* Version numbers to validate. Zeroes are ignored. */ + + int modeOffset; /* Byte offset of the format native encryption mode */ + char modeValue; /* Value expected at offset */ + + int payloadOffset; /* start offset of the volume data (in 512 byte sectors) */ +}; + +struct FileTypeInfo { + int magicOffset; /* Byte offset of the magic */ + const char *magic; /* Optional string of file magic + * to check at head of file */ + enum lv_endian endian; /* Endianness of file format */ + + int versionOffset; /* Byte offset from start of file + * where we find version number, + * -1 to always fail the version test, + * -2 to always pass the version test */ + int versionSize; /* Size in bytes of version data (0, 2, or 4) */ + int versionNumbers[FILE_TYPE_VERSIONS_LAST]; + /* Version numbers to validate. Zeroes are ignored. */ + int sizeOffset; /* Byte offset from start of file + * where we find capacity info, + * -1 to use st_size as capacity */ + int sizeBytes; /* Number of bytes for size field */ + int sizeMultiplier; /* A scaling factor if size is not in bytes */ + /* Store a COW base image path (possibly relative), + * or NULL if there is no COW base image, to RES; + * return BACKING_STORE_* */ + const struct FileEncryptionInfo *cryptInfo; /* Encryption info */ + int (*getBackingStore)(char **res, int *format, + const char *buf, size_t buf_size); + int (*getFeatures)(virBitmapPtr *features, int format, + char *buf, ssize_t len); +}; + + +static int +cowGetBackingStore(char **, + int *, + const char *, + size_t); + +static int +qcowXGetBackingStore(char **, int *, + const char *, + size_t); + +static int +qcow2GetFeatures(virBitmapPtr *features, + int format, + char *buf, + ssize_t len); + +static int +vmdk4GetBackingStore(char **, + int *, + const char *, + size_t); + +static int +qedGetBackingStore(char **, + int *, + const char *, + size_t);
I'd also strongly prefer if this image detection code is stashed into a separate file so that I don't have to see it any time I deal with virStorageSource. I would not worry with a rename except for maybe the exported functions.
Sure, sounds good. Thanks for the review. Pavel

On Thu, Jan 07, 2021 at 14:17:04 +0100, Pavel Hrdina wrote:
On Mon, Jan 04, 2021 at 05:43:32PM +0100, Peter Krempa wrote:
On Mon, Dec 14, 2020 at 16:55:33 +0100, Pavel Hrdina wrote:
[...]
Thanks for the explanation for what the functions are used but it doesn't make it clear to me where they should be placed.
The naming mismatch comes from the years this code was gradually modified and I'd really prefer if everything unifies on virStorageSource since everything is actually related to virStorageSource.
For the backends which do the actual access of storage we could rename it to virStorageSourceBackend.
Sounds good to match the naming of functions to the structure that it operates with but I'm afraid that it will make more confusion in the future if someone tries to add a new functionality into the virStorageSource code, especially if the conf or storage_file place should be used.
This should actually go well with the proposed rename. 'virStorageSourceConf' can become a structure dealing with just the XML bits, and it will be included in 'virStorageSource' which will house other stuff.

It's used only by storage file code so it doesn't make sense to have it in util directory. Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- po/POTFILES.in | 2 +- src/libvirt_private.syms | 8 ++++---- src/storage_file/meson.build | 3 ++- src/storage_file/storage_file.c | 2 +- .../storage_file_backend.c} | 4 ++-- .../storage_file_backend.h} | 2 +- src/storage_file/storage_file_fs.c | 2 +- src/storage_file/storage_file_gluster.c | 2 +- src/util/meson.build | 1 - 9 files changed, 13 insertions(+), 13 deletions(-) rename src/{util/virstoragefilebackend.c => storage_file/storage_file_backend.c} (97%) rename src/{util/virstoragefilebackend.h => storage_file/storage_file_backend.h} (97%) diff --git a/po/POTFILES.in b/po/POTFILES.in index 6d6678bd36..d3a0f85d91 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -224,6 +224,7 @@ @SRCDIR@src/storage/storage_driver.c @SRCDIR@src/storage/storage_util.c @SRCDIR@src/storage_file/storage_file.c +@SRCDIR@src/storage_file/storage_file_backend.c @SRCDIR@src/storage_file/storage_file_fs.c @SRCDIR@src/storage_file/storage_file_gluster.c @SRCDIR@src/test/test_driver.c @@ -306,7 +307,6 @@ @SRCDIR@src/util/virsocketaddr.c @SRCDIR@src/util/virstorageencryption.c @SRCDIR@src/util/virstoragefile.c -@SRCDIR@src/util/virstoragefilebackend.c @SRCDIR@src/util/virstring.c @SRCDIR@src/util/virsysinfo.c @SRCDIR@src/util/virsystemd.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 43be8dd61a..fe6683f728 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1655,6 +1655,10 @@ virStorageSourceUpdateCapacity; virStorageSourceUpdatePhysicalSize; +# storage_file/storage_file_backend.h +virStorageFileBackendRegister; + + # util/glibcompat.h vir_g_canonicalize_filename; vir_g_fsync; @@ -3211,10 +3215,6 @@ virStorageTypeFromString; virStorageTypeToString; -# util/virstoragefilebackend.h -virStorageFileBackendRegister; - - # util/virstring.h virSkipSpaces; virSkipSpacesAndBackslash; diff --git a/src/storage_file/meson.build b/src/storage_file/meson.build index b25ca63b40..4d85d16929 100644 --- a/src/storage_file/meson.build +++ b/src/storage_file/meson.build @@ -1,5 +1,6 @@ storage_file_sources = [ - 'storage_file.c' + 'storage_file.c', + 'storage_file_backend.c', ] stoarge_file_fs_sources = [ diff --git a/src/storage_file/storage_file.c b/src/storage_file/storage_file.c index 6bfeb26233..e5b2f68420 100644 --- a/src/storage_file/storage_file.c +++ b/src/storage_file/storage_file.c @@ -25,13 +25,13 @@ #include <unistd.h> #include "storage_file.h" +#include "storage_file_backend.h" #include "viralloc.h" #include "virendian.h" #include "virfile.h" #include "virhash.h" #include "virjson.h" #include "virlog.h" -#include "virstoragefilebackend.h" #include "virstring.h" #include "viruri.h" diff --git a/src/util/virstoragefilebackend.c b/src/storage_file/storage_file_backend.c similarity index 97% rename from src/util/virstoragefilebackend.c rename to src/storage_file/storage_file_backend.c index 55c62b0212..6e4b2f408b 100644 --- a/src/util/virstoragefilebackend.c +++ b/src/storage_file/storage_file_backend.c @@ -1,5 +1,5 @@ /* - * virstoragefilebackend.c: internal storage source backend contract + * storage_file_backend.c: internal storage source backend contract * * Copyright (C) 2007-2018 Red Hat, Inc. * Copyright (C) 2007-2008 Daniel P. Berrange @@ -27,7 +27,7 @@ #include "virerror.h" #include "viralloc.h" #include "internal.h" -#include "virstoragefilebackend.h" +#include "storage_file_backend.h" #include "virlog.h" #include "virmodule.h" #include "virfile.h" diff --git a/src/util/virstoragefilebackend.h b/src/storage_file/storage_file_backend.h similarity index 97% rename from src/util/virstoragefilebackend.h rename to src/storage_file/storage_file_backend.h index 43b36e95bc..ad0f66212c 100644 --- a/src/util/virstoragefilebackend.h +++ b/src/storage_file/storage_file_backend.h @@ -1,5 +1,5 @@ /* - * virstoragefilebackend.h: internal storage source backend contract + * storage_file_backend.h: internal storage source backend contract * * Copyright (C) 2007-2018 Red Hat, Inc. * diff --git a/src/storage_file/storage_file_fs.c b/src/storage_file/storage_file_fs.c index 5a44ef8c2d..1cc9233512 100644 --- a/src/storage_file/storage_file_fs.c +++ b/src/storage_file/storage_file_fs.c @@ -25,9 +25,9 @@ #include <fcntl.h> #include "virerror.h" +#include "storage_file_backend.h" #include "storage_file_fs.h" #include "storage_util.h" -#include "virstoragefilebackend.h" #include "vircommand.h" #include "viralloc.h" #include "virfile.h" diff --git a/src/storage_file/storage_file_gluster.c b/src/storage_file/storage_file_gluster.c index 599afada2c..786b39b6de 100644 --- a/src/storage_file/storage_file_gluster.c +++ b/src/storage_file/storage_file_gluster.c @@ -24,11 +24,11 @@ #include <glusterfs/api/glfs.h> #include "storage_file.h" +#include "storage_file_backend.h" #include "storage_file_gluster.h" #include "viralloc.h" #include "virerror.h" #include "virlog.h" -#include "virstoragefilebackend.h" #include "virstring.h" #define VIR_FROM_THIS VIR_FROM_STORAGE diff --git a/src/util/meson.build b/src/util/meson.build index 395e70fd38..b510f0ebe9 100644 --- a/src/util/meson.build +++ b/src/util/meson.build @@ -90,7 +90,6 @@ util_sources = [ 'virsocketaddr.c', 'virstorageencryption.c', 'virstoragefile.c', - 'virstoragefilebackend.c', 'virstring.c', 'virsysinfo.c', 'virsystemd.c', -- 2.28.0

The code handles XML bits and internal definition and should be in conf directory. Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- po/POTFILES.in | 1 + src/conf/backup_conf.c | 2 +- src/conf/checkpoint_conf.c | 2 +- src/conf/domain_conf.c | 2 +- src/conf/domain_conf.h | 4 +- src/conf/meson.build | 1 + src/conf/snapshot_conf.c | 2 +- src/conf/storage_conf.c | 2 +- src/conf/storage_conf.h | 2 +- src/conf/storage_source.c | 1316 +++++++++++++++++++++++ src/conf/storage_source.h | 518 +++++++++ src/esx/esx_storage_backend_iscsi.c | 2 +- src/esx/esx_storage_backend_vmfs.c | 2 +- src/libvirt_private.syms | 97 +- src/locking/lock_driver_lockd.c | 1 + src/qemu/qemu_backup.c | 2 +- src/qemu/qemu_blockjob.c | 2 +- src/qemu/qemu_command.c | 2 +- src/qemu/qemu_domain.c | 2 +- src/qemu/qemu_driver.c | 2 +- src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_migration.c | 2 +- src/qemu/qemu_process.h | 2 +- src/storage/storage_backend.c | 2 +- src/storage/storage_util.c | 1 + src/storage_file/meson.build | 3 + src/storage_file/storage_file.h | 2 +- src/storage_file/storage_file_backend.h | 2 +- src/util/virstoragefile.c | 1281 ---------------------- src/util/virstoragefile.h | 436 +------- 30 files changed, 1913 insertions(+), 1784 deletions(-) create mode 100644 src/conf/storage_source.c create mode 100644 src/conf/storage_source.h diff --git a/po/POTFILES.in b/po/POTFILES.in index d3a0f85d91..135198820a 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -43,6 +43,7 @@ @SRCDIR@src/conf/snapshot_conf.c @SRCDIR@src/conf/storage_adapter_conf.c @SRCDIR@src/conf/storage_conf.c +@SRCDIR@src/conf/storage_source.c @SRCDIR@src/conf/virchrdev.c @SRCDIR@src/conf/virdomainmomentobjlist.c @SRCDIR@src/conf/virdomainobjlist.c diff --git a/src/conf/backup_conf.c b/src/conf/backup_conf.c index 90ffcc51d1..7d4c466a9e 100644 --- a/src/conf/backup_conf.c +++ b/src/conf/backup_conf.c @@ -26,7 +26,7 @@ #include "virlog.h" #include "viralloc.h" #include "backup_conf.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "virfile.h" #include "virerror.h" #include "virxml.h" diff --git a/src/conf/checkpoint_conf.c b/src/conf/checkpoint_conf.c index 7edc7daa12..7b8e53ca75 100644 --- a/src/conf/checkpoint_conf.c +++ b/src/conf/checkpoint_conf.c @@ -30,7 +30,7 @@ #include "virlog.h" #include "viralloc.h" #include "checkpoint_conf.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "viruuid.h" #include "virfile.h" #include "virerror.h" diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 26c48ef38c..e1fc61073e 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -43,7 +43,7 @@ #include "nwfilter_conf.h" #include "virnetworkportdef.h" #include "storage_conf.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "virfile.h" #include "virbitmap.h" #include "secret_conf.h" diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index ce8ed48a04..5c95317501 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -48,7 +48,7 @@ #include "virobject.h" #include "device_conf.h" #include "virbitmap.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "virseclabel.h" #include "virprocess.h" #include "virgic.h" @@ -359,7 +359,7 @@ struct _virDomainHostdevDef { /* Types of disk frontend (guest view). For backends (host view), see - * virStorageType in util/virstoragefile.h */ + * virStorageType in util/storage_source.h */ typedef enum { VIR_DOMAIN_DISK_DEVICE_DISK, VIR_DOMAIN_DISK_DEVICE_CDROM, diff --git a/src/conf/meson.build b/src/conf/meson.build index 8ddcc9968d..12efaf56cc 100644 --- a/src/conf/meson.build +++ b/src/conf/meson.build @@ -54,6 +54,7 @@ storage_conf_sources = [ 'storage_adapter_conf.c', 'storage_capabilities.c', 'storage_conf.c', + 'storage_source.c', 'virstorageobj.c', ] diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c index f896fd1cf2..2abb14b4f6 100644 --- a/src/conf/snapshot_conf.c +++ b/src/conf/snapshot_conf.c @@ -38,7 +38,7 @@ #include "nwfilter_conf.h" #include "secret_conf.h" #include "snapshot_conf.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "viruuid.h" #include "virfile.h" #include "virerror.h" diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c index 0c50529ace..fa11d965cd 100644 --- a/src/conf/storage_conf.c +++ b/src/conf/storage_conf.c @@ -31,7 +31,7 @@ #include "node_device_conf.h" #include "storage_adapter_conf.h" #include "storage_conf.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "virxml.h" #include "viruuid.h" diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h index ffd406e093..47f2bf602d 100644 --- a/src/conf/storage_conf.h +++ b/src/conf/storage_conf.h @@ -23,7 +23,7 @@ #include "internal.h" #include "virstorageencryption.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "virbitmap.h" #include "virthread.h" #include "device_conf.h" diff --git a/src/conf/storage_source.c b/src/conf/storage_source.c new file mode 100644 index 0000000000..271dbc06c0 --- /dev/null +++ b/src/conf/storage_source.c @@ -0,0 +1,1316 @@ +/* + * 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 "storage_source.h" + +#include "viralloc.h" +#include "virbuffer.h" +#include "virerror.h" +#include "virlog.h" +#include "virstring.h" + +#define VIR_FROM_THIS VIR_FROM_STORAGE + +VIR_LOG_INIT("conf.storage_source"); + + +static virClassPtr virStorageSourceClass; + + +VIR_ENUM_IMPL(virStorage, + VIR_STORAGE_TYPE_LAST, + "none", + "file", + "block", + "dir", + "network", + "volume", + "nvme", +); + + +VIR_ENUM_IMPL(virStorageFileFormat, + VIR_STORAGE_FILE_LAST, + "none", + "raw", "dir", "bochs", + "cloop", "dmg", "iso", + "vpc", "vdi", + /* Not direct file formats, but used for various drivers */ + "fat", "vhd", "ploop", + /* Formats with backing file below here */ + "cow", "qcow", "qcow2", "qed", "vmdk", +); + + +VIR_ENUM_IMPL(virStorageFileFeature, + VIR_STORAGE_FILE_FEATURE_LAST, + "lazy_refcounts", +); + + +VIR_ENUM_IMPL(virStorageNetProtocol, + VIR_STORAGE_NET_PROTOCOL_LAST, + "none", + "nbd", + "rbd", + "sheepdog", + "gluster", + "iscsi", + "http", + "https", + "ftp", + "ftps", + "tftp", + "ssh", + "vxhs", +); + + +VIR_ENUM_IMPL(virStorageNetHostTransport, + VIR_STORAGE_NET_HOST_TRANS_LAST, + "tcp", + "unix", + "rdma", +); + + +VIR_ENUM_IMPL(virStorageSourcePoolMode, + VIR_STORAGE_SOURCE_POOL_MODE_LAST, + "default", + "host", + "direct", +); + + +VIR_ENUM_IMPL(virStorageAuth, + VIR_STORAGE_AUTH_TYPE_LAST, + "none", "chap", "ceph", +); + + +/** + * virStorageSourceIsBacking: + * @src: storage source + * + * Returns true if @src is a eligible backing store structure. Useful + * for iterators. + */ +bool +virStorageSourceIsBacking(const virStorageSource *src) +{ + return src && src->type != VIR_STORAGE_TYPE_NONE; +} + +/** + * virStorageSourceHasBacking: + * @src: storage source + * + * Returns true if @src has backing store/chain. + */ +bool +virStorageSourceHasBacking(const virStorageSource *src) +{ + return virStorageSourceIsBacking(src) && src->backingStore && + src->backingStore->type != VIR_STORAGE_TYPE_NONE; +} + + +void +virStorageNetHostDefClear(virStorageNetHostDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->name); + VIR_FREE(def->socket); +} + + +void +virStorageNetHostDefFree(size_t nhosts, + virStorageNetHostDefPtr hosts) +{ + size_t i; + + if (!hosts) + return; + + for (i = 0; i < nhosts; i++) + virStorageNetHostDefClear(&hosts[i]); + + VIR_FREE(hosts); +} + + +static void +virStoragePermsFree(virStoragePermsPtr def) +{ + if (!def) + return; + + VIR_FREE(def->label); + VIR_FREE(def); +} + + +virStorageNetHostDefPtr +virStorageNetHostDefCopy(size_t nhosts, + virStorageNetHostDefPtr hosts) +{ + virStorageNetHostDefPtr ret = NULL; + size_t i; + + ret = g_new0(virStorageNetHostDef, nhosts); + + for (i = 0; i < nhosts; i++) { + virStorageNetHostDefPtr src = &hosts[i]; + virStorageNetHostDefPtr dst = &ret[i]; + + dst->transport = src->transport; + dst->port = src->port; + + dst->name = g_strdup(src->name); + dst->socket = g_strdup(src->socket); + } + + return ret; +} + + +void +virStorageAuthDefFree(virStorageAuthDefPtr authdef) +{ + if (!authdef) + return; + + VIR_FREE(authdef->username); + VIR_FREE(authdef->secrettype); + virSecretLookupDefClear(&authdef->seclookupdef); + VIR_FREE(authdef); +} + + +virStorageAuthDefPtr +virStorageAuthDefCopy(const virStorageAuthDef *src) +{ + g_autoptr(virStorageAuthDef) authdef = NULL; + + authdef = g_new0(virStorageAuthDef, 1); + + authdef->username = g_strdup(src->username); + /* Not present for storage pool, but used for disk source */ + authdef->secrettype = g_strdup(src->secrettype); + authdef->authType = src->authType; + + virSecretLookupDefCopy(&authdef->seclookupdef, &src->seclookupdef); + + return g_steal_pointer(&authdef); +} + + +virStorageAuthDefPtr +virStorageAuthDefParse(xmlNodePtr node, + xmlXPathContextPtr ctxt) +{ + VIR_XPATH_NODE_AUTORESTORE(ctxt) + virStorageAuthDefPtr ret = NULL; + xmlNodePtr secretnode = NULL; + g_autoptr(virStorageAuthDef) authdef = NULL; + g_autofree char *authtype = NULL; + + ctxt->node = node; + + authdef = g_new0(virStorageAuthDef, 1); + + if (!(authdef->username = virXPathString("string(./@username)", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing username for auth")); + goto cleanup; + } + + authdef->authType = VIR_STORAGE_AUTH_TYPE_NONE; + authtype = virXPathString("string(./@type)", ctxt); + if (authtype) { + /* Used by the storage pool instead of the secret type field + * to define whether chap or ceph being used + */ + if ((authdef->authType = virStorageAuthTypeFromString(authtype)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown auth type '%s'"), authtype); + goto cleanup; + } + } + + if (!(secretnode = virXPathNode("./secret ", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing <secret> element in auth")); + goto cleanup; + } + + /* Used by the domain disk xml parsing in order to ensure the + * <secret type='%s' value matches the expected secret type for + * the style of disk (iscsi is chap, nbd is ceph). For some reason + * the virSecretUsageType{From|To}String() cannot be linked here + * and because only the domain parsing code cares - just keep + * it as a string. + */ + authdef->secrettype = virXMLPropString(secretnode, "type"); + + if (virSecretLookupParseSecret(secretnode, &authdef->seclookupdef) < 0) + goto cleanup; + + ret = g_steal_pointer(&authdef); + + cleanup: + + return ret; +} + + +void +virStorageAuthDefFormat(virBufferPtr buf, + virStorageAuthDefPtr authdef) +{ + if (authdef->authType == VIR_STORAGE_AUTH_TYPE_NONE) { + virBufferEscapeString(buf, "<auth username='%s'>\n", authdef->username); + } else { + virBufferAsprintf(buf, "<auth type='%s' ", + virStorageAuthTypeToString(authdef->authType)); + virBufferEscapeString(buf, "username='%s'>\n", authdef->username); + } + + virBufferAdjustIndent(buf, 2); + virSecretLookupFormatSecret(buf, authdef->secrettype, + &authdef->seclookupdef); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</auth>\n"); +} + + +void +virStoragePRDefFree(virStoragePRDefPtr prd) +{ + if (!prd) + return; + + VIR_FREE(prd->path); + VIR_FREE(prd->mgralias); + VIR_FREE(prd); +} + + +virStoragePRDefPtr +virStoragePRDefParseXML(xmlXPathContextPtr ctxt) +{ + virStoragePRDefPtr prd; + virStoragePRDefPtr ret = NULL; + g_autofree char *managed = NULL; + g_autofree char *type = NULL; + g_autofree char *path = NULL; + g_autofree char *mode = NULL; + + prd = g_new0(virStoragePRDef, 1); + + if (!(managed = virXPathString("string(./@managed)", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing @managed attribute for <reservations/>")); + goto cleanup; + } + + if ((prd->managed = virTristateBoolTypeFromString(managed)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid value for 'managed': %s"), managed); + goto cleanup; + } + + type = virXPathString("string(./source[1]/@type)", ctxt); + path = virXPathString("string(./source[1]/@path)", ctxt); + mode = virXPathString("string(./source[1]/@mode)", ctxt); + + if (prd->managed == VIR_TRISTATE_BOOL_NO || type || path || mode) { + if (!type) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing connection type for <reservations/>")); + goto cleanup; + } + + if (!path) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing path for <reservations/>")); + goto cleanup; + } + + if (!mode) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing connection mode for <reservations/>")); + goto cleanup; + } + } + + if (type && STRNEQ(type, "unix")) { + virReportError(VIR_ERR_XML_ERROR, + _("unsupported connection type for <reservations/>: %s"), + type); + goto cleanup; + } + + if (mode && STRNEQ(mode, "client")) { + virReportError(VIR_ERR_XML_ERROR, + _("unsupported connection mode for <reservations/>: %s"), + mode); + goto cleanup; + } + + prd->path = g_steal_pointer(&path); + ret = g_steal_pointer(&prd); + + cleanup: + virStoragePRDefFree(prd); + return ret; +} + + +void +virStoragePRDefFormat(virBufferPtr buf, + virStoragePRDefPtr prd, + bool migratable) +{ + virBufferAsprintf(buf, "<reservations managed='%s'", + virTristateBoolTypeToString(prd->managed)); + if (prd->path && + (prd->managed == VIR_TRISTATE_BOOL_NO || !migratable)) { + virBufferAddLit(buf, ">\n"); + virBufferAdjustIndent(buf, 2); + virBufferAddLit(buf, "<source type='unix'"); + virBufferEscapeString(buf, " path='%s'", prd->path); + virBufferAddLit(buf, " mode='client'/>\n"); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</reservations>\n"); + } else { + virBufferAddLit(buf, "/>\n"); + } +} + + +bool +virStoragePRDefIsEqual(virStoragePRDefPtr a, + virStoragePRDefPtr b) +{ + if (!a && !b) + return true; + + if (!a || !b) + return false; + + if (a->managed != b->managed || + STRNEQ_NULLABLE(a->path, b->path)) + return false; + + return true; +} + + +bool +virStoragePRDefIsManaged(virStoragePRDefPtr prd) +{ + return prd && prd->managed == VIR_TRISTATE_BOOL_YES; +} + + +bool +virStorageSourceChainHasManagedPR(virStorageSourcePtr src) +{ + virStorageSourcePtr n; + + for (n = src; virStorageSourceIsBacking(n); n = n->backingStore) { + if (virStoragePRDefIsManaged(n->pr)) + return true; + } + + return false; +} + + +static virStoragePRDefPtr +virStoragePRDefCopy(virStoragePRDefPtr src) +{ + virStoragePRDefPtr copy = NULL; + virStoragePRDefPtr ret = NULL; + + copy = g_new0(virStoragePRDef, 1); + + copy->managed = src->managed; + + copy->path = g_strdup(src->path); + copy->mgralias = g_strdup(src->mgralias); + + ret = g_steal_pointer(©); + + virStoragePRDefFree(copy); + return ret; +} + + +static virStorageSourceNVMeDefPtr +virStorageSourceNVMeDefCopy(const virStorageSourceNVMeDef *src) +{ + virStorageSourceNVMeDefPtr ret = NULL; + + ret = g_new0(virStorageSourceNVMeDef, 1); + + ret->namespc = src->namespc; + ret->managed = src->managed; + virPCIDeviceAddressCopy(&ret->pciAddr, &src->pciAddr); + return ret; +} + + +static bool +virStorageSourceNVMeDefIsEqual(const virStorageSourceNVMeDef *a, + const virStorageSourceNVMeDef *b) +{ + if (!a && !b) + return true; + + if (!a || !b) + return false; + + if (a->namespc != b->namespc || + a->managed != b->managed || + !virPCIDeviceAddressEqual(&a->pciAddr, &b->pciAddr)) + return false; + + return true; +} + + +void +virStorageSourceNVMeDefFree(virStorageSourceNVMeDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def); +} + + +bool +virStorageSourceChainHasNVMe(const virStorageSource *src) +{ + const virStorageSource *n; + + for (n = src; virStorageSourceIsBacking(n); n = n->backingStore) { + if (n->type == VIR_STORAGE_TYPE_NVME) + return true; + } + + return false; +} + + +virSecurityDeviceLabelDefPtr +virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src, + const char *model) +{ + size_t i; + + for (i = 0; i < src->nseclabels; i++) { + if (STREQ_NULLABLE(src->seclabels[i]->model, model)) + return src->seclabels[i]; + } + + return NULL; +} + + +static void +virStorageSourceSeclabelsClear(virStorageSourcePtr def) +{ + size_t i; + + if (def->seclabels) { + for (i = 0; i < def->nseclabels; i++) + virSecurityDeviceLabelDefFree(def->seclabels[i]); + VIR_FREE(def->seclabels); + } +} + + +static int +virStorageSourceSeclabelsCopy(virStorageSourcePtr to, + const virStorageSource *from) +{ + size_t i; + + if (from->nseclabels == 0) + return 0; + + to->seclabels = g_new0(virSecurityDeviceLabelDefPtr, from->nseclabels); + to->nseclabels = from->nseclabels; + + for (i = 0; i < to->nseclabels; i++) { + if (!(to->seclabels[i] = virSecurityDeviceLabelDefCopy(from->seclabels[i]))) + goto error; + } + + return 0; + + error: + virStorageSourceSeclabelsClear(to); + return -1; +} + + +void +virStorageNetCookieDefFree(virStorageNetCookieDefPtr def) +{ + if (!def) + return; + + g_free(def->name); + g_free(def->value); + + g_free(def); +} + + +static void +virStorageSourceNetCookiesClear(virStorageSourcePtr src) +{ + size_t i; + + if (!src || !src->cookies) + return; + + for (i = 0; i < src->ncookies; i++) + virStorageNetCookieDefFree(src->cookies[i]); + + g_clear_pointer(&src->cookies, g_free); + src->ncookies = 0; +} + + +static void +virStorageSourceNetCookiesCopy(virStorageSourcePtr to, + const virStorageSource *from) +{ + size_t i; + + if (from->ncookies == 0) + return; + + to->cookies = g_new0(virStorageNetCookieDefPtr, from->ncookies); + to->ncookies = from->ncookies; + + for (i = 0; i < from->ncookies; i++) { + to->cookies[i]->name = g_strdup(from->cookies[i]->name); + to->cookies[i]->value = g_strdup(from->cookies[i]->value); + } +} + + +/* see https://tools.ietf.org/html/rfc6265#section-4.1.1 */ +static const char virStorageSourceCookieValueInvalidChars[] = + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" + " \",;\\"; + +/* in addition cookie name can't contain these */ +static const char virStorageSourceCookieNameInvalidChars[] = + "()<>@:/[]?={}"; + +static int +virStorageSourceNetCookieValidate(virStorageNetCookieDefPtr def) +{ + g_autofree char *val = g_strdup(def->value); + const char *checkval = val; + size_t len = strlen(val); + + /* name must have at least 1 character */ + if (*(def->name) == '\0') { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("cookie name must not be empty")); + return -1; + } + + /* check invalid characters in name */ + if (virStringHasChars(def->name, virStorageSourceCookieValueInvalidChars) || + virStringHasChars(def->name, virStorageSourceCookieNameInvalidChars)) { + virReportError(VIR_ERR_XML_ERROR, + _("cookie name '%s' contains invalid characters"), + def->name); + return -1; + } + + /* check for optional quotes around the cookie value string */ + if (val[0] == '"') { + if (val[len - 1] != '"') { + virReportError(VIR_ERR_XML_ERROR, + _("value of cookie '%s' contains invalid characters"), + def->name); + return -1; + } + + val[len - 1] = '\0'; + checkval++; + } + + /* check invalid characters in value */ + if (virStringHasChars(checkval, virStorageSourceCookieValueInvalidChars)) { + virReportError(VIR_ERR_XML_ERROR, + _("value of cookie '%s' contains invalid characters"), + def->name); + return -1; + } + + return 0; +} + + +int +virStorageSourceNetCookiesValidate(virStorageSourcePtr src) +{ + size_t i; + size_t j; + + for (i = 0; i < src->ncookies; i++) { + if (virStorageSourceNetCookieValidate(src->cookies[i]) < 0) + return -1; + + for (j = i + 1; j < src->ncookies; j++) { + if (STREQ(src->cookies[i]->name, src->cookies[j]->name)) { + virReportError(VIR_ERR_XML_ERROR, _("duplicate cookie '%s'"), + src->cookies[i]->name); + return -1; + } + } + } + + return 0; +} + + +static virStorageTimestampsPtr +virStorageTimestampsCopy(const virStorageTimestamps *src) +{ + virStorageTimestampsPtr ret; + + ret = g_new0(virStorageTimestamps, 1); + + memcpy(ret, src, sizeof(*src)); + + return ret; +} + + +static virStoragePermsPtr +virStoragePermsCopy(const virStoragePerms *src) +{ + virStoragePermsPtr ret; + + ret = g_new0(virStoragePerms, 1); + + ret->mode = src->mode; + ret->uid = src->uid; + ret->gid = src->gid; + + ret->label = g_strdup(src->label); + + return ret; +} + + +static virStorageSourcePoolDefPtr +virStorageSourcePoolDefCopy(const virStorageSourcePoolDef *src) +{ + virStorageSourcePoolDefPtr ret; + + ret = g_new0(virStorageSourcePoolDef, 1); + + ret->voltype = src->voltype; + ret->pooltype = src->pooltype; + ret->actualtype = src->actualtype; + ret->mode = src->mode; + + ret->pool = g_strdup(src->pool); + ret->volume = g_strdup(src->volume); + + return ret; +} + + +static virStorageSourceSlicePtr +virStorageSourceSliceCopy(const virStorageSourceSlice *src) +{ + virStorageSourceSlicePtr ret = g_new0(virStorageSourceSlice, 1); + + ret->offset = src->offset; + ret->size = src->size; + ret->nodename = g_strdup(src->nodename); + + return ret; +} + + +static void +virStorageSourceSliceFree(virStorageSourceSlicePtr slice) +{ + if (!slice) + return; + + g_free(slice->nodename); + g_free(slice); +} + + +/** + * virStorageSourcePtr: + * + * Deep-copies a virStorageSource structure. If @backing chain is true + * then also copies the backing chain recursively, otherwise just + * the top element is copied. This function doesn't copy the + * storage driver access structure and thus the struct needs to be initialized + * separately. + */ +virStorageSourcePtr +virStorageSourceCopy(const virStorageSource *src, + bool backingChain) +{ + g_autoptr(virStorageSource) def = virStorageSourceNew(); + + def->id = src->id; + def->type = src->type; + def->protocol = src->protocol; + def->format = src->format; + def->capacity = src->capacity; + def->allocation = src->allocation; + def->has_allocation = src->has_allocation; + def->physical = src->physical; + def->readonly = src->readonly; + def->shared = src->shared; + def->haveTLS = src->haveTLS; + def->tlsFromConfig = src->tlsFromConfig; + def->detected = src->detected; + def->debugLevel = src->debugLevel; + def->debug = src->debug; + def->iomode = src->iomode; + def->cachemode = src->cachemode; + def->discard = src->discard; + def->detect_zeroes = src->detect_zeroes; + def->sslverify = src->sslverify; + def->readahead = src->readahead; + def->timeout = src->timeout; + + /* storage driver metadata are not copied */ + def->drv = NULL; + + def->path = g_strdup(src->path); + def->volume = g_strdup(src->volume); + def->relPath = g_strdup(src->relPath); + def->backingStoreRaw = g_strdup(src->backingStoreRaw); + def->backingStoreRawFormat = src->backingStoreRawFormat; + def->snapshot = g_strdup(src->snapshot); + def->configFile = g_strdup(src->configFile); + def->nodeformat = g_strdup(src->nodeformat); + def->nodestorage = g_strdup(src->nodestorage); + def->compat = g_strdup(src->compat); + def->tlsAlias = g_strdup(src->tlsAlias); + def->tlsCertdir = g_strdup(src->tlsCertdir); + def->query = g_strdup(src->query); + + if (src->sliceStorage) + def->sliceStorage = virStorageSourceSliceCopy(src->sliceStorage); + + if (src->nhosts) { + if (!(def->hosts = virStorageNetHostDefCopy(src->nhosts, src->hosts))) + return NULL; + + def->nhosts = src->nhosts; + } + + virStorageSourceNetCookiesCopy(def, src); + + if (src->srcpool && + !(def->srcpool = virStorageSourcePoolDefCopy(src->srcpool))) + return NULL; + + if (src->features) + def->features = virBitmapNewCopy(src->features); + + if (src->encryption && + !(def->encryption = virStorageEncryptionCopy(src->encryption))) + return NULL; + + if (src->perms && + !(def->perms = virStoragePermsCopy(src->perms))) + return NULL; + + if (src->timestamps && + !(def->timestamps = virStorageTimestampsCopy(src->timestamps))) + return NULL; + + if (virStorageSourceSeclabelsCopy(def, src) < 0) + return NULL; + + if (src->auth && + !(def->auth = virStorageAuthDefCopy(src->auth))) + return NULL; + + if (src->pr && + !(def->pr = virStoragePRDefCopy(src->pr))) + return NULL; + + if (src->nvme) + def->nvme = virStorageSourceNVMeDefCopy(src->nvme); + + if (virStorageSourceInitiatorCopy(&def->initiator, &src->initiator) < 0) + return NULL; + + if (backingChain && src->backingStore) { + if (!(def->backingStore = virStorageSourceCopy(src->backingStore, + true))) + return NULL; + } + + /* ssh config passthrough for libguestfs */ + def->ssh_host_key_check_disabled = src->ssh_host_key_check_disabled; + def->ssh_user = g_strdup(src->ssh_user); + + return g_steal_pointer(&def); +} + + +/** + * virStorageSourceIsSameLocation: + * + * Returns true if the sources @a and @b point to the same storage location. + * This does not compare any other configuration option + */ +bool +virStorageSourceIsSameLocation(virStorageSourcePtr a, + virStorageSourcePtr b) +{ + size_t i; + + /* there are multiple possibilities to define an empty source */ + if (virStorageSourceIsEmpty(a) && + virStorageSourceIsEmpty(b)) + return true; + + if (virStorageSourceGetActualType(a) != virStorageSourceGetActualType(b)) + return false; + + if (STRNEQ_NULLABLE(a->path, b->path) || + STRNEQ_NULLABLE(a->volume, b->volume) || + STRNEQ_NULLABLE(a->snapshot, b->snapshot)) + return false; + + if (a->type == VIR_STORAGE_TYPE_NETWORK) { + if (a->protocol != b->protocol || + a->nhosts != b->nhosts) + return false; + + for (i = 0; i < a->nhosts; i++) { + if (a->hosts[i].transport != b->hosts[i].transport || + a->hosts[i].port != b->hosts[i].port || + STRNEQ_NULLABLE(a->hosts[i].name, b->hosts[i].name) || + STRNEQ_NULLABLE(a->hosts[i].socket, b->hosts[i].socket)) + return false; + } + } + + if (a->type == VIR_STORAGE_TYPE_NVME && + !virStorageSourceNVMeDefIsEqual(a->nvme, b->nvme)) + return false; + + return true; +} + + +/** + * virStorageSourceInitChainElement: + * @newelem: New backing chain element disk source + * @old: Existing top level disk source + * @transferLabels: Transfer security labels. + * + * Transfers relevant information from the existing disk source to the new + * backing chain element if they weren't supplied so that labelling info + * and possibly other stuff is correct. + * + * If @transferLabels is true, security labels from the existing disk are copied + * to the new disk. Otherwise the default domain imagelabel label will be used. + * + * Returns 0 on success, -1 on error. + */ +int +virStorageSourceInitChainElement(virStorageSourcePtr newelem, + virStorageSourcePtr old, + bool transferLabels) +{ + if (transferLabels && + !newelem->seclabels && + virStorageSourceSeclabelsCopy(newelem, old) < 0) + return -1; + + newelem->shared = old->shared; + newelem->readonly = old->readonly; + + return 0; +} + + +void +virStorageSourcePoolDefFree(virStorageSourcePoolDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->pool); + VIR_FREE(def->volume); + + VIR_FREE(def); +} + + +/** + * virStorageSourceGetActualType: + * @def: storage source definition + * + * Returns type of @def. In case when the type is VIR_STORAGE_TYPE_VOLUME + * and virDomainDiskTranslateSourcePool was called on @def the actual type + * of the storage volume is returned rather than VIR_STORAGE_TYPE_VOLUME. + */ +int +virStorageSourceGetActualType(const virStorageSource *def) +{ + if (def->type == VIR_STORAGE_TYPE_VOLUME && + def->srcpool && + def->srcpool->actualtype != VIR_STORAGE_TYPE_NONE) + return def->srcpool->actualtype; + + return def->type; +} + + +bool +virStorageSourceIsLocalStorage(const virStorageSource *src) +{ + virStorageType type = virStorageSourceGetActualType(src); + + switch (type) { + case VIR_STORAGE_TYPE_FILE: + case VIR_STORAGE_TYPE_BLOCK: + case VIR_STORAGE_TYPE_DIR: + return true; + + case VIR_STORAGE_TYPE_NETWORK: + case VIR_STORAGE_TYPE_VOLUME: + /* While NVMe disks are local, they are not accessible via src->path. + * Therefore, we have to return false here. */ + case VIR_STORAGE_TYPE_NVME: + case VIR_STORAGE_TYPE_LAST: + case VIR_STORAGE_TYPE_NONE: + return false; + } + + return false; +} + + +/** + * virStorageSourceIsEmpty: + * + * @src: disk source to check + * + * Returns true if the guest disk has no associated host storage source + * (such as an empty cdrom drive). + */ +bool +virStorageSourceIsEmpty(virStorageSourcePtr src) +{ + if (virStorageSourceIsLocalStorage(src) && !src->path) + return true; + + if (src->type == VIR_STORAGE_TYPE_NONE) + return true; + + if (src->type == VIR_STORAGE_TYPE_NETWORK && + src->protocol == VIR_STORAGE_NET_PROTOCOL_NONE) + return true; + + return false; +} + + +/** + * virStorageSourceIsBlockLocal: + * @src: disk source definition + * + * Returns true if @src describes a locally accessible block storage source. + * This includes block devices and host-mapped iSCSI volumes. + */ +bool +virStorageSourceIsBlockLocal(const virStorageSource *src) +{ + return virStorageSourceGetActualType(src) == VIR_STORAGE_TYPE_BLOCK; +} + + +/** + * virStorageSourceBackingStoreClear: + * + * @src: disk source to clear + * + * Clears information about backing store of the current storage file. + */ +void +virStorageSourceBackingStoreClear(virStorageSourcePtr def) +{ + if (!def) + return; + + VIR_FREE(def->relPath); + VIR_FREE(def->backingStoreRaw); + + /* recursively free backing chain */ + virObjectUnref(def->backingStore); + def->backingStore = NULL; +} + + +void +virStorageSourceClear(virStorageSourcePtr def) +{ + if (!def) + return; + + VIR_FREE(def->path); + VIR_FREE(def->volume); + VIR_FREE(def->snapshot); + VIR_FREE(def->configFile); + VIR_FREE(def->query); + virStorageSourceNetCookiesClear(def); + virStorageSourcePoolDefFree(def->srcpool); + virBitmapFree(def->features); + VIR_FREE(def->compat); + virStorageEncryptionFree(def->encryption); + virStoragePRDefFree(def->pr); + virStorageSourceNVMeDefFree(def->nvme); + virStorageSourceSeclabelsClear(def); + virStoragePermsFree(def->perms); + VIR_FREE(def->timestamps); + + virStorageSourceSliceFree(def->sliceStorage); + + virStorageNetHostDefFree(def->nhosts, def->hosts); + virStorageAuthDefFree(def->auth); + virObjectUnref(def->privateData); + + VIR_FREE(def->nodestorage); + VIR_FREE(def->nodeformat); + + virStorageSourceBackingStoreClear(def); + + VIR_FREE(def->tlsAlias); + VIR_FREE(def->tlsCertdir); + + VIR_FREE(def->ssh_user); + + virStorageSourceInitiatorClear(&def->initiator); + + /* clear everything except the class header as the object APIs + * will break otherwise */ + memset((char *) def + sizeof(def->parent), 0, + sizeof(*def) - sizeof(def->parent)); +} + + +static void +virStorageSourceDispose(void *obj) +{ + virStorageSourcePtr src = obj; + + virStorageSourceClear(src); +} + + +static int +virStorageSourceOnceInit(void) +{ + if (!VIR_CLASS_NEW(virStorageSource, virClassForObject())) + return -1; + + return 0; +} + + +VIR_ONCE_GLOBAL_INIT(virStorageSource); + + +virStorageSourcePtr +virStorageSourceNew(void) +{ + virStorageSourcePtr ret; + + if (virStorageSourceInitialize() < 0) + abort(); + + if (!(ret = virObjectNew(virStorageSourceClass))) + abort(); + + return ret; +} + + +/** + * virStorageSourceIsRelative: + * @src: storage source to check + * + * Returns true if given storage source definition is a relative path. + */ +bool +virStorageSourceIsRelative(virStorageSourcePtr src) +{ + virStorageType actual_type = virStorageSourceGetActualType(src); + + if (!src->path) + return false; + + switch (actual_type) { + case VIR_STORAGE_TYPE_FILE: + case VIR_STORAGE_TYPE_BLOCK: + case VIR_STORAGE_TYPE_DIR: + return src->path[0] != '/'; + + case VIR_STORAGE_TYPE_NETWORK: + case VIR_STORAGE_TYPE_VOLUME: + case VIR_STORAGE_TYPE_NVME: + case VIR_STORAGE_TYPE_NONE: + case VIR_STORAGE_TYPE_LAST: + return false; + } + + return false; +} + + +static unsigned int +virStorageSourceNetworkDefaultPort(virStorageNetProtocol protocol) +{ + switch (protocol) { + case VIR_STORAGE_NET_PROTOCOL_HTTP: + return 80; + + case VIR_STORAGE_NET_PROTOCOL_HTTPS: + return 443; + + case VIR_STORAGE_NET_PROTOCOL_FTP: + return 21; + + case VIR_STORAGE_NET_PROTOCOL_FTPS: + return 990; + + case VIR_STORAGE_NET_PROTOCOL_TFTP: + return 69; + + case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: + return 7000; + + case VIR_STORAGE_NET_PROTOCOL_NBD: + return 10809; + + case VIR_STORAGE_NET_PROTOCOL_SSH: + return 22; + + case VIR_STORAGE_NET_PROTOCOL_ISCSI: + return 3260; + + case VIR_STORAGE_NET_PROTOCOL_GLUSTER: + return 24007; + + case VIR_STORAGE_NET_PROTOCOL_RBD: + /* we don't provide a default for RBD */ + return 0; + + case VIR_STORAGE_NET_PROTOCOL_VXHS: + return 9999; + + case VIR_STORAGE_NET_PROTOCOL_LAST: + case VIR_STORAGE_NET_PROTOCOL_NONE: + return 0; + } + + return 0; +} + + +void +virStorageSourceNetworkAssignDefaultPorts(virStorageSourcePtr src) +{ + size_t i; + + for (i = 0; i < src->nhosts; i++) { + if (src->hosts[i].transport == VIR_STORAGE_NET_HOST_TRANS_TCP && + src->hosts[i].port == 0) + src->hosts[i].port = virStorageSourceNetworkDefaultPort(src->protocol); + } +} + + +void +virStorageSourceInitiatorParseXML(xmlXPathContextPtr ctxt, + virStorageSourceInitiatorDefPtr initiator) +{ + initiator->iqn = virXPathString("string(./initiator/iqn/@name)", ctxt); +} + + +void +virStorageSourceInitiatorFormatXML(virStorageSourceInitiatorDefPtr initiator, + virBufferPtr buf) +{ + if (!initiator->iqn) + return; + + virBufferAddLit(buf, "<initiator>\n"); + virBufferAdjustIndent(buf, 2); + virBufferEscapeString(buf, "<iqn name='%s'/>\n", initiator->iqn); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</initiator>\n"); +} + + +int +virStorageSourceInitiatorCopy(virStorageSourceInitiatorDefPtr dest, + const virStorageSourceInitiatorDef *src) +{ + dest->iqn = g_strdup(src->iqn); + return 0; +} + + +void +virStorageSourceInitiatorClear(virStorageSourceInitiatorDefPtr initiator) +{ + VIR_FREE(initiator->iqn); +} diff --git a/src/conf/storage_source.h b/src/conf/storage_source.h new file mode 100644 index 0000000000..89bba38599 --- /dev/null +++ b/src/conf/storage_source.h @@ -0,0 +1,518 @@ +/* + * 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 "virbitmap.h" +#include "virenum.h" +#include "virobject.h" +#include "virpci.h" +#include "virseclabel.h" +#include "virsecret.h" +#include "virstorageencryption.h" + +/* Types of disk backends (host resource). Comparable to the public + * virStorageVolType, except we have an undetermined state, don't have + * a netdir type, and add a volume type for reference through a + * storage pool. */ +typedef enum { + VIR_STORAGE_TYPE_NONE, + VIR_STORAGE_TYPE_FILE, + VIR_STORAGE_TYPE_BLOCK, + VIR_STORAGE_TYPE_DIR, + VIR_STORAGE_TYPE_NETWORK, + VIR_STORAGE_TYPE_VOLUME, + VIR_STORAGE_TYPE_NVME, + + VIR_STORAGE_TYPE_LAST +} virStorageType; + +VIR_ENUM_DECL(virStorage); + + +typedef enum { + VIR_STORAGE_FILE_AUTO_SAFE = -2, + VIR_STORAGE_FILE_AUTO = -1, + VIR_STORAGE_FILE_NONE = 0, + VIR_STORAGE_FILE_RAW, + VIR_STORAGE_FILE_DIR, + VIR_STORAGE_FILE_BOCHS, + VIR_STORAGE_FILE_CLOOP, + VIR_STORAGE_FILE_DMG, + VIR_STORAGE_FILE_ISO, + VIR_STORAGE_FILE_VPC, + VIR_STORAGE_FILE_VDI, + + /* Not direct file formats, but used for various drivers */ + VIR_STORAGE_FILE_FAT, + VIR_STORAGE_FILE_VHD, + VIR_STORAGE_FILE_PLOOP, + + /* Not a format, but a marker: all formats below this point have + * libvirt support for following a backing chain */ + VIR_STORAGE_FILE_BACKING, + + VIR_STORAGE_FILE_COW = VIR_STORAGE_FILE_BACKING, + VIR_STORAGE_FILE_QCOW, + VIR_STORAGE_FILE_QCOW2, + VIR_STORAGE_FILE_QED, + VIR_STORAGE_FILE_VMDK, + + VIR_STORAGE_FILE_LAST, +} virStorageFileFormat; + +VIR_ENUM_DECL(virStorageFileFormat); + + +typedef enum { + VIR_STORAGE_FILE_FEATURE_LAZY_REFCOUNTS = 0, + + VIR_STORAGE_FILE_FEATURE_LAST +} virStorageFileFeature; + +VIR_ENUM_DECL(virStorageFileFeature); + + +typedef struct _virStoragePerms virStoragePerms; +typedef virStoragePerms *virStoragePermsPtr; +struct _virStoragePerms { + mode_t mode; + uid_t uid; + gid_t gid; + char *label; +}; + + +typedef struct _virStorageTimestamps virStorageTimestamps; +typedef virStorageTimestamps *virStorageTimestampsPtr; +struct _virStorageTimestamps { + struct timespec atime; + struct timespec btime; /* birth time unknown if btime.tv_nsec == -1 */ + struct timespec ctime; + struct timespec mtime; +}; + + +/* Information related to network storage */ +typedef enum { + VIR_STORAGE_NET_PROTOCOL_NONE, + VIR_STORAGE_NET_PROTOCOL_NBD, + VIR_STORAGE_NET_PROTOCOL_RBD, + VIR_STORAGE_NET_PROTOCOL_SHEEPDOG, + VIR_STORAGE_NET_PROTOCOL_GLUSTER, + VIR_STORAGE_NET_PROTOCOL_ISCSI, + VIR_STORAGE_NET_PROTOCOL_HTTP, + VIR_STORAGE_NET_PROTOCOL_HTTPS, + VIR_STORAGE_NET_PROTOCOL_FTP, + VIR_STORAGE_NET_PROTOCOL_FTPS, + VIR_STORAGE_NET_PROTOCOL_TFTP, + VIR_STORAGE_NET_PROTOCOL_SSH, + VIR_STORAGE_NET_PROTOCOL_VXHS, + + VIR_STORAGE_NET_PROTOCOL_LAST +} virStorageNetProtocol; + +VIR_ENUM_DECL(virStorageNetProtocol); + + +typedef enum { + VIR_STORAGE_NET_HOST_TRANS_TCP, + VIR_STORAGE_NET_HOST_TRANS_UNIX, + VIR_STORAGE_NET_HOST_TRANS_RDMA, + + VIR_STORAGE_NET_HOST_TRANS_LAST +} virStorageNetHostTransport; + +VIR_ENUM_DECL(virStorageNetHostTransport); + + +typedef struct _virStorageNetHostDef virStorageNetHostDef; +typedef virStorageNetHostDef *virStorageNetHostDefPtr; +struct _virStorageNetHostDef { + char *name; + unsigned int port; + int transport; /* virStorageNetHostTransport */ + char *socket; /* path to unix socket */ +}; + + +typedef struct _virStorageNetCookieDef virStorageNetCookieDef; +typedef virStorageNetCookieDef *virStorageNetCookieDefPtr; +struct _virStorageNetCookieDef { + char *name; + char *value; +}; + + +void +virStorageNetCookieDefFree(virStorageNetCookieDefPtr def); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageNetCookieDef, virStorageNetCookieDefFree); + + +/* Information for a storage volume from a virStoragePool */ + +/* + * Used for volume "type" disk to indicate how to represent + * the disk source if the specified "pool" is of iscsi type. + */ +typedef enum { + VIR_STORAGE_SOURCE_POOL_MODE_DEFAULT = 0, + + /* Use the path as it shows up on host, e.g. + * /dev/disk/by-path/ip-$ip-iscsi-$iqn:iscsi.iscsi-pool0-lun-1 + */ + VIR_STORAGE_SOURCE_POOL_MODE_HOST, + + /* Use the URI from the storage pool source element host attribute. E.g. + * file=iscsi://demo.org:6000/iqn.1992-01.com.example/1. + */ + VIR_STORAGE_SOURCE_POOL_MODE_DIRECT, + + VIR_STORAGE_SOURCE_POOL_MODE_LAST +} virStorageSourcePoolMode; + +VIR_ENUM_DECL(virStorageSourcePoolMode); + + +typedef struct _virStorageSourcePoolDef virStorageSourcePoolDef; +typedef virStorageSourcePoolDef *virStorageSourcePoolDefPtr; +struct _virStorageSourcePoolDef { + char *pool; /* pool name */ + char *volume; /* volume name */ + int voltype; /* virStorageVolType, internal only */ + int pooltype; /* virStoragePoolType from storage_conf.h, internal only */ + int actualtype; /* virStorageType, internal only */ + int mode; /* virStorageSourcePoolMode, currently makes sense only for iscsi pool */ +}; + + +typedef enum { + VIR_STORAGE_AUTH_TYPE_NONE, + VIR_STORAGE_AUTH_TYPE_CHAP, + VIR_STORAGE_AUTH_TYPE_CEPHX, + + VIR_STORAGE_AUTH_TYPE_LAST, +} virStorageAuthType; + +VIR_ENUM_DECL(virStorageAuth); + + +typedef struct _virStorageAuthDef virStorageAuthDef; +typedef virStorageAuthDef *virStorageAuthDefPtr; +struct _virStorageAuthDef { + char *username; + char *secrettype; /* <secret type='%s' for disk source */ + int authType; /* virStorageAuthType */ + virSecretLookupTypeDef seclookupdef; +}; + + +typedef struct _virStoragePRDef virStoragePRDef; +typedef virStoragePRDef *virStoragePRDefPtr; +struct _virStoragePRDef { + int managed; /* enum virTristateBool */ + char *path; + + /* manager object alias */ + char *mgralias; +}; + + +typedef struct _virStorageSourceInitiatorDef virStorageSourceInitiatorDef; +typedef virStorageSourceInitiatorDef *virStorageSourceInitiatorDefPtr; +struct _virStorageSourceInitiatorDef { + char *iqn; /* Initiator IQN */ +}; + + +typedef struct _virStorageSourceNVMeDef virStorageSourceNVMeDef; +typedef virStorageSourceNVMeDef *virStorageSourceNVMeDefPtr; +struct _virStorageSourceNVMeDef { + unsigned long long namespc; + int managed; /* enum virTristateBool */ + virPCIDeviceAddress pciAddr; + + /* Don't forget to update virStorageSourceNVMeDefCopy */ +}; + + +typedef struct _virStorageSourceSlice virStorageSourceSlice; +typedef virStorageSourceSlice *virStorageSourceSlicePtr; +struct _virStorageSourceSlice { + unsigned long long offset; + unsigned long long size; + char *nodename; +}; + + +typedef struct _virStorageSource virStorageSource; +typedef virStorageSource *virStorageSourcePtr; + +/* Stores information related to a host resource. In the case of backing + * chains, multiple source disks join to form a single guest view. + * + * IMPORTANT: When adding fields to this struct it's also necessary to add + * appropriate code to the virStorageSourceCopy deep copy function */ +struct _virStorageSource { + virObject parent; + + unsigned int id; /* backing chain identifier, 0 is unset */ + int type; /* virStorageType */ + char *path; + int protocol; /* virStorageNetProtocol */ + char *volume; /* volume name for remote storage */ + char *snapshot; /* for storage systems supporting internal snapshots */ + char *configFile; /* some storage systems use config file as part of + the source definition */ + char *query; /* query string for HTTP based protocols */ + size_t nhosts; + virStorageNetHostDefPtr hosts; + size_t ncookies; + virStorageNetCookieDefPtr *cookies; + virStorageSourcePoolDefPtr srcpool; + virStorageAuthDefPtr auth; + virStorageEncryptionPtr encryption; + virStoragePRDefPtr pr; + virTristateBool sslverify; + /* both values below have 0 as default value */ + unsigned long long readahead; /* size of the readahead buffer in bytes */ + unsigned long long timeout; /* connection timeout in seconds */ + + virStorageSourceNVMeDefPtr nvme; /* type == VIR_STORAGE_TYPE_NVME */ + + virStorageSourceInitiatorDef initiator; + + virObjectPtr privateData; + + int format; /* virStorageFileFormat in domain backing chains, but + * pool-specific enum for storage volumes */ + virBitmapPtr features; + char *compat; + bool nocow; + bool sparse; + + virStorageSourceSlicePtr sliceStorage; + + virStoragePermsPtr perms; + virStorageTimestampsPtr timestamps; + unsigned long long capacity; /* in bytes, 0 if unknown */ + unsigned long long allocation; /* in bytes, 0 if unknown */ + unsigned long long physical; /* in bytes, 0 if unknown */ + unsigned long long clusterSize; /* in bytes, 0 if unknown */ + bool has_allocation; /* Set to true when provided in XML */ + + size_t nseclabels; + virSecurityDeviceLabelDefPtr *seclabels; + + /* Don't ever write to the image */ + bool readonly; + + /* image is shared across hosts */ + bool shared; + + /* backing chain of the storage source */ + virStorageSourcePtr backingStore; + + /* metadata for storage driver access to remote and local volumes */ + void *drv; + + /* metadata about storage image which need separate fields */ + /* Relative name by which this image was opened from its parent, or NULL + * if this image was opened by absolute name */ + char *relPath; + /* Name of the child backing store recorded in metadata of the + * current file. */ + char *backingStoreRaw; + virStorageFileFormat backingStoreRawFormat; + + /* metadata that allows identifying given storage source */ + char *nodeformat; /* name of the format handler object */ + char *nodestorage; /* name of the storage object */ + + /* An optional setting to enable usage of TLS for the storage source */ + int haveTLS; /* enum virTristateBool */ + + /* Indication whether the haveTLS value was altered due to qemu.conf + * setting when haveTLS is missing from the domain config file */ + bool tlsFromConfig; + + /* If TLS is used, then mgmt of the TLS credentials occurs via an + * object that is generated using a specific alias for a specific + * certificate directory with listen and verify bools. */ + char *tlsAlias; + char *tlsCertdir; + + bool detected; /* true if this entry was not provided by the user */ + + unsigned int debugLevel; + bool debug; + + /* Libvirt currently stores the following properties in virDomainDiskDef. + * These instances are currently just copies from the parent definition and + * are not mapped back to the XML */ + int iomode; /* enum virDomainDiskIo */ + int cachemode; /* enum virDomainDiskCache */ + int discard; /* enum virDomainDiskDiscard */ + int detect_zeroes; /* enum virDomainDiskDetectZeroes */ + + bool floppyimg; /* set to true if the storage source is going to be used + as a source for floppy drive */ + + bool hostcdrom; /* backing device is a cdrom */ + + /* passthrough variables for the ssh driver which we don't handle properly */ + /* these must not be used apart from formatting the output JSON in the qemu driver */ + char *ssh_user; + bool ssh_host_key_check_disabled; +}; + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageSource, virObjectUnref); + +void +virStorageAuthDefFree(virStorageAuthDefPtr def); + +virStorageAuthDefPtr +virStorageAuthDefCopy(const virStorageAuthDef *src); + +virStorageAuthDefPtr +virStorageAuthDefParse(xmlNodePtr node, + xmlXPathContextPtr ctxt); + +void +virStorageAuthDefFormat(virBufferPtr buf, + virStorageAuthDefPtr authdef); + +void +virStoragePRDefFree(virStoragePRDefPtr prd); + +virStoragePRDefPtr +virStoragePRDefParseXML(xmlXPathContextPtr ctxt); + +void +virStoragePRDefFormat(virBufferPtr buf, + virStoragePRDefPtr prd, + bool migratable); + +bool +virStoragePRDefIsEqual(virStoragePRDefPtr a, + virStoragePRDefPtr b); + +bool +virStoragePRDefIsManaged(virStoragePRDefPtr prd); + +bool +virStorageSourceChainHasManagedPR(virStorageSourcePtr src); + +void +virStorageSourceNVMeDefFree(virStorageSourceNVMeDefPtr def); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageSourceNVMeDef, virStorageSourceNVMeDefFree); + +bool +virStorageSourceChainHasNVMe(const virStorageSource *src); + +virSecurityDeviceLabelDefPtr +virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src, + const char *model); + +void +virStorageNetHostDefClear(virStorageNetHostDefPtr def); + +void +virStorageNetHostDefFree(size_t nhosts, + virStorageNetHostDefPtr hosts); + +virStorageNetHostDefPtr +virStorageNetHostDefCopy(size_t nhosts, + virStorageNetHostDefPtr hosts); + +int +virStorageSourceInitChainElement(virStorageSourcePtr newelem, + virStorageSourcePtr old, + bool force); + +void +virStorageSourcePoolDefFree(virStorageSourcePoolDefPtr def); + +void +virStorageSourceClear(virStorageSourcePtr def); + +int +virStorageSourceGetActualType(const virStorageSource *def); + +bool +virStorageSourceIsLocalStorage(const virStorageSource *src); + +bool +virStorageSourceIsEmpty(virStorageSourcePtr src); + +bool +virStorageSourceIsBlockLocal(const virStorageSource *src); + +virStorageSourcePtr +virStorageSourceNew(void); + +void +virStorageSourceBackingStoreClear(virStorageSourcePtr def); + +int +virStorageSourceNetCookiesValidate(virStorageSourcePtr src); + +virStorageSourcePtr +virStorageSourceCopy(const virStorageSource *src, + bool backingChain) + ATTRIBUTE_NONNULL(1); + +bool +virStorageSourceIsSameLocation(virStorageSourcePtr a, + virStorageSourcePtr b) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + +bool +virStorageSourceIsRelative(virStorageSourcePtr src); + +void +virStorageSourceNetworkAssignDefaultPorts(virStorageSourcePtr src) + ATTRIBUTE_NONNULL(1); + +bool +virStorageSourceIsBacking(const virStorageSource *src); + +bool +virStorageSourceHasBacking(const virStorageSource *src); + +void +virStorageSourceInitiatorParseXML(xmlXPathContextPtr ctxt, + virStorageSourceInitiatorDefPtr initiator); + +void +virStorageSourceInitiatorFormatXML(virStorageSourceInitiatorDefPtr initiator, + virBufferPtr buf); + +int +virStorageSourceInitiatorCopy(virStorageSourceInitiatorDefPtr dest, + const virStorageSourceInitiatorDef *src); + +void +virStorageSourceInitiatorClear(virStorageSourceInitiatorDefPtr initiator); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageAuthDef, virStorageAuthDefFree); diff --git a/src/esx/esx_storage_backend_iscsi.c b/src/esx/esx_storage_backend_iscsi.c index edbc65f5c0..00cc129b1f 100644 --- a/src/esx/esx_storage_backend_iscsi.c +++ b/src/esx/esx_storage_backend_iscsi.c @@ -28,7 +28,7 @@ #include "viralloc.h" #include "viruuid.h" #include "storage_conf.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "esx_storage_backend_iscsi.h" #include "esx_private.h" #include "esx_vi.h" diff --git a/src/esx/esx_storage_backend_vmfs.c b/src/esx/esx_storage_backend_vmfs.c index c7a7863a61..1533cbfe58 100644 --- a/src/esx/esx_storage_backend_vmfs.c +++ b/src/esx/esx_storage_backend_vmfs.c @@ -32,7 +32,7 @@ #include "virlog.h" #include "viruuid.h" #include "storage_conf.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "esx_storage_backend_vmfs.h" #include "esx_private.h" #include "esx_vi.h" diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fe6683f728..0b0a57e85d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1053,6 +1053,56 @@ virStoragePoolEventRefreshNew; virStoragePoolEventStateRegisterID; +# conf/storage_source.h +virStorageAuthDefCopy; +virStorageAuthDefFormat; +virStorageAuthDefFree; +virStorageAuthDefParse; +virStorageFileFeatureTypeFromString; +virStorageFileFeatureTypeToString; +virStorageFileFormatTypeFromString; +virStorageFileFormatTypeToString; +virStorageNetHostDefClear; +virStorageNetHostDefCopy; +virStorageNetHostDefFree; +virStorageNetHostTransportTypeFromString; +virStorageNetHostTransportTypeToString; +virStorageNetProtocolTypeToString; +virStoragePRDefFormat; +virStoragePRDefFree; +virStoragePRDefIsEqual; +virStoragePRDefIsManaged; +virStoragePRDefParseXML; +virStorageSourceBackingStoreClear; +virStorageSourceChainHasManagedPR; +virStorageSourceChainHasNVMe; +virStorageSourceClear; +virStorageSourceCopy; +virStorageSourceGetActualType; +virStorageSourceGetSecurityLabelDef; +virStorageSourceHasBacking; +virStorageSourceInitChainElement; +virStorageSourceInitiatorClear; +virStorageSourceInitiatorCopy; +virStorageSourceInitiatorFormatXML; +virStorageSourceInitiatorParseXML; +virStorageSourceIsBacking; +virStorageSourceIsBlockLocal; +virStorageSourceIsEmpty; +virStorageSourceIsLocalStorage; +virStorageSourceIsRelative; +virStorageSourceIsSameLocation; +virStorageSourceNetCookiesValidate; +virStorageSourceNetworkAssignDefaultPorts; +virStorageSourceNew; +virStorageSourceNVMeDefFree; +virStorageSourcePoolDefFree; +virStorageSourcePoolModeTypeFromString; +virStorageSourcePoolModeTypeToString; +virStorageTypeFromString; +virStorageTypeToString; + + # conf/virchrdev.h virChrdevAlloc; virChrdevFree; @@ -3164,55 +3214,8 @@ virStorageEncryptionParseNode; # util/virstoragefile.h -virStorageAuthDefCopy; -virStorageAuthDefFormat; -virStorageAuthDefFree; -virStorageAuthDefParse; -virStorageFileFeatureTypeFromString; -virStorageFileFeatureTypeToString; -virStorageFileFormatTypeFromString; -virStorageFileFormatTypeToString; virStorageFileGetNPIVKey; virStorageFileGetSCSIKey; -virStorageNetHostDefClear; -virStorageNetHostDefCopy; -virStorageNetHostDefFree; -virStorageNetHostTransportTypeFromString; -virStorageNetHostTransportTypeToString; -virStorageNetProtocolTypeToString; -virStoragePRDefFormat; -virStoragePRDefFree; -virStoragePRDefIsEqual; -virStoragePRDefIsManaged; -virStoragePRDefParseXML; -virStorageSourceBackingStoreClear; -virStorageSourceChainHasManagedPR; -virStorageSourceChainHasNVMe; -virStorageSourceClear; -virStorageSourceCopy; -virStorageSourceGetActualType; -virStorageSourceGetSecurityLabelDef; -virStorageSourceHasBacking; -virStorageSourceInitChainElement; -virStorageSourceInitiatorClear; -virStorageSourceInitiatorCopy; -virStorageSourceInitiatorFormatXML; -virStorageSourceInitiatorParseXML; -virStorageSourceIsBacking; -virStorageSourceIsBlockLocal; -virStorageSourceIsEmpty; -virStorageSourceIsLocalStorage; -virStorageSourceIsRelative; -virStorageSourceIsSameLocation; -virStorageSourceNetCookiesValidate; -virStorageSourceNetworkAssignDefaultPorts; -virStorageSourceNew; -virStorageSourceNVMeDefFree; -virStorageSourcePoolDefFree; -virStorageSourcePoolModeTypeFromString; -virStorageSourcePoolModeTypeToString; -virStorageTypeFromString; -virStorageTypeToString; # util/virstring.h diff --git a/src/locking/lock_driver_lockd.c b/src/locking/lock_driver_lockd.c index b8c2121e78..5523f8cbb8 100644 --- a/src/locking/lock_driver_lockd.c +++ b/src/locking/lock_driver_lockd.c @@ -35,6 +35,7 @@ #include "configmake.h" #include "virstring.h" #include "virutil.h" +#include "virstoragefile.h" #include "lock_driver_lockd.h" diff --git a/src/qemu/qemu_backup.c b/src/qemu/qemu_backup.c index bd699ad29f..997214a840 100644 --- a/src/qemu/qemu_backup.c +++ b/src/qemu/qemu_backup.c @@ -35,7 +35,7 @@ #include "virbuffer.h" #include "viralloc.h" #include "virxml.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "virstring.h" #include "backup_conf.h" #include "virdomaincheckpointobjlist.h" diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 2a5a5e66f4..85abbed21a 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -33,7 +33,7 @@ #include "conf/domain_event.h" #include "virlog.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "virthread.h" #include "virtime.h" #include "locking/domain_lock.h" diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index b06a086e18..a2537a1583 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -53,7 +53,7 @@ #include "virnetdevtap.h" #include "virnetdevopenvswitch.h" #include "device_conf.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "virtpm.h" #include "virscsi.h" #include "virnuma.h" diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index ef073e5395..8ddb7b70d2 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -51,7 +51,7 @@ #include "domain_validate.h" #include "virtime.h" #include "virnetdevopenvswitch.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "storage_file.h" #include "virstring.h" #include "virthreadjob.h" diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 00ce56c6b9..f6554e1506 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -82,7 +82,7 @@ #include "virsysinfo.h" #include "domain_nwfilter.h" #include "virhook.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "storage_file.h" #include "virfile.h" #include "virfdstream.h" diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index a0727b8b90..d32f97ef5c 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -52,7 +52,7 @@ #include "virnetdevopenvswitch.h" #include "virnetdevmidonet.h" #include "device_conf.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "storage_file.h" #include "virstring.h" #include "virtime.h" diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 16c9f18e0a..50c5de68b8 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -52,7 +52,7 @@ #include "virtime.h" #include "locking/domain_lock.h" #include "rpc/virnetsocket.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "viruri.h" #include "virhook.h" #include "virstring.h" diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h index f4feeaa68f..1ced774fec 100644 --- a/src/qemu/qemu_process.h +++ b/src/qemu/qemu_process.h @@ -23,7 +23,7 @@ #include "qemu_conf.h" #include "qemu_domain.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "vireventthread.h" int qemuProcessPrepareMonitorChr(virDomainChrSourceDefPtr monConfig, diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index 2bce445575..87c8bc525d 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -27,7 +27,7 @@ #include "virerror.h" #include "viralloc.h" #include "internal.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "storage_backend.h" #include "virlog.h" #include "virmodule.h" diff --git a/src/storage/storage_util.c b/src/storage/storage_util.c index a1949a5a24..7616ec63a0 100644 --- a/src/storage/storage_util.c +++ b/src/storage/storage_util.c @@ -63,6 +63,7 @@ #include "viruuid.h" #include "virstoragefile.h" #include "storage_file.h" +#include "storage_source.h" #include "storage_util.h" #include "virlog.h" #include "virfile.h" diff --git a/src/storage_file/meson.build b/src/storage_file/meson.build index 4d85d16929..ef1345c866 100644 --- a/src/storage_file/meson.build +++ b/src/storage_file/meson.build @@ -18,6 +18,9 @@ virt_storage_file_lib = static_library( [ storage_file_sources, ], + include_directories: [ + conf_inc_dir, + ], dependencies: [ src_dep, ], diff --git a/src/storage_file/storage_file.h b/src/storage_file/storage_file.h index 8afedc35b3..181efded87 100644 --- a/src/storage_file/storage_file.h +++ b/src/storage_file/storage_file.h @@ -21,7 +21,7 @@ #pragma once -#include "virstoragefile.h" +#include "storage_source.h" /* Minimum header size required to probe all known formats with diff --git a/src/storage_file/storage_file_backend.h b/src/storage_file/storage_file_backend.h index ad0f66212c..3d6b2b7fc0 100644 --- a/src/storage_file/storage_file_backend.h +++ b/src/storage_file/storage_file_backend.h @@ -22,7 +22,7 @@ #include <sys/stat.h> -#include "virstoragefile.h" +#include "storage_source.h" /* ------- virStorageFile backends ------------ */ typedef struct _virStorageFileBackend virStorageFileBackend; diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 3489934546..1c61f3c143 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -23,84 +23,8 @@ #include "virstoragefile.h" #include "viralloc.h" -#include "virxml.h" #include "virerror.h" -#include "virlog.h" #include "vircommand.h" -#include "virstring.h" -#include "virbuffer.h" -#include "virstorageencryption.h" -#include "virsecret.h" - -#define VIR_FROM_THIS VIR_FROM_STORAGE - -VIR_LOG_INIT("util.storagefile"); - -static virClassPtr virStorageSourceClass; - -VIR_ENUM_IMPL(virStorage, - VIR_STORAGE_TYPE_LAST, - "none", - "file", - "block", - "dir", - "network", - "volume", - "nvme", -); - -VIR_ENUM_IMPL(virStorageFileFormat, - VIR_STORAGE_FILE_LAST, - "none", - "raw", "dir", "bochs", - "cloop", "dmg", "iso", - "vpc", "vdi", - /* Not direct file formats, but used for various drivers */ - "fat", "vhd", "ploop", - /* Formats with backing file below here */ - "cow", "qcow", "qcow2", "qed", "vmdk", -); - -VIR_ENUM_IMPL(virStorageFileFeature, - VIR_STORAGE_FILE_FEATURE_LAST, - "lazy_refcounts", -); - -VIR_ENUM_IMPL(virStorageNetProtocol, - VIR_STORAGE_NET_PROTOCOL_LAST, - "none", - "nbd", - "rbd", - "sheepdog", - "gluster", - "iscsi", - "http", - "https", - "ftp", - "ftps", - "tftp", - "ssh", - "vxhs", -); - -VIR_ENUM_IMPL(virStorageNetHostTransport, - VIR_STORAGE_NET_HOST_TRANS_LAST, - "tcp", - "unix", - "rdma", -); - -VIR_ENUM_IMPL(virStorageSourcePoolMode, - VIR_STORAGE_SOURCE_POOL_MODE_LAST, - "default", - "host", - "direct", -); - -VIR_ENUM_IMPL(virStorageAuth, - VIR_STORAGE_AUTH_TYPE_LAST, - "none", "chap", "ceph", -); #ifdef WITH_UDEV @@ -239,1208 +163,3 @@ int virStorageFileGetNPIVKey(const char *path G_GNUC_UNUSED, return -1; } #endif - - -/** - * virStorageSourceIsBacking: - * @src: storage source - * - * Returns true if @src is a eligible backing store structure. Useful - * for iterators. - */ -bool -virStorageSourceIsBacking(const virStorageSource *src) -{ - return src && src->type != VIR_STORAGE_TYPE_NONE; -} - -/** - * virStorageSourceHasBacking: - * @src: storage source - * - * Returns true if @src has backing store/chain. - */ -bool -virStorageSourceHasBacking(const virStorageSource *src) -{ - return virStorageSourceIsBacking(src) && src->backingStore && - src->backingStore->type != VIR_STORAGE_TYPE_NONE; -} - - -void -virStorageNetHostDefClear(virStorageNetHostDefPtr def) -{ - if (!def) - return; - - VIR_FREE(def->name); - VIR_FREE(def->socket); -} - - -void -virStorageNetHostDefFree(size_t nhosts, - virStorageNetHostDefPtr hosts) -{ - size_t i; - - if (!hosts) - return; - - for (i = 0; i < nhosts; i++) - virStorageNetHostDefClear(&hosts[i]); - - VIR_FREE(hosts); -} - - -static void -virStoragePermsFree(virStoragePermsPtr def) -{ - if (!def) - return; - - VIR_FREE(def->label); - VIR_FREE(def); -} - - -virStorageNetHostDefPtr -virStorageNetHostDefCopy(size_t nhosts, - virStorageNetHostDefPtr hosts) -{ - virStorageNetHostDefPtr ret = NULL; - size_t i; - - ret = g_new0(virStorageNetHostDef, nhosts); - - for (i = 0; i < nhosts; i++) { - virStorageNetHostDefPtr src = &hosts[i]; - virStorageNetHostDefPtr dst = &ret[i]; - - dst->transport = src->transport; - dst->port = src->port; - - dst->name = g_strdup(src->name); - dst->socket = g_strdup(src->socket); - } - - return ret; -} - - -void -virStorageAuthDefFree(virStorageAuthDefPtr authdef) -{ - if (!authdef) - return; - - VIR_FREE(authdef->username); - VIR_FREE(authdef->secrettype); - virSecretLookupDefClear(&authdef->seclookupdef); - VIR_FREE(authdef); -} - - -virStorageAuthDefPtr -virStorageAuthDefCopy(const virStorageAuthDef *src) -{ - g_autoptr(virStorageAuthDef) authdef = NULL; - - authdef = g_new0(virStorageAuthDef, 1); - - authdef->username = g_strdup(src->username); - /* Not present for storage pool, but used for disk source */ - authdef->secrettype = g_strdup(src->secrettype); - authdef->authType = src->authType; - - virSecretLookupDefCopy(&authdef->seclookupdef, &src->seclookupdef); - - return g_steal_pointer(&authdef); -} - - -virStorageAuthDefPtr -virStorageAuthDefParse(xmlNodePtr node, - xmlXPathContextPtr ctxt) -{ - VIR_XPATH_NODE_AUTORESTORE(ctxt) - virStorageAuthDefPtr ret = NULL; - xmlNodePtr secretnode = NULL; - g_autoptr(virStorageAuthDef) authdef = NULL; - g_autofree char *authtype = NULL; - - ctxt->node = node; - - authdef = g_new0(virStorageAuthDef, 1); - - if (!(authdef->username = virXPathString("string(./@username)", ctxt))) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("missing username for auth")); - goto cleanup; - } - - authdef->authType = VIR_STORAGE_AUTH_TYPE_NONE; - authtype = virXPathString("string(./@type)", ctxt); - if (authtype) { - /* Used by the storage pool instead of the secret type field - * to define whether chap or ceph being used - */ - if ((authdef->authType = virStorageAuthTypeFromString(authtype)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown auth type '%s'"), authtype); - goto cleanup; - } - } - - if (!(secretnode = virXPathNode("./secret ", ctxt))) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Missing <secret> element in auth")); - goto cleanup; - } - - /* Used by the domain disk xml parsing in order to ensure the - * <secret type='%s' value matches the expected secret type for - * the style of disk (iscsi is chap, nbd is ceph). For some reason - * the virSecretUsageType{From|To}String() cannot be linked here - * and because only the domain parsing code cares - just keep - * it as a string. - */ - authdef->secrettype = virXMLPropString(secretnode, "type"); - - if (virSecretLookupParseSecret(secretnode, &authdef->seclookupdef) < 0) - goto cleanup; - - ret = g_steal_pointer(&authdef); - - cleanup: - - return ret; -} - - -void -virStorageAuthDefFormat(virBufferPtr buf, - virStorageAuthDefPtr authdef) -{ - if (authdef->authType == VIR_STORAGE_AUTH_TYPE_NONE) { - virBufferEscapeString(buf, "<auth username='%s'>\n", authdef->username); - } else { - virBufferAsprintf(buf, "<auth type='%s' ", - virStorageAuthTypeToString(authdef->authType)); - virBufferEscapeString(buf, "username='%s'>\n", authdef->username); - } - - virBufferAdjustIndent(buf, 2); - virSecretLookupFormatSecret(buf, authdef->secrettype, - &authdef->seclookupdef); - virBufferAdjustIndent(buf, -2); - virBufferAddLit(buf, "</auth>\n"); -} - - -void -virStoragePRDefFree(virStoragePRDefPtr prd) -{ - if (!prd) - return; - - VIR_FREE(prd->path); - VIR_FREE(prd->mgralias); - VIR_FREE(prd); -} - - -virStoragePRDefPtr -virStoragePRDefParseXML(xmlXPathContextPtr ctxt) -{ - virStoragePRDefPtr prd; - virStoragePRDefPtr ret = NULL; - g_autofree char *managed = NULL; - g_autofree char *type = NULL; - g_autofree char *path = NULL; - g_autofree char *mode = NULL; - - prd = g_new0(virStoragePRDef, 1); - - if (!(managed = virXPathString("string(./@managed)", ctxt))) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("missing @managed attribute for <reservations/>")); - goto cleanup; - } - - if ((prd->managed = virTristateBoolTypeFromString(managed)) <= 0) { - virReportError(VIR_ERR_XML_ERROR, - _("invalid value for 'managed': %s"), managed); - goto cleanup; - } - - type = virXPathString("string(./source[1]/@type)", ctxt); - path = virXPathString("string(./source[1]/@path)", ctxt); - mode = virXPathString("string(./source[1]/@mode)", ctxt); - - if (prd->managed == VIR_TRISTATE_BOOL_NO || type || path || mode) { - if (!type) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("missing connection type for <reservations/>")); - goto cleanup; - } - - if (!path) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("missing path for <reservations/>")); - goto cleanup; - } - - if (!mode) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("missing connection mode for <reservations/>")); - goto cleanup; - } - } - - if (type && STRNEQ(type, "unix")) { - virReportError(VIR_ERR_XML_ERROR, - _("unsupported connection type for <reservations/>: %s"), - type); - goto cleanup; - } - - if (mode && STRNEQ(mode, "client")) { - virReportError(VIR_ERR_XML_ERROR, - _("unsupported connection mode for <reservations/>: %s"), - mode); - goto cleanup; - } - - prd->path = g_steal_pointer(&path); - ret = g_steal_pointer(&prd); - - cleanup: - virStoragePRDefFree(prd); - return ret; -} - - -void -virStoragePRDefFormat(virBufferPtr buf, - virStoragePRDefPtr prd, - bool migratable) -{ - virBufferAsprintf(buf, "<reservations managed='%s'", - virTristateBoolTypeToString(prd->managed)); - if (prd->path && - (prd->managed == VIR_TRISTATE_BOOL_NO || !migratable)) { - virBufferAddLit(buf, ">\n"); - virBufferAdjustIndent(buf, 2); - virBufferAddLit(buf, "<source type='unix'"); - virBufferEscapeString(buf, " path='%s'", prd->path); - virBufferAddLit(buf, " mode='client'/>\n"); - virBufferAdjustIndent(buf, -2); - virBufferAddLit(buf, "</reservations>\n"); - } else { - virBufferAddLit(buf, "/>\n"); - } -} - - -bool -virStoragePRDefIsEqual(virStoragePRDefPtr a, - virStoragePRDefPtr b) -{ - if (!a && !b) - return true; - - if (!a || !b) - return false; - - if (a->managed != b->managed || - STRNEQ_NULLABLE(a->path, b->path)) - return false; - - return true; -} - - -bool -virStoragePRDefIsManaged(virStoragePRDefPtr prd) -{ - return prd && prd->managed == VIR_TRISTATE_BOOL_YES; -} - - -bool -virStorageSourceChainHasManagedPR(virStorageSourcePtr src) -{ - virStorageSourcePtr n; - - for (n = src; virStorageSourceIsBacking(n); n = n->backingStore) { - if (virStoragePRDefIsManaged(n->pr)) - return true; - } - - return false; -} - - -static virStoragePRDefPtr -virStoragePRDefCopy(virStoragePRDefPtr src) -{ - virStoragePRDefPtr copy = NULL; - virStoragePRDefPtr ret = NULL; - - copy = g_new0(virStoragePRDef, 1); - - copy->managed = src->managed; - - copy->path = g_strdup(src->path); - copy->mgralias = g_strdup(src->mgralias); - - ret = g_steal_pointer(©); - - virStoragePRDefFree(copy); - return ret; -} - - -static virStorageSourceNVMeDefPtr -virStorageSourceNVMeDefCopy(const virStorageSourceNVMeDef *src) -{ - virStorageSourceNVMeDefPtr ret = NULL; - - ret = g_new0(virStorageSourceNVMeDef, 1); - - ret->namespc = src->namespc; - ret->managed = src->managed; - virPCIDeviceAddressCopy(&ret->pciAddr, &src->pciAddr); - return ret; -} - - -static bool -virStorageSourceNVMeDefIsEqual(const virStorageSourceNVMeDef *a, - const virStorageSourceNVMeDef *b) -{ - if (!a && !b) - return true; - - if (!a || !b) - return false; - - if (a->namespc != b->namespc || - a->managed != b->managed || - !virPCIDeviceAddressEqual(&a->pciAddr, &b->pciAddr)) - return false; - - return true; -} - - -void -virStorageSourceNVMeDefFree(virStorageSourceNVMeDefPtr def) -{ - if (!def) - return; - - VIR_FREE(def); -} - - -bool -virStorageSourceChainHasNVMe(const virStorageSource *src) -{ - const virStorageSource *n; - - for (n = src; virStorageSourceIsBacking(n); n = n->backingStore) { - if (n->type == VIR_STORAGE_TYPE_NVME) - return true; - } - - return false; -} - - -virSecurityDeviceLabelDefPtr -virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src, - const char *model) -{ - size_t i; - - for (i = 0; i < src->nseclabels; i++) { - if (STREQ_NULLABLE(src->seclabels[i]->model, model)) - return src->seclabels[i]; - } - - return NULL; -} - - -static void -virStorageSourceSeclabelsClear(virStorageSourcePtr def) -{ - size_t i; - - if (def->seclabels) { - for (i = 0; i < def->nseclabels; i++) - virSecurityDeviceLabelDefFree(def->seclabels[i]); - VIR_FREE(def->seclabels); - } -} - - -static int -virStorageSourceSeclabelsCopy(virStorageSourcePtr to, - const virStorageSource *from) -{ - size_t i; - - if (from->nseclabels == 0) - return 0; - - to->seclabels = g_new0(virSecurityDeviceLabelDefPtr, from->nseclabels); - to->nseclabels = from->nseclabels; - - for (i = 0; i < to->nseclabels; i++) { - if (!(to->seclabels[i] = virSecurityDeviceLabelDefCopy(from->seclabels[i]))) - goto error; - } - - return 0; - - error: - virStorageSourceSeclabelsClear(to); - return -1; -} - - -void -virStorageNetCookieDefFree(virStorageNetCookieDefPtr def) -{ - if (!def) - return; - - g_free(def->name); - g_free(def->value); - - g_free(def); -} - - -static void -virStorageSourceNetCookiesClear(virStorageSourcePtr src) -{ - size_t i; - - if (!src || !src->cookies) - return; - - for (i = 0; i < src->ncookies; i++) - virStorageNetCookieDefFree(src->cookies[i]); - - g_clear_pointer(&src->cookies, g_free); - src->ncookies = 0; -} - - -static void -virStorageSourceNetCookiesCopy(virStorageSourcePtr to, - const virStorageSource *from) -{ - size_t i; - - if (from->ncookies == 0) - return; - - to->cookies = g_new0(virStorageNetCookieDefPtr, from->ncookies); - to->ncookies = from->ncookies; - - for (i = 0; i < from->ncookies; i++) { - to->cookies[i]->name = g_strdup(from->cookies[i]->name); - to->cookies[i]->value = g_strdup(from->cookies[i]->value); - } -} - - -/* see https://tools.ietf.org/html/rfc6265#section-4.1.1 */ -static const char virStorageSourceCookieValueInvalidChars[] = - "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" - "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" - " \",;\\"; - -/* in addition cookie name can't contain these */ -static const char virStorageSourceCookieNameInvalidChars[] = - "()<>@:/[]?={}"; - -static int -virStorageSourceNetCookieValidate(virStorageNetCookieDefPtr def) -{ - g_autofree char *val = g_strdup(def->value); - const char *checkval = val; - size_t len = strlen(val); - - /* name must have at least 1 character */ - if (*(def->name) == '\0') { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("cookie name must not be empty")); - return -1; - } - - /* check invalid characters in name */ - if (virStringHasChars(def->name, virStorageSourceCookieValueInvalidChars) || - virStringHasChars(def->name, virStorageSourceCookieNameInvalidChars)) { - virReportError(VIR_ERR_XML_ERROR, - _("cookie name '%s' contains invalid characters"), - def->name); - return -1; - } - - /* check for optional quotes around the cookie value string */ - if (val[0] == '"') { - if (val[len - 1] != '"') { - virReportError(VIR_ERR_XML_ERROR, - _("value of cookie '%s' contains invalid characters"), - def->name); - return -1; - } - - val[len - 1] = '\0'; - checkval++; - } - - /* check invalid characters in value */ - if (virStringHasChars(checkval, virStorageSourceCookieValueInvalidChars)) { - virReportError(VIR_ERR_XML_ERROR, - _("value of cookie '%s' contains invalid characters"), - def->name); - return -1; - } - - return 0; -} - - -int -virStorageSourceNetCookiesValidate(virStorageSourcePtr src) -{ - size_t i; - size_t j; - - for (i = 0; i < src->ncookies; i++) { - if (virStorageSourceNetCookieValidate(src->cookies[i]) < 0) - return -1; - - for (j = i + 1; j < src->ncookies; j++) { - if (STREQ(src->cookies[i]->name, src->cookies[j]->name)) { - virReportError(VIR_ERR_XML_ERROR, _("duplicate cookie '%s'"), - src->cookies[i]->name); - return -1; - } - } - } - - return 0; -} - - -static virStorageTimestampsPtr -virStorageTimestampsCopy(const virStorageTimestamps *src) -{ - virStorageTimestampsPtr ret; - - ret = g_new0(virStorageTimestamps, 1); - - memcpy(ret, src, sizeof(*src)); - - return ret; -} - - -static virStoragePermsPtr -virStoragePermsCopy(const virStoragePerms *src) -{ - virStoragePermsPtr ret; - - ret = g_new0(virStoragePerms, 1); - - ret->mode = src->mode; - ret->uid = src->uid; - ret->gid = src->gid; - - ret->label = g_strdup(src->label); - - return ret; -} - - -static virStorageSourcePoolDefPtr -virStorageSourcePoolDefCopy(const virStorageSourcePoolDef *src) -{ - virStorageSourcePoolDefPtr ret; - - ret = g_new0(virStorageSourcePoolDef, 1); - - ret->voltype = src->voltype; - ret->pooltype = src->pooltype; - ret->actualtype = src->actualtype; - ret->mode = src->mode; - - ret->pool = g_strdup(src->pool); - ret->volume = g_strdup(src->volume); - - return ret; -} - - -static virStorageSourceSlicePtr -virStorageSourceSliceCopy(const virStorageSourceSlice *src) -{ - virStorageSourceSlicePtr ret = g_new0(virStorageSourceSlice, 1); - - ret->offset = src->offset; - ret->size = src->size; - ret->nodename = g_strdup(src->nodename); - - return ret; -} - - -static void -virStorageSourceSliceFree(virStorageSourceSlicePtr slice) -{ - if (!slice) - return; - - g_free(slice->nodename); - g_free(slice); -} - - -/** - * virStorageSourcePtr: - * - * Deep-copies a virStorageSource structure. If @backing chain is true - * then also copies the backing chain recursively, otherwise just - * the top element is copied. This function doesn't copy the - * storage driver access structure and thus the struct needs to be initialized - * separately. - */ -virStorageSourcePtr -virStorageSourceCopy(const virStorageSource *src, - bool backingChain) -{ - g_autoptr(virStorageSource) def = virStorageSourceNew(); - - def->id = src->id; - def->type = src->type; - def->protocol = src->protocol; - def->format = src->format; - def->capacity = src->capacity; - def->allocation = src->allocation; - def->has_allocation = src->has_allocation; - def->physical = src->physical; - def->readonly = src->readonly; - def->shared = src->shared; - def->haveTLS = src->haveTLS; - def->tlsFromConfig = src->tlsFromConfig; - def->detected = src->detected; - def->debugLevel = src->debugLevel; - def->debug = src->debug; - def->iomode = src->iomode; - def->cachemode = src->cachemode; - def->discard = src->discard; - def->detect_zeroes = src->detect_zeroes; - def->sslverify = src->sslverify; - def->readahead = src->readahead; - def->timeout = src->timeout; - - /* storage driver metadata are not copied */ - def->drv = NULL; - - def->path = g_strdup(src->path); - def->volume = g_strdup(src->volume); - def->relPath = g_strdup(src->relPath); - def->backingStoreRaw = g_strdup(src->backingStoreRaw); - def->backingStoreRawFormat = src->backingStoreRawFormat; - def->snapshot = g_strdup(src->snapshot); - def->configFile = g_strdup(src->configFile); - def->nodeformat = g_strdup(src->nodeformat); - def->nodestorage = g_strdup(src->nodestorage); - def->compat = g_strdup(src->compat); - def->tlsAlias = g_strdup(src->tlsAlias); - def->tlsCertdir = g_strdup(src->tlsCertdir); - def->query = g_strdup(src->query); - - if (src->sliceStorage) - def->sliceStorage = virStorageSourceSliceCopy(src->sliceStorage); - - if (src->nhosts) { - if (!(def->hosts = virStorageNetHostDefCopy(src->nhosts, src->hosts))) - return NULL; - - def->nhosts = src->nhosts; - } - - virStorageSourceNetCookiesCopy(def, src); - - if (src->srcpool && - !(def->srcpool = virStorageSourcePoolDefCopy(src->srcpool))) - return NULL; - - if (src->features) - def->features = virBitmapNewCopy(src->features); - - if (src->encryption && - !(def->encryption = virStorageEncryptionCopy(src->encryption))) - return NULL; - - if (src->perms && - !(def->perms = virStoragePermsCopy(src->perms))) - return NULL; - - if (src->timestamps && - !(def->timestamps = virStorageTimestampsCopy(src->timestamps))) - return NULL; - - if (virStorageSourceSeclabelsCopy(def, src) < 0) - return NULL; - - if (src->auth && - !(def->auth = virStorageAuthDefCopy(src->auth))) - return NULL; - - if (src->pr && - !(def->pr = virStoragePRDefCopy(src->pr))) - return NULL; - - if (src->nvme) - def->nvme = virStorageSourceNVMeDefCopy(src->nvme); - - if (virStorageSourceInitiatorCopy(&def->initiator, &src->initiator) < 0) - return NULL; - - if (backingChain && src->backingStore) { - if (!(def->backingStore = virStorageSourceCopy(src->backingStore, - true))) - return NULL; - } - - /* ssh config passthrough for libguestfs */ - def->ssh_host_key_check_disabled = src->ssh_host_key_check_disabled; - def->ssh_user = g_strdup(src->ssh_user); - - return g_steal_pointer(&def); -} - - -/** - * virStorageSourceIsSameLocation: - * - * Returns true if the sources @a and @b point to the same storage location. - * This does not compare any other configuration option - */ -bool -virStorageSourceIsSameLocation(virStorageSourcePtr a, - virStorageSourcePtr b) -{ - size_t i; - - /* there are multiple possibilities to define an empty source */ - if (virStorageSourceIsEmpty(a) && - virStorageSourceIsEmpty(b)) - return true; - - if (virStorageSourceGetActualType(a) != virStorageSourceGetActualType(b)) - return false; - - if (STRNEQ_NULLABLE(a->path, b->path) || - STRNEQ_NULLABLE(a->volume, b->volume) || - STRNEQ_NULLABLE(a->snapshot, b->snapshot)) - return false; - - if (a->type == VIR_STORAGE_TYPE_NETWORK) { - if (a->protocol != b->protocol || - a->nhosts != b->nhosts) - return false; - - for (i = 0; i < a->nhosts; i++) { - if (a->hosts[i].transport != b->hosts[i].transport || - a->hosts[i].port != b->hosts[i].port || - STRNEQ_NULLABLE(a->hosts[i].name, b->hosts[i].name) || - STRNEQ_NULLABLE(a->hosts[i].socket, b->hosts[i].socket)) - return false; - } - } - - if (a->type == VIR_STORAGE_TYPE_NVME && - !virStorageSourceNVMeDefIsEqual(a->nvme, b->nvme)) - return false; - - return true; -} - - -/** - * virStorageSourceInitChainElement: - * @newelem: New backing chain element disk source - * @old: Existing top level disk source - * @transferLabels: Transfer security labels. - * - * Transfers relevant information from the existing disk source to the new - * backing chain element if they weren't supplied so that labelling info - * and possibly other stuff is correct. - * - * If @transferLabels is true, security labels from the existing disk are copied - * to the new disk. Otherwise the default domain imagelabel label will be used. - * - * Returns 0 on success, -1 on error. - */ -int -virStorageSourceInitChainElement(virStorageSourcePtr newelem, - virStorageSourcePtr old, - bool transferLabels) -{ - if (transferLabels && - !newelem->seclabels && - virStorageSourceSeclabelsCopy(newelem, old) < 0) - return -1; - - newelem->shared = old->shared; - newelem->readonly = old->readonly; - - return 0; -} - - -void -virStorageSourcePoolDefFree(virStorageSourcePoolDefPtr def) -{ - if (!def) - return; - - VIR_FREE(def->pool); - VIR_FREE(def->volume); - - VIR_FREE(def); -} - - -/** - * virStorageSourceGetActualType: - * @def: storage source definition - * - * Returns type of @def. In case when the type is VIR_STORAGE_TYPE_VOLUME - * and virDomainDiskTranslateSourcePool was called on @def the actual type - * of the storage volume is returned rather than VIR_STORAGE_TYPE_VOLUME. - */ -int -virStorageSourceGetActualType(const virStorageSource *def) -{ - if (def->type == VIR_STORAGE_TYPE_VOLUME && - def->srcpool && - def->srcpool->actualtype != VIR_STORAGE_TYPE_NONE) - return def->srcpool->actualtype; - - return def->type; -} - - -bool -virStorageSourceIsLocalStorage(const virStorageSource *src) -{ - virStorageType type = virStorageSourceGetActualType(src); - - switch (type) { - case VIR_STORAGE_TYPE_FILE: - case VIR_STORAGE_TYPE_BLOCK: - case VIR_STORAGE_TYPE_DIR: - return true; - - case VIR_STORAGE_TYPE_NETWORK: - case VIR_STORAGE_TYPE_VOLUME: - /* While NVMe disks are local, they are not accessible via src->path. - * Therefore, we have to return false here. */ - case VIR_STORAGE_TYPE_NVME: - case VIR_STORAGE_TYPE_LAST: - case VIR_STORAGE_TYPE_NONE: - return false; - } - - return false; -} - - -/** - * virStorageSourceIsEmpty: - * - * @src: disk source to check - * - * Returns true if the guest disk has no associated host storage source - * (such as an empty cdrom drive). - */ -bool -virStorageSourceIsEmpty(virStorageSourcePtr src) -{ - if (virStorageSourceIsLocalStorage(src) && !src->path) - return true; - - if (src->type == VIR_STORAGE_TYPE_NONE) - return true; - - if (src->type == VIR_STORAGE_TYPE_NETWORK && - src->protocol == VIR_STORAGE_NET_PROTOCOL_NONE) - return true; - - return false; -} - - -/** - * virStorageSourceIsBlockLocal: - * @src: disk source definition - * - * Returns true if @src describes a locally accessible block storage source. - * This includes block devices and host-mapped iSCSI volumes. - */ -bool -virStorageSourceIsBlockLocal(const virStorageSource *src) -{ - return virStorageSourceGetActualType(src) == VIR_STORAGE_TYPE_BLOCK; -} - - -/** - * virStorageSourceBackingStoreClear: - * - * @src: disk source to clear - * - * Clears information about backing store of the current storage file. - */ -void -virStorageSourceBackingStoreClear(virStorageSourcePtr def) -{ - if (!def) - return; - - VIR_FREE(def->relPath); - VIR_FREE(def->backingStoreRaw); - - /* recursively free backing chain */ - virObjectUnref(def->backingStore); - def->backingStore = NULL; -} - - -void -virStorageSourceClear(virStorageSourcePtr def) -{ - if (!def) - return; - - VIR_FREE(def->path); - VIR_FREE(def->volume); - VIR_FREE(def->snapshot); - VIR_FREE(def->configFile); - VIR_FREE(def->query); - virStorageSourceNetCookiesClear(def); - virStorageSourcePoolDefFree(def->srcpool); - virBitmapFree(def->features); - VIR_FREE(def->compat); - virStorageEncryptionFree(def->encryption); - virStoragePRDefFree(def->pr); - virStorageSourceNVMeDefFree(def->nvme); - virStorageSourceSeclabelsClear(def); - virStoragePermsFree(def->perms); - VIR_FREE(def->timestamps); - - virStorageSourceSliceFree(def->sliceStorage); - - virStorageNetHostDefFree(def->nhosts, def->hosts); - virStorageAuthDefFree(def->auth); - virObjectUnref(def->privateData); - - VIR_FREE(def->nodestorage); - VIR_FREE(def->nodeformat); - - virStorageSourceBackingStoreClear(def); - - VIR_FREE(def->tlsAlias); - VIR_FREE(def->tlsCertdir); - - VIR_FREE(def->ssh_user); - - virStorageSourceInitiatorClear(&def->initiator); - - /* clear everything except the class header as the object APIs - * will break otherwise */ - memset((char *) def + sizeof(def->parent), 0, - sizeof(*def) - sizeof(def->parent)); -} - - -static void -virStorageSourceDispose(void *obj) -{ - virStorageSourcePtr src = obj; - - virStorageSourceClear(src); -} - - -static int -virStorageSourceOnceInit(void) -{ - if (!VIR_CLASS_NEW(virStorageSource, virClassForObject())) - return -1; - - return 0; -} - - -VIR_ONCE_GLOBAL_INIT(virStorageSource); - - -virStorageSourcePtr -virStorageSourceNew(void) -{ - virStorageSourcePtr ret; - - if (virStorageSourceInitialize() < 0) - abort(); - - if (!(ret = virObjectNew(virStorageSourceClass))) - abort(); - - return ret; -} - - -/** - * virStorageSourceIsRelative: - * @src: storage source to check - * - * Returns true if given storage source definition is a relative path. - */ -bool -virStorageSourceIsRelative(virStorageSourcePtr src) -{ - virStorageType actual_type = virStorageSourceGetActualType(src); - - if (!src->path) - return false; - - switch (actual_type) { - case VIR_STORAGE_TYPE_FILE: - case VIR_STORAGE_TYPE_BLOCK: - case VIR_STORAGE_TYPE_DIR: - return src->path[0] != '/'; - - case VIR_STORAGE_TYPE_NETWORK: - case VIR_STORAGE_TYPE_VOLUME: - case VIR_STORAGE_TYPE_NVME: - case VIR_STORAGE_TYPE_NONE: - case VIR_STORAGE_TYPE_LAST: - return false; - } - - return false; -} - - -static unsigned int -virStorageSourceNetworkDefaultPort(virStorageNetProtocol protocol) -{ - switch (protocol) { - case VIR_STORAGE_NET_PROTOCOL_HTTP: - return 80; - - case VIR_STORAGE_NET_PROTOCOL_HTTPS: - return 443; - - case VIR_STORAGE_NET_PROTOCOL_FTP: - return 21; - - case VIR_STORAGE_NET_PROTOCOL_FTPS: - return 990; - - case VIR_STORAGE_NET_PROTOCOL_TFTP: - return 69; - - case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: - return 7000; - - case VIR_STORAGE_NET_PROTOCOL_NBD: - return 10809; - - case VIR_STORAGE_NET_PROTOCOL_SSH: - return 22; - - case VIR_STORAGE_NET_PROTOCOL_ISCSI: - return 3260; - - case VIR_STORAGE_NET_PROTOCOL_GLUSTER: - return 24007; - - case VIR_STORAGE_NET_PROTOCOL_RBD: - /* we don't provide a default for RBD */ - return 0; - - case VIR_STORAGE_NET_PROTOCOL_VXHS: - return 9999; - - case VIR_STORAGE_NET_PROTOCOL_LAST: - case VIR_STORAGE_NET_PROTOCOL_NONE: - return 0; - } - - return 0; -} - - -void -virStorageSourceNetworkAssignDefaultPorts(virStorageSourcePtr src) -{ - size_t i; - - for (i = 0; i < src->nhosts; i++) { - if (src->hosts[i].transport == VIR_STORAGE_NET_HOST_TRANS_TCP && - src->hosts[i].port == 0) - src->hosts[i].port = virStorageSourceNetworkDefaultPort(src->protocol); - } -} - - -void -virStorageSourceInitiatorParseXML(xmlXPathContextPtr ctxt, - virStorageSourceInitiatorDefPtr initiator) -{ - initiator->iqn = virXPathString("string(./initiator/iqn/@name)", ctxt); -} - -void -virStorageSourceInitiatorFormatXML(virStorageSourceInitiatorDefPtr initiator, - virBufferPtr buf) -{ - if (!initiator->iqn) - return; - - virBufferAddLit(buf, "<initiator>\n"); - virBufferAdjustIndent(buf, 2); - virBufferEscapeString(buf, "<iqn name='%s'/>\n", initiator->iqn); - virBufferAdjustIndent(buf, -2); - virBufferAddLit(buf, "</initiator>\n"); -} - -int -virStorageSourceInitiatorCopy(virStorageSourceInitiatorDefPtr dest, - const virStorageSourceInitiatorDef *src) -{ - dest->iqn = g_strdup(src->iqn); - return 0; -} - -void -virStorageSourceInitiatorClear(virStorageSourceInitiatorDefPtr initiator) -{ - VIR_FREE(initiator->iqn); -} diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 557e7da81e..d8a8f36831 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -21,444 +21,10 @@ #pragma once -#include "virbitmap.h" -#include "virobject.h" -#include "virseclabel.h" -#include "virstorageencryption.h" -#include "virsecret.h" -#include "virenum.h" -#include "virpci.h" - - -/* Types of disk backends (host resource). Comparable to the public - * virStorageVolType, except we have an undetermined state, don't have - * a netdir type, and add a volume type for reference through a - * storage pool. */ -typedef enum { - VIR_STORAGE_TYPE_NONE, - VIR_STORAGE_TYPE_FILE, - VIR_STORAGE_TYPE_BLOCK, - VIR_STORAGE_TYPE_DIR, - VIR_STORAGE_TYPE_NETWORK, - VIR_STORAGE_TYPE_VOLUME, - VIR_STORAGE_TYPE_NVME, - - VIR_STORAGE_TYPE_LAST -} virStorageType; - -VIR_ENUM_DECL(virStorage); - - -typedef enum { - VIR_STORAGE_FILE_AUTO_SAFE = -2, - VIR_STORAGE_FILE_AUTO = -1, - VIR_STORAGE_FILE_NONE = 0, - VIR_STORAGE_FILE_RAW, - VIR_STORAGE_FILE_DIR, - VIR_STORAGE_FILE_BOCHS, - VIR_STORAGE_FILE_CLOOP, - VIR_STORAGE_FILE_DMG, - VIR_STORAGE_FILE_ISO, - VIR_STORAGE_FILE_VPC, - VIR_STORAGE_FILE_VDI, - - /* Not direct file formats, but used for various drivers */ - VIR_STORAGE_FILE_FAT, - VIR_STORAGE_FILE_VHD, - VIR_STORAGE_FILE_PLOOP, - - /* Not a format, but a marker: all formats below this point have - * libvirt support for following a backing chain */ - VIR_STORAGE_FILE_BACKING, - - VIR_STORAGE_FILE_COW = VIR_STORAGE_FILE_BACKING, - VIR_STORAGE_FILE_QCOW, - VIR_STORAGE_FILE_QCOW2, - VIR_STORAGE_FILE_QED, - VIR_STORAGE_FILE_VMDK, - - VIR_STORAGE_FILE_LAST, -} virStorageFileFormat; - -VIR_ENUM_DECL(virStorageFileFormat); - -typedef enum { - VIR_STORAGE_FILE_FEATURE_LAZY_REFCOUNTS = 0, - - VIR_STORAGE_FILE_FEATURE_LAST -} virStorageFileFeature; - -VIR_ENUM_DECL(virStorageFileFeature); - -typedef struct _virStoragePerms virStoragePerms; -typedef virStoragePerms *virStoragePermsPtr; -struct _virStoragePerms { - mode_t mode; - uid_t uid; - gid_t gid; - char *label; -}; - - -typedef struct _virStorageTimestamps virStorageTimestamps; -typedef virStorageTimestamps *virStorageTimestampsPtr; -struct _virStorageTimestamps { - struct timespec atime; - struct timespec btime; /* birth time unknown if btime.tv_nsec == -1 */ - struct timespec ctime; - struct timespec mtime; -}; - - -/* Information related to network storage */ -typedef enum { - VIR_STORAGE_NET_PROTOCOL_NONE, - VIR_STORAGE_NET_PROTOCOL_NBD, - VIR_STORAGE_NET_PROTOCOL_RBD, - VIR_STORAGE_NET_PROTOCOL_SHEEPDOG, - VIR_STORAGE_NET_PROTOCOL_GLUSTER, - VIR_STORAGE_NET_PROTOCOL_ISCSI, - VIR_STORAGE_NET_PROTOCOL_HTTP, - VIR_STORAGE_NET_PROTOCOL_HTTPS, - VIR_STORAGE_NET_PROTOCOL_FTP, - VIR_STORAGE_NET_PROTOCOL_FTPS, - VIR_STORAGE_NET_PROTOCOL_TFTP, - VIR_STORAGE_NET_PROTOCOL_SSH, - VIR_STORAGE_NET_PROTOCOL_VXHS, - - VIR_STORAGE_NET_PROTOCOL_LAST -} virStorageNetProtocol; - -VIR_ENUM_DECL(virStorageNetProtocol); - - -typedef enum { - VIR_STORAGE_NET_HOST_TRANS_TCP, - VIR_STORAGE_NET_HOST_TRANS_UNIX, - VIR_STORAGE_NET_HOST_TRANS_RDMA, - - VIR_STORAGE_NET_HOST_TRANS_LAST -} virStorageNetHostTransport; - -VIR_ENUM_DECL(virStorageNetHostTransport); - -typedef struct _virStorageNetHostDef virStorageNetHostDef; -typedef virStorageNetHostDef *virStorageNetHostDefPtr; -struct _virStorageNetHostDef { - char *name; - unsigned int port; - int transport; /* virStorageNetHostTransport */ - char *socket; /* path to unix socket */ -}; - -typedef struct _virStorageNetCookieDef virStorageNetCookieDef; -typedef virStorageNetCookieDef *virStorageNetCookieDefPtr; -struct _virStorageNetCookieDef { - char *name; - char *value; -}; - -void virStorageNetCookieDefFree(virStorageNetCookieDefPtr def); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageNetCookieDef, virStorageNetCookieDefFree); - -/* Information for a storage volume from a virStoragePool */ - -/* - * Used for volume "type" disk to indicate how to represent - * the disk source if the specified "pool" is of iscsi type. - */ -typedef enum { - VIR_STORAGE_SOURCE_POOL_MODE_DEFAULT = 0, - - /* Use the path as it shows up on host, e.g. - * /dev/disk/by-path/ip-$ip-iscsi-$iqn:iscsi.iscsi-pool0-lun-1 - */ - VIR_STORAGE_SOURCE_POOL_MODE_HOST, - - /* Use the URI from the storage pool source element host attribute. E.g. - * file=iscsi://demo.org:6000/iqn.1992-01.com.example/1. - */ - VIR_STORAGE_SOURCE_POOL_MODE_DIRECT, - - VIR_STORAGE_SOURCE_POOL_MODE_LAST -} virStorageSourcePoolMode; - -VIR_ENUM_DECL(virStorageSourcePoolMode); - -typedef struct _virStorageSourcePoolDef virStorageSourcePoolDef; -struct _virStorageSourcePoolDef { - char *pool; /* pool name */ - char *volume; /* volume name */ - int voltype; /* virStorageVolType, internal only */ - int pooltype; /* virStoragePoolType from storage_conf.h, internal only */ - int actualtype; /* virStorageType, internal only */ - int mode; /* virStorageSourcePoolMode, currently makes sense only for iscsi pool */ -}; -typedef virStorageSourcePoolDef *virStorageSourcePoolDefPtr; - - -typedef enum { - VIR_STORAGE_AUTH_TYPE_NONE, - VIR_STORAGE_AUTH_TYPE_CHAP, - VIR_STORAGE_AUTH_TYPE_CEPHX, - - VIR_STORAGE_AUTH_TYPE_LAST, -} virStorageAuthType; -VIR_ENUM_DECL(virStorageAuth); - -typedef struct _virStorageAuthDef virStorageAuthDef; -typedef virStorageAuthDef *virStorageAuthDefPtr; -struct _virStorageAuthDef { - char *username; - char *secrettype; /* <secret type='%s' for disk source */ - int authType; /* virStorageAuthType */ - virSecretLookupTypeDef seclookupdef; -}; - -typedef struct _virStoragePRDef virStoragePRDef; -typedef virStoragePRDef *virStoragePRDefPtr; -struct _virStoragePRDef { - int managed; /* enum virTristateBool */ - char *path; - - /* manager object alias */ - char *mgralias; -}; - -typedef struct _virStorageSourceInitiatorDef virStorageSourceInitiatorDef; -typedef virStorageSourceInitiatorDef *virStorageSourceInitiatorDefPtr; -struct _virStorageSourceInitiatorDef { - char *iqn; /* Initiator IQN */ -}; - -typedef struct _virStorageSourceNVMeDef virStorageSourceNVMeDef; -typedef virStorageSourceNVMeDef *virStorageSourceNVMeDefPtr; -struct _virStorageSourceNVMeDef { - unsigned long long namespc; - int managed; /* enum virTristateBool */ - virPCIDeviceAddress pciAddr; - - /* Don't forget to update virStorageSourceNVMeDefCopy */ -}; - - -typedef struct _virStorageSourceSlice virStorageSourceSlice; -typedef virStorageSourceSlice *virStorageSourceSlicePtr; -struct _virStorageSourceSlice { - unsigned long long offset; - unsigned long long size; - char *nodename; -}; - - -typedef struct _virStorageSource virStorageSource; -typedef virStorageSource *virStorageSourcePtr; - -/* Stores information related to a host resource. In the case of backing - * chains, multiple source disks join to form a single guest view. - * - * IMPORTANT: When adding fields to this struct it's also necessary to add - * appropriate code to the virStorageSourceCopy deep copy function */ -struct _virStorageSource { - virObject parent; - - unsigned int id; /* backing chain identifier, 0 is unset */ - int type; /* virStorageType */ - char *path; - int protocol; /* virStorageNetProtocol */ - char *volume; /* volume name for remote storage */ - char *snapshot; /* for storage systems supporting internal snapshots */ - char *configFile; /* some storage systems use config file as part of - the source definition */ - char *query; /* query string for HTTP based protocols */ - size_t nhosts; - virStorageNetHostDefPtr hosts; - size_t ncookies; - virStorageNetCookieDefPtr *cookies; - virStorageSourcePoolDefPtr srcpool; - virStorageAuthDefPtr auth; - virStorageEncryptionPtr encryption; - virStoragePRDefPtr pr; - virTristateBool sslverify; - /* both values below have 0 as default value */ - unsigned long long readahead; /* size of the readahead buffer in bytes */ - unsigned long long timeout; /* connection timeout in seconds */ - - virStorageSourceNVMeDefPtr nvme; /* type == VIR_STORAGE_TYPE_NVME */ - - virStorageSourceInitiatorDef initiator; - - virObjectPtr privateData; - - int format; /* virStorageFileFormat in domain backing chains, but - * pool-specific enum for storage volumes */ - virBitmapPtr features; - char *compat; - bool nocow; - bool sparse; - - virStorageSourceSlicePtr sliceStorage; - - virStoragePermsPtr perms; - virStorageTimestampsPtr timestamps; - unsigned long long capacity; /* in bytes, 0 if unknown */ - unsigned long long allocation; /* in bytes, 0 if unknown */ - unsigned long long physical; /* in bytes, 0 if unknown */ - unsigned long long clusterSize; /* in bytes, 0 if unknown */ - bool has_allocation; /* Set to true when provided in XML */ - - size_t nseclabels; - virSecurityDeviceLabelDefPtr *seclabels; - - /* Don't ever write to the image */ - bool readonly; - - /* image is shared across hosts */ - bool shared; - - /* backing chain of the storage source */ - virStorageSourcePtr backingStore; - - /* metadata for storage driver access to remote and local volumes */ - void *drv; - - /* metadata about storage image which need separate fields */ - /* Relative name by which this image was opened from its parent, or NULL - * if this image was opened by absolute name */ - char *relPath; - /* Name of the child backing store recorded in metadata of the - * current file. */ - char *backingStoreRaw; - virStorageFileFormat backingStoreRawFormat; - - /* metadata that allows identifying given storage source */ - char *nodeformat; /* name of the format handler object */ - char *nodestorage; /* name of the storage object */ - - /* An optional setting to enable usage of TLS for the storage source */ - int haveTLS; /* enum virTristateBool */ - - /* Indication whether the haveTLS value was altered due to qemu.conf - * setting when haveTLS is missing from the domain config file */ - bool tlsFromConfig; - - /* If TLS is used, then mgmt of the TLS credentials occurs via an - * object that is generated using a specific alias for a specific - * certificate directory with listen and verify bools. */ - char *tlsAlias; - char *tlsCertdir; - - bool detected; /* true if this entry was not provided by the user */ - - unsigned int debugLevel; - bool debug; - - /* Libvirt currently stores the following properties in virDomainDiskDef. - * These instances are currently just copies from the parent definition and - * are not mapped back to the XML */ - int iomode; /* enum virDomainDiskIo */ - int cachemode; /* enum virDomainDiskCache */ - int discard; /* enum virDomainDiskDiscard */ - int detect_zeroes; /* enum virDomainDiskDetectZeroes */ - - bool floppyimg; /* set to true if the storage source is going to be used - as a source for floppy drive */ - - bool hostcdrom; /* backing device is a cdrom */ - - /* passthrough variables for the ssh driver which we don't handle properly */ - /* these must not be used apart from formatting the output JSON in the qemu driver */ - char *ssh_user; - bool ssh_host_key_check_disabled; -}; - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageSource, virObjectUnref); - +#include "internal.h" int virStorageFileGetSCSIKey(const char *path, char **key, bool ignoreError); int virStorageFileGetNPIVKey(const char *path, char **key); - -void virStorageAuthDefFree(virStorageAuthDefPtr def); -virStorageAuthDefPtr virStorageAuthDefCopy(const virStorageAuthDef *src); -virStorageAuthDefPtr virStorageAuthDefParse(xmlNodePtr node, - xmlXPathContextPtr ctxt); -void virStorageAuthDefFormat(virBufferPtr buf, virStorageAuthDefPtr authdef); - -void virStoragePRDefFree(virStoragePRDefPtr prd); -virStoragePRDefPtr virStoragePRDefParseXML(xmlXPathContextPtr ctxt); -void virStoragePRDefFormat(virBufferPtr buf, - virStoragePRDefPtr prd, - bool migratable); -bool virStoragePRDefIsEqual(virStoragePRDefPtr a, - virStoragePRDefPtr b); -bool virStoragePRDefIsManaged(virStoragePRDefPtr prd); - -bool -virStorageSourceChainHasManagedPR(virStorageSourcePtr src); - -void virStorageSourceNVMeDefFree(virStorageSourceNVMeDefPtr def); -G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageSourceNVMeDef, virStorageSourceNVMeDefFree); - -bool virStorageSourceChainHasNVMe(const virStorageSource *src); - -virSecurityDeviceLabelDefPtr -virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src, - const char *model); - -void virStorageNetHostDefClear(virStorageNetHostDefPtr def); -void virStorageNetHostDefFree(size_t nhosts, virStorageNetHostDefPtr hosts); -virStorageNetHostDefPtr virStorageNetHostDefCopy(size_t nhosts, - virStorageNetHostDefPtr hosts); -int virStorageSourceInitChainElement(virStorageSourcePtr newelem, - virStorageSourcePtr old, - bool force); -void virStorageSourcePoolDefFree(virStorageSourcePoolDefPtr def); -void virStorageSourceClear(virStorageSourcePtr def); -int virStorageSourceGetActualType(const virStorageSource *def); -bool virStorageSourceIsLocalStorage(const virStorageSource *src); -bool virStorageSourceIsEmpty(virStorageSourcePtr src); -bool virStorageSourceIsBlockLocal(const virStorageSource *src); -virStorageSourcePtr virStorageSourceNew(void); -void virStorageSourceBackingStoreClear(virStorageSourcePtr def); - -int virStorageSourceNetCookiesValidate(virStorageSourcePtr src); - -virStorageSourcePtr virStorageSourceCopy(const virStorageSource *src, - bool backingChain) - ATTRIBUTE_NONNULL(1); -bool virStorageSourceIsSameLocation(virStorageSourcePtr a, - virStorageSourcePtr b) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); - -bool virStorageSourceIsRelative(virStorageSourcePtr src); - -void -virStorageSourceNetworkAssignDefaultPorts(virStorageSourcePtr src) - ATTRIBUTE_NONNULL(1); - -bool -virStorageSourceIsBacking(const virStorageSource *src); -bool -virStorageSourceHasBacking(const virStorageSource *src); - - -void -virStorageSourceInitiatorParseXML(xmlXPathContextPtr ctxt, - virStorageSourceInitiatorDefPtr initiator); - -void -virStorageSourceInitiatorFormatXML(virStorageSourceInitiatorDefPtr initiator, - virBufferPtr buf); - -int -virStorageSourceInitiatorCopy(virStorageSourceInitiatorDefPtr dest, - const virStorageSourceInitiatorDef *src); - -void -virStorageSourceInitiatorClear(virStorageSourceInitiatorDefPtr initiator); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageAuthDef, virStorageAuthDefFree); -- 2.28.0

The code handles XML bits and internal definition and should be in conf directory. Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- po/POTFILES.in | 2 +- src/conf/domain_conf.h | 2 +- src/conf/meson.build | 1 + src/conf/storage_conf.h | 2 +- .../storage_encryption.c} | 4 ++-- .../storage_encryption.h} | 2 +- src/conf/storage_source.h | 2 +- src/libvirt_private.syms | 12 ++++++------ src/util/meson.build | 1 - 9 files changed, 14 insertions(+), 14 deletions(-) rename src/{util/virstorageencryption.c => conf/storage_encryption.c} (99%) rename src/{util/virstorageencryption.h => conf/storage_encryption.h} (98%) diff --git a/po/POTFILES.in b/po/POTFILES.in index 135198820a..3062a4080c 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -43,6 +43,7 @@ @SRCDIR@src/conf/snapshot_conf.c @SRCDIR@src/conf/storage_adapter_conf.c @SRCDIR@src/conf/storage_conf.c +@SRCDIR@src/conf/storage_encryption.c @SRCDIR@src/conf/storage_source.c @SRCDIR@src/conf/virchrdev.c @SRCDIR@src/conf/virdomainmomentobjlist.c @@ -306,7 +307,6 @@ @SRCDIR@src/util/virscsivhost.c @SRCDIR@src/util/virsecret.c @SRCDIR@src/util/virsocketaddr.c -@SRCDIR@src/util/virstorageencryption.c @SRCDIR@src/util/virstoragefile.c @SRCDIR@src/util/virstring.c @SRCDIR@src/util/virsysinfo.c diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 5c95317501..298a1ac261 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -29,7 +29,7 @@ #include "internal.h" #include "virconftypes.h" #include "capabilities.h" -#include "virstorageencryption.h" +#include "storage_encryption.h" #include "cpu_conf.h" #include "virthread.h" #include "virhash.h" diff --git a/src/conf/meson.build b/src/conf/meson.build index 12efaf56cc..709389c9ba 100644 --- a/src/conf/meson.build +++ b/src/conf/meson.build @@ -54,6 +54,7 @@ storage_conf_sources = [ 'storage_adapter_conf.c', 'storage_capabilities.c', 'storage_conf.c', + 'storage_encryption.c', 'storage_source.c', 'virstorageobj.c', ] diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h index 47f2bf602d..7835c7aed8 100644 --- a/src/conf/storage_conf.h +++ b/src/conf/storage_conf.h @@ -22,7 +22,7 @@ #pragma once #include "internal.h" -#include "virstorageencryption.h" +#include "storage_encryption.h" #include "storage_source.h" #include "virbitmap.h" #include "virthread.h" diff --git a/src/util/virstorageencryption.c b/src/conf/storage_encryption.c similarity index 99% rename from src/util/virstorageencryption.c rename to src/conf/storage_encryption.c index c893f0babe..020dfdc6a2 100644 --- a/src/util/virstorageencryption.c +++ b/src/conf/storage_encryption.c @@ -1,5 +1,5 @@ /* - * virstorageencryption.c: volume encryption information + * storage_encryption.c: volume encryption information * * Copyright (C) 2009-2014 Red Hat, Inc. * @@ -27,7 +27,7 @@ #include "virbuffer.h" #include "viralloc.h" -#include "virstorageencryption.h" +#include "storage_encryption.h" #include "virxml.h" #include "virerror.h" #include "viruuid.h" diff --git a/src/util/virstorageencryption.h b/src/conf/storage_encryption.h similarity index 98% rename from src/util/virstorageencryption.h rename to src/conf/storage_encryption.h index 352dd373d6..c5673053e6 100644 --- a/src/util/virstorageencryption.h +++ b/src/conf/storage_encryption.h @@ -1,5 +1,5 @@ /* - * virstorageencryption.h: volume encryption information + * storage_encryption.h: volume encryption information * * Copyright (C) 2009-2011, 2014 Red Hat, Inc. * diff --git a/src/conf/storage_source.h b/src/conf/storage_source.h index 89bba38599..dac2e8b71c 100644 --- a/src/conf/storage_source.h +++ b/src/conf/storage_source.h @@ -21,13 +21,13 @@ #pragma once +#include "storage_encryption.h" #include "virbitmap.h" #include "virenum.h" #include "virobject.h" #include "virpci.h" #include "virseclabel.h" #include "virsecret.h" -#include "virstorageencryption.h" /* Types of disk backends (host resource). Comparable to the public * virStorageVolType, except we have an undetermined state, don't have diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0b0a57e85d..91f795adfc 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1047,6 +1047,12 @@ virStorageVolTypeFromString; virStorageVolTypeToString; +# conf/storage_encryption.h +virStorageEncryptionFormat; +virStorageEncryptionFree; +virStorageEncryptionParseNode; + + # conf/storage_event.h virStoragePoolEventLifecycleNew; virStoragePoolEventRefreshNew; @@ -3207,12 +3213,6 @@ virSocketAddrSetIPv6AddrNetOrder; virSocketAddrSetPort; -# util/virstorageencryption.h -virStorageEncryptionFormat; -virStorageEncryptionFree; -virStorageEncryptionParseNode; - - # util/virstoragefile.h virStorageFileGetNPIVKey; virStorageFileGetSCSIKey; diff --git a/src/util/meson.build b/src/util/meson.build index b510f0ebe9..c077c5cc99 100644 --- a/src/util/meson.build +++ b/src/util/meson.build @@ -88,7 +88,6 @@ util_sources = [ 'virsecret.c', 'virsocket.c', 'virsocketaddr.c', - 'virstorageencryption.c', 'virstoragefile.c', 'virstring.c', 'virsysinfo.c', -- 2.28.0

Now that storage file code is separated from storage source definition it makes sense to rename the functions to make it clear. Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- src/libvirt_private.syms | 18 +++++----- src/libxl/xen_xl.c | 2 +- src/qemu/qemu_domain.c | 16 ++++----- src/qemu/qemu_driver.c | 6 ++-- src/storage/storage_util.c | 6 ++-- src/storage_file/storage_file.c | 60 ++++++++++++++++----------------- src/storage_file/storage_file.h | 42 +++++++++++------------ tests/qemublocktest.c | 6 ++-- tests/virstoragetest.c | 2 +- 9 files changed, 79 insertions(+), 79 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 91f795adfc..c1a5aab7d6 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1681,6 +1681,7 @@ virStorageFileChainLookup; virStorageFileChown; virStorageFileCreate; virStorageFileDeinit; +virStorageFileFindByNodeName; virStorageFileGetBackingStoreStr; virStorageFileGetMetadata; virStorageFileGetMetadataFromBuf; @@ -1689,8 +1690,13 @@ virStorageFileGetRelativeBackingPath; virStorageFileGetUniqueIdentifier; virStorageFileInit; virStorageFileInitAs; +virStorageFileNewFromBacking; +virStorageFileNewFromBackingAbsolute; virStorageFileParseBackingStoreStr; virStorageFileParseChainIndex; +virStorageFileParseRBDColonString; +virStorageFilePrivateDataFormatRelPath; +virStorageFilePrivateDataParseRelPath; virStorageFileProbeFormat; virStorageFileRead; virStorageFileReportBrokenChain; @@ -1700,15 +1706,9 @@ virStorageFileSupportsBackingChainTraversal; virStorageFileSupportsCreate; virStorageFileSupportsSecurityDriver; virStorageFileUnlink; -virStorageSourceFindByNodeName; -virStorageSourceNewFromBacking; -virStorageSourceNewFromBackingAbsolute; -virStorageSourceParseRBDColonString; -virStorageSourcePrivateDataFormatRelPath; -virStorageSourcePrivateDataParseRelPath; -virStorageSourceUpdateBackingSizes; -virStorageSourceUpdateCapacity; -virStorageSourceUpdatePhysicalSize; +virStorageFileUpdateBackingSizes; +virStorageFileUpdateCapacity; +virStorageFileUpdatePhysicalSize; # storage_file/storage_file_backend.h diff --git a/src/libxl/xen_xl.c b/src/libxl/xen_xl.c index 1f97e6bdd4..f26f9bb5b7 100644 --- a/src/libxl/xen_xl.c +++ b/src/libxl/xen_xl.c @@ -640,7 +640,7 @@ xenParseXLDiskSrc(virDomainDiskDefPtr disk, char *srcstr) virDomainDiskSetType(disk, VIR_STORAGE_TYPE_NETWORK); disk->src->protocol = VIR_STORAGE_NET_PROTOCOL_RBD; - ret = virStorageSourceParseRBDColonString(tmpstr, disk->src); + ret = virStorageFileParseRBDColonString(tmpstr, disk->src); } else { virDomainDiskSetSource(disk, srcstr); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 8ddb7b70d2..f193c5e417 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1894,7 +1894,7 @@ qemuStorageSourcePrivateDataParse(xmlXPathContextPtr ctxt, return -1; } - if (virStorageSourcePrivateDataParseRelPath(ctxt, src) < 0) + if (virStorageFilePrivateDataParseRelPath(ctxt, src) < 0) return -1; return 0; @@ -1936,7 +1936,7 @@ qemuStorageSourcePrivateDataFormat(virStorageSourcePtr src, if (src->pr) virBufferAsprintf(buf, "<reservations mgralias='%s'/>\n", src->pr->mgralias); - if (virStorageSourcePrivateDataFormatRelPath(src, buf) < 0) + if (virStorageFilePrivateDataFormatRelPath(src, buf) < 0) return -1; if (srcPriv) { @@ -2597,15 +2597,15 @@ qemuDomainObjPrivateXMLParseBlockjobNodename(qemuBlockJobDataPtr job, return; if (job->disk && - (*src = virStorageSourceFindByNodeName(job->disk->src, nodename))) + (*src = virStorageFileFindByNodeName(job->disk->src, nodename))) return; if (job->chain && - (*src = virStorageSourceFindByNodeName(job->chain, nodename))) + (*src = virStorageFileFindByNodeName(job->chain, nodename))) return; if (job->mirrorChain && - (*src = virStorageSourceFindByNodeName(job->mirrorChain, nodename))) + (*src = virStorageFileFindByNodeName(job->mirrorChain, nodename))) return; /* the node was in the XML but was not found in the job definitions */ @@ -9920,11 +9920,11 @@ qemuDomainDiskLookupByNodename(virDomainDefPtr def, for (i = 0; i < def->ndisks; i++) { virDomainDiskDefPtr domdisk = def->disks[i]; - if ((*src = virStorageSourceFindByNodeName(domdisk->src, nodename))) + if ((*src = virStorageFileFindByNodeName(domdisk->src, nodename))) return domdisk; if (domdisk->mirror && - (*src = virStorageSourceFindByNodeName(domdisk->mirror, nodename))) + (*src = virStorageFileFindByNodeName(domdisk->mirror, nodename))) return domdisk; } @@ -9933,7 +9933,7 @@ qemuDomainDiskLookupByNodename(virDomainDefPtr def, virDomainBackupDiskDefPtr backupdisk = backupdef->disks + i; if (backupdisk->store && - (*src = virStorageSourceFindByNodeName(backupdisk->store, nodename))) + (*src = virStorageFileFindByNodeName(backupdisk->store, nodename))) return virDomainDiskByTarget(def, backupdisk->name); } } diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f6554e1506..0818bb9f89 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10838,7 +10838,7 @@ qemuDomainStorageUpdatePhysical(virQEMUDriverPtr driver, return -1; } - ret = virStorageSourceUpdatePhysicalSize(src, fd, &sb); + ret = virStorageFileUpdatePhysicalSize(src, fd, &sb); qemuDomainStorageCloseStat(src, &fd); @@ -10906,10 +10906,10 @@ qemuStorageLimitsRefresh(virQEMUDriverPtr driver, goto cleanup; } - if (virStorageSourceUpdateBackingSizes(src, fd, &sb) < 0) + if (virStorageFileUpdateBackingSizes(src, fd, &sb) < 0) goto cleanup; - if (virStorageSourceUpdateCapacity(src, buf, len) < 0) + if (virStorageFileUpdateCapacity(src, buf, len) < 0) goto cleanup; /* If guest is not using raw disk format and is on a host block diff --git a/src/storage/storage_util.c b/src/storage/storage_util.c index 7616ec63a0..9274e91c47 100644 --- a/src/storage/storage_util.c +++ b/src/storage/storage_util.c @@ -1842,7 +1842,7 @@ storageBackendUpdateVolTargetInfo(virStorageVolType voltype, } } - if (virStorageSourceUpdateCapacity(target, buf, len) < 0) + if (virStorageFileUpdateCapacity(target, buf, len) < 0) return -1; } @@ -1906,7 +1906,7 @@ virStorageBackendUpdateVolTargetInfoFD(virStorageSourcePtr target, char *filecon = NULL; #endif - if (virStorageSourceUpdateBackingSizes(target, fd, sb) < 0) + if (virStorageFileUpdateBackingSizes(target, fd, sb) < 0) return -1; if (!target->perms) @@ -3449,7 +3449,7 @@ storageBackendProbeTarget(virStorageSourcePtr target, return -1; if (meta->backingStoreRaw) { - virStorageSourceNewFromBacking(meta, &target->backingStore); + virStorageFileNewFromBacking(meta, &target->backingStore); /* XXX: Remote storage doesn't play nicely with volumes backed by * remote storage. To avoid trouble, just fake the backing store is RAW diff --git a/src/storage_file/storage_file.c b/src/storage_file/storage_file.c index e5b2f68420..7de3daec33 100644 --- a/src/storage_file/storage_file.c +++ b/src/storage_file/storage_file.c @@ -1292,8 +1292,8 @@ virStorageFileChainLookup(virStorageSourcePtr chain, static virStorageSourcePtr -virStorageSourceNewFromBackingRelative(virStorageSourcePtr parent, - const char *rel) +virStorageFileNewFromBackingRelative(virStorageSourcePtr parent, + const char *rel) { g_autofree char *dirname = NULL; g_autoptr(virStorageSource) def = virStorageSourceNew(); @@ -1480,8 +1480,8 @@ virStorageSourceRBDAddHost(virStorageSourcePtr src, int -virStorageSourceParseRBDColonString(const char *rbdstr, - virStorageSourcePtr src) +virStorageFileParseRBDColonString(const char *rbdstr, + virStorageSourcePtr src) { char *p, *e, *next; g_autofree char *options = NULL; @@ -1691,7 +1691,7 @@ virStorageSourceParseBackingColon(virStorageSourcePtr src, break; case VIR_STORAGE_NET_PROTOCOL_RBD: - if (virStorageSourceParseRBDColonString(path, src) < 0) + if (virStorageFileParseRBDColonString(path, src) < 0) return -1; break; @@ -2253,7 +2253,7 @@ virStorageSourceParseBackingJSONRBD(virStorageSourcePtr src, /* legacy syntax passed via 'filename' option */ if ((filename = virJSONValueObjectGetString(json, "filename"))) - return virStorageSourceParseRBDColonString(filename, src); + return virStorageFileParseRBDColonString(filename, src); if (!pool || !image) { virReportError(VIR_ERR_INVALID_ARG, "%s", @@ -2487,7 +2487,7 @@ virStorageSourceParseBackingJSON(virStorageSourcePtr src, /** - * virStorageSourceNewFromBackingAbsolute + * virStorageFileNewFromBackingAbsolute * @path: string representing absolute location of a storage source * @src: filled with virStorageSource object representing @path * @@ -2497,8 +2497,8 @@ virStorageSourceParseBackingJSON(virStorageSourcePtr src, * error is reported. */ int -virStorageSourceNewFromBackingAbsolute(const char *path, - virStorageSourcePtr *src) +virStorageFileNewFromBackingAbsolute(const char *path, + virStorageSourcePtr *src) { const char *json; const char *dirpath; @@ -2580,10 +2580,10 @@ virStorageSourceNewFromChild(virStorageSourcePtr parent, *child = NULL; if (virFileIsRelative(parentRaw)) { - if (!(def = virStorageSourceNewFromBackingRelative(parent, parentRaw))) + if (!(def = virStorageFileNewFromBackingRelative(parent, parentRaw))) return -1; } else { - if ((rc = virStorageSourceNewFromBackingAbsolute(parentRaw, &def)) < 0) + if ((rc = virStorageFileNewFromBackingAbsolute(parentRaw, &def)) < 0) return -1; } @@ -2611,8 +2611,8 @@ virStorageSourceNewFromChild(virStorageSourcePtr parent, int -virStorageSourceNewFromBacking(virStorageSourcePtr parent, - virStorageSourcePtr *backing) +virStorageFileNewFromBacking(virStorageSourcePtr parent, + virStorageSourcePtr *backing) { int rc; @@ -2639,9 +2639,9 @@ virStorageSourceNewFromBacking(virStorageSourcePtr parent, * Returns 0 on success, -1 on error. No libvirt errors are reported. */ int -virStorageSourceUpdatePhysicalSize(virStorageSourcePtr src, - int fd, - struct stat const *sb) +virStorageFileUpdatePhysicalSize(virStorageSourcePtr src, + int fd, + struct stat const *sb) { off_t end; virStorageType actual_type = virStorageSourceGetActualType(src); @@ -2687,9 +2687,9 @@ virStorageSourceUpdatePhysicalSize(virStorageSourcePtr src, * Returns 0 on success, -1 on error. */ int -virStorageSourceUpdateBackingSizes(virStorageSourcePtr src, - int fd, - struct stat const *sb) +virStorageFileUpdateBackingSizes(virStorageSourcePtr src, + int fd, + struct stat const *sb) { /* Get info for normal formats */ if (S_ISREG(sb->st_mode) || fd == -1) { @@ -2750,9 +2750,9 @@ virStorageSourceUpdateBackingSizes(virStorageSourcePtr src, * Returns 0 on success, -1 on error. */ int -virStorageSourceUpdateCapacity(virStorageSourcePtr src, - char *buf, - ssize_t len) +virStorageFileUpdateCapacity(virStorageSourcePtr src, + char *buf, + ssize_t len) { int format = src->format; g_autoptr(virStorageSource) meta = NULL; @@ -3066,7 +3066,7 @@ virStorageFileGetRelativeBackingPath(virStorageSourcePtr top, /** - * virStorageSourceFindByNodeName: + * virStorageFileFindByNodeName: * @top: backing chain top * @nodeName: node name to find in backing chain * @@ -3075,8 +3075,8 @@ virStorageFileGetRelativeBackingPath(virStorageSourcePtr top, * On failure NULL is returned and no error is reported. */ virStorageSourcePtr -virStorageSourceFindByNodeName(virStorageSourcePtr top, - const char *nodeName) +virStorageFileFindByNodeName(virStorageSourcePtr top, + const char *nodeName) { virStorageSourcePtr tmp; @@ -3091,8 +3091,8 @@ virStorageSourceFindByNodeName(virStorageSourcePtr top, int -virStorageSourcePrivateDataParseRelPath(xmlXPathContextPtr ctxt, - virStorageSourcePtr src) +virStorageFilePrivateDataParseRelPath(xmlXPathContextPtr ctxt, + virStorageSourcePtr src) { src->relPath = virXPathString("string(./relPath)", ctxt); return 0; @@ -3100,8 +3100,8 @@ virStorageSourcePrivateDataParseRelPath(xmlXPathContextPtr ctxt, int -virStorageSourcePrivateDataFormatRelPath(virStorageSourcePtr src, - virBufferPtr buf) +virStorageFilePrivateDataFormatRelPath(virStorageSourcePtr src, + virBufferPtr buf) { if (src->relPath) virBufferEscapeString(buf, "<relPath>%s</relPath>\n", src->relPath); @@ -3708,7 +3708,7 @@ virStorageFileGetMetadataRecurse(virStorageSourcePtr src, } if (src->backingStoreRaw) { - if ((rv = virStorageSourceNewFromBacking(src, &backingStore)) < 0) + if ((rv = virStorageFileNewFromBacking(src, &backingStore)) < 0) return -1; /* the backing file would not be usable for VM usage */ diff --git a/src/storage_file/storage_file.h b/src/storage_file/storage_file.h index 181efded87..67eb20a866 100644 --- a/src/storage_file/storage_file.h +++ b/src/storage_file/storage_file.h @@ -74,26 +74,26 @@ virStorageFileChainLookup(virStorageSourcePtr chain, ATTRIBUTE_NONNULL(1); int -virStorageSourceUpdatePhysicalSize(virStorageSourcePtr src, - int fd, - struct stat const *sb); +virStorageFileUpdatePhysicalSize(virStorageSourcePtr src, + int fd, + struct stat const *sb); int -virStorageSourceUpdateBackingSizes(virStorageSourcePtr src, - int fd, - struct stat const *sb); +virStorageFileUpdateBackingSizes(virStorageSourcePtr src, + int fd, + struct stat const *sb); int -virStorageSourceUpdateCapacity(virStorageSourcePtr src, - char *buf, - ssize_t len); +virStorageFileUpdateCapacity(virStorageSourcePtr src, + char *buf, + ssize_t len); int -virStorageSourceNewFromBacking(virStorageSourcePtr parent, - virStorageSourcePtr *backing); +virStorageFileNewFromBacking(virStorageSourcePtr parent, + virStorageSourcePtr *backing); int -virStorageSourceParseRBDColonString(const char *rbdstr, - virStorageSourcePtr src) +virStorageFileParseRBDColonString(const char *rbdstr, + virStorageSourcePtr src) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); typedef int @@ -113,21 +113,21 @@ virStorageFileGetRelativeBackingPath(virStorageSourcePtr from, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); int -virStorageSourceNewFromBackingAbsolute(const char *path, - virStorageSourcePtr *src); +virStorageFileNewFromBackingAbsolute(const char *path, + virStorageSourcePtr *src); virStorageSourcePtr -virStorageSourceFindByNodeName(virStorageSourcePtr top, - const char *nodeName) +virStorageFileFindByNodeName(virStorageSourcePtr top, + const char *nodeName) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); int -virStorageSourcePrivateDataParseRelPath(xmlXPathContextPtr ctxt, - virStorageSourcePtr src); +virStorageFilePrivateDataParseRelPath(xmlXPathContextPtr ctxt, + virStorageSourcePtr src); int -virStorageSourcePrivateDataFormatRelPath(virStorageSourcePtr src, - virBufferPtr buf); +virStorageFilePrivateDataFormatRelPath(virStorageSourcePtr src, + virBufferPtr buf); int virStorageFileInit(virStorageSourcePtr src); diff --git a/tests/qemublocktest.c b/tests/qemublocktest.c index ee57449868..b31746fa17 100644 --- a/tests/qemublocktest.c +++ b/tests/qemublocktest.c @@ -106,8 +106,8 @@ testBackingXMLjsonXML(const void *args) protocolwrapper = g_strdup_printf("json:%s", propsstr); - if (virStorageSourceNewFromBackingAbsolute(protocolwrapper, - &jsonsrc) < 0) { + if (virStorageFileNewFromBackingAbsolute(protocolwrapper, + &jsonsrc) < 0) { fprintf(stderr, "failed to parse disk json\n"); return -1; } @@ -155,7 +155,7 @@ testJSONtoJSON(const void *args) if (virTestLoadFile(infile, &in) < 0) return -1; - if (virStorageSourceNewFromBackingAbsolute(in, &src) < 0) { + if (virStorageFileNewFromBackingAbsolute(in, &src) < 0) { fprintf(stderr, "failed to parse disk json\n"); return -1; } diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c index 59c03255d4..0882c6c86d 100644 --- a/tests/virstoragetest.c +++ b/tests/virstoragetest.c @@ -592,7 +592,7 @@ testBackingParse(const void *args) if (!data->expect) erc = -1; - if ((rc = virStorageSourceNewFromBackingAbsolute(data->backing, &src)) != erc) { + if ((rc = virStorageFileNewFromBackingAbsolute(data->backing, &src)) != erc) { fprintf(stderr, "expected return value '%d' actual '%d'\n", erc, rc); return -1; } -- 2.28.0
participants (2)
-
Pavel Hrdina
-
Peter Krempa