'blockdev-add' allows us to use qemu to format images to our desired
format. This patch implements helpers which convert a
virStorageSourcePtr into JSON objects describing the required
configuration.
Signed-off-by: Peter Krempa <pkrempa(a)redhat.com>
---
src/qemu/qemu_block.c | 321 ++++++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_block.h | 6 +
2 files changed, 327 insertions(+)
diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c
index 9abdac5ca3..54dd2b5328 100644
--- a/src/qemu/qemu_block.c
+++ b/src/qemu/qemu_block.c
@@ -1866,3 +1866,324 @@ qemuBlockStorageGetCopyOnReadProps(virDomainDiskDefPtr disk)
return ret;
}
+
+
+static int
+qemuBlockStorageSourceCreateAddBacking(virStorageSourcePtr backing,
+ virJSONValuePtr props,
+ bool format)
+{
+ VIR_AUTOPTR(virJSONValue) backingProps = NULL;
+ VIR_AUTOFREE(char *) backingJSON = NULL;
+ VIR_AUTOFREE(char *) backingPseudoprotocol = NULL;
+ const char *backingFileStr = NULL;
+ const char *backingFormatStr = NULL;
+
+ if (!virStorageSourceIsBacking(backing))
+ return 0;
+
+ if (format) {
+ if (backing->encryption &&
+ backing->encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS)
+ backingFormatStr = "luks";
+ else
+ backingFormatStr = virStorageFileFormatTypeToString(backing->format);
+ }
+
+ if (virStorageSourceIsLocalStorage(backing)) {
+ backingFileStr = backing->path;
+ } else {
+ if (!(backingProps = qemuBlockStorageSourceGetBackendProps(backing, false,
+ true, false))) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("failed to generate backing file JSON
properties"));
+ return -1;
+ }
+
+ if (!(backingJSON = virJSONValueToString(backingProps, false)))
+ return -1;
+
+ if (virAsprintf(&backingPseudoprotocol, "json:%s", backingJSON)
< 0)
+ return -1;
+
+ backingFileStr = backingPseudoprotocol;
+ }
+
+ if (virJSONValueObjectAdd(props,
+ "S:backing-file", backingFileStr,
+ "S:backing-fmt", backingFormatStr,
+ NULL) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int
+qemuBlockStorageSourceCreateGetFormatPropsGeneric(virStorageSourcePtr src,
+ const char *driver,
+ virJSONValuePtr *retprops,
+ virStorageSourcePtr backing)
+{
+ VIR_AUTOPTR(virJSONValue) props = NULL;
+
+ if (virJSONValueObjectCreate(&props,
+ "s:driver", driver,
+ "s:file", src->nodestorage,
+ "u:size", src->capacity,
+ NULL) < 0)
+ return -1;
+
+ if (backing &&
+ qemuBlockStorageSourceCreateAddBacking(backing, props, false) < 0)
+ return -1;
+
+ VIR_STEAL_PTR(*retprops, props);
+ return 0;
+}
+
+
+static int
+qemuBlockStorageSourceCreateGetEncryptionLUKS(virStorageSourcePtr src,
+ virJSONValuePtr *luksProps)
+{
+ qemuDomainStorageSourcePrivatePtr srcpriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
+ VIR_AUTOPTR(virJSONValue) props = NULL;
+ VIR_AUTOFREE(char *) cipheralg = NULL;
+ const char *keysecret = NULL;
+
+ if (srcpriv &&
+ srcpriv->encinfo &&
+ srcpriv->encinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES)
+ keysecret = srcpriv->encinfo->s.aes.alias;
+
+ if (virJSONValueObjectCreate(&props,
+ "s:key-secret", keysecret,
+ NULL) < 0)
+ return -1;
+
+ if (src->encryption) {
+ if (src->encryption->encinfo.cipher_name &&
+ virAsprintf(&cipheralg, "%s-%u",
+ src->encryption->encinfo.cipher_name,
+ src->encryption->encinfo.cipher_size) < 0)
+ return -1;
+
+ if (virJSONValueObjectAdd(props,
+ "S:cipher-alg", cipheralg,
+ "S:cipher-mode",
src->encryption->encinfo.cipher_mode,
+ "S:hash-alg",
src->encryption->encinfo.cipher_hash,
+ "S:ivgen-alg",
src->encryption->encinfo.ivgen_name,
+ "S:ivgen-hash-alg",
src->encryption->encinfo.ivgen_hash,
+ NULL) < 0)
+ return -1;
+ }
+
+ VIR_STEAL_PTR(*luksProps, props);
+ return 0;
+}
+
+
+static int
+qemuBlockStorageSourceCreateGetFormatPropsLUKS(virStorageSourcePtr src,
+ virJSONValuePtr *props)
+{
+ VIR_AUTOPTR(virJSONValue) luksprops = NULL;
+
+ if (qemuBlockStorageSourceCreateGetEncryptionLUKS(src, &luksprops) < 0)
+ return -1;
+
+ if (virJSONValueObjectAdd(luksprops,
+ "s:driver", "luks",
+ "s:file", src->nodestorage,
+ "u:size", src->capacity,
+ NULL) < 0)
+ return -1;
+
+ VIR_STEAL_PTR(*props, luksprops);
+ return 0;
+}
+
+
+static int
+qemuBlockStorageSourceCreateAddEncryptionQcow(virStorageSourcePtr src,
+ virJSONValuePtr props)
+{
+ VIR_AUTOPTR(virJSONValue) encryptProps = NULL;
+
+ if (!src->encryption)
+ return 0;
+
+ if (src->encryption->format != VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("creation of qcow/qcow2 files supports only 'luks'
encryption"));
+ return -1;
+ }
+
+ if (qemuBlockStorageSourceCreateGetEncryptionLUKS(src, &encryptProps) < 0)
+ return -1;
+
+ if (virJSONValueObjectAdd(encryptProps, "s:format", "luks", NULL)
< 0)
+ return -1;
+
+ if (virJSONValueObjectAdd(props, "a:encrypt", &encryptProps, NULL) <
0)
+ return -1;
+
+ return 0;
+}
+
+
+static int
+qemuBlockStorageSourceCreateGetFormatPropsQcow2(virStorageSourcePtr src,
+ virStorageSourcePtr backing,
+ virJSONValuePtr *props)
+{
+ VIR_AUTOPTR(virJSONValue) qcow2props = NULL;
+ const char *qcow2version = NULL;
+
+ if (STREQ_NULLABLE(src->compat, "0.10"))
+ qcow2version = "v2";
+ else if (STREQ_NULLABLE(src->compat, "1.1"))
+ qcow2version = "v3";
+
+ if (virJSONValueObjectCreate(&qcow2props,
+ "s:driver", "qcow2",
+ "s:file", src->nodestorage,
+ "u:size", src->capacity,
+ "S:version", qcow2version,
+ NULL) < 0)
+ return -1;
+
+ if (qemuBlockStorageSourceCreateAddBacking(backing, qcow2props, true) < 0 ||
+ qemuBlockStorageSourceCreateAddEncryptionQcow(src, qcow2props) < 0)
+ return -1;
+
+ VIR_STEAL_PTR(*props, qcow2props);
+ return 0;
+}
+
+
+static int
+qemuBlockStorageSourceCreateGetFormatPropsQcow(virStorageSourcePtr src,
+ virStorageSourcePtr backing,
+ virJSONValuePtr *props)
+{
+ VIR_AUTOPTR(virJSONValue) qcowprops = NULL;
+
+ if (virJSONValueObjectCreate(&qcowprops,
+ "s:driver", "qcow",
+ "s:file", src->nodestorage,
+ "u:size", src->capacity,
+ NULL) < 0)
+ return -1;
+
+ if (qemuBlockStorageSourceCreateAddBacking(backing, qcowprops, false) < 0 ||
+ qemuBlockStorageSourceCreateAddEncryptionQcow(src, qcowprops) < 0)
+ return -1;
+
+ VIR_STEAL_PTR(*props, qcowprops);
+ return 0;
+}
+
+
+static int
+qemuBlockStorageSourceCreateGetFormatPropsQed(virStorageSourcePtr src,
+ virStorageSourcePtr backing,
+ virJSONValuePtr *props)
+{
+ VIR_AUTOPTR(virJSONValue) qedprops = NULL;
+
+ if (virJSONValueObjectCreate(&qedprops,
+ "s:driver", "qed",
+ "s:file", src->nodestorage,
+ "u:size", src->capacity,
+ NULL) < 0)
+ return -1;
+
+ if (qemuBlockStorageSourceCreateAddBacking(backing, qedprops, true) < 0)
+ return -1;
+
+ VIR_STEAL_PTR(*props, qedprops);
+ return 0;
+}
+
+
+/**
+ * qemuBlockStorageSourceCreateGetFormatProps:
+ * @src: storage source to format
+ * @backing: storage source describing backing image of @src (if necessary)
+ * @props: filled with props to be used with 'blockdev-create' to format @src
+ *
+ * @src must be properly initialized to contain node-names of the protocol layer
+ * which should be formatted. @props may be NULL with success returned in which
+ * case creation of given storage format is not supported. Note that creation
+ * of 'raw' storage is also returns NULL as there is nothing to do.
+ */
+int
+qemuBlockStorageSourceCreateGetFormatProps(virStorageSourcePtr src,
+ virStorageSourcePtr backing,
+ virJSONValuePtr *props)
+{
+ switch ((virStorageFileFormat) src->format) {
+ case VIR_STORAGE_FILE_RAW:
+ if (!src->encryption ||
+ src->encryption->format != VIR_STORAGE_ENCRYPTION_FORMAT_LUKS)
+ return 0;
+
+ return qemuBlockStorageSourceCreateGetFormatPropsLUKS(src, props);
+
+ case VIR_STORAGE_FILE_QCOW2:
+ return qemuBlockStorageSourceCreateGetFormatPropsQcow2(src, backing, props);
+
+ case VIR_STORAGE_FILE_QCOW:
+ return qemuBlockStorageSourceCreateGetFormatPropsQcow(src, backing, props);
+
+ case VIR_STORAGE_FILE_QED:
+ return qemuBlockStorageSourceCreateGetFormatPropsQed(src, backing, props);
+
+ case VIR_STORAGE_FILE_VPC:
+ return qemuBlockStorageSourceCreateGetFormatPropsGeneric(src, "vpc",
+ props, NULL);
+
+ case VIR_STORAGE_FILE_PLOOP:
+ return qemuBlockStorageSourceCreateGetFormatPropsGeneric(src,
"parallels",
+ props, NULL);
+
+ case VIR_STORAGE_FILE_VDI:
+ return qemuBlockStorageSourceCreateGetFormatPropsGeneric(src, "vdi",
+ props, NULL);
+
+ case VIR_STORAGE_FILE_VHD:
+ return qemuBlockStorageSourceCreateGetFormatPropsGeneric(src, "vhdx",
+ props, NULL);
+
+ case VIR_STORAGE_FILE_VMDK:
+ return qemuBlockStorageSourceCreateGetFormatPropsGeneric(src, "vmdk",
+ props, backing);
+
+ /* unsupported by qemu / impossible */
+ case VIR_STORAGE_FILE_FAT:
+ case VIR_STORAGE_FILE_BOCHS:
+ case VIR_STORAGE_FILE_CLOOP:
+ case VIR_STORAGE_FILE_DMG:
+ case VIR_STORAGE_FILE_COW:
+ case VIR_STORAGE_FILE_ISO:
+ case VIR_STORAGE_FILE_DIR:
+ return 0;
+
+ case VIR_STORAGE_FILE_AUTO_SAFE:
+ case VIR_STORAGE_FILE_AUTO:
+ case VIR_STORAGE_FILE_NONE:
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("mishandled storage format '%s'"),
+ virStorageFileFormatTypeToString(src->format));
+ return -1;
+
+ case VIR_STORAGE_FILE_LAST:
+ default:
+ break;
+ }
+
+ virReportEnumRangeError(virStorageFileFormat, src->format);
+ return -1;
+}
diff --git a/src/qemu/qemu_block.h b/src/qemu/qemu_block.h
index 12ddfad2ac..738ef9e8b0 100644
--- a/src/qemu/qemu_block.h
+++ b/src/qemu/qemu_block.h
@@ -162,3 +162,9 @@ qemuBlockSnapshotAddLegacy(virJSONValuePtr actions,
virDomainDiskDefPtr disk,
virStorageSourcePtr newsrc,
bool reuse);
+
+int
+qemuBlockStorageSourceCreateGetFormatProps(virStorageSourcePtr src,
+ virStorageSourcePtr backing,
+ virJSONValuePtr *props)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
--
2.21.0