[libvirt] [PATCH v7 3/4] vbox_tmpl.c: Patch for redefining snapshots

Yohan BELLEGUIC yohan.belleguic at diateam.net
Fri Apr 18 09:51:33 UTC 2014


The machine is unregistered and its vbox XML file is changed in order to
add snapshot information. The machine is then registered with the
snapshot to redefine.
---
 src/vbox/vbox_tmpl.c |  949 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 942 insertions(+), 7 deletions(-)

diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
index ac000cf..f8667f6 100644
--- a/src/vbox/vbox_tmpl.c
+++ b/src/vbox/vbox_tmpl.c
@@ -38,12 +38,12 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#include <libxml/xmlwriter.h>
 
 #include "internal.h"
 #include "datatypes.h"
 #include "domain_conf.h"
 #include "snapshot_conf.h"
+#include "vbox_snapshot_conf.h"
 #include "network_conf.h"
 #include "virerror.h"
 #include "domain_event.h"
@@ -6023,6 +6023,930 @@ vboxDomainSnapshotGet(vboxGlobalData *data,
     return snapshot;
 }
 
+#if VBOX_API_VERSION >= 4002000
+static int vboxCloseDisksRecursively(virDomainPtr dom, char *location)
+{
+    VBOX_OBJECT_CHECK(dom->conn, int, -1);
+    nsresult rc;
+    size_t i = 0;
+    PRUnichar *locationUtf = NULL;
+    IMedium *medium = NULL;
+    IMedium **children = NULL;
+    PRUint32 childrenSize = 0;
+    VBOX_UTF8_TO_UTF16(location, &locationUtf);
+    rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj,
+                                         locationUtf,
+                                         DeviceType_HardDisk,
+                                         AccessMode_ReadWrite,
+                                         false,
+                                         &medium);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                       , _("Unable to open HardDisk"), rc);
+        goto cleanup;
+    }
+    rc = medium->vtbl->GetChildren(medium, &childrenSize, &children);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+                       , _("Unable to get disk children"));
+        goto cleanup;
+    }
+    for (i = 0; i < childrenSize; i++) {
+        IMedium *childMedium = children[i];
+        if (childMedium) {
+            PRUnichar *childLocationUtf = NULL;
+            char *childLocation = NULL;
+            rc = childMedium->vtbl->GetLocation(childMedium, &childLocationUtf);
+            VBOX_UTF16_TO_UTF8(childLocationUtf, &childLocation);
+            VBOX_UTF16_FREE(childLocationUtf);
+            if (vboxCloseDisksRecursively(dom, childLocation) < 0) {
+                VIR_DEBUG("Something wrong in the recurse");
+                goto cleanup;
+            }
+            VIR_FREE(childLocation);
+        }
+    }
+    rc = medium->vtbl->Close(medium);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                       , _("Unable to CLOSE HardDisk"), rc);
+        goto cleanup;
+    }
+
+    ret = 0;
+ cleanup:
+    VBOX_UTF16_FREE(locationUtf);
+    return ret;
+}
+
+static int
+vboxSnapshotRedefine(virDomainPtr dom,
+                     virDomainSnapshotDefPtr def,
+                     bool isCurrent)
+{
+    /*
+     * If your snapshot has a parent,
+     * it will only be redefined if you have already
+     * redefined the parent.
+     *
+     * The general algorithm of this function is below :
+     * First of all, we are going to create our vboxSnapshotXmlMachinePtr struct from
+     * the machine settings path.
+     * Then, if the machine current snapshot xml file is saved in the machine location,
+     * it means that this snapshot was previously modified by us and has fake disks.
+     * Fake disks are added when the flag VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT was not set
+     * yet, in order to not corrupt read-only disks. The first thing to do is to remove those
+     * disks and restore the read-write disks, if any, in the vboxSnapshotXmlMachinePtr struct.
+     * We also delete the current snapshot xml file.
+     *
+     * After that, we are going to register the snapshot read-only disks that we want to redefine,
+     * if they are not in the media registry struct.
+     *
+     * The next step is to unregister the machine and close all disks.
+     *
+     * Then, we check if the flag VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE has already been set.
+     * If this flag was set, we just add read-write disks to the media registry
+     * struct. Otherwise, we save the snapshot xml file into the machine location in order
+     * to recover the read-write disks during the next redefine and we create differential disks
+     * from the snapshot read-only disks and add them to the media registry struct.
+     *
+     * Finally, we register the machine with the new virtualbox description file.
+     */
+    VBOX_OBJECT_CHECK(dom->conn, int, -1);
+    vboxIID domiid = VBOX_IID_INITIALIZER;
+    IMachine *machine = NULL;
+    nsresult rc;
+    PRUnichar *settingsFilePath = NULL;
+    char *settingsFilePath_Utf8 = NULL;
+    vboxSnapshotXmlMachinePtr snapshotMachineDesc = NULL;
+    char *currentSnapshotXmlFilePath = NULL;
+    PRUnichar *machineNameUtf16 = NULL;
+    char *machineName = NULL;
+    char **realReadWriteDisksPath = NULL;
+    int realReadWriteDisksPathSize = 0;
+    char **realReadOnlyDisksPath = NULL;
+    int realReadOnlyDisksPathSize = 0;
+    vboxSnapshotXmlSnapshotPtr newSnapshotPtr = NULL;
+    unsigned char snapshotUuid[VIR_UUID_BUFLEN];
+    int it = 0;
+    int jt = 0;
+    PRUint32 aMediaSize = 0;
+    IMedium **aMedia = NULL;
+    char *machineLocationPath = NULL;
+    char *nameTmpUse = NULL;
+    bool snapshotFileExists = false;
+    bool needToChangeStorageController = false;
+
+    vboxIIDFromUUID(&domiid, dom->uuid);
+    rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_NO_DOMAIN, "%s",
+                       _("no domain with matching UUID"));
+        goto cleanup;
+    }
+
+    rc = machine->vtbl->SaveSettings(machine);
+    /*It may failed when the machine is not mutable.*/
+    rc = machine->vtbl->GetSettingsFilePath(machine, &settingsFilePath);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot get settings file path"));
+        goto cleanup;
+    }
+    VBOX_UTF16_TO_UTF8(settingsFilePath, &settingsFilePath_Utf8);
+
+    /*Getting the machine name to retrieve the machine location path.*/
+    rc = machine->vtbl->GetName(machine, &machineNameUtf16);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot get machine name"));
+        goto cleanup;
+    }
+    VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineName);
+
+    if (virAsprintf(&nameTmpUse, "%s.vbox", machineName) < 0)
+        goto cleanup;
+    machineLocationPath = virStringReplace(settingsFilePath_Utf8, nameTmpUse, "");
+    if (machineLocationPath == NULL) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Unable to get the machine location path"));
+        goto cleanup;
+    }
+
+    /*We create the xml struct with the settings file path.*/
+    snapshotMachineDesc = vboxSnapshotLoadVboxFile(settingsFilePath_Utf8, machineLocationPath);
+    if (snapshotMachineDesc == NULL) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot create a vboxSnapshotXmlPtr"));
+        goto cleanup;
+    }
+    if (snapshotMachineDesc->currentSnapshot != NULL) {
+        if (virAsprintf(&currentSnapshotXmlFilePath, "%s%s.xml", machineLocationPath,
+                       snapshotMachineDesc->currentSnapshot) < 0)
+            goto cleanup;
+        snapshotFileExists = virFileExists(currentSnapshotXmlFilePath);
+    }
+
+    if (snapshotFileExists) {
+        /*
+         * We have created fake disks, so we have to remove them and replace them with
+         * the read-write disks if there are any. The fake disks will be closed during
+         * the machine unregistration.
+         */
+        if (removeFakeDisks(snapshotMachineDesc) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Unable to remove Fake Disks"));
+            goto cleanup;
+        }
+        realReadWriteDisksPathSize = getRWDisksPathsFromLibvirtXML(currentSnapshotXmlFilePath,
+                                                             &realReadWriteDisksPath);
+        realReadOnlyDisksPathSize = getRODisksPathsFromLibvirtXML(currentSnapshotXmlFilePath,
+                                                                         &realReadOnlyDisksPath);
+        /*The read-only disk number is necessarily greater or equal to the
+         *read-write disk number*/
+        if (realReadOnlyDisksPathSize < realReadWriteDisksPathSize) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("The read only disk number must be greater or equal to the "
+                           " read write disk number"));
+            goto cleanup;
+        }
+        for (it = 0; it < realReadWriteDisksPathSize; it++) {
+            vboxSnapshotXmlHardDiskPtr readWriteDisk = NULL;
+            PRUnichar *locationUtf = NULL;
+            IMedium *readWriteMedium = NULL;
+            PRUnichar *uuidUtf = NULL;
+            char *uuid = NULL;
+            PRUnichar *formatUtf = NULL;
+            char *format = NULL;
+            char *parentUuid = NULL;
+
+            VBOX_UTF8_TO_UTF16(realReadWriteDisksPath[it], &locationUtf);
+            rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj,
+                                                 locationUtf,
+                                                 DeviceType_HardDisk,
+                                                 AccessMode_ReadWrite,
+                                                 false,
+                                                 &readWriteMedium);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                               , _("Unable to open HardDisk"), rc);
+                VBOX_UTF16_FREE(locationUtf);
+                goto cleanup;
+            }
+            VBOX_UTF16_FREE(locationUtf);
+
+            rc = readWriteMedium->vtbl->GetId(readWriteMedium, &uuidUtf);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Unable to get the read write medium id"));
+                goto cleanup;
+            }
+            VBOX_UTF16_TO_UTF8(uuidUtf, &uuid);
+            VBOX_UTF16_FREE(uuidUtf);
+
+            rc = readWriteMedium->vtbl->GetFormat(readWriteMedium, &formatUtf);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Unable to get the read write medium format"));
+                VIR_FREE(uuid);
+                goto cleanup;
+            }
+            VBOX_UTF16_TO_UTF8(formatUtf, &format);
+            VBOX_UTF16_FREE(formatUtf);
+
+            if (VIR_ALLOC(readWriteDisk) < 0) {
+                VIR_FREE(uuid);
+                VIR_FREE(formatUtf);
+                goto cleanup;
+            }
+
+            readWriteDisk->format = format;
+            readWriteDisk->uuid = uuid;
+            readWriteDisk->location = realReadWriteDisksPath[it];
+            /*
+             * We get the current snapshot's read-only disk uuid in order to add the
+             * read-write disk to the media registry as it's child. The read-only disk
+             * is already in the media registry because it is the fake disk's parent.
+             */
+            parentUuid = hardDiskUuidByLocation(snapshotMachineDesc,
+                                                      realReadOnlyDisksPath[it]);
+            if (addHardDiskToMediaRegistry(readWriteDisk,
+                                           snapshotMachineDesc->mediaRegistry,
+                                           parentUuid) < 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Unable to add hard disk to media Registry"));
+                VIR_FREE(readWriteDisk);
+                goto cleanup;
+            }
+            rc = readWriteMedium->vtbl->Close(readWriteMedium);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                               , _("Unable to close HardDisk"), rc);
+                goto cleanup;
+            }
+        }
+        /*
+         * Now we have done this swap, we remove the snapshot xml file from the
+         * current machine location.
+         */
+        if (remove(currentSnapshotXmlFilePath) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Unable to delete file %s"), currentSnapshotXmlFilePath);
+            goto cleanup;
+        }
+    }
+    /*
+     * Before unregistering the machine, while all disks are still open, ensure that all
+     * read-only disks are in the redefined snapshot's media registry (the disks need to
+     * be open to query their uuid).
+     */
+    for (it = 0; it < def->dom->ndisks; it++) {
+        int diskInMediaRegistry = 0;
+        IMedium *readOnlyMedium = NULL;
+        PRUnichar *locationUtf = NULL;
+        PRUnichar *uuidUtf = NULL;
+        char *uuid = NULL;
+        PRUnichar *formatUtf = NULL;
+        char *format = NULL;
+        PRUnichar *parentUuidUtf = NULL;
+        char *parentUuid = NULL;
+        vboxSnapshotXmlHardDiskPtr readOnlyDisk = NULL;
+
+        diskInMediaRegistry = diskIsInMediaRegistry(snapshotMachineDesc,
+                                                        def->dom->disks[it]->src.path);
+        if (diskInMediaRegistry == -1) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                 _("Unable to know if disk is in media registry"));
+            goto cleanup;
+        }
+        if (diskInMediaRegistry == 1) /*Nothing to do.*/
+            continue;
+        /*The read only disk is not in the media registry*/
+
+        VBOX_UTF8_TO_UTF16(def->dom->disks[it]->src.path, &locationUtf);
+        rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj,
+                                             locationUtf,
+                                             DeviceType_HardDisk,
+                                             AccessMode_ReadWrite,
+                                             false,
+                                             &readOnlyMedium);
+        if (NS_FAILED(rc)) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                           , _("Unable to open HardDisk"), rc);
+            VBOX_UTF16_FREE(locationUtf);
+            goto cleanup;
+        }
+        VBOX_UTF16_FREE(locationUtf);
+
+        rc = readOnlyMedium->vtbl->GetId(readOnlyMedium, &uuidUtf);
+        if (NS_FAILED(rc)) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+                           , _("Unable to get hard disk id"));
+            goto cleanup;
+        }
+        VBOX_UTF16_TO_UTF8(uuidUtf, &uuid);
+        VBOX_UTF16_FREE(uuidUtf);
+
+        rc = readOnlyMedium->vtbl->GetFormat(readOnlyMedium, &formatUtf);
+        if (NS_FAILED(rc)) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+                           , _("Unable to get hard disk format"));
+            VIR_FREE(uuid);
+            goto cleanup;
+        }
+        VBOX_UTF16_TO_UTF8(formatUtf, &format);
+        VBOX_UTF16_FREE(formatUtf);
+
+        /*This disk is already in the media registry*/
+        IMedium *parentReadOnlyMedium = NULL;
+        rc = readOnlyMedium->vtbl->GetParent(readOnlyMedium, &parentReadOnlyMedium);
+        if (NS_FAILED(rc)) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+                           , _("Unable to get parent hard disk"));
+            VIR_FREE(uuid);
+            goto cleanup;
+        }
+
+        rc = parentReadOnlyMedium->vtbl->GetId(parentReadOnlyMedium, &parentUuidUtf);
+        if (NS_FAILED(rc)) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                           , _("Unable to get hard disk id"), rc);
+            VIR_FREE(uuid);
+            goto cleanup;
+        }
+        VBOX_UTF16_TO_UTF8(parentUuidUtf, &parentUuid);
+        VBOX_UTF16_FREE(parentUuidUtf);
+
+        rc = readOnlyMedium->vtbl->Close(readOnlyMedium);
+        if (NS_FAILED(rc)) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                           , _("Unable to close HardDisk"), rc);
+            VIR_FREE(uuid);
+            VIR_FREE(parentUuid);
+            goto cleanup;
+        }
+
+        if (VIR_ALLOC(readOnlyDisk) < 0) {
+            VIR_FREE(uuid);
+            VIR_FREE(parentUuid);
+            goto cleanup;
+        }
+
+        readOnlyDisk->format = format;
+        readOnlyDisk->uuid = uuid;
+        if (VIR_STRDUP(readOnlyDisk->location, def->dom->disks[it]->src.path) < 0) {
+            VIR_FREE(readOnlyDisk);
+            goto cleanup;
+        }
+
+        if (addHardDiskToMediaRegistry(readOnlyDisk, snapshotMachineDesc->mediaRegistry,
+                                       parentUuid) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Unable to add hard disk to media registry"));
+            VIR_FREE(readOnlyDisk);
+            goto cleanup;
+        }
+    }
+
+    /*Now, we can unregister the machine*/
+    rc = machine->vtbl->Unregister(machine,
+                              CleanupMode_DetachAllReturnHardDisksOnly,
+                              &aMediaSize,
+                              &aMedia);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                       , _("Unable to unregister machine"), rc);
+        goto cleanup;
+    }
+    VBOX_RELEASE(machine);
+
+    /*
+     * Unregister the machine, and then close all disks returned by the unregister method.
+     * Some close operations will fail because some disks that need to be closed will not
+     * be returned by virtualbox. We will close them just after. We have to use this
+     * solution because it is the only way to delete fake disks.
+     */
+    for (it = 0; it < aMediaSize; it++) {
+        IMedium *medium = aMedia[it];
+        if (medium) {
+            PRUnichar *locationUtf16 = NULL;
+            char *locationUtf8 = NULL;
+            rc = medium->vtbl->GetLocation(medium, &locationUtf16);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Unable to get medium location"));
+                goto cleanup;
+            }
+            VBOX_UTF16_TO_UTF8(locationUtf16, &locationUtf8);
+            VBOX_UTF16_FREE(locationUtf16);
+            if (strstr(locationUtf8, "fake") != NULL) {
+                /*we delete the fake disk because we don't need it anymore*/
+                IProgress *progress = NULL;
+                PRInt32 resultCode = -1;
+                rc = medium->vtbl->DeleteStorage(medium, &progress);
+                if (NS_FAILED(rc)) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                                   , _("Unable to delete medium"), rc);
+                    VIR_FREE(locationUtf8);
+                    goto cleanup;
+                }
+                progress->vtbl->WaitForCompletion(progress, -1);
+                progress->vtbl->GetResultCode(progress, &resultCode);
+                if (NS_FAILED(resultCode)) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                                   , _("Error while closing medium"), resultCode);
+                    VIR_FREE(locationUtf8);
+                    goto cleanup;
+                }
+                VBOX_RELEASE(progress);
+            } else {
+                /*
+                 * This a comment from vboxmanage code in the handleUnregisterVM
+                 * function in VBoxManageMisc.cpp :
+                 * Note that the IMachine::Unregister method will return the medium
+                 * reference in a sane order, which means that closing will normally
+                 * succeed, unless there is still another machine which uses the
+                 * medium. No harm done if we ignore the error.
+                 */
+                rc = medium->vtbl->Close(medium);
+            }
+            VBOX_UTF8_FREE(locationUtf8);
+        }
+    }
+    /*Close all disks that failed to close normally.*/
+    for (it = 0; it < snapshotMachineDesc->mediaRegistry->ndisks; it++) {
+        if (vboxCloseDisksRecursively(dom, snapshotMachineDesc->mediaRegistry->disks[it]->location) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Unable to close recursively all disks"));
+            goto cleanup;
+        }
+    }
+    /*Here, all disks are closed or deleted*/
+
+    /*We are now going to create and fill the Snapshot xml struct*/
+    if (VIR_ALLOC(newSnapshotPtr) < 0)
+        goto cleanup;
+
+    if (virUUIDGenerate(snapshotUuid) < 0)
+        goto cleanup;
+
+    char uuidtmp[VIR_UUID_STRING_BUFLEN];
+    virUUIDFormat(snapshotUuid, uuidtmp);
+    if (VIR_STRDUP(newSnapshotPtr->uuid, uuidtmp) < 0)
+        goto cleanup;
+
+    VIR_DEBUG("New snapshot UUID: %s", newSnapshotPtr->uuid);
+    if (VIR_STRDUP(newSnapshotPtr->name, def->name) < 0)
+        goto cleanup;
+
+    newSnapshotPtr->timeStamp = virTimeStringThen(def->creationTime * 1000);
+
+    if (VIR_STRDUP(newSnapshotPtr->description, def->description) < 0)
+        goto cleanup;
+
+    if (VIR_STRDUP(newSnapshotPtr->hardware, snapshotMachineDesc->hardware) < 0)
+        goto cleanup;
+
+    if (VIR_STRDUP(newSnapshotPtr->storageController, snapshotMachineDesc->storageController) < 0)
+        goto cleanup;
+
+    /*We get the parent disk uuid from the parent disk location to correctly fill the storage controller.*/
+    for (it = 0; it < def->dom->ndisks; it++) {
+        char *location = NULL;
+        char *uuidReplacing = NULL;
+        char **searchResultTab = NULL;
+        ssize_t resultSize = 0;
+        char *tmp = NULL;
+
+        location = def->dom->disks[it]->src.path;
+        if (!location)
+            goto cleanup;
+        /*Replacing the uuid*/
+        uuidReplacing = hardDiskUuidByLocation(snapshotMachineDesc, location);
+        if (uuidReplacing == NULL)
+            goto cleanup;
+
+        resultSize = virStringSearch(newSnapshotPtr->storageController,
+                                     VBOX_UUID_REGEX,
+                                     it + 1,
+                                     &searchResultTab);
+        if (resultSize != it + 1)
+            goto cleanup;
+
+        tmp = virStringReplace(newSnapshotPtr->storageController,
+                               searchResultTab[it],
+                               uuidReplacing);
+        virStringFreeList(searchResultTab);
+        VIR_FREE(newSnapshotPtr->storageController);
+        if (!tmp)
+            goto cleanup;
+        if (VIR_STRDUP(newSnapshotPtr->storageController, tmp) < 0)
+            goto cleanup;
+
+        VIR_FREE(tmp);
+    }
+    if (addSnapshotToXmlMachine(newSnapshotPtr, snapshotMachineDesc, def->parent) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Unable to add the snapshot to the machine description"));
+        goto cleanup;
+    }
+    /*
+     * We change the current snapshot only if there is no current snapshot or if the
+     * snapshotFile exists, otherwise, it means that the correct current snapshot is
+     * already set.
+     */
+
+    if (snapshotMachineDesc->currentSnapshot == NULL || snapshotFileExists) {
+        snapshotMachineDesc->currentSnapshot = newSnapshotPtr->uuid;
+        needToChangeStorageController = true;
+    }
+
+    /*
+     * Open the snapshot's read-write disk's full ancestry to allow opening the
+     * read-write disk itself.
+     */
+    for (it = 0; it < def->dom->ndisks; it++) {
+        char *location = NULL;
+        vboxSnapshotXmlHardDiskPtr *hardDiskToOpen = NULL;
+        size_t hardDiskToOpenSize = 0;
+
+        location = def->dom->disks[it]->src.path;
+        if (!location)
+            goto cleanup;
+
+        hardDiskToOpenSize = diskListToOpen(snapshotMachineDesc,
+                                                   &hardDiskToOpen, location);
+        for (jt = hardDiskToOpenSize -1; jt >= 0; jt--) {
+            IMedium *medium = NULL;
+            PRUnichar *locationUtf16 = NULL;
+            VBOX_UTF8_TO_UTF16(hardDiskToOpen[jt]->location, &locationUtf16);
+
+            rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj,
+                                                 locationUtf16,
+                                                 DeviceType_HardDisk,
+                                                 AccessMode_ReadWrite,
+                                                 false,
+                                                 &medium);
+            VBOX_UTF16_FREE(locationUtf16);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                               , _("Unable to open HardDisk"), rc);
+                goto cleanup;
+            }
+        }
+    }
+    if (isCurrent || !needToChangeStorageController) {
+        /* We don't create a differential hard disk because either the current snapshot
+         * has already been defined or the snapshot to redefine is the current snapshot.
+         * If the snapshot to redefine is the current snapshot, we add read-write disks in
+         * the machine storage controllers.
+         */
+        for (it = 0; it < def->ndisks; it++) {
+            IMedium *medium = NULL;
+            PRUnichar *locationUtf16 = NULL;
+            vboxSnapshotXmlHardDiskPtr disk = NULL;
+            PRUnichar *formatUtf16 = NULL;
+            char *format = NULL;
+            PRUnichar *uuidUtf16 = NULL;
+            char *uuid = NULL;
+            IMedium *parentDisk = NULL;
+            PRUnichar *parentUuidUtf16 = NULL;
+            char *parentUuid = NULL;
+
+            VBOX_UTF8_TO_UTF16(def->disks[it].src.path, &locationUtf16);
+            rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj,
+                                                 locationUtf16,
+                                                 DeviceType_HardDisk,
+                                                 AccessMode_ReadWrite,
+                                                 false,
+                                                 &medium);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                               , _("Unable to open HardDisk"), rc);
+                goto cleanup;
+            }
+            VBOX_UTF16_FREE(locationUtf16);
+
+            if (VIR_ALLOC(disk) < 0)
+                goto cleanup;
+
+            rc = medium->vtbl->GetFormat(medium, &formatUtf16);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+                               , _("Unable to get disk format"));
+                VIR_FREE(disk);
+                goto cleanup;
+            }
+
+            VBOX_UTF16_TO_UTF8(formatUtf16, &format);
+            disk->format = format;
+            VBOX_UTF16_FREE(formatUtf16);
+
+            if (VIR_STRDUP(disk->location, def->disks[it].src.path) < 0) {
+                VIR_FREE(disk);
+                goto cleanup;
+            }
+
+            rc = medium->vtbl->GetId(medium, &uuidUtf16);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+                               , _("Unable to get disk uuid"));
+                VIR_FREE(disk);
+                goto cleanup;
+            }
+            VBOX_UTF16_TO_UTF8(uuidUtf16, &uuid);
+            disk->uuid  = uuid;
+            VBOX_UTF16_FREE(uuidUtf16);
+
+            rc = medium->vtbl->GetParent(medium, &parentDisk);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+                               , _("Unable to get disk parent"));
+                VIR_FREE(disk);
+                goto cleanup;
+            }
+
+            parentDisk->vtbl->GetId(parentDisk, &parentUuidUtf16);
+            VBOX_UTF16_TO_UTF8(parentUuidUtf16, &parentUuid);
+            VBOX_UTF16_FREE(parentUuidUtf16);
+            if (addHardDiskToMediaRegistry(disk,
+                                           snapshotMachineDesc->mediaRegistry,
+                                           parentUuid) < 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Unable to add hard disk to the media registry"));
+                VIR_FREE(disk);
+                goto cleanup;
+            }
+
+            if (needToChangeStorageController) {
+                /*We need to append this disk in the storage controller*/
+                char **searchResultTab = NULL;
+                ssize_t resultSize = 0;
+                char *tmp = NULL;
+                resultSize = virStringSearch(snapshotMachineDesc->storageController,
+                                             VBOX_UUID_REGEX,
+                                             it + 1,
+                                             &searchResultTab);
+                if (resultSize != it + 1) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("Unable to find UUID %s"), searchResultTab[it]);
+                    goto cleanup;
+                }
+
+                tmp = virStringReplace(snapshotMachineDesc->storageController,
+                                       searchResultTab[it],
+                                       disk->uuid);
+                virStringFreeList(searchResultTab);
+                VIR_FREE(snapshotMachineDesc->storageController);
+                if (!tmp)
+                    goto cleanup;
+                if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0)
+                    goto cleanup;
+
+                VIR_FREE(tmp);
+            }
+            /*Close disk*/
+            rc = medium->vtbl->Close(medium);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                               , _("Unable to close HardDisk"), rc);
+                goto cleanup;
+            }
+        }
+    } else {
+        /*Create a "fake" disk to avoid corrupting children snapshot disks.*/
+        for (it = 0; it < def->dom->ndisks; it++) {
+            IMedium *medium = NULL;
+            PRUnichar *locationUtf16 = NULL;
+            PRUnichar *parentUuidUtf16 = NULL;
+            char *parentUuid = NULL;
+            IMedium *newMedium = NULL;
+            PRUnichar *formatUtf16 = NULL;
+            PRUnichar *newLocation = NULL;
+            char *newLocationUtf8 = NULL;
+            PRInt32 resultCode = -1;
+            vboxSnapshotXmlHardDiskPtr disk = NULL;
+            PRUnichar *uuidUtf16 = NULL;
+            char *uuid = NULL;
+            char *format = NULL;
+            char **searchResultTab = NULL;
+            ssize_t resultSize = 0;
+            char *tmp = NULL;
+
+            VBOX_UTF8_TO_UTF16(def->dom->disks[it]->src.path, &locationUtf16);
+            rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj,
+                                                 locationUtf16,
+                                                 DeviceType_HardDisk,
+                                                 AccessMode_ReadWrite,
+                                                 false,
+                                                 &medium);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                               , _("Unable to open HardDisk"), rc);
+                VBOX_UTF16_FREE(locationUtf16);
+                goto cleanup;
+            }
+            VBOX_UTF16_FREE(locationUtf16);
+
+            rc = medium->vtbl->GetId(medium, &parentUuidUtf16);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                               , _("Unable to get hardDisk Id"), rc);
+                goto cleanup;
+            }
+            VBOX_UTF16_TO_UTF8(parentUuidUtf16, &parentUuid);
+            VBOX_UTF16_FREE(parentUuidUtf16);
+            VBOX_UTF8_TO_UTF16("VDI", &formatUtf16);
+
+            if (virAsprintf(&newLocationUtf8, "%sfakedisk-%d.vdi", machineLocationPath, it) < 0)
+                goto cleanup;
+            VBOX_UTF8_TO_UTF16(newLocationUtf8, &newLocation);
+            rc = data->vboxObj->vtbl->CreateHardDisk(data->vboxObj,
+                                                formatUtf16,
+                                                newLocation,
+                                                &newMedium);
+            VBOX_UTF16_FREE(newLocation);
+            VBOX_UTF16_FREE(formatUtf16);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                               , _("Unable to create HardDisk"), rc);
+                goto cleanup;
+            }
+
+            IProgress *progress = NULL;
+# if VBOX_API_VERSION < 4003000
+            medium->vtbl->CreateDiffStorage(medium, newMedium, MediumVariant_Diff, &progress);
+# else
+            PRUint32 tab[1];
+            tab[0] =  MediumVariant_Diff;
+            medium->vtbl->CreateDiffStorage(medium, newMedium, 1, tab, &progress);
+# endif
+
+            progress->vtbl->WaitForCompletion(progress, -1);
+            progress->vtbl->GetResultCode(progress, &resultCode);
+            if (NS_FAILED(resultCode)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                               , _("Error while creating diff storage"), resultCode);
+                goto cleanup;
+            }
+            VBOX_RELEASE(progress);
+            /*
+             * The differential disk is created, we add it to the media registry and the
+             * machine storage controllers.
+             */
+
+            if (VIR_ALLOC(disk) < 0)
+                goto cleanup;
+
+            rc = newMedium->vtbl->GetId(newMedium, &uuidUtf16);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                               , _("Unable to get medium uuid"), rc);
+                goto cleanup;
+            }
+            VBOX_UTF16_TO_UTF8(uuidUtf16, &uuid);
+            disk->uuid = uuid;
+            VBOX_UTF16_FREE(uuidUtf16);
+
+            if (VIR_STRDUP(disk->location, newLocationUtf8) < 0)
+                goto cleanup;
+
+            rc = newMedium->vtbl->GetFormat(newMedium, &formatUtf16);
+            VBOX_UTF16_TO_UTF8(formatUtf16, &format);
+            disk->format = format;
+            VBOX_UTF16_FREE(formatUtf16);
+
+            if (addHardDiskToMediaRegistry(disk,
+                                           snapshotMachineDesc->mediaRegistry,
+                                           parentUuid) < 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Unable to add hard disk to the media registry"));
+                goto cleanup;
+            }
+            /*Adding the fake disk to the machine storage controllers*/
+
+            resultSize = virStringSearch(snapshotMachineDesc->storageController,
+                                         VBOX_UUID_REGEX,
+                                         it + 1,
+                                         &searchResultTab);
+            if (resultSize != it + 1) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("Unable to find UUID %s"), searchResultTab[it]);
+                goto cleanup;
+            }
+
+            tmp = virStringReplace(snapshotMachineDesc->storageController,
+                                   searchResultTab[it],
+                                   disk->uuid);
+            virStringFreeList(searchResultTab);
+            VIR_FREE(snapshotMachineDesc->storageController);
+            if (!tmp)
+                goto cleanup;
+            if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0)
+                goto cleanup;
+
+            VIR_FREE(tmp);
+            /*Closing the "fake" disk*/
+            rc = newMedium->vtbl->Close(newMedium);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                               , _("Unable to close the new medium"), rc);
+                goto cleanup;
+            }
+        }
+        /*
+         * We save the snapshot xml file to retrieve the real read-write disk during the
+         * next define. This file is saved as "'machineLocation'/snapshot-'uuid'.xml"
+         */
+        VIR_FREE(currentSnapshotXmlFilePath);
+        if (virAsprintf(&currentSnapshotXmlFilePath, "%s%s.xml", machineLocationPath, snapshotMachineDesc->currentSnapshot) < 0)
+            goto cleanup;
+        char *snapshotContent = virDomainSnapshotDefFormat(NULL, def, VIR_DOMAIN_XML_SECURE, 0);
+        xmlDocPtr newXml = virXMLParse(NULL, snapshotContent, NULL);
+        VIR_FREE(snapshotContent);
+        if (newXml && xmlSaveFile(currentSnapshotXmlFilePath, newXml) < 0) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("Unable to save new snapshot xml file"));
+            goto cleanup;
+        }
+    }
+    /*
+     * All the snapshot structure manipulation is done, we close the disks we have
+     * previously opened.
+     */
+    for (it = 0; it < def->dom->ndisks; it++) {
+        char *location = def->dom->disks[it]->src.path;
+        if (!location)
+            goto cleanup;
+
+        vboxSnapshotXmlHardDiskPtr *hardDiskToOpen = NULL;
+        size_t hardDiskToOpenSize = diskListToOpen(snapshotMachineDesc,
+                                                   &hardDiskToOpen, location);
+        for (jt = 0; jt < hardDiskToOpenSize; jt++) {
+            IMedium *medium = NULL;
+            PRUnichar *locationUtf16 = NULL;
+            VBOX_UTF8_TO_UTF16(hardDiskToOpen[jt]->location, &locationUtf16);
+            rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj,
+                                                 locationUtf16,
+                                                 DeviceType_HardDisk,
+                                                 AccessMode_ReadWrite,
+                                                 false,
+                                                 &medium);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                               , _("Unable to open HardDisk"), rc);
+                goto cleanup;
+            }
+            rc = medium->vtbl->Close(medium);
+            if (NS_FAILED(rc)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                               , _("Unable to CLOSE HardDisk"), rc);
+                goto cleanup;
+            }
+            VBOX_UTF16_FREE(locationUtf16);
+        }
+    }
+
+    /*Now, we rewrite the 'machineName'.vbox file to redefine the machine.*/
+    if (vboxSnapshotSaveVboxFile(snapshotMachineDesc, settingsFilePath_Utf8) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Unable to serialize the machine description"));
+        goto cleanup;
+    }
+    rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj,
+                                     settingsFilePath,
+                                     &machine);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                       , _("Unable to open Machine"), rc);
+        goto cleanup;
+    }
+
+    rc = data->vboxObj->vtbl->RegisterMachine(data->vboxObj, machine);
+    if (NS_FAILED(rc)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s %x"
+                       , _("Unable to register Machine"), rc);
+        goto cleanup;
+    }
+
+    ret = 0;
+ cleanup:
+    VBOX_RELEASE(machine);
+    VBOX_UTF16_FREE(settingsFilePath);
+    VBOX_UTF8_FREE(settingsFilePath_Utf8);
+    VIR_FREE(snapshotMachineDesc);
+    VIR_FREE(currentSnapshotXmlFilePath);
+    VBOX_UTF16_FREE(machineNameUtf16);
+    VBOX_UTF8_FREE(machineName);
+    virStringFreeList(realReadOnlyDisksPath);
+    virStringFreeList(realReadWriteDisksPath);
+    VIR_FREE(newSnapshotPtr);
+    VIR_FREE(machineLocationPath);
+    VIR_FREE(nameTmpUse);
+    return ret;
+}
+#endif
+
 static virDomainSnapshotPtr
 vboxDomainSnapshotCreateXML(virDomainPtr dom,
                             const char *xmlDesc,
@@ -6044,9 +6968,15 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom,
 #else
     PRInt32 result;
 #endif
+#if VBOX_API_VERSION >= 4002000
+    bool isCurrent = false;
+#endif
+
 
     /* VBox has no snapshot metadata, so this flag is trivial.  */
-    virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA, NULL);
+    virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA |
+                  VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE |
+                  VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT, NULL);
 
     if (!(def = virDomainSnapshotDefParseString(xmlDesc, data->caps,
                                                 data->xmlopt, -1,
@@ -6054,11 +6984,6 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom,
                                                 VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE)))
         goto cleanup;
 
-    if (def->ndisks) {
-        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                       _("disk snapshots not supported yet"));
-        goto cleanup;
-    }
 
     vboxIIDFromUUID(&domiid, dom->uuid);
     rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine);
@@ -6068,6 +6993,16 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom,
         goto cleanup;
     }
 
+#if VBOX_API_VERSION >= 4002000
+    isCurrent = flags & VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT;
+    if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) {
+        if (vboxSnapshotRedefine(dom, def, isCurrent) < 0)
+            goto cleanup;
+        ret = virGetDomainSnapshot(dom, def->name);
+        goto cleanup;
+    }
+#endif
+
     rc = machine->vtbl->GetState(machine, &state);
     if (NS_FAILED(rc)) {
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-- 
1.7.10.4




More information about the libvir-list mailing list