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(¤tSnapshotXmlFilePath, "%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]);
+ if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(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,
+ _("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(¤tSnapshotXmlFilePath, "%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",
--
1.7.10.4