Blob Blame History Raw
From b7638da5f8a4ad19fc45f530063044d0b5020422 Mon Sep 17 00:00:00 2001
Message-Id: <b7638da5f8a4ad19fc45f530063044d0b5020422@dist-git>
From: Jiri Denemark <jdenemar@redhat.com>
Date: Thu, 25 May 2017 10:20:57 +0200
Subject: [PATCH] qemu: Store save cookie in save images and snapshots

The following patches will add an actual content in the cookie and use
the data when restoring a domain.

Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
(cherry picked from commit 5c2f01abcb43810e7617f9a6544ce0d0b6caed34)

https://bugzilla.redhat.com/show_bug.cgi?id=1441662

Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
---
 docs/formatsnapshot.html.in     |  6 +++
 docs/schemas/domainsnapshot.rng |  7 +++
 src/qemu/qemu_driver.c          | 96 ++++++++++++++++++++++++++++++++++++-----
 3 files changed, 99 insertions(+), 10 deletions(-)

diff --git a/docs/formatsnapshot.html.in b/docs/formatsnapshot.html.in
index c3ab516fa4..5e8e21c8a7 100644
--- a/docs/formatsnapshot.html.in
+++ b/docs/formatsnapshot.html.in
@@ -235,6 +235,12 @@
         at the time of the snapshot (<span class="since">since
         0.9.5</span>).  Readonly.
       </dd>
+      <dt><code>cookie</code></dt>
+      <dd>Save image cookie containing additional data libvirt may need to
+        properly restore a domain from an active snapshot when such data
+        cannot be stored directly in the <code>domain</code> to maintain
+        compatibility with older libvirt or hypervisor. Readonly.
+      </dd>
     </dl>
 
     <h2><a name="example">Examples</a></h2>
diff --git a/docs/schemas/domainsnapshot.rng b/docs/schemas/domainsnapshot.rng
index 4ab1b828f2..2680887095 100644
--- a/docs/schemas/domainsnapshot.rng
+++ b/docs/schemas/domainsnapshot.rng
@@ -90,6 +90,13 @@
             </element>
           </element>
         </optional>
+        <optional>
+          <element name='cookie'>
+            <zeroOrMore>
+              <ref name='customElement'/>
+            </zeroOrMore>
+          </element>
+        </optional>
       </interleave>
     </element>
   </define>
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 7ce0f01f70..dbb1ea9475 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -2813,7 +2813,8 @@ struct _virQEMUSaveHeader {
     uint32_t data_len;
     uint32_t was_running;
     uint32_t compressed;
-    uint32_t unused[15];
+    uint32_t cookieOffset;
+    uint32_t unused[14];
 };
 
 typedef struct _virQEMUSaveData virQEMUSaveData;
@@ -2821,6 +2822,7 @@ typedef virQEMUSaveData *virQEMUSaveDataPtr;
 struct _virQEMUSaveData {
     virQEMUSaveHeader header;
     char *xml;
+    char *cookie;
 };
 
 
@@ -2831,6 +2833,7 @@ bswap_header(virQEMUSaveHeaderPtr hdr)
     hdr->data_len = bswap_32(hdr->data_len);
     hdr->was_running = bswap_32(hdr->was_running);
     hdr->compressed = bswap_32(hdr->compressed);
+    hdr->cookieOffset = bswap_32(hdr->cookieOffset);
 }
 
 
@@ -2841,6 +2844,7 @@ virQEMUSaveDataFree(virQEMUSaveDataPtr data)
         return;
 
     VIR_FREE(data->xml);
+    VIR_FREE(data->cookie);
     VIR_FREE(data);
 }
 
@@ -2850,8 +2854,10 @@ virQEMUSaveDataFree(virQEMUSaveDataPtr data)
  */
 static virQEMUSaveDataPtr
 virQEMUSaveDataNew(char *domXML,
+                   qemuDomainSaveCookiePtr cookieObj,
                    bool running,
-                   int compressed)
+                   int compressed,
+                   virDomainXMLOptionPtr xmlopt)
 {
     virQEMUSaveDataPtr data = NULL;
     virQEMUSaveHeaderPtr header;
@@ -2861,6 +2867,11 @@ virQEMUSaveDataNew(char *domXML,
 
     VIR_STEAL_PTR(data->xml, domXML);
 
+    if (cookieObj &&
+        !(data->cookie = virSaveCookieFormat((virObjectPtr) cookieObj,
+                                             virDomainXMLOptionGetSaveCookie(xmlopt))))
+        goto error;
+
     header = &data->header;
     memcpy(header->magic, QEMU_SAVE_PARTIAL, sizeof(header->magic));
     header->version = QEMU_SAVE_VERSION;
@@ -2868,6 +2879,10 @@ virQEMUSaveDataNew(char *domXML,
     header->compressed = compressed;
 
     return data;
+
+ error:
+    virQEMUSaveDataFree(data);
+    return NULL;
 }
 
 
@@ -2887,11 +2902,17 @@ virQEMUSaveDataWrite(virQEMUSaveDataPtr data,
 {
     virQEMUSaveHeaderPtr header = &data->header;
     size_t len;
+    size_t xml_len;
+    size_t cookie_len = 0;
     int ret = -1;
     size_t zerosLen = 0;
     char *zeros = NULL;
 
-    len = strlen(data->xml) + 1;
+    xml_len = strlen(data->xml) + 1;
+    if (data->cookie)
+        cookie_len = strlen(data->cookie) + 1;
+
+    len = xml_len + cookie_len;
 
     if (header->data_len > 0) {
         if (len > header->data_len) {
@@ -2907,6 +2928,9 @@ virQEMUSaveDataWrite(virQEMUSaveDataPtr data,
         header->data_len = len;
     }
 
+    if (data->cookie)
+        header->cookieOffset = xml_len;
+
     if (safewrite(fd, header, sizeof(*header)) != sizeof(*header)) {
         virReportSystemError(errno,
                              _("failed to write header to domain save file '%s'"),
@@ -2914,14 +2938,28 @@ virQEMUSaveDataWrite(virQEMUSaveDataPtr data,
         goto cleanup;
     }
 
-    if (safewrite(fd, data->xml, header->data_len) != header->data_len ||
-        safewrite(fd, zeros, zerosLen) != zerosLen) {
+    if (safewrite(fd, data->xml, xml_len) != xml_len) {
         virReportSystemError(errno,
                              _("failed to write domain xml to '%s'"),
                              path);
         goto cleanup;
     }
 
+    if (data->cookie &&
+        safewrite(fd, data->cookie, cookie_len) != cookie_len) {
+        virReportSystemError(errno,
+                             _("failed to write cookie to '%s'"),
+                             path);
+        goto cleanup;
+    }
+
+    if (safewrite(fd, zeros, zerosLen) != zerosLen) {
+        virReportSystemError(errno,
+                             _("failed to write padding to '%s'"),
+                             path);
+        goto cleanup;
+    }
+
     ret = 0;
 
  cleanup:
@@ -3236,6 +3274,7 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver, virDomainPtr dom,
     qemuDomainObjPrivatePtr priv = vm->privateData;
     virCapsPtr caps;
     virQEMUSaveDataPtr data = NULL;
+    qemuDomainSaveCookiePtr cookie = NULL;
 
     if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
         goto cleanup;
@@ -3301,7 +3340,11 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver, virDomainPtr dom,
         goto endjob;
     }
 
-    if (!(data = virQEMUSaveDataNew(xml, was_running, compressed)))
+    if (!(cookie = qemuDomainSaveCookieNew(vm)))
+        goto endjob;
+
+    if (!(data = virQEMUSaveDataNew(xml, cookie, was_running, compressed,
+                                    driver->xmlopt)))
         goto endjob;
     xml = NULL;
 
@@ -3338,6 +3381,7 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver, virDomainPtr dom,
         qemuDomainRemoveInactive(driver, vm);
 
  cleanup:
+    virObjectUnref(cookie);
     VIR_FREE(xml);
     virQEMUSaveDataFree(data);
     qemuDomainEventQueue(driver, event);
@@ -6282,6 +6326,8 @@ qemuDomainSaveImageOpen(virQEMUDriverPtr driver,
     virDomainDefPtr def = NULL;
     int oflags = open_write ? O_RDWR : O_RDONLY;
     virCapsPtr caps = NULL;
+    size_t xml_len;
+    size_t cookie_len;
 
     if (bypass_cache) {
         int directFlag = virFileDirectFdFlag();
@@ -6362,15 +6408,33 @@ qemuDomainSaveImageOpen(virQEMUDriverPtr driver,
         goto error;
     }
 
-    if (VIR_ALLOC_N(data->xml, header->data_len) < 0)
+    if (header->cookieOffset)
+        xml_len = header->cookieOffset;
+    else
+        xml_len = header->data_len;
+
+    cookie_len = header->data_len - xml_len;
+
+    if (VIR_ALLOC_N(data->xml, xml_len) < 0)
         goto error;
 
-    if (saferead(fd, data->xml, header->data_len) != header->data_len) {
+    if (saferead(fd, data->xml, xml_len) != xml_len) {
         virReportError(VIR_ERR_OPERATION_FAILED,
                        "%s", _("failed to read domain XML"));
         goto error;
     }
 
+    if (cookie_len > 0) {
+        if (VIR_ALLOC_N(data->cookie, cookie_len) < 0)
+            goto error;
+
+        if (saferead(fd, data->cookie, cookie_len) != cookie_len) {
+            virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                           _("failed to read cookie"));
+            goto error;
+        }
+    }
+
     /* Create a domain from this XML */
     if (!(def = virDomainDefParseString(data->xml, caps, driver->xmlopt, NULL,
                                         VIR_DOMAIN_DEF_PARSE_INACTIVE |
@@ -6411,6 +6475,11 @@ qemuDomainSaveImageStartVM(virConnectPtr conn,
     char *errbuf = NULL;
     virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
     virQEMUSaveHeaderPtr header = &data->header;
+    qemuDomainSaveCookiePtr cookie = NULL;
+
+    if (virSaveCookieParseString(data->cookie, (virObjectPtr *) &cookie,
+                                 virDomainXMLOptionGetSaveCookie(driver->xmlopt)) < 0)
+        goto cleanup;
 
     if ((header->version == 2) &&
         (header->compressed != QEMU_SAVE_FORMAT_RAW)) {
@@ -6501,6 +6570,7 @@ qemuDomainSaveImageStartVM(virConnectPtr conn,
     ret = 0;
 
  cleanup:
+    virObjectUnref(cookie);
     virCommandFree(cmd);
     VIR_FREE(errbuf);
     if (qemuSecurityRestoreSavedStateLabel(driver->securityManager,
@@ -13561,6 +13631,9 @@ qemuDomainSnapshotCreateActiveInternal(virConnectPtr conn,
     if (ret < 0)
         goto cleanup;
 
+    if (!(snap->def->cookie = (virObjectPtr) qemuDomainSaveCookieNew(vm)))
+        goto cleanup;
+
     if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_HALT) {
         event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED,
                                          VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT);
@@ -14439,10 +14512,13 @@ qemuDomainSnapshotCreateActiveExternal(virConnectPtr conn,
                                                     "snapshot", false)) < 0)
             goto cleanup;
 
-        if (!(xml = qemuDomainDefFormatLive(driver, vm->def, true, true)))
+        if (!(xml = qemuDomainDefFormatLive(driver, vm->def, true, true)) ||
+            !(snap->def->cookie = (virObjectPtr) qemuDomainSaveCookieNew(vm)))
             goto cleanup;
 
-        if (!(data = virQEMUSaveDataNew(xml, resume, compressed)))
+        if (!(data = virQEMUSaveDataNew(xml,
+                                        (qemuDomainSaveCookiePtr) snap->def->cookie,
+                                        resume, compressed, driver->xmlopt)))
             goto cleanup;
         xml = NULL;
 
-- 
2.13.1