The vboxDomainSnapshotCreateXML integrated the snapshot redefine
with this patch:
http://www.redhat.com/archives/libvir-list/2014-May/msg00589.html
This patch introduced vboxSnapshotRedefine in vboxUniformedAPI to
enable the features.
This patch replace all version specified APIs to the uniformed api,
then, moving the whole implementation to vbox_common.c. As there
is only API level changes, the behavior of the function doesn't
change.
Some old version's defects has brought to the new one. The already
known things are:
*goto cleanup in a loop without releasing the pointers in the
loop.
*When function failed after machine unregister, no roll back
to recovery it and the virtual machine would disappear.
---
src/vbox/vbox_common.c | 1069 ++++++++++++++++++++++++++++++++++++++++
src/vbox/vbox_common.h | 18 +
src/vbox/vbox_tmpl.c | 1087 -----------------------------------------
src/vbox/vbox_uniformed_api.h | 5 +
4 files changed, 1092 insertions(+), 1087 deletions(-)
diff --git a/src/vbox/vbox_common.c b/src/vbox/vbox_common.c
index 4e0b990..8f3fe03 100644
--- a/src/vbox/vbox_common.c
+++ b/src/vbox/vbox_common.c
@@ -28,6 +28,10 @@
#include "viralloc.h"
#include "nodeinfo.h"
#include "virstring.h"
+#include "virfile.h"
+#include "virtime.h"
+#include "snapshot_conf.h"
+#include "vbox_snapshot_conf.h"
#include "vbox_common.h"
#include "vbox_uniformed_api.h"
@@ -4300,3 +4304,1068 @@ int vboxDomainDetachDeviceFlags(virDomainPtr dom, const char
*xml,
return vboxDomainDetachDevice(dom, xml);
}
+
+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;
+
+ if (!gVBoxAPI.vboxSnapshotRedefine)
+ VIR_WARN("This function may not work in current version");
+
+ VBOX_UTF8_TO_UTF16(location, &locationUtf);
+ rc = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
+ locationUtf,
+ DeviceType_HardDisk,
+ AccessMode_ReadWrite,
+ &medium);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to open HardDisk, rc=%08x"),
+ (unsigned)rc);
+ goto cleanup;
+ }
+ rc = gVBoxAPI.UIMedium.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 = gVBoxAPI.UIMedium.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 = gVBoxAPI.UIMedium.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);
+ vboxIIDUnion domiid;
+ 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;
+
+ if (!gVBoxAPI.vboxSnapshotRedefine)
+ VIR_WARN("This function may not work in current version");
+
+ if (openSessionForMachine(data, dom->uuid, &domiid, &machine, false) <
0)
+ goto cleanup;
+
+ rc = gVBoxAPI.UIMachine.SaveSettings(machine);
+ /*It may failed when the machine is not mutable.*/
+ rc = gVBoxAPI.UIMachine.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 = gVBoxAPI.UIMachine.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;
+ char *uuid = NULL;
+ PRUnichar *formatUtf = NULL;
+ char *format = NULL;
+ const char *parentUuid = NULL;
+ vboxIIDUnion iid;
+
+ VBOX_IID_INITIALIZE(&iid);
+ VBOX_UTF8_TO_UTF16(realReadWriteDisksPath[it], &locationUtf);
+ rc = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
+ locationUtf,
+ DeviceType_HardDisk,
+ AccessMode_ReadWrite,
+ &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 = gVBoxAPI.UIMedium.GetId(readWriteMedium, &iid);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to get the read write medium id"));
+ goto cleanup;
+ }
+ gVBoxAPI.UIID.vboxIIDToUtf8(data, &iid, &uuid);
+ vboxIIDUnalloc(&iid);
+
+ rc = gVBoxAPI.UIMedium.GetFormat(readWriteMedium, &formatUtf);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to get the read write medium
format"));
+ goto cleanup;
+ }
+ VBOX_UTF16_TO_UTF8(formatUtf, &format);
+ VBOX_UTF16_FREE(formatUtf);
+
+ if (VIR_ALLOC(readWriteDisk) < 0) {
+ 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 (parentUuid == NULL) {
+ VIR_FREE(readWriteDisk);
+ goto cleanup;
+ }
+
+ 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 = gVBoxAPI.UIMedium.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;
+ char *uuid = NULL;
+ PRUnichar *formatUtf = NULL;
+ char *format = NULL;
+ char *parentUuid = NULL;
+ virVBoxSnapshotConfHardDiskPtr readOnlyDisk = NULL;
+ vboxIIDUnion iid, parentiid;
+
+ VBOX_IID_INITIALIZE(&iid);
+ VBOX_IID_INITIALIZE(&parentiid);
+ 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 = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
+ locationUtf,
+ DeviceType_HardDisk,
+ AccessMode_ReadWrite,
+ &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 = gVBoxAPI.UIMedium.GetId(readOnlyMedium, &iid);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to get hard disk id"));
+ goto cleanup;
+ }
+ gVBoxAPI.UIID.vboxIIDToUtf8(data, &iid, &uuid);
+ vboxIIDUnalloc(&iid);
+
+ rc = gVBoxAPI.UIMedium.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 = gVBoxAPI.UIMedium.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 = gVBoxAPI.UIMedium.GetId(parentReadOnlyMedium, &parentiid);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to get hard disk id, rc=%08x"),
+ (unsigned)rc);
+ VIR_FREE(uuid);
+ goto cleanup;
+ }
+ gVBoxAPI.UIID.vboxIIDToUtf8(data, &parentiid, &parentUuid);
+ vboxIIDUnalloc(&parentiid);
+
+ rc = gVBoxAPI.UIMedium.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 = gVBoxAPI.UIMachine.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 = gVBoxAPI.UIMedium.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;
+ resultCodeUnion resultCode;
+ rc = gVBoxAPI.UIMedium.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;
+ }
+ gVBoxAPI.UIProgress.WaitForCompletion(progress, -1);
+ gVBoxAPI.UIProgress.GetResultCode(progress, &resultCode);
+ if (RC_FAILED(resultCode)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Error while closing medium, rc=%08x"),
+ resultCode.uResultCode);
+ 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 = gVBoxAPI.UIMedium.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;
+ const 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 = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
+ locationUtf16,
+ DeviceType_HardDisk,
+ AccessMode_ReadWrite,
+ &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;
+ char *uuid = NULL;
+ IMedium *parentDisk = NULL;
+ char *parentUuid = NULL;
+ vboxIIDUnion iid, parentiid;
+
+ VBOX_IID_INITIALIZE(&iid);
+ VBOX_IID_INITIALIZE(&parentiid);
+ VBOX_UTF8_TO_UTF16(def->disks[it].src->path, &locationUtf16);
+ rc = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
+ locationUtf16,
+ DeviceType_HardDisk,
+ AccessMode_ReadWrite,
+ &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 = gVBoxAPI.UIMedium.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 = gVBoxAPI.UIMedium.GetId(medium, &iid);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to get disk uuid"));
+ VIR_FREE(disk);
+ goto cleanup;
+ }
+ gVBoxAPI.UIID.vboxIIDToUtf8(data, &iid, &uuid);
+ disk->uuid = uuid;
+ vboxIIDUnalloc(&iid);
+
+ rc = gVBoxAPI.UIMedium.GetParent(medium, &parentDisk);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to get disk parent"));
+ VIR_FREE(disk);
+ goto cleanup;
+ }
+
+ gVBoxAPI.UIMedium.GetId(parentDisk, &parentiid);
+ gVBoxAPI.UIID.vboxIIDToUtf8(data, &parentiid, &parentUuid);
+ vboxIIDUnalloc(&parentiid);
+ 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 = gVBoxAPI.UIMedium.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;
+ char *parentUuid = NULL;
+ IMedium *newMedium = NULL;
+ PRUnichar *formatUtf16 = NULL;
+ PRUnichar *newLocation = NULL;
+ char *newLocationUtf8 = NULL;
+ resultCodeUnion resultCode;
+ virVBoxSnapshotConfHardDiskPtr disk = NULL;
+ char *uuid = NULL;
+ char *format = NULL;
+ char **searchResultTab = NULL;
+ ssize_t resultSize = 0;
+ char *tmp = NULL;
+ vboxIIDUnion iid, parentiid;
+
+ VBOX_IID_INITIALIZE(&iid);
+ VBOX_IID_INITIALIZE(&parentiid);
+ VBOX_UTF8_TO_UTF16(def->dom->disks[it]->src->path,
&locationUtf16);
+ rc = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
+ locationUtf16,
+ DeviceType_HardDisk,
+ AccessMode_ReadWrite,
+ &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 = gVBoxAPI.UIMedium.GetId(medium, &parentiid);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to get hardDisk Id, rc=%08x"),
+ (unsigned)rc);
+ goto cleanup;
+ }
+ gVBoxAPI.UIID.vboxIIDToUtf8(data, &parentiid, &parentUuid);
+ vboxIIDUnalloc(&parentiid);
+ VBOX_UTF8_TO_UTF16("VDI", &formatUtf16);
+
+ if (virAsprintf(&newLocationUtf8, "%sfakedisk-%d.vdi",
machineLocationPath, it) < 0)
+ goto cleanup;
+ VBOX_UTF8_TO_UTF16(newLocationUtf8, &newLocation);
+ rc = gVBoxAPI.UIVirtualBox.CreateHardDiskMedium(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;
+ PRUint32 tab[1];
+ tab[0] = MediumVariant_Diff;
+ gVBoxAPI.UIMedium.CreateDiffStorage(medium, newMedium, 1, tab,
&progress);
+
+ gVBoxAPI.UIProgress.WaitForCompletion(progress, -1);
+ gVBoxAPI.UIProgress.GetResultCode(progress, &resultCode);
+ if (RC_FAILED(resultCode)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Error while creating diff storage,
rc=%08x"),
+ resultCode.uResultCode);
+ 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 = gVBoxAPI.UIMedium.GetId(newMedium, &iid);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to get medium uuid, rc=%08x"),
+ (unsigned)rc);
+ goto cleanup;
+ }
+ gVBoxAPI.UIID.vboxIIDToUtf8(data, &iid, &uuid);
+ disk->uuid = uuid;
+ vboxIIDUnalloc(&iid);
+
+ if (VIR_STRDUP(disk->location, newLocationUtf8) < 0)
+ goto cleanup;
+
+ rc = gVBoxAPI.UIMedium.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 = gVBoxAPI.UIMedium.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 = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
+ locationUtf16,
+ DeviceType_HardDisk,
+ AccessMode_ReadWrite,
+ &medium);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to open HardDisk, rc=%08x"),
+ (unsigned)rc);
+ goto cleanup;
+ }
+ rc = gVBoxAPI.UIMedium.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 = gVBoxAPI.UIVirtualBox.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 = gVBoxAPI.UIVirtualBox.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;
+}
+
+virDomainSnapshotPtr
+vboxDomainSnapshotCreateXML(virDomainPtr dom,
+ const char *xmlDesc,
+ unsigned int flags)
+{
+ VBOX_OBJECT_CHECK(dom->conn, virDomainSnapshotPtr, NULL);
+ virDomainSnapshotDefPtr def = NULL;
+ vboxIIDUnion domiid;
+ IMachine *machine = NULL;
+ IConsole *console = NULL;
+ IProgress *progress = NULL;
+ ISnapshot *snapshot = NULL;
+ PRUnichar *name = NULL;
+ PRUnichar *description = NULL;
+ PRUint32 state;
+ nsresult rc;
+ resultCodeUnion result;
+
+ VBOX_IID_INITIALIZE(&domiid);
+ /* VBox has no snapshot metadata, so this flag is trivial. */
+ 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,
+ VIR_DOMAIN_SNAPSHOT_PARSE_DISKS |
+ VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE)))
+ goto cleanup;
+
+
+ if (openSessionForMachine(data, dom->uuid, &domiid, &machine, false) <
0)
+ goto cleanup;
+
+ if (gVBoxAPI.vboxSnapshotRedefine) {
+ PRBool 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;
+ }
+ }
+
+ rc = gVBoxAPI.UIMachine.GetState(machine, &state);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("could not get domain state"));
+ goto cleanup;
+ }
+
+ if (gVBoxAPI.machineStateChecker.Online(state)) {
+ rc = gVBoxAPI.UISession.OpenExisting(data, &domiid, machine);
+ } else {
+ rc = gVBoxAPI.UISession.Open(data, &domiid, machine);
+ }
+
+ if (NS_SUCCEEDED(rc))
+ rc = gVBoxAPI.UISession.GetConsole(data->vboxSession, &console);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("could not open VirtualBox session with domain %s"),
+ dom->name);
+ goto cleanup;
+ }
+
+ VBOX_UTF8_TO_UTF16(def->name, &name);
+ if (!name) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (def->description) {
+ VBOX_UTF8_TO_UTF16(def->description, &description);
+ if (!description) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ }
+
+ rc = gVBoxAPI.UIConsole.TakeSnapshot(console, name, description, &progress);
+ if (NS_FAILED(rc) || !progress) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("could not take snapshot of domain %s"),
dom->name);
+ goto cleanup;
+ }
+
+ gVBoxAPI.UIProgress.WaitForCompletion(progress, -1);
+ gVBoxAPI.UIProgress.GetResultCode(progress, &result);
+ if (RC_FAILED(result)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("could not take snapshot of domain %s"),
dom->name);
+ goto cleanup;
+ }
+
+ rc = gVBoxAPI.UIMachine.GetCurrentSnapshot(machine, &snapshot);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("could not get current snapshot of domain %s"),
+ dom->name);
+ goto cleanup;
+ }
+
+ ret = virGetDomainSnapshot(dom, def->name);
+
+ cleanup:
+ VBOX_RELEASE(progress);
+ VBOX_UTF16_FREE(description);
+ VBOX_UTF16_FREE(name);
+ VBOX_RELEASE(console);
+ gVBoxAPI.UISession.Close(data->vboxSession);
+ VBOX_RELEASE(machine);
+ vboxIIDUnalloc(&domiid);
+ virDomainSnapshotDefFree(def);
+ return ret;
+}
diff --git a/src/vbox/vbox_common.h b/src/vbox/vbox_common.h
index a093481..1c01fe8 100644
--- a/src/vbox/vbox_common.h
+++ b/src/vbox/vbox_common.h
@@ -247,6 +247,24 @@ enum MediumType
MediumType_Writethrough = 2,
};
+enum CleanupMode
+{
+ CleanupMode_UnregisterOnly = 1,
+ CleanupMode_DetachAllReturnNone = 2,
+ CleanupMode_DetachAllReturnHardDisksOnly = 3,
+ CleanupMode_Full = 4
+};
+
+enum MediumVariant
+{
+ MediumVariant_Standard = 0,
+ MediumVariant_VmdkSplit2G = 0x01,
+ MediumVariant_VmdkStreamOptimized = 0x04,
+ MediumVariant_VmdkESX = 0x08,
+ MediumVariant_Fixed = 0x10000,
+ MediumVariant_Diff = 0x20000
+};
+
/* Simplied definitions in vbox_CAPI_*.h */
typedef void const *PCVBOXXPCOM;
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
index 2d5ea5c..2218baf 100644
--- a/src/vbox/vbox_tmpl.c
+++ b/src/vbox/vbox_tmpl.c
@@ -1588,1093 +1588,6 @@ 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;
- const 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 (parentUuid == NULL) {
- VIR_FREE(readWriteDisk);
- goto cleanup;
- }
-
- 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;
- const 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,
- unsigned int flags)
-{
- VBOX_OBJECT_CHECK(dom->conn, virDomainSnapshotPtr, NULL);
- virDomainSnapshotDefPtr def = NULL;
- vboxIID domiid = VBOX_IID_INITIALIZER;
- IMachine *machine = NULL;
- IConsole *console = NULL;
- IProgress *progress = NULL;
- ISnapshot *snapshot = NULL;
- PRUnichar *name = NULL;
- PRUnichar *description = NULL;
- PRUint32 state;
- nsresult rc;
-#if VBOX_API_VERSION == 2002000
- nsresult result;
-#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 |
- VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE |
- VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT, NULL);
-
- if (!(def = virDomainSnapshotDefParseString(xmlDesc, data->caps,
- data->xmlopt, -1,
- VIR_DOMAIN_SNAPSHOT_PARSE_DISKS |
- VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE)))
- goto cleanup;
-
-
- 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;
- }
-
-#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",
- _("could not get domain state"));
- goto cleanup;
- }
-
- if ((state >= MachineState_FirstOnline)
- && (state <= MachineState_LastOnline)) {
- rc = VBOX_SESSION_OPEN_EXISTING(domiid.value, machine);
- } else {
- rc = VBOX_SESSION_OPEN(domiid.value, machine);
- }
-
- if (NS_SUCCEEDED(rc))
- rc = data->vboxSession->vtbl->GetConsole(data->vboxSession,
&console);
- if (NS_FAILED(rc)) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("could not open VirtualBox session with domain %s"),
- dom->name);
- goto cleanup;
- }
-
- VBOX_UTF8_TO_UTF16(def->name, &name);
- if (!name) {
- virReportOOMError();
- goto cleanup;
- }
-
- if (def->description) {
- VBOX_UTF8_TO_UTF16(def->description, &description);
- if (!description) {
- virReportOOMError();
- goto cleanup;
- }
- }
-
- rc = console->vtbl->TakeSnapshot(console, name, description, &progress);
- if (NS_FAILED(rc) || !progress) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("could not take snapshot of domain %s"),
dom->name);
- goto cleanup;
- }
-
- progress->vtbl->WaitForCompletion(progress, -1);
- progress->vtbl->GetResultCode(progress, &result);
- if (NS_FAILED(result)) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("could not take snapshot of domain %s"),
dom->name);
- goto cleanup;
- }
-
- rc = machine->vtbl->GetCurrentSnapshot(machine, &snapshot);
- if (NS_FAILED(rc)) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("could not get current snapshot of domain %s"),
- dom->name);
- goto cleanup;
- }
-
- ret = virGetDomainSnapshot(dom, def->name);
-
- cleanup:
- VBOX_RELEASE(progress);
- VBOX_UTF16_FREE(description);
- VBOX_UTF16_FREE(name);
- VBOX_RELEASE(console);
- VBOX_SESSION_CLOSE();
- VBOX_RELEASE(machine);
- vboxIIDUnalloc(&domiid);
- virDomainSnapshotDefFree(def);
- return ret;
-}
-
#if VBOX_API_VERSION >=4002000
static
int vboxSnapshotGetReadWriteDisks(virDomainSnapshotDefPtr def,
diff --git a/src/vbox/vbox_uniformed_api.h b/src/vbox/vbox_uniformed_api.h
index b36e154..8357df5 100644
--- a/src/vbox/vbox_uniformed_api.h
+++ b/src/vbox/vbox_uniformed_api.h
@@ -552,6 +552,11 @@ int vboxDomainUpdateDeviceFlags(virDomainPtr dom, const char *xml,
int vboxDomainDetachDevice(virDomainPtr dom, const char *xml);
int vboxDomainDetachDeviceFlags(virDomainPtr dom, const char *xml,
unsigned int flags);
+virDomainSnapshotPtr
+vboxDomainSnapshotCreateXML(virDomainPtr dom,
+ const char *xmlDesc,
+ unsigned int flags);
+
/* Version specified functions for installing uniformed API */
void vbox22InstallUniformedAPI(vboxUniformedAPI *pVBoxAPI);
--
1.7.9.5