6ae9ed
From 0e29f2d3041b0ab8dfcbc6bd4032aba6a69b56f6 Mon Sep 17 00:00:00 2001
6ae9ed
Message-Id: <0e29f2d3041b0ab8dfcbc6bd4032aba6a69b56f6@dist-git>
6ae9ed
From: John Ferlan <jferlan@redhat.com>
6ae9ed
Date: Mon, 25 Jul 2016 12:42:57 -0400
6ae9ed
Subject: [PATCH] storage: Add support to create a luks volume
6ae9ed
6ae9ed
Partially resolves:
6ae9ed
https://bugzilla.redhat.com/show_bug.cgi?id=1301021
6ae9ed
6ae9ed
If the volume xml was looking to create a luks volume take the necessary
6ae9ed
steps in order to make that happen.
6ae9ed
6ae9ed
The processing will be:
6ae9ed
 1. create a temporary file (virStorageBackendCreateQemuImgSecretPath)
6ae9ed
   1a. use the storage driver state dir path that uses the pool and
6ae9ed
       volume name as a base.
6ae9ed
6ae9ed
 2. create a secret object (virStorageBackendCreateQemuImgSecretObject)
6ae9ed
   2a. use an alias combinding the volume name and "_luks0"
6ae9ed
   2b. add the file to the object
6ae9ed
6ae9ed
 3. create/add luks options to the commandline (virQEMUBuildLuksOpts)
6ae9ed
   3a. at the very least a "key-secret=%s" using the secret object alias
6ae9ed
   3b. if found in the XML the various "cipher" and "ivgen" options
6ae9ed
6ae9ed
Signed-off-by: John Ferlan <jferlan@redhat.com>
6ae9ed
(cherry picked from commit 5e46d7d6b693c1e3c9197c182302ac7125a856d9)
6ae9ed
---
6ae9ed
 src/libvirt_private.syms       |   1 +
6ae9ed
 src/storage/storage_backend.c  | 214 ++++++++++++++++++++++++++++++++++++++---
6ae9ed
 src/storage/storage_backend.h  |   3 +-
6ae9ed
 src/util/virqemu.c             |  58 +++++++++++
6ae9ed
 src/util/virqemu.h             |   6 ++
6ae9ed
 tests/storagevolxml2argvtest.c |   3 +-
6ae9ed
 6 files changed, 271 insertions(+), 14 deletions(-)
6ae9ed
6ae9ed
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
6ae9ed
index c205fd8..f73cfa2 100644
6ae9ed
--- a/src/libvirt_private.syms
6ae9ed
+++ b/src/libvirt_private.syms
6ae9ed
@@ -2193,6 +2193,7 @@ virProcessWait;
6ae9ed
 
6ae9ed
 # util/virqemu.h
6ae9ed
 virQEMUBuildBufferEscapeComma;
6ae9ed
+virQEMUBuildLuksOpts;
6ae9ed
 virQEMUBuildObjectCommandlineFromJSON;
6ae9ed
 
6ae9ed
 
6ae9ed
diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c
6ae9ed
index 97f6ffe..84ef4f2 100644
6ae9ed
--- a/src/storage/storage_backend.c
6ae9ed
+++ b/src/storage/storage_backend.c
6ae9ed
@@ -55,11 +55,14 @@
6ae9ed
 #include "viralloc.h"
6ae9ed
 #include "internal.h"
6ae9ed
 #include "secret_conf.h"
6ae9ed
+#include "secret_util.h"
6ae9ed
 #include "viruuid.h"
6ae9ed
 #include "virstoragefile.h"
6ae9ed
 #include "storage_backend.h"
6ae9ed
 #include "virlog.h"
6ae9ed
 #include "virfile.h"
6ae9ed
+#include "virjson.h"
6ae9ed
+#include "virqemu.h"
6ae9ed
 #include "stat-time.h"
6ae9ed
 #include "virstring.h"
6ae9ed
 #include "virxml.h"
6ae9ed
@@ -907,6 +910,7 @@ virStorageBackendQemuImgSupportsCompat(const char *qemuimg)
6ae9ed
     return ret;
6ae9ed
 }
6ae9ed
 
6ae9ed
+
6ae9ed
 static int
6ae9ed
 virStorageBackendQEMUImgBackingFormat(const char *qemuimg)
6ae9ed
 {
6ae9ed
@@ -925,6 +929,10 @@ virStorageBackendQEMUImgBackingFormat(const char *qemuimg)
6ae9ed
     return ret;
6ae9ed
 }
6ae9ed
 
6ae9ed
+/* The _virStorageBackendQemuImgInfo separates the command line building from
6ae9ed
+ * the volume definition so that qemuDomainSnapshotCreateInactiveExternal can
6ae9ed
+ * use it without needing to deal with a volume.
6ae9ed
+ */
6ae9ed
 struct _virStorageBackendQemuImgInfo {
6ae9ed
     int format;
6ae9ed
     const char *path;
6ae9ed
@@ -941,21 +949,36 @@ struct _virStorageBackendQemuImgInfo {
6ae9ed
     const char *inputPath;
6ae9ed
     const char *inputFormatStr;
6ae9ed
     int inputFormat;
6ae9ed
+
6ae9ed
+    char *secretAlias;
6ae9ed
+    const char *secretPath;
6ae9ed
 };
6ae9ed
 
6ae9ed
+
6ae9ed
 static int
6ae9ed
-virStorageBackendCreateQemuImgOpts(char **opts,
6ae9ed
+virStorageBackendCreateQemuImgOpts(virStorageEncryptionInfoDefPtr enc,
6ae9ed
+                                   char **opts,
6ae9ed
                                    struct _virStorageBackendQemuImgInfo info)
6ae9ed
 {
6ae9ed
     virBuffer buf = VIR_BUFFER_INITIALIZER;
6ae9ed
 
6ae9ed
-    if (info.backingPath)
6ae9ed
-        virBufferAsprintf(&buf, "backing_fmt=%s,",
6ae9ed
-                          virStorageFileFormatTypeToString(info.backingFormat));
6ae9ed
-    if (info.encryption)
6ae9ed
-        virBufferAddLit(&buf, "encryption=on,");
6ae9ed
-    if (info.preallocate)
6ae9ed
-        virBufferAddLit(&buf, "preallocation=metadata,");
6ae9ed
+    if (info.format == VIR_STORAGE_FILE_LUKS) {
6ae9ed
+        if (!enc) {
6ae9ed
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
6ae9ed
+                           _("missing luks encryption information"));
6ae9ed
+            goto error;
6ae9ed
+        }
6ae9ed
+        virQEMUBuildLuksOpts(&buf, enc, info.secretAlias);
6ae9ed
+    } else {
6ae9ed
+        if (info.backingPath)
6ae9ed
+            virBufferAsprintf(&buf, "backing_fmt=%s,",
6ae9ed
+                              virStorageFileFormatTypeToString(info.backingFormat));
6ae9ed
+        if (info.encryption)
6ae9ed
+            virBufferAddLit(&buf, "encryption=on,");
6ae9ed
+        if (info.preallocate)
6ae9ed
+            virBufferAddLit(&buf, "preallocation=metadata,");
6ae9ed
+    }
6ae9ed
+
6ae9ed
     if (info.nocow)
6ae9ed
         virBufferAddLit(&buf, "nocow=on,");
6ae9ed
 
6ae9ed
@@ -1025,6 +1048,22 @@ virStorageBackendCreateQemuImgCheckEncryption(int format,
6ae9ed
             if (virStorageGenerateQcowEncryption(conn, vol) < 0)
6ae9ed
                 return -1;
6ae9ed
         }
6ae9ed
+    } else if (format == VIR_STORAGE_FILE_LUKS) {
6ae9ed
+        if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) {
6ae9ed
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
6ae9ed
+                           _("unsupported volume encryption format %d"),
6ae9ed
+                           vol->target.encryption->format);
6ae9ed
+            return -1;
6ae9ed
+        }
6ae9ed
+        if (enc->nsecrets > 1) {
6ae9ed
+            virReportError(VIR_ERR_XML_ERROR, "%s",
6ae9ed
+                           _("too many secrets for luks encryption"));
6ae9ed
+            return -1;
6ae9ed
+        }
6ae9ed
+        if (enc->nsecrets == 0) {
6ae9ed
+            virReportError(VIR_ERR_XML_ERROR, "%s",
6ae9ed
+                           _("no secret provided for luks encryption"));
6ae9ed
+        }
6ae9ed
     } else {
6ae9ed
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
6ae9ed
                        _("volume encryption unsupported with format %s"), type);
6ae9ed
@@ -1069,6 +1108,12 @@ virStorageBackendCreateQemuImgSetBacking(virStoragePoolObjPtr pool,
6ae9ed
     int accessRetCode = -1;
6ae9ed
     char *absolutePath = NULL;
6ae9ed
 
6ae9ed
+    if (info->format == VIR_STORAGE_FILE_LUKS) {
6ae9ed
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
6ae9ed
+                       _("cannot set backing store for luks volume"));
6ae9ed
+        return -1;
6ae9ed
+    }
6ae9ed
+
6ae9ed
     info->backingFormat = vol->target.backingStore->format;
6ae9ed
     info->backingPath = vol->target.backingStore->path;
6ae9ed
 
6ae9ed
@@ -1122,6 +1167,7 @@ virStorageBackendCreateQemuImgSetBacking(virStoragePoolObjPtr pool,
6ae9ed
 static int
6ae9ed
 virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd,
6ae9ed
                                          int imgformat,
6ae9ed
+                                         virStorageEncryptionInfoDefPtr enc,
6ae9ed
                                          struct _virStorageBackendQemuImgInfo info)
6ae9ed
 {
6ae9ed
     char *opts = NULL;
6ae9ed
@@ -1130,7 +1176,7 @@ virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd,
6ae9ed
         imgformat >= QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT)
6ae9ed
         info.compat = "0.10";
6ae9ed
 
6ae9ed
-    if (virStorageBackendCreateQemuImgOpts(&opts, info) < 0)
6ae9ed
+    if (virStorageBackendCreateQemuImgOpts(enc, &opts, info) < 0)
6ae9ed
         return -1;
6ae9ed
     if (opts)
6ae9ed
         virCommandAddArgList(cmd, "-o", opts, NULL);
6ae9ed
@@ -1140,6 +1186,39 @@ virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd,
6ae9ed
 }
6ae9ed
 
6ae9ed
 
6ae9ed
+/* Add a secret object to the command line:
6ae9ed
+ *    --object secret,id=$secretAlias,file=$secretPath
6ae9ed
+ *
6ae9ed
+ *    NB: format=raw is assumed
6ae9ed
+ */
6ae9ed
+static int
6ae9ed
+virStorageBackendCreateQemuImgSecretObject(virCommandPtr cmd,
6ae9ed
+                                           virStorageVolDefPtr vol,
6ae9ed
+                                           struct _virStorageBackendQemuImgInfo *info)
6ae9ed
+{
6ae9ed
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
6ae9ed
+    char *commandStr = NULL;
6ae9ed
+
6ae9ed
+    if (virAsprintf(&info->secretAlias, "%s_luks0", vol->name) < 0)
6ae9ed
+        return -1;
6ae9ed
+
6ae9ed
+    virBufferAsprintf(&buf, "secret,id=%s,file=", info->secretAlias);
6ae9ed
+    virQEMUBuildBufferEscapeComma(&buf, info->secretPath);
6ae9ed
+
6ae9ed
+    if (virBufferCheckError(&buf) < 0) {
6ae9ed
+        virBufferFreeAndReset(&buf;;
6ae9ed
+        return -1;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    commandStr = virBufferContentAndReset(&buf;;
6ae9ed
+
6ae9ed
+    virCommandAddArgList(cmd, "--object", commandStr, NULL);
6ae9ed
+
6ae9ed
+    VIR_FREE(commandStr);
6ae9ed
+    return 0;
6ae9ed
+}
6ae9ed
+
6ae9ed
+
6ae9ed
 /* Create a qemu-img virCommand from the supplied binary path,
6ae9ed
  * volume definitions and imgformat
6ae9ed
  */
6ae9ed
@@ -1150,7 +1229,8 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
6ae9ed
                                          virStorageVolDefPtr inputvol,
6ae9ed
                                          unsigned int flags,
6ae9ed
                                          const char *create_tool,
6ae9ed
-                                         int imgformat)
6ae9ed
+                                         int imgformat,
6ae9ed
+                                         const char *secretPath)
6ae9ed
 {
6ae9ed
     virCommandPtr cmd = NULL;
6ae9ed
     const char *type;
6ae9ed
@@ -1162,7 +1242,10 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
6ae9ed
         .compat = vol->target.compat,
6ae9ed
         .features = vol->target.features,
6ae9ed
         .nocow = vol->target.nocow,
6ae9ed
+        .secretPath = secretPath,
6ae9ed
+        .secretAlias = NULL,
6ae9ed
     };
6ae9ed
+    virStorageEncryptionInfoDefPtr enc = NULL;
6ae9ed
 
6ae9ed
     virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, NULL);
6ae9ed
 
6ae9ed
@@ -1192,6 +1275,18 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
6ae9ed
                        _("format features only available with qcow2"));
6ae9ed
         return NULL;
6ae9ed
     }
6ae9ed
+    if (info.format == VIR_STORAGE_FILE_LUKS) {
6ae9ed
+        if (inputvol) {
6ae9ed
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
6ae9ed
+                           _("cannot use inputvol with luks volume"));
6ae9ed
+            return NULL;
6ae9ed
+        }
6ae9ed
+        if (!info.encryption) {
6ae9ed
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
6ae9ed
+                           _("missing encryption description"));
6ae9ed
+            return NULL;
6ae9ed
+        }
6ae9ed
+    }
6ae9ed
 
6ae9ed
     if (inputvol &&
6ae9ed
         virStorageBackendCreateQemuImgSetInput(inputvol, &info) < 0)
6ae9ed
@@ -1226,10 +1321,22 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
6ae9ed
     if (info.backingPath)
6ae9ed
         virCommandAddArgList(cmd, "-b", info.backingPath, NULL);
6ae9ed
 
6ae9ed
-    if (virStorageBackendCreateQemuImgSetOptions(cmd, imgformat, info) < 0) {
6ae9ed
+    if (info.format == VIR_STORAGE_FILE_LUKS) {
6ae9ed
+        if (virStorageBackendCreateQemuImgSecretObject(cmd, vol, &info) < 0) {
6ae9ed
+            VIR_FREE(info.secretAlias);
6ae9ed
+            virCommandFree(cmd);
6ae9ed
+            return NULL;
6ae9ed
+        }
6ae9ed
+        enc = &vol->target.encryption->encinfo;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if (virStorageBackendCreateQemuImgSetOptions(cmd, imgformat,
6ae9ed
+                                                 enc, info) < 0) {
6ae9ed
+        VIR_FREE(info.secretAlias);
6ae9ed
         virCommandFree(cmd);
6ae9ed
         return NULL;
6ae9ed
     }
6ae9ed
+    VIR_FREE(info.secretAlias);
6ae9ed
 
6ae9ed
     if (info.inputPath)
6ae9ed
         virCommandAddArg(cmd, info.inputPath);
6ae9ed
@@ -1240,6 +1347,77 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
6ae9ed
     return cmd;
6ae9ed
 }
6ae9ed
 
6ae9ed
+
6ae9ed
+static char *
6ae9ed
+virStorageBackendCreateQemuImgSecretPath(virConnectPtr conn,
6ae9ed
+                                         virStoragePoolObjPtr pool,
6ae9ed
+                                         virStorageVolDefPtr vol)
6ae9ed
+{
6ae9ed
+    virStorageEncryptionPtr enc = vol->target.encryption;
6ae9ed
+    char *secretPath = NULL;
6ae9ed
+    int fd = -1;
6ae9ed
+    uint8_t *secret = NULL;
6ae9ed
+    size_t secretlen = 0;
6ae9ed
+
6ae9ed
+    if (!enc) {
6ae9ed
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
6ae9ed
+                       _("missing encryption description"));
6ae9ed
+        return NULL;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if (!conn || !conn->secretDriver ||
6ae9ed
+        !conn->secretDriver->secretLookupByUUID ||
6ae9ed
+        !conn->secretDriver->secretLookupByUsage ||
6ae9ed
+        !conn->secretDriver->secretGetValue) {
6ae9ed
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
6ae9ed
+                       _("unable to look up encryption secret"));
6ae9ed
+        return NULL;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if (!(secretPath = virStoragePoolObjBuildTempFilePath(pool, vol)))
6ae9ed
+        goto cleanup;
6ae9ed
+
6ae9ed
+    if ((fd = mkostemp(secretPath, O_CLOEXEC)) < 0) {
6ae9ed
+        virReportSystemError(errno, "%s",
6ae9ed
+                             _("failed to open luks secret file for write"));
6ae9ed
+        goto error;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if (virSecretGetSecretString(conn, &enc->secrets[0]->seclookupdef,
6ae9ed
+                                 VIR_SECRET_USAGE_TYPE_VOLUME,
6ae9ed
+                                 &secret, &secretlen) < 0)
6ae9ed
+        goto error;
6ae9ed
+
6ae9ed
+    if (safewrite(fd, secret, secretlen) < 0) {
6ae9ed
+        virReportSystemError(errno, "%s",
6ae9ed
+                             _("failed to write luks secret file"));
6ae9ed
+        goto error;
6ae9ed
+    }
6ae9ed
+    VIR_FORCE_CLOSE(fd);
6ae9ed
+
6ae9ed
+    if ((vol->target.perms->uid != (uid_t) -1) &&
6ae9ed
+        (vol->target.perms->gid != (gid_t) -1)) {
6ae9ed
+        if (chown(secretPath, vol->target.perms->uid,
6ae9ed
+                  vol->target.perms->gid) < 0) {
6ae9ed
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
6ae9ed
+                           _("failed to chown luks secret file"));
6ae9ed
+            goto error;
6ae9ed
+        }
6ae9ed
+    }
6ae9ed
+
6ae9ed
+ cleanup:
6ae9ed
+    VIR_DISPOSE_N(secret, secretlen);
6ae9ed
+    VIR_FORCE_CLOSE(fd);
6ae9ed
+
6ae9ed
+    return secretPath;
6ae9ed
+
6ae9ed
+ error:
6ae9ed
+    unlink(secretPath);
6ae9ed
+    VIR_FREE(secretPath);
6ae9ed
+    goto cleanup;
6ae9ed
+}
6ae9ed
+
6ae9ed
+
6ae9ed
 int
6ae9ed
 virStorageBackendCreateQemuImg(virConnectPtr conn,
6ae9ed
                                virStoragePoolObjPtr pool,
6ae9ed
@@ -1251,6 +1429,7 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
6ae9ed
     char *create_tool;
6ae9ed
     int imgformat;
6ae9ed
     virCommandPtr cmd;
6ae9ed
+    char *secretPath = NULL;
6ae9ed
 
6ae9ed
     virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1);
6ae9ed
 
6ae9ed
@@ -1266,8 +1445,15 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
6ae9ed
     if (imgformat < 0)
6ae9ed
         goto cleanup;
6ae9ed
 
6ae9ed
+    if (vol->target.format == VIR_STORAGE_FILE_LUKS) {
6ae9ed
+        if (!(secretPath =
6ae9ed
+              virStorageBackendCreateQemuImgSecretPath(conn, pool, vol)))
6ae9ed
+            goto cleanup;
6ae9ed
+    }
6ae9ed
+
6ae9ed
     cmd = virStorageBackendCreateQemuImgCmdFromVol(conn, pool, vol, inputvol,
6ae9ed
-                                                   flags, create_tool, imgformat);
6ae9ed
+                                                   flags, create_tool,
6ae9ed
+                                                   imgformat, secretPath);
6ae9ed
     if (!cmd)
6ae9ed
         goto cleanup;
6ae9ed
 
6ae9ed
@@ -1275,6 +1461,10 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
6ae9ed
 
6ae9ed
     virCommandFree(cmd);
6ae9ed
  cleanup:
6ae9ed
+    if (secretPath) {
6ae9ed
+        unlink(secretPath);
6ae9ed
+        VIR_FREE(secretPath);
6ae9ed
+    }
6ae9ed
     VIR_FREE(create_tool);
6ae9ed
     return ret;
6ae9ed
 }
6ae9ed
diff --git a/src/storage/storage_backend.h b/src/storage/storage_backend.h
6ae9ed
index 5bc622c..28e1a65 100644
6ae9ed
--- a/src/storage/storage_backend.h
6ae9ed
+++ b/src/storage/storage_backend.h
6ae9ed
@@ -241,7 +241,8 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
6ae9ed
                                          virStorageVolDefPtr inputvol,
6ae9ed
                                          unsigned int flags,
6ae9ed
                                          const char *create_tool,
6ae9ed
-                                         int imgformat);
6ae9ed
+                                         int imgformat,
6ae9ed
+                                         const char *secretPath);
6ae9ed
 
6ae9ed
 /* ------- virStorageFile backends ------------ */
6ae9ed
 typedef struct _virStorageFileBackend virStorageFileBackend;
6ae9ed
diff --git a/src/util/virqemu.c b/src/util/virqemu.c
6ae9ed
index 79a4292..7d181e1 100644
6ae9ed
--- a/src/util/virqemu.c
6ae9ed
+++ b/src/util/virqemu.c
6ae9ed
@@ -155,3 +155,61 @@ virQEMUBuildBufferEscapeComma(virBufferPtr buf, const char *str)
6ae9ed
 {
6ae9ed
     virBufferEscape(buf, ',', ",", "%s", str);
6ae9ed
 }
6ae9ed
+
6ae9ed
+
6ae9ed
+/**
6ae9ed
+ * virQEMUBuildLuksOpts:
6ae9ed
+ * @buf: buffer to build the string into
6ae9ed
+ * @enc: pointer to encryption info
6ae9ed
+ * @alias: alias to use
6ae9ed
+ *
6ae9ed
+ * Generate the string for id=$alias and any encryption options for
6ae9ed
+ * into the buffer.
6ae9ed
+ *
6ae9ed
+ * Important note, a trailing comma (",") is built into the return since
6ae9ed
+ * it's expected other arguments are appended after the id=$alias string.
6ae9ed
+ * So either turn something like:
6ae9ed
+ *
6ae9ed
+ *     "key-secret=$alias,"
6ae9ed
+ *
6ae9ed
+ * or
6ae9ed
+ *     "key-secret=$alias,cipher-alg=twofish-256,cipher-mode=cbc,
6ae9ed
+ *     hash-alg=sha256,ivgen-alg=plain64,igven-hash-alg=sha256,"
6ae9ed
+ *
6ae9ed
+ */
6ae9ed
+void
6ae9ed
+virQEMUBuildLuksOpts(virBufferPtr buf,
6ae9ed
+                     virStorageEncryptionInfoDefPtr enc,
6ae9ed
+                     const char *alias)
6ae9ed
+{
6ae9ed
+    virBufferAsprintf(buf, "key-secret=%s,", alias);
6ae9ed
+
6ae9ed
+    if (!enc->cipher_name)
6ae9ed
+        return;
6ae9ed
+
6ae9ed
+    virBufferAddLit(buf, "cipher-alg=");
6ae9ed
+    virQEMUBuildBufferEscapeComma(buf, enc->cipher_name);
6ae9ed
+    virBufferAsprintf(buf, "-%u,", enc->cipher_size);
6ae9ed
+    if (enc->cipher_mode) {
6ae9ed
+        virBufferAddLit(buf, "cipher-mode=");
6ae9ed
+        virQEMUBuildBufferEscapeComma(buf, enc->cipher_mode);
6ae9ed
+        virBufferAddLit(buf, ",");
6ae9ed
+    }
6ae9ed
+    if (enc->cipher_hash) {
6ae9ed
+        virBufferAddLit(buf, "hash-alg=");
6ae9ed
+        virQEMUBuildBufferEscapeComma(buf, enc->cipher_hash);
6ae9ed
+        virBufferAddLit(buf, ",");
6ae9ed
+    }
6ae9ed
+    if (!enc->ivgen_name)
6ae9ed
+        return;
6ae9ed
+
6ae9ed
+    virBufferAddLit(buf, "ivgen-alg=");
6ae9ed
+    virQEMUBuildBufferEscapeComma(buf, enc->ivgen_name);
6ae9ed
+    virBufferAddLit(buf, ",");
6ae9ed
+
6ae9ed
+    if (enc->ivgen_hash) {
6ae9ed
+        virBufferAddLit(buf, "ivgen-hash-alg=");
6ae9ed
+        virQEMUBuildBufferEscapeComma(buf, enc->ivgen_hash);
6ae9ed
+        virBufferAddLit(buf, ",");
6ae9ed
+    }
6ae9ed
+}
6ae9ed
diff --git a/src/util/virqemu.h b/src/util/virqemu.h
6ae9ed
index 1033412..eb75d1b 100644
6ae9ed
--- a/src/util/virqemu.h
6ae9ed
+++ b/src/util/virqemu.h
6ae9ed
@@ -27,10 +27,16 @@
6ae9ed
 # include "internal.h"
6ae9ed
 # include "virbuffer.h"
6ae9ed
 # include "virjson.h"
6ae9ed
+# include "virstorageencryption.h"
6ae9ed
 
6ae9ed
 char *virQEMUBuildObjectCommandlineFromJSON(const char *type,
6ae9ed
                                             const char *alias,
6ae9ed
                                             virJSONValuePtr props);
6ae9ed
 
6ae9ed
 void virQEMUBuildBufferEscapeComma(virBufferPtr buf, const char *str);
6ae9ed
+void virQEMUBuildLuksOpts(virBufferPtr buf,
6ae9ed
+                          virStorageEncryptionInfoDefPtr enc,
6ae9ed
+                          const char *alias)
6ae9ed
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
6ae9ed
+
6ae9ed
 #endif /* __VIR_QEMU_H_ */
6ae9ed
diff --git a/tests/storagevolxml2argvtest.c b/tests/storagevolxml2argvtest.c
6ae9ed
index ccfe9ab..e300821 100644
6ae9ed
--- a/tests/storagevolxml2argvtest.c
6ae9ed
+++ b/tests/storagevolxml2argvtest.c
6ae9ed
@@ -83,7 +83,8 @@ testCompareXMLToArgvFiles(bool shouldFail,
6ae9ed
 
6ae9ed
     cmd = virStorageBackendCreateQemuImgCmdFromVol(conn, &poolobj, vol,
6ae9ed
                                                    inputvol, flags,
6ae9ed
-                                                   create_tool, imgformat);
6ae9ed
+                                                   create_tool, imgformat,
6ae9ed
+                                                   NULL);
6ae9ed
     if (!cmd) {
6ae9ed
         if (shouldFail) {
6ae9ed
             virResetLastError();
6ae9ed
-- 
6ae9ed
2.9.2
6ae9ed