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

John Ferlan jferlan at redhat.com
Wed Jun 11 12:52:34 UTC 2014


Another coverity error as listed in-line below in vboxSnapshotRedefine()


John

On 05/19/2014 08:47 AM, Yohan BELLEGUIC wrote:
> 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 |  976 +++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 970 insertions(+), 6 deletions(-)
> 
> diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
> index 5d4a7ba..a7f15d4 100644
> --- a/src/vbox/vbox_tmpl.c
> +++ b/src/vbox/vbox_tmpl.c
> @@ -43,6 +43,7 @@
>  #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"
> @@ -6015,6 +6016,958 @@ 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,
> +                       _("Unable to open HardDisk, rc=%08x"),
> +                       (unsigned)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) {
> +                virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
> +                               , _("Unable to close disk children"));
> +                goto cleanup;
> +            }
> +            VIR_FREE(childLocation);
> +        }
> +    }
> +    rc = medium->vtbl->Close(medium);
> +    if (NS_FAILED(rc)) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       _("Unable to close HardDisk, rc=%08x"),
> +                       (unsigned)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;
> +    virVBoxSnapshotConfMachinePtr snapshotMachineDesc = NULL;
> +    char *currentSnapshotXmlFilePath = NULL;
> +    PRUnichar *machineNameUtf16 = NULL;
> +    char *machineName = NULL;
> +    char **realReadWriteDisksPath = NULL;
> +    int realReadWriteDisksPathSize = 0;
> +    char **realReadOnlyDisksPath = NULL;
> +    int realReadOnlyDisksPathSize = 0;
> +    virVBoxSnapshotConfSnapshotPtr 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 = virVBoxSnapshotConfLoadVboxFile(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 (virVBoxSnapshotConfRemoveFakeDisks(snapshotMachineDesc) < 0) {
> +            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                           _("Unable to remove Fake Disks"));
> +            goto cleanup;
> +        }
> +        realReadWriteDisksPathSize = virVBoxSnapshotConfGetRWDisksPathsFromLibvirtXML(currentSnapshotXmlFilePath,
> +                                                             &realReadWriteDisksPath);
> +        realReadOnlyDisksPathSize = virVBoxSnapshotConfGetRODisksPathsFromLibvirtXML(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++) {
> +            virVBoxSnapshotConfHardDiskPtr 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,
> +                               _("Unable to open HardDisk, rc=%08x"),
> +                               (unsigned)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 = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc,
> +                                                      realReadOnlyDisksPath[it]);

(35) Event returned_null: 	"virVBoxSnapshotConfHardDiskUuidByLocation"
returns null (checked 4 out of 5 times). [details]
(44) Event var_assigned: 	Assigning: "parentUuid" = null return value
from "virVBoxSnapshotConfHardDiskUuidByLocation".

Looks like a

            if (parentUuid == NULL) {
                VIR_FREE(readWriteDisk);
                goto cleanup;
            }

Will resolve this.


> +            if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(readWriteDisk,
> +                                           snapshotMachineDesc->mediaRegistry,
> +                                           parentUuid) < 0) {

(45) Event dereference: 	Dereferencing a pointer that might be null
"parentUuid" when calling
"virVBoxSnapshotConfAddHardDiskToMediaRegistry". [details]


> +                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,
> +                               _("Unable to close HardDisk, rc=%08x"),
> +                               (unsigned)rc);
> +                goto cleanup;
> +            }
> +        }
> +        /*
> +         * Now we have done this swap, we remove the snapshot xml file from the
> +         * current machine location.
> +         */
> +        if (unlink(currentSnapshotXmlFilePath) < 0) {
> +            virReportSystemError(errno,
> +                                 _("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;
> +        virVBoxSnapshotConfHardDiskPtr readOnlyDisk = NULL;
> +
> +        diskInMediaRegistry = virVBoxSnapshotConfDiskIsInMediaRegistry(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,
> +                           _("Unable to open HardDisk, rc=%08x"),
> +                           (unsigned)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,
> +                           _("Unable to get hard disk id, rc=%08x"),
> +                           (unsigned)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,
> +                           _("Unable to close HardDisk, rc=%08x"),
> +                           (unsigned)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 (virVBoxSnapshotConfAddHardDiskToMediaRegistry(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,
> +                       _("Unable to unregister machine, rc=%08x"),
> +                       (unsigned)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,
> +                                   _("Unable to delete medium, rc=%08x"),
> +                                   (unsigned)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,
> +                                   _("Error while closing medium, rc=%08x"),
> +                                   (unsigned)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 = virVBoxSnapshotConfHardDiskUuidByLocation(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 (virVBoxSnapshotConfAddSnapshotToXmlMachine(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;
> +        virVBoxSnapshotConfHardDiskPtr *hardDiskToOpen = NULL;
> +        size_t hardDiskToOpenSize = 0;
> +
> +        location = def->dom->disks[it]->src.path;
> +        if (!location)
> +            goto cleanup;
> +
> +        hardDiskToOpenSize = virVBoxSnapshotConfDiskListToOpen(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,
> +                               _("Unable to open HardDisk, rc=%08x"),
> +                               (unsigned)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;
> +            virVBoxSnapshotConfHardDiskPtr 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,
> +                               _("Unable to open HardDisk, rc=%08x"),
> +                               (unsigned)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 (virVBoxSnapshotConfAddHardDiskToMediaRegistry(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,
> +                               _("Unable to close HardDisk, rc=%08x"),
> +                               (unsigned)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;
> +            virVBoxSnapshotConfHardDiskPtr 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,
> +                               _("Unable to open HardDisk, rc=%08x"),
> +                               (unsigned)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,
> +                               _("Unable to get hardDisk Id, rc=%08x"),
> +                               (unsigned)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,
> +                               _("Unable to create HardDisk, rc=%08x"),
> +                               (unsigned)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,
> +                               _("Error while creating diff storage, rc=%08x"),
> +                               (unsigned)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,
> +                               _("Unable to get medium uuid, rc=%08x"),
> +                               (unsigned)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 (virVBoxSnapshotConfAddHardDiskToMediaRegistry(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,
> +                               _("Unable to close the new medium, rc=%08x"),
> +                               (unsigned)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);
> +        if (snapshotContent == NULL) {
> +            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                           _("Unable to get snapshot content"));
> +            goto cleanup;
> +        }
> +        if (virFileWriteStr(currentSnapshotXmlFilePath, snapshotContent, 0644) < 0) {
> +            virReportSystemError(errno, "%s",
> +                                 _("Unable to save new snapshot xml file"));
> +            goto cleanup;
> +        }
> +        VIR_FREE(snapshotContent);
> +    }
> +    /*
> +     * 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;
> +
> +        virVBoxSnapshotConfHardDiskPtr *hardDiskToOpen = NULL;
> +        size_t hardDiskToOpenSize = virVBoxSnapshotConfDiskListToOpen(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,
> +                               _("Unable to open HardDisk, rc=%08x"),
> +                               (unsigned)rc);
> +                goto cleanup;
> +            }
> +            rc = medium->vtbl->Close(medium);
> +            if (NS_FAILED(rc)) {
> +                virReportError(VIR_ERR_INTERNAL_ERROR,
> +                               _("Unable to close HardDisk, rc=%08x"),
> +                               (unsigned)rc);
> +                goto cleanup;
> +            }
> +            VBOX_UTF16_FREE(locationUtf16);
> +        }
> +    }
> +
> +    /*Now, we rewrite the 'machineName'.vbox file to redefine the machine.*/
> +    if (virVBoxSnapshotConfSaveVboxFile(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,
> +                       _("Unable to open Machine, rc=%08x"),
> +                       (unsigned)rc);
> +        goto cleanup;
> +    }
> +
> +    rc = data->vboxObj->vtbl->RegisterMachine(data->vboxObj, machine);
> +    if (NS_FAILED(rc)) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       _("Unable to register Machine, rc=%08x"),
> +                       (unsigned)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,
> @@ -6036,9 +6989,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,
> @@ -6046,11 +7005,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);
> @@ -6060,6 +7014,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",
> 




More information about the libvir-list mailing list