The snapshots are saved in xml files, and then can be redefined
---
src/vbox/vbox_tmpl.c | 1083 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 1075 insertions(+), 8 deletions(-)
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
index 98724c1..a43a875 100644
--- a/src/vbox/vbox_tmpl.c
+++ b/src/vbox/vbox_tmpl.c
@@ -61,6 +61,7 @@
#include "virstring.h"
#include "virtime.h"
#include "virutil.h"
+#include "dirname.h"
/* This one changes from version to version. */
#if VBOX_API_VERSION == 2002
@@ -276,9 +277,19 @@ static vboxGlobalData *g_pVBoxGlobalData = NULL;
#endif /* VBOX_API_VERSION >= 4000 */
+/*This error is a bit specific
+ *In the VBOX API it is named E_ACCESSDENIED
+ *It is returned when the called object is not ready. In
+ *particular when we do any call on a disk which has been closed
+*/
+#define VBOX_E_ACCESSDENIED 0x80070005
static virDomainPtr vboxDomainDefineXML(virConnectPtr conn, const char *xml);
static int vboxDomainCreate(virDomainPtr dom);
static int vboxDomainUndefineFlags(virDomainPtr dom, unsigned int flags);
+static virStorageVolPtr vboxStorageVolLookupByPath(virConnectPtr conn, const char
*path);
+static int vboxStorageDeleteOrClose(virStorageVolPtr vol,
+ unsigned int flags,
+ unsigned int flagDeleteOrClose);
static void vboxDriverLock(vboxGlobalData *data) {
virMutexLock(&data->lock);
@@ -5951,6 +5962,1055 @@ cleanup:
return snapshot;
}
+#if VBOX_API_VERSION >=4002
+static void
+vboxSnapshotXmlRetrieveSnapshotNodeByName(xmlNodePtr a_node,
+ const char *name,
+ xmlNodePtr *snap_node)
+{
+ xmlNodePtr cur_node = NULL;
+ char *xmlName;
+ for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
+ if (cur_node->type == XML_ELEMENT_NODE) {
+ if (!xmlStrcmp(cur_node->name, (const xmlChar *) "Snapshot")) {
+ if ((xmlName = virXMLPropString(cur_node, "name"))) {
+ if (STREQ(xmlName, name)) {
+ VIR_FREE(xmlName);
+ *snap_node = cur_node;
+ return;
+ }
+ VIR_FREE(xmlName);
+ }
+ }
+ }
+ if (cur_node->children)
+ vboxSnapshotXmlRetrieveSnapshotNodeByName(cur_node->children, name,
+ snap_node);
+ }
+}
+
+static int
+vboxDetachAndCloseDisks(virDomainPtr dom,
+ IMedium *disk)
+{
+ VBOX_OBJECT_CHECK(dom->conn, int, -1);
+ nsresult rc;
+ PRUnichar *location = NULL;
+ vboxArray childrenDiskArray = VBOX_ARRAY_INITIALIZER;
+ virStorageVolPtr volPtr = NULL;
+ char *location_utf8 = NULL;
+ PRUint32 dummyState = 0;
+ size_t i = 0;
+ if (disk == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("null pointer to disk"));
+ goto cleanup;
+ }
+ rc = disk->vtbl->GetLocation(disk, &location);
+ if (rc == VBOX_E_ACCESSDENIED) {
+ ret = 0;
+ VIR_DEBUG("Disk already closed");
+ goto cleanup;
+ }
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot get disk location"));
+ goto cleanup;
+ }
+ rc = vboxArrayGet(&childrenDiskArray, disk, disk->vtbl->GetChildren);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot get children disks"));
+ goto cleanup;
+ }
+ for (i = 0; i < childrenDiskArray.count; ++i) {
+ IMedium *childDisk = childrenDiskArray.items[i];
+ if (childDisk) {
+ vboxDetachAndCloseDisks(dom, childDisk);
+ }
+ }
+ rc = disk->vtbl->RefreshState(disk, &dummyState);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot refresh state"));
+ goto cleanup;
+ }
+ VBOX_UTF16_TO_UTF8(location, &location_utf8);
+ volPtr = vboxStorageVolLookupByPath(dom->conn, location_utf8);
+
+ if (volPtr) {
+ VIR_DEBUG("Closing %s", location_utf8);
+ if (vboxStorageDeleteOrClose(volPtr, 0, VBOX_STORAGE_CLOSE_FLAG) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("error while closing disk"));
+ goto cleanup;
+ }
+ ret = 0;
+ }
+cleanup:
+ VBOX_UTF8_FREE(location_utf8);
+ VBOX_UTF16_FREE(location);
+ vboxArrayRelease(&childrenDiskArray);
+ return ret;
+}
+
+static void
+vboxSnapshotXmlAddChild(xmlNodePtr parent,
+ xmlNodePtr child)
+{
+ /*Used in order to add child without writing the stuff concerning xml namespaces*/
+ xmlBufferPtr tmpBuf = xmlBufferCreate();
+ char *tmpString = NULL;
+ xmlNodePtr tmpNode = NULL;
+ xmlNodeDump(tmpBuf, parent->doc, child, 0, 0);
+ if (VIR_STRDUP(tmpString, (char *)xmlBufferContent(tmpBuf)) >= 0) {
+ xmlParseInNodeContext(parent, tmpString, (int)strlen(tmpString), 0,
&tmpNode);
+ if (tmpNode) {
+ if (xmlAddChild(parent, xmlCopyNode(tmpNode, 1)) == NULL) {
+ VIR_DEBUG("Error while adding %s to %s",
+ (char *)tmpNode->name,
+ (char *)parent->name);
+ }
+ }
+ }
+ xmlFree(tmpNode);
+ xmlBufferFree(tmpBuf);
+}
+
+static void
+vboxSnapshotXmlRetrieveMachineNode(xmlNodePtr root,
+ xmlNodePtr *machineNode)
+{
+ xmlNodePtr cur = root->xmlChildrenNode;
+ while (cur && xmlIsBlankNode(cur)) {
+ cur = cur -> next;
+ }
+ if (xmlStrcmp(cur->name, (const xmlChar *) "Machine")) {
+ VIR_DEBUG("Problem, found %s, Machine expected", (char
*)cur->name);
+ return;
+ }
+ *machineNode = cur;
+}
+
+static void
+vboxSnapshotXmlRetrieveSnapshotsNode(xmlNodePtr snapshotNode,
+ xmlNodePtr *snapshotsNode)
+{
+ xmlNodePtr cur_node = NULL;
+
+ for (cur_node = snapshotNode; cur_node; cur_node = cur_node->next) {
+ if (cur_node->type == XML_ELEMENT_NODE) {
+ if (!xmlStrcmp(cur_node->name, (const xmlChar *) "Snapshots"))
{
+ *snapshotsNode = cur_node;
+ return;
+ }
+ }
+ if (cur_node->children)
+ vboxSnapshotXmlRetrieveSnapshotsNode(cur_node->children, snapshotsNode);
+ }
+}
+
+static void
+vboxSnapshotXmlRetrieveRootNodeByName(xmlNodePtr root,
+ const char *name,
+ xmlNodePtr *returnNode)
+{
+ xmlNodePtr cur = root->xmlChildrenNode;
+ while (cur && xmlIsBlankNode(cur)) {
+ cur = cur -> next;
+ }
+ if (xmlStrcmp(cur->name, (const xmlChar *) "Machine")) {
+ VIR_DEBUG("Problem, found %s, Machine expected", (char
*)cur->name);
+ }
+ cur=cur->xmlChildrenNode;
+ while (cur != NULL) {
+ if (!xmlStrcmp(cur->name, (const xmlChar*) name)) {
+ *returnNode = cur;
+ return;
+ }
+ cur = cur -> next;
+ }
+}
+
+static void
+vboxSnapshotXmlAppendDiskToMediaRegistry(xmlNodePtr *inMediaRegistry,
+ char *parentId,
+ char *childId,
+ char *childLocation,
+ char *childFormat)
+{
+ /*This function will modify the inMediaregistry node to append
+ * all the informations about the child disk
+ */
+ xmlNodePtr cur_node = NULL;
+ for (cur_node = *inMediaRegistry; cur_node; cur_node = cur_node->next) {
+ if (cur_node) {
+ if (cur_node->type == XML_ELEMENT_NODE
+ && !xmlStrcmp(cur_node->name, (const xmlChar *)
"HardDisk")
+ && xmlHasProp(cur_node, BAD_CAST "uuid") != NULL
+ && STREQ((char *)xmlHasProp(cur_node, BAD_CAST
"uuid")->children->content,
+ parentId)) {
+
+ xmlNodePtr childDiskNode = xmlNewTextChild(cur_node,
+ NULL,
+ BAD_CAST
"HardDisk",
+ NULL);
+ xmlNewProp(childDiskNode, BAD_CAST "uuid", BAD_CAST childId);
+ xmlNewProp(childDiskNode, BAD_CAST "location", BAD_CAST
childLocation);
+ xmlNewProp(childDiskNode, BAD_CAST "format", BAD_CAST
childFormat);
+ return;
+ }
+ }
+ if (&(cur_node->children))
+ vboxSnapshotXmlAppendDiskToMediaRegistry(&(cur_node->children),
+ parentId,
+ childId,
+ childLocation,
+ childFormat);
+
+ }
+}
+
+static int
+vboxSnapshotGenerateVboxXML(xmlNodePtr rootElementVboxXML,
+ char *storageControllerString,
+ char *parent,
+ char *defName,
+ char *timeStamp,
+ char *uuid,
+ xmlNodePtr *snapshotNodeReturned)
+{
+ xmlDocPtr doc;
+ xmlNodePtr snapshotNode;
+ xmlNodePtr snapshotsNode;
+ xmlNodePtr machineHardwareNode = NULL;
+ char *uuidstrWithBrackets = NULL;
+ char *timeVboxFormat = NULL;
+ int ret = -1;
+ /* We change the date format from "yyyy-MM-dd hh:mm:ss.msec+timeZone"
+ * to "yyyy-MM-ddThh:mm:ssZ"
+ */
+ char **splittedOnPlus = virStringSplit(timeStamp, "+", 0);
+ const char **splittedOnSpace = (const char**)virStringSplit(splittedOnPlus[0],
+ " ",
+ 0);
+ char *joinWithT = virStringJoin(splittedOnSpace, "T");
+ char *date = virStringSplit(joinWithT, ".", 0)[0];
+ VIR_FREE(splittedOnPlus);
+ VIR_FREE(splittedOnSpace);
+ VIR_FREE(joinWithT);
+
+ /*Retrieve hardware*/
+ vboxSnapshotXmlRetrieveRootNodeByName(rootElementVboxXML, "Hardware",
+ &machineHardwareNode);
+ /* Create a new XML DOM tree, to which the XML document will be
+ * written */
+ doc = xmlNewDoc(BAD_CAST XML_DEFAULT_VERSION);
+ if (doc == NULL) {
+ VIR_DEBUG("Error creating the xml document tree\n");
+ ret = -1;
+ goto cleanup;
+ }
+ /* Create a new XML node, to which the XML document will be
+ * appended */
+ snapshotNode = xmlNewDocNode(doc, NULL, BAD_CAST "Snapshot", NULL);
+ if (snapshotNode == NULL) {
+ VIR_DEBUG("Error creating the xml node Snapshot\n");
+ ret = -1;
+ goto cleanup;
+ }
+ if (virAsprintf(&uuidstrWithBrackets, "{%s}", uuid) != -1) {
+ xmlNewProp(snapshotNode, BAD_CAST "uuid", BAD_CAST
uuidstrWithBrackets);
+ VIR_FREE(uuidstrWithBrackets);
+ }
+ xmlNewProp(snapshotNode, BAD_CAST "name", BAD_CAST defName);
+ if (virAsprintf(&timeVboxFormat, "%sZ", date) != -1) {
+ xmlNewProp(snapshotNode, BAD_CAST "timeStamp", BAD_CAST
timeVboxFormat);
+ VIR_FREE(timeVboxFormat);
+ }
+ xmlDocSetRootElement(doc, snapshotNode);
+ if (machineHardwareNode) {
+ vboxSnapshotXmlAddChild(snapshotNode, machineHardwareNode);
+ }
+ xmlNodePtr listStorageControllerNodes;
+ xmlParseInNodeContext(snapshotNode, storageControllerString,
+ (int)strlen(storageControllerString),
+ 0,
+ &listStorageControllerNodes);
+ if (listStorageControllerNodes) {
+ if (xmlAddChild(snapshotNode, xmlCopyNode(listStorageControllerNodes, 1)) ==
NULL) {
+ VIR_DEBUG("Could not add listStorageControllerNodes node to snapshot
node");
+ ret = -1;
+ goto cleanup;
+ }
+ }
+ snapshotsNode = xmlNewDocNode(doc, NULL, BAD_CAST "Snapshots", NULL);
+ if (snapshotsNode == NULL) {
+ VIR_DEBUG("Error creating the xml node Snapshots\n");
+ ret = -1;
+ goto cleanup;
+ }
+ if (snapshotsNode) {
+ if (xmlAddChild(snapshotNode, xmlCopyNode(snapshotsNode, 1)) == NULL) {
+ VIR_DEBUG("Could not add snapshotsNode node to snapshot node");
+ ret = -1;
+ goto cleanup;
+ }
+ }
+ if (parent) {
+ xmlNodePtr rootSnapshotNode = NULL;
+ xmlNodePtr parentNode = NULL;
+ xmlNodePtr parentSnapshotsNode = NULL;
+ vboxSnapshotXmlRetrieveRootNodeByName(rootElementVboxXML,
+ "Snapshot",
+ &rootSnapshotNode);
+ if (!rootSnapshotNode) {
+ VIR_DEBUG("Could not retrieve Snapshot node");
+ ret = -1;
+ goto cleanup;
+ }
+ vboxSnapshotXmlRetrieveSnapshotNodeByName(rootSnapshotNode,
+ parent,
+ &parentNode);
+ if (!parentNode) {
+ VIR_DEBUG("Could not retrieve Snapshot node of %s", parent);
+ ret = -1;
+ goto cleanup;
+ }
+ vboxSnapshotXmlRetrieveSnapshotsNode(parentNode->children,
+ &parentSnapshotsNode);
+ if (!parentSnapshotsNode) {
+ VIR_DEBUG("Could not retrieve Snapshots node");
+ ret = -1;
+ goto cleanup;
+ }
+ if (xmlAddChild(parentSnapshotsNode, xmlCopyNode(snapshotNode, 1)) == NULL) {
+ VIR_DEBUG("Could not add snapshotsNode node to its parent");
+ ret = -1;
+ goto cleanup;
+ } else {
+ *snapshotNodeReturned = xmlCopyNode(rootSnapshotNode, 1);
+ }
+ } else {
+ *snapshotNodeReturned = xmlCopyNode(snapshotNode, 1);
+ }
+ ret = 0;
+cleanup:
+ return ret;
+}
+
+static xmlBufferPtr
+vboxSnapshotGenerateXmlSkeleton(char *domainUuid,
+ char *machineName,
+ char *snapshotUuid,
+ char *snapshotFolder,
+ PRInt64 lastStateChange,
+ bool isCurrent,
+ char *savedCurrentSnapshotUuid)
+{
+ char *snapshotUuidWithBrackets = NULL;
+ char *lastStateChangeStr = NULL;
+ /*Let's write a new xml
+ */
+ xmlTextWriterPtr writer;
+ xmlBufferPtr buf = xmlBufferCreate();
+ xmlBufferPtr ret = NULL;
+ int resultXml;
+ char *uuidWithBracket = NULL;
+
+ /* Create a new XmlWriter with no compression. */
+ writer = xmlNewTextWriterMemory(buf, 0);
+ if (writer == NULL) {
+ VIR_DEBUG("Error creating the xml writer\n");
+ goto cleanup;
+ }
+
+ /* Start the document with the xml default for the version,
+ * encoding ISO 8859-1 and the default for the standalone
+ * declaration. */
+ resultXml = xmlTextWriterStartDocument(writer, NULL, "ISO-8859-1", NULL);
+ if (resultXml < 0) {
+ VIR_DEBUG("Error at xmlTextWriterStartDocument\n");
+ goto cleanup;
+ }
+ /* Write a comment as child of root.
+ * Please observe, that the input to the xmlTextWriter functions
+ * HAS to be in UTF-8, even if the output XML is encoded
+ * in iso-8859-1 */
+ resultXml = xmlTextWriterWriteFormatComment(writer,
+ "WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
\n"
+ "OVERWRITTEN AND LOST.\n"
+ "Changes to this xml configuration should be made using Virtualbox \n"
+ "or other application using the libvirt API");
+
+ if (resultXml < 0) {
+ VIR_DEBUG("Error at xmlTextWriterWriteComment\n");
+ goto cleanup;
+ }
+ xmlTextWriterWriteRaw(writer, BAD_CAST "\n"); /*Break line after comment*/
+
+ /* Start an element named "VirtualBox". Since thist is the first
+ * element, this will be the root element of the document. */
+ resultXml = xmlTextWriterStartElement(writer, BAD_CAST "VirtualBox");
+ if (resultXml < 0) {
+ VIR_DEBUG("Error creating the xml node\n");
+ goto cleanup;
+ }
+ resultXml = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "version",
+ BAD_CAST "1.12-linux");
+ if (resultXml < 0) {
+ VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+ goto cleanup;
+ }
+ /* Start an element named "Machine" as child of VirtualBox. */
+ resultXml = xmlTextWriterStartElement(writer, BAD_CAST "Machine");
+ if (resultXml < 0) {
+ VIR_DEBUG("Error at xmlTextWriterStartElement\n");
+ goto cleanup;
+ }
+ /* Add an attribute with name "uuid" and value "{uuid}" to
Machine. */
+ if (virAsprintf(&uuidWithBracket, "{%s}", domainUuid) != -1) {
+ resultXml = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "uuid",
+ BAD_CAST uuidWithBracket);
+ if (resultXml < 0) {
+ VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+ goto cleanup;
+ }
+ VIR_FREE(uuidWithBracket);
+ }
+
+ if (!machineName) {
+ VIR_DEBUG("No machine name");
+ goto cleanup;
+ }
+ resultXml = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "name",
+ BAD_CAST machineName);
+ if (resultXml < 0) {
+ VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+ goto cleanup;
+ }
+ resultXml = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "OSType",
+ BAD_CAST "Other");
+ if (resultXml < 0) {
+ VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+ goto cleanup;
+ }
+
+ if (!snapshotFolder) {
+ VIR_DEBUG("No machine snapshotFolder");
+ goto cleanup;
+ }
+ resultXml = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "snapshotFolder",
+ BAD_CAST snapshotFolder);
+ if (resultXml < 0) {
+ VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+ goto cleanup;
+ }
+ if (virAsprintf(&lastStateChangeStr, "%ld", lastStateChange) < 0) {
+ VIR_DEBUG("Could not convert lastStateChange from long to string");
+ goto cleanup;
+ }
+ resultXml = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "lastStateChange",
+ BAD_CAST lastStateChangeStr);
+ if (resultXml < 0) {
+ VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+ goto cleanup;
+ }
+ /* Current Snapshot
+ *
https://www.virtualbox.org/sdkref/interface_i_machine.html#ac785dbe04eccc...
+ * There is always a current snapshot, except when there is no snapshot (obvious)
+ * or when they have all been deleted (obvious) or when the current snapshot
+ * is deleted and it does not have a parent (not so obvious).
+ *
+ * This can happen only when the last snapshot is deleted because there can
+ * only be one snapshot with no parent, the first one.
+ *
+ * For the moment we always put the snapshot we are defining as current snapshot.
+ * When all the snapshots are redefined, you can always set the current snapshot
+ * to one you have previously saved.
+ */
+ if (isCurrent) {
+ if (virAsprintf(&snapshotUuidWithBrackets, "{%s}", snapshotUuid) !=
-1) {
+ resultXml = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST
"currentSnapshot",
+ BAD_CAST snapshotUuidWithBrackets);
+ if (resultXml < 0) {
+ VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+ goto cleanup;
+ }
+ VIR_FREE(snapshotUuidWithBrackets);
+ }
+ }
+ if (savedCurrentSnapshotUuid != NULL) {
+ if (virAsprintf(&snapshotUuidWithBrackets, "{%s}",
savedCurrentSnapshotUuid) != -1) {
+ resultXml = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST
"currentSnapshot",
+ BAD_CAST snapshotUuidWithBrackets);
+ if (resultXml < 0) {
+ VIR_DEBUG("Error at xmlTextWriterWriteAttribute\n");
+ goto cleanup;
+ }
+ VIR_FREE(snapshotUuidWithBrackets);
+ }
+ }
+
+ /* Here we could close the elements Machine and VirtualBox using the
+ * function xmlTextWriterEndElement, but since we do not want to
+ * write any other elements, we simply call xmlTextWriterEndDocument,
+ * which will do all the work. */
+ resultXml = xmlTextWriterEndDocument(writer);
+ if (resultXml < 0) {
+ VIR_DEBUG("Error at xmlTextWriterEndDocument\n");
+ goto cleanup;
+ }
+ xmlFreeTextWriter(writer);
+ /* Now we have a file containing the skeleton for the xml we want to build
+ * It should look like this:
+ * <?xml version="1.0" encoding="ISO-8859-1"?>
+ * <!--comment
+ * -->
+ * <VirtualBox>
+ * <Machine uuid="{anUuid}" name="the name"
OSType="Other"
+ * snapshotFolder="/path/to/SnapshotFolder"
+ * lastStateChange="a date"/>
+ * </VirtualBox>
+ */
+ ret = buf;
+cleanup:
+ 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.
+ *
+ */
+
+ VBOX_OBJECT_CHECK(dom->conn, int, 0);
+ vboxIID domiid = VBOX_IID_INITIALIZER;
+ IMachine *machine = NULL;
+ nsresult rc;
+ PRUnichar *settingsFilePath = NULL;
+ char *settingsFilePath_Utf8 = NULL;
+ size_t it = 0;
+
+ IMachine *newMachine = NULL;
+ xmlDocPtr xml = NULL;
+ bool snapshotHasParents = !(def->parent == NULL);
+
+ 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"));
+ ret = -1;
+ goto cleanup;
+ }
+
+
+ rc = machine->vtbl->SaveSettings(machine);
+ 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);
+ xml = virXMLParse(settingsFilePath_Utf8, NULL, NULL);
+ if (xml) {
+ xmlNodePtr cur = NULL;
+ xmlNodePtr machineHardwareNode = NULL;
+ xmlNodePtr extraDataNode = NULL;
+ xmlNodePtr mediaRegistryNodeOriginal = NULL;
+ xmlNodePtr snapshotStorageControllersNode = NULL;
+ xmlNodePtr modifiedMachineStorageControllersNode = NULL;
+ char *newMachineStorageControllersString = NULL;
+ char *newSnapshotStorageControllersString = NULL;
+ char snapshotuuidstr[VIR_UUID_STRING_BUFLEN];
+ unsigned char snapshotUuid[VIR_UUID_BUFLEN];
+
+ char *domainUuid = NULL;
+ PRUnichar *machineNameUtf16 = NULL;
+ char *machineName = NULL;
+ PRUnichar *snapshotFolderUtf16 = NULL;
+ PRInt64 lastStateChange = 0;
+ ISnapshot *savedCurrentSnapshot = NULL;
+ vboxIID savedCurrentSnapshotIid = VBOX_IID_INITIALIZER;
+ char *savedCurrentSnapshotUuid = NULL;
+ cur = xmlDocGetRootElement(xml);
+ xmlBufferPtr buf = xmlBufferCreate();
+ if (cur == NULL) {
+ VIR_DEBUG("empty document\n");
+ ret = -1;
+ goto cleanup;
+ }
+ /*Retrieve hardware*/
+ vboxSnapshotXmlRetrieveRootNodeByName(cur, "Hardware",
&machineHardwareNode);
+ if (!machineHardwareNode) {
+ VIR_DEBUG("Could not retrieve Hardware node");
+ ret = -1;
+ goto cleanup;
+ }
+ /*Retrieve storageController*/
+ buf = xmlBufferCreate();
+ vboxSnapshotXmlRetrieveRootNodeByName(cur,
+ "StorageControllers",
+ &snapshotStorageControllersNode);
+ if (!snapshotStorageControllersNode) {
+ VIR_DEBUG("Could not retrieve StorageControllers node");
+ ret = -1;
+ goto cleanup;
+ }
+ xmlNodeDump(buf, xml, snapshotStorageControllersNode, 0, 0);
+ ignore_value(VIR_STRDUP(newMachineStorageControllersString,
+ (char *)xmlBufferContent(buf)));
+ ignore_value(VIR_STRDUP(newSnapshotStorageControllersString,
+ newMachineStorageControllersString));
+ xmlBufferFree(buf);
+ /*Retrieve ExtraData*/
+ vboxSnapshotXmlRetrieveRootNodeByName(cur, "ExtraData",
&extraDataNode);
+ if (!extraDataNode) {
+ VIR_DEBUG("Could not retrieve ExtraData node");
+ ret = -1;
+ goto cleanup;
+ }
+ /*Retrieve MediaRegistry*/
+ vboxSnapshotXmlRetrieveRootNodeByName(cur,
+ "MediaRegistry",
+ &mediaRegistryNodeOriginal);
+ if (!mediaRegistryNodeOriginal) {
+ VIR_DEBUG("Could not retrieve MediaRegistry node");
+ ret = -1;
+ goto cleanup;
+ }
+ vboxArray mediumAttachments = VBOX_ARRAY_INITIALIZER;
+ rc = vboxArrayGet(&mediumAttachments,
+ machine,
+ machine->vtbl->GetMediumAttachments);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot get medium attachments"));
+ goto cleanup;
+ }
+
+ /*Read only disks*/
+ for (it = 0; it<def->dom->ndisks; ++it) {
+ IMedium *mediumReadOnly = NULL;
+ PRUnichar *locationReadOnlyDiskUtf16 = NULL;
+ PRUnichar *mediumReadOnlyFormatUtf16 = NULL;
+ PRUnichar *mediumReadOnlyIdUtf16 = NULL;
+ char *mediumReadOnlyFormat = NULL;
+ char *mediumReadOnlyId = NULL;
+ char *mediaRegistryNodeOriginalString = NULL;
+ VBOX_UTF8_TO_UTF16(def->dom->disks[it]->src,
&locationReadOnlyDiskUtf16);
+ data->vboxObj->vtbl->OpenMedium(data->vboxObj,
+ locationReadOnlyDiskUtf16,
+ DeviceType_HardDisk,
+ AccessMode_ReadWrite,
+ false,
+ &mediumReadOnly);
+ if (mediumReadOnly) {
+ rc = mediumReadOnly->vtbl->GetId(mediumReadOnly,
+ &mediumReadOnlyIdUtf16);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot get medium id"));
+ goto cleanup;
+ }
+ rc = mediumReadOnly->vtbl->GetFormat(mediumReadOnly,
+ &mediumReadOnlyFormatUtf16);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot get medium format"));
+ goto cleanup;
+ }
+ VBOX_UTF16_TO_UTF8(mediumReadOnlyIdUtf16, &mediumReadOnlyId);
+ VBOX_UTF16_TO_UTF8(mediumReadOnlyFormatUtf16,
&mediumReadOnlyFormat);
+ char *uuidToReplace = NULL;
+ virSearchRegex(newSnapshotStorageControllersString,
+ it,
+
"([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})",
+ &uuidToReplace);
+ if (STRNEQ(uuidToReplace, mediumReadOnlyId)) {
+ char *tmp = NULL;
+ tmp = virStrReplace(newSnapshotStorageControllersString,
+ uuidToReplace,
+ mediumReadOnlyId);
+ VIR_FREE(newSnapshotStorageControllersString);
+ if (!tmp)
+ goto cleanup;
+ if (VIR_STRDUP(newSnapshotStorageControllersString, tmp) < 0) {
+ VIR_DEBUG("Error while using strdup");
+ }
+ VIR_FREE(tmp);
+ }
+ IMedium *mediumReadOnlyParent = NULL;
+ rc = mediumReadOnly->vtbl->GetParent(mediumReadOnly,
+ &mediumReadOnlyParent);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot get medium parent"));
+ goto cleanup;
+ }
+ if (mediumReadOnlyParent) {
+ PRUnichar *parentIdUtf16 = NULL;
+ char *parentId = NULL;
+ char *parentIdWithBrackets = NULL;
+ char *childIdWithBrackets = NULL;
+ rc = mediumReadOnlyParent->vtbl->GetId(mediumReadOnlyParent,
&parentIdUtf16);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot get medium id"));
+ goto cleanup;
+ }
+ if (parentIdUtf16) {
+ VBOX_UTF16_TO_UTF8(parentIdUtf16, &parentId);
+ if ((virAsprintf(&parentIdWithBrackets, "{%s}",
parentId) != -1)
+ && (virAsprintf(&childIdWithBrackets,
"{%s}", mediumReadOnlyId) != -1)) {
+ buf = xmlBufferCreate();
+ xmlNodeDump(buf, mediaRegistryNodeOriginal->doc,
mediaRegistryNodeOriginal, 0, 0);
+ ignore_value(VIR_STRDUP(mediaRegistryNodeOriginalString,
+ (char *)xmlBufferContent(buf)));
+ if (strstr(mediaRegistryNodeOriginalString,
childIdWithBrackets) == NULL) {
+
vboxSnapshotXmlAppendDiskToMediaRegistry(&mediaRegistryNodeOriginal->children,
+
parentIdWithBrackets,
+
childIdWithBrackets,
+
def->dom->disks[it]->src,
+
mediumReadOnlyFormat);
+ } else {
+ VIR_DEBUG("Already added");
+ }
+ VIR_FREE(parentIdWithBrackets);
+ VIR_FREE(childIdWithBrackets);
+ }
+ VBOX_UTF8_FREE(parentId);
+ }
+ VBOX_RELEASE(mediumReadOnlyParent);
+ VBOX_UTF16_FREE(parentIdUtf16);
+ }
+ VBOX_UTF8_FREE(mediumReadOnlyFormat);
+ VBOX_UTF8_FREE(mediumReadOnlyId);
+ VBOX_RELEASE(mediumReadOnly);
+ }
+ VBOX_UTF16_FREE(mediumReadOnlyIdUtf16);
+ VBOX_UTF16_FREE(mediumReadOnlyFormatUtf16);
+ VBOX_UTF16_FREE(locationReadOnlyDiskUtf16);
+ }
+
+ /*Read Write disks*/
+ for (it = 0; it< def->ndisks; ++it) {
+ IMedium *mediumReadWrite = NULL;
+ PRUnichar *mediumReadWriteFormatUtf16 = NULL;
+ PRUnichar *mediumReadWriteIdUtf16 = NULL;
+ PRUnichar *locationReadWriteDiskUtf16 = NULL;
+ char *mediumReadWriteFormat = NULL;
+ char *mediumReadWriteId = NULL;
+ char *mediaRegistryNodeOriginalString = NULL;
+ VBOX_UTF8_TO_UTF16(def->disks[it].file, &locationReadWriteDiskUtf16);
+ rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj,
+ locationReadWriteDiskUtf16,
+ DeviceType_HardDisk,
+ AccessMode_ReadWrite,
+ false,
+ &mediumReadWrite);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+ "%s",
+ _("Could not open medium"),
def->disks[it].file);
+ ret = -1;
+ goto cleanup;
+ }
+ if (mediumReadWrite) {
+ rc = mediumReadWrite->vtbl->GetId(mediumReadWrite,
+ &mediumReadWriteIdUtf16);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot get medium id"));
+ goto cleanup;
+ }
+ rc = mediumReadWrite->vtbl->GetFormat(mediumReadWrite,
+ &mediumReadWriteFormatUtf16);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot get medium format"));
+ goto cleanup;
+ }
+ VBOX_UTF16_TO_UTF8(mediumReadWriteIdUtf16, &mediumReadWriteId);
+ VBOX_UTF16_TO_UTF8(mediumReadWriteFormatUtf16,
&mediumReadWriteFormat);
+ char *uuidToReplace = NULL;
+ virSearchRegex(newMachineStorageControllersString,
+ it,
+
"([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})",
+ &uuidToReplace);
+ if (STRNEQ(uuidToReplace, mediumReadWriteId)) {
+ char *tmp = NULL;
+ tmp = virStrReplace(newMachineStorageControllersString,
+ uuidToReplace,
+ mediumReadWriteId);
+ VIR_FREE(newMachineStorageControllersString);
+ if (!tmp)
+ goto cleanup;
+ if (VIR_STRDUP(newMachineStorageControllersString, tmp) < 0) {
+ VIR_DEBUG("Error while using strdup");
+ }
+ VIR_FREE(tmp);
+ }
+ buf = xmlBufferCreate();
+ xmlNodeDump(buf, mediaRegistryNodeOriginal->doc,
mediaRegistryNodeOriginal, 0, 0);
+ ignore_value(VIR_STRDUP(mediaRegistryNodeOriginalString,
+ (char *)xmlBufferContent(buf)));
+ xmlBufferFree(buf);
+ if (mediaRegistryNodeOriginalString &&
(strstr(mediaRegistryNodeOriginalString,
+ mediumReadWriteId) ==
NULL)) {
+ IMedium *mediumReadWriteParent = NULL;
+ rc = mediumReadWrite->vtbl->GetParent(mediumReadWrite,
+ &mediumReadWriteParent);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot get disk parent"));
+ goto cleanup;
+ }
+ if (mediumReadWriteParent) {
+ PRUnichar *parentIdUtf16 = NULL;
+ char *parentId = NULL;
+ char *parentIdWithBrackets = NULL;
+ char *childIdWithBrackets = NULL;
+ rc =
mediumReadWriteParent->vtbl->GetId(mediumReadWriteParent,
+ &parentIdUtf16);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot get disk id"));
+ goto cleanup;
+ }
+ if (parentIdUtf16) {
+ VBOX_UTF16_TO_UTF8(parentIdUtf16, &parentId);
+ if ((virAsprintf(&parentIdWithBrackets, "{%s}",
parentId) != -1)
+ && (virAsprintf(&childIdWithBrackets,
"{%s}", mediumReadWriteId) != -1)) {
+
vboxSnapshotXmlAppendDiskToMediaRegistry(&(mediaRegistryNodeOriginal->children),
+
parentIdWithBrackets,
+
childIdWithBrackets,
+
def->disks[it].file,
+
mediumReadWriteFormat);
+ VIR_FREE(parentIdWithBrackets);
+ VIR_FREE(childIdWithBrackets);
+ }
+ VBOX_UTF8_FREE(parentId);
+ }
+ VBOX_RELEASE(mediumReadWriteParent);
+ VBOX_UTF16_FREE(parentIdUtf16);
+ }
+ }
+ VBOX_UTF8_FREE(mediumReadWriteFormat);
+ VBOX_UTF8_FREE(mediumReadWriteId);
+ VBOX_RELEASE(mediumReadWrite);
+ }
+ VBOX_UTF16_FREE(mediumReadWriteIdUtf16);
+ VBOX_UTF16_FREE(mediumReadWriteFormatUtf16);
+ VBOX_UTF16_FREE(locationReadWriteDiskUtf16);
+ }
+ vboxArrayRelease(&mediumAttachments);
+
+ /*Generation of the skeleton*/
+
+ VBOX_UTF16_TO_UTF8((PRUnichar *)domiid.value, &domainUuid);
+ rc = machine->vtbl->GetName(machine, &machineNameUtf16);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot get machine name"));
+ goto cleanup;
+ }
+ rc = machine->vtbl->GetSnapshotFolder(machine, &snapshotFolderUtf16);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot get snapshot folder"));
+ goto cleanup;
+ }
+ char *snapshotFolder = NULL;
+ VBOX_UTF16_TO_UTF8(snapshotFolderUtf16, &snapshotFolder);
+ VBOX_UTF16_FREE(snapshotFolderUtf16);
+ VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineName);
+ VBOX_UTF16_FREE(machineNameUtf16);
+ if (snapshotUuid == NULL) {
+ VIR_DEBUG("Bad initialization");
+ }
+ if (virUUIDGenerate(snapshotUuid) == -1) {
+ VIR_DEBUG("Could not generate a snapshot uuid");
+ } else {
+ virUUIDFormat(snapshotUuid, snapshotuuidstr);
+ VIR_DEBUG("UUID: %s", snapshotuuidstr);
+ }
+ rc = machine->vtbl->GetCurrentSnapshot(machine,
&savedCurrentSnapshot);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot get current snapshot"));
+ goto cleanup;
+ }
+ if (savedCurrentSnapshot) {
+ savedCurrentSnapshot->vtbl->GetId(savedCurrentSnapshot,
+ &savedCurrentSnapshotIid.value);
+ VBOX_UTF16_TO_UTF8(savedCurrentSnapshotIid.value,
+ &savedCurrentSnapshotUuid);
+ vboxIIDUnalloc(&savedCurrentSnapshotIid);
+ VBOX_RELEASE(savedCurrentSnapshot);
+ }
+ rc = machine ->vtbl->GetLastStateChange(machine, &lastStateChange);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot get last state change"));
+ goto cleanup;
+ }
+ xmlBufferPtr skeletonBuf = vboxSnapshotGenerateXmlSkeleton(domainUuid,
+ machineName,
+ snapshotuuidstr,
+ snapshotFolder,
+ lastStateChange,
+ isCurrent,
+
savedCurrentSnapshotUuid);
+ /* Now we can reopen our file in tree mode and append node we want. */
+ xmlDocPtr newXml = virXMLParse(NULL, (char *)skeletonBuf->content, NULL);
+ if (newXml) {
+ xmlNodePtr newXmlRootElement = xmlDocGetRootElement(newXml);
+ xmlNodePtr newXmlMachineNode = NULL;
+ xmlNodePtr snapshotNodeToAppend = NULL;
+ IMedium *array[] = { NULL };
+ IProgress *progress = NULL;
+ vboxArray media = VBOX_ARRAY_INITIALIZER;
+
+ vboxSnapshotXmlRetrieveMachineNode(newXmlRootElement,
&newXmlMachineNode);
+ if (newXmlMachineNode) {
+ if (mediaRegistryNodeOriginal) {
+ vboxSnapshotXmlAddChild(newXmlMachineNode,
mediaRegistryNodeOriginal);
+ }
+ if (extraDataNode) {
+ vboxSnapshotXmlAddChild(newXmlMachineNode, extraDataNode);
+ }
+ }
+ if (!snapshotHasParents) {
+ vboxSnapshotGenerateVboxXML(xmlDocGetRootElement(xml),
+ newSnapshotStorageControllersString,
+ NULL,
+ def->name,
+
virTimeStringThen(def->creationTime*1000),
+ snapshotuuidstr,
+ &snapshotNodeToAppend);
+ } else {
+ vboxSnapshotGenerateVboxXML(xmlDocGetRootElement(xml),
+ newSnapshotStorageControllersString,
+ def->parent,
+ def->name,
+
virTimeStringThen(def->creationTime*1000),
+ snapshotuuidstr,
+ &snapshotNodeToAppend);
+ }
+ if (snapshotNodeToAppend) {
+ if (xmlAddChild(newXmlMachineNode, xmlCopyNode(snapshotNodeToAppend, 1))
== NULL) {
+ VIR_DEBUG("Error while building the xml");
+ }
+ }
+ if (newXmlMachineNode) {
+ if (machineHardwareNode) {
+ vboxSnapshotXmlAddChild(newXmlMachineNode, machineHardwareNode);
+ }
+ xmlParseInNodeContext(newXmlMachineNode,
+ newMachineStorageControllersString,
+ (int)strlen(newMachineStorageControllersString),
+ 0,
+ &modifiedMachineStorageControllersNode);
+ if (modifiedMachineStorageControllersNode) {
+ if (xmlAddChild(newXmlMachineNode,
xmlCopyNode(modifiedMachineStorageControllersNode, 1)) == NULL) {
+ VIR_DEBUG("Could not add
modifiedSnapshotStorageControllersNode node to snapshot node");
+ }
+ }
+ }
+ xmlNewProp(newXmlRootElement, BAD_CAST "xmlns", BAD_CAST
"http://www.innotek.de/VirtualBox-settings");
+ rc = vboxArrayGetWithUintArg(&media,
+ machine,
+ machine->vtbl->Unregister,
+ CleanupMode_DetachAllReturnHardDisksOnly);
+ for (it = 0; it < media.count; ++it) {
+ IMedium *disk = media.items[it];
+ if (disk)
+ vboxDetachAndCloseDisks(dom, disk);
+ }
+# if VBOX_API_VERSION == 4002
+ rc = machine->vtbl->Delete(machine, 0, array, &progress);
+# elif VBOX_API_VERSION >= 4003
+ rc = machine->vtbl->DeleteConfig(machine, 0, array, &progress);
+# endif
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot delete machine"));
+ goto cleanup;
+ }
+ if (progress != NULL) {
+ progress->vtbl->WaitForCompletion(progress, -1);
+ VBOX_RELEASE(progress);
+ }
+ vboxArrayRelease(&media);
+ if (virFileMakePath(mdir_name(settingsFilePath_Utf8)) < 0)
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+ " %s",
+ _("Cannot create path :"),
+ settingsFilePath_Utf8);
+ if (xmlSaveFormatFileEnc(settingsFilePath_Utf8, newXml,
"ISO-8859-1", 1) <= 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+ " %s",
+ _("Cannot save xml file to:"),
+ settingsFilePath_Utf8);
+ }
+ rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj,
+ settingsFilePath,
+ &newMachine);
+ unsigned int failedCounter = 10;
+ while (NS_FAILED(rc) && (rc == 0x80004005 /*NSCOM standard error*/)
&& failedCounter != 0) {
+ /* There is some random fails happening and they are not the error
+ * supposed to happen.
+ * I didn't have some solution on #vbox-dev
+ * but it appears that with another try it works
+ * so that's why we are trying 10 times maximum
+ */
+ rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj,
+ settingsFilePath,
+ &newMachine);
+ failedCounter--;
+ }
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot open machine"));
+ goto cleanup;
+ }
+ if (newMachine) {
+ rc = data->vboxObj->vtbl->RegisterMachine(data->vboxObj,
newMachine);
+ if (NS_FAILED(rc)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot register machine"));
+ goto cleanup;
+ }
+ }
+ else {
+ ret = -1;
+ goto cleanup;
+ }
+ }
+ }
+ ret = 0;
+cleanup:
+ xmlFreeDoc(xml);
+ return ret;
+}
+#endif
static virDomainSnapshotPtr
vboxDomainSnapshotCreateXML(virDomainPtr dom,
const char *xmlDesc,
@@ -5972,9 +7032,14 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom,
#else
PRInt32 result;
#endif
+#if VBOX_API_VERSION >= 4002
+ 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,
@@ -5982,11 +7047,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);
@@ -5996,6 +7056,15 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom,
goto cleanup;
}
+#if VBOX_API_VERSION >= 4002
+ isCurrent = flags & VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT;
+ if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) {
+ vboxSnapshotRedefine(dom, def, isCurrent);
+ ret = virGetDomainSnapshot(dom, def->name);
+ goto cleanup;
+ }
+#endif
+
rc = machine->vtbl->GetState(machine, &state);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
@@ -7339,8 +8408,6 @@ cleanup:
return ret;
}
-
-
static int
vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
unsigned int flags)
--
1.7.10.4