The snapshots are saved in xml files, and then can be redefined
---
src/vbox/vbox_tmpl.c | 852 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 844 insertions(+), 8 deletions(-)
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
index e05ed97..23f8aab 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,6 +277,12 @@ 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
#define reportInternalErrorIfNS_FAILED(message) \
if (NS_FAILED(rc)) { \
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(message)); \
@@ -286,6 +293,8 @@ static vboxGlobalData *g_pVBoxGlobalData = NULL;
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);
}
@@ -5946,6 +5955,827 @@ cleanup:
return snapshot;
}
+#if VBOX_API_VERSION >=4002
+static void
+vboxSnapshotXmlRetrieveSnapshotNodeByName(xmlNodePtr a_node,
+ const char *name,
+ xmlNodePtr *snap_node)
+{
+ xmlNodePtr cur_node = NULL;
+
+ 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")
&&
+ STREQ(virXMLPropString(cur_node, "name"), name)) {
+ *snap_node = cur_node;
+ return;
+ }
+ }
+ 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) {
+ VIR_DEBUG("Null pointer to disk");
+ return -1;
+ }
+ rc = disk->vtbl->GetLocation(disk, &location);
+ if (rc == VBOX_E_ACCESSDENIED) {
+ VIR_DEBUG("Disk already closed");
+ goto cleanup;
+ }
+ reportInternalErrorIfNS_FAILED("cannot get disk location");
+ rc = vboxArrayGet(&childrenDiskArray, disk, disk->vtbl->GetChildren);
+ reportInternalErrorIfNS_FAILED("cannot get children disks");
+ for (i = 0; i < childrenDiskArray.count; ++i) {
+ IMedium *childDisk = childrenDiskArray.items[i];
+ if (childDisk) {
+ vboxDetachAndCloseDisks(dom, childDisk);
+ }
+ }
+ rc = disk->vtbl->RefreshState(disk, &dummyState);
+ reportInternalErrorIfNS_FAILED("cannot refresh state");
+ 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) {
+ VIR_DEBUG("Error while closing disk");
+ }
+ }
+ VBOX_UTF8_FREE(location_utf8);
+cleanup:
+ 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);
+ ignore_value(VIR_STRDUP(tmpString, (char *)xmlBufferContent(tmpBuf)));
+ 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 *date = virStringSplit(virStringJoin((const
char**)virStringSplit(virStringSplit(timeStamp, "+", 0)[0], " ", 0),
"T"), ".", 0)[0];
+
+ /*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);
+ reportInternalErrorIfNS_FAILED("cannot get settings file path");
+ 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);
+ reportInternalErrorIfNS_FAILED("cannot get medium attachments");
+
+ /*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);
+ reportInternalErrorIfNS_FAILED("cannot get medium id");
+ rc = mediumReadOnly->vtbl->GetFormat(mediumReadOnly,
&mediumReadOnlyFormatUtf16);
+ reportInternalErrorIfNS_FAILED("cannot get medium format");
+ VBOX_UTF16_TO_UTF8(mediumReadOnlyIdUtf16, &mediumReadOnlyId);
+ VBOX_UTF16_TO_UTF8(mediumReadOnlyFormatUtf16,
&mediumReadOnlyFormat);
+ char *uuidToReplace = NULL;
+ virSearchUuid(newSnapshotStorageControllersString, it+1,
&uuidToReplace);
+ if (STRNEQ(uuidToReplace, mediumReadOnlyId)) {
+ char *tmp = NULL;
+ tmp = virStrReplace(newSnapshotStorageControllersString,
uuidToReplace, mediumReadOnlyId);
+ VIR_FREE(newSnapshotStorageControllersString);
+ if (VIR_STRDUP(newSnapshotStorageControllersString, tmp) < 0) {
+ VIR_DEBUG("Error while using strdup");
+ }
+ VIR_FREE(tmp);
+ }
+ IMedium *mediumReadOnlyParent = NULL;
+ rc = mediumReadOnly->vtbl->GetParent(mediumReadOnly,
&mediumReadOnlyParent);
+ reportInternalErrorIfNS_FAILED("cannot get medium parent");
+ if (mediumReadOnlyParent) {
+ PRUnichar *parentIdUtf16 = NULL;
+ char *parentId = NULL;
+ char *parentIdWithBrackets = NULL;
+ char *childIdWithBrackets = NULL;
+ rc = mediumReadOnlyParent->vtbl->GetId(mediumReadOnlyParent,
&parentIdUtf16);
+ reportInternalErrorIfNS_FAILED("cannot get medium id");
+ 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);
+ reportInternalErrorIfNS_FAILED("cannot get medium id");
+ rc = mediumReadWrite->vtbl->GetFormat(mediumReadWrite,
&mediumReadWriteFormatUtf16);
+ reportInternalErrorIfNS_FAILED("cannot get medium format");
+ VBOX_UTF16_TO_UTF8(mediumReadWriteIdUtf16, &mediumReadWriteId);
+ VBOX_UTF16_TO_UTF8(mediumReadWriteFormatUtf16,
&mediumReadWriteFormat);
+ char *uuidToReplace = NULL;
+ virSearchUuid(newMachineStorageControllersString, it+1,
&uuidToReplace);
+ if (STRNEQ(uuidToReplace, mediumReadWriteId)) {
+ char *tmp = NULL;
+ tmp = virStrReplace(newMachineStorageControllersString,
uuidToReplace, mediumReadWriteId);
+ VIR_FREE(newMachineStorageControllersString);
+ 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);
+ reportInternalErrorIfNS_FAILED("cannot get disk parent");
+ if (mediumReadWriteParent) {
+ PRUnichar *parentIdUtf16 = NULL;
+ char *parentId = NULL;
+ char *parentIdWithBrackets = NULL;
+ char *childIdWithBrackets = NULL;
+ rc =
mediumReadWriteParent->vtbl->GetId(mediumReadWriteParent, &parentIdUtf16);
+ reportInternalErrorIfNS_FAILED("cannot get disk id");
+ 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);
+ reportInternalErrorIfNS_FAILED("cannot get machine name");
+ rc = machine->vtbl->GetSnapshotFolder(machine, &snapshotFolderUtf16);
+ reportInternalErrorIfNS_FAILED("cannot get snapshot folder");
+ 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);
+ reportInternalErrorIfNS_FAILED("cannot get current snapshot");
+ 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);
+ reportInternalErrorIfNS_FAILED("cannot get last state change");
+ 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
+ reportInternalErrorIfNS_FAILED("cannot delete machine");
+ 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--;
+ }
+ reportInternalErrorIfNS_FAILED("cannot open machine");
+ if (newMachine) {
+ rc = data->vboxObj->vtbl->RegisterMachine(data->vboxObj,
newMachine);
+ reportInternalErrorIfNS_FAILED("cannot register machine");
+ }
+ else {
+ ret = -1;
+ goto cleanup;
+ }
+ }
+ }
+ ret = 0;
+cleanup:
+ xmlFreeDoc(xml);
+ return ret;
+}
+#endif
static virDomainSnapshotPtr
vboxDomainSnapshotCreateXML(virDomainPtr dom,
const char *xmlDesc,
@@ -5967,20 +6797,20 @@ 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,
VIR_DOMAIN_SNAPSHOT_PARSE_DISKS |
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);
if (NS_FAILED(rc)) {
@@ -5988,6 +6818,14 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom,
_("no domain with matching UUID"));
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",
@@ -7239,8 +8077,6 @@ cleanup:
return ret;
}
-
-
static int
vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
unsigned int flags)
--
1.7.10.4