diff --git a/po/POTFILES.in b/po/POTFILES.in
index 2ee7225a68a4f7672486470a23e3a2c8d80a20d1..d01116bd03954e62be5e10fd3c317b61c66cb240 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -212,6 +212,7 @@ src/util/virxml.c
src/vbox/vbox_MSCOMGlue.c
src/vbox/vbox_XPCOMCGlue.c
src/vbox/vbox_driver.c
+src/vbox/vbox_snapshot_conf.c
src/vbox/vbox_tmpl.c
src/vmware/vmware_conf.c
src/vmware/vmware_driver.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 01af16477df0c7361545c502ed2001db791d99b6..30df68f7944af5796a6c2918cdba96ce0c5ef20e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -663,6 +663,7 @@ VMWARE_DRIVER_SOURCES = \
VBOX_DRIVER_SOURCES = \
vbox/vbox_glue.c vbox/vbox_glue.h \
vbox/vbox_driver.c vbox/vbox_driver.h \
+ vbox/vbox_snapshot_conf.c vbox/vbox_snapshot_conf.h \
vbox/vbox_V2_2.c vbox/vbox_CAPI_v2_2.h \
vbox/vbox_V3_0.c vbox/vbox_CAPI_v3_0.h \
vbox/vbox_V3_1.c vbox/vbox_CAPI_v3_1.h \
diff --git a/src/vbox/vbox_snapshot_conf.c b/src/vbox/vbox_snapshot_conf.c
new file mode 100644
index 0000000000000000000000000000000000000000..49096650af10fe9bcde2ff57da4d8d82da6124da
--- /dev/null
+++ b/src/vbox/vbox_snapshot_conf.c
@@ -0,0 +1,1537 @@
+/*
+ * Copyright 2014, diateam (www.diateam.net)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * .
+ */
+
+#include
+
+#include "vbox_snapshot_conf.h"
+#include "virerror.h"
+#include "viralloc.h"
+#include "virlog.h"
+#include "virstring.h"
+#include "virxml.h"
+
+#include
+
+#define VIR_FROM_THIS VIR_FROM_VBOX
+VIR_LOG_INIT("vbox.vbox_snapshot_conf");
+
+static virVBoxSnapshotConfHardDiskPtr
+virVBoxSnapshotConfCreateVBoxSnapshotConfHardDiskPtr(xmlNodePtr diskNode,
+ xmlXPathContextPtr xPathContext,
+ const char *machineLocation)
+{
+ virVBoxSnapshotConfHardDiskPtr hardDisk = NULL;
+ xmlNodePtr *nodes = NULL;
+ char *uuid = NULL;
+ char **searchTabResult = NULL;
+ int resultSize = 0;
+ size_t i = 0;
+ int result = -1;
+ char *location = NULL;
+ char *tmp = NULL;
+ int n = 0;
+ if (VIR_ALLOC(hardDisk) < 0)
+ goto cleanup;
+
+ xPathContext->node = diskNode;
+
+ n = virXPathNodeSet("./vbox:HardDisk", xPathContext, &nodes);
+ if (n < 0)
+ goto cleanup;
+
+ if (n && VIR_ALLOC_N(hardDisk->children, n) < 0)
+ goto cleanup;
+ hardDisk->nchildren = n;
+ for (i = 0; i < hardDisk->nchildren; i++) {
+ hardDisk->children[i] = virVBoxSnapshotConfCreateVBoxSnapshotConfHardDiskPtr(nodes[i], xPathContext, machineLocation);
+ if (hardDisk->children[i] == NULL)
+ goto cleanup;
+ hardDisk->children[i]->parent = hardDisk;
+ }
+ uuid = virXMLPropString(diskNode, "uuid");
+ /*we use virStringSearch because the uuid is between brackets*/
+ resultSize = virStringSearch(uuid,
+ VBOX_UUID_REGEX,
+ 1,
+ &searchTabResult);
+ if (resultSize != 1) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse 'uuid' attribute"));
+ goto cleanup;
+ }
+ if (VIR_STRDUP(hardDisk->uuid, searchTabResult[0]) < 0)
+ goto cleanup;
+
+ location = virXMLPropString(diskNode, "location");
+ if (location == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse 'location' attribute"));
+ goto cleanup;
+ }
+ if (location[0] != '/') {
+ /*The location is a relative path, so we must change it into an absolute one. */
+ if (virAsprintf(&tmp, "%s%s", machineLocation, location) < 0)
+ goto cleanup;
+ if (VIR_STRDUP(hardDisk->location, tmp) < 0)
+ goto cleanup;
+ } else {
+ if (VIR_STRDUP(hardDisk->location, location) < 0)
+ goto cleanup;
+ }
+ hardDisk->format = virXMLPropString(diskNode, "format");
+ if (hardDisk->format == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse 'format' attribute"));
+ goto cleanup;
+ }
+ hardDisk->type = virXMLPropString(diskNode, "type");
+ result = 0;
+
+ cleanup:
+ VIR_FREE(uuid);
+ VIR_FREE(nodes);
+ VIR_FREE(location);
+ VIR_FREE(tmp);
+ virStringFreeList(searchTabResult);
+ if (result < 0) {
+ virVboxSnapshotConfHardDiskFree(hardDisk);
+ hardDisk = NULL;
+ }
+ return hardDisk;
+}
+
+static virVBoxSnapshotConfMediaRegistryPtr
+virVBoxSnapshotConfRetrieveMediaRegistry(xmlNodePtr mediaRegistryNode,
+ xmlXPathContextPtr xPathContext,
+ const char *machineLocation)
+{
+ virVBoxSnapshotConfMediaRegistryPtr mediaRegistry = NULL;
+ xmlNodePtr hardDisksNode = NULL;
+ xmlNodePtr *nodes = NULL;
+ size_t i = 0;
+ int result = -1;
+ int n = 0;
+
+ if (VIR_ALLOC(mediaRegistry) < 0)
+ goto cleanup;
+
+ xPathContext->node = mediaRegistryNode;
+ hardDisksNode = virXPathNode("./vbox:HardDisks", xPathContext);
+
+ xPathContext->node = hardDisksNode;
+ n = virXPathNodeSet("./vbox:HardDisk", xPathContext, &nodes);
+ if (n < 0)
+ goto cleanup;
+ if (n && VIR_ALLOC_N(mediaRegistry->disks, n) < 0)
+ goto cleanup;
+ mediaRegistry->ndisks = n;
+ for (i = 0; i < mediaRegistry->ndisks; i++) {
+ mediaRegistry->disks[i] = virVBoxSnapshotConfCreateVBoxSnapshotConfHardDiskPtr(nodes[i],
+ xPathContext,
+ machineLocation);
+ if (mediaRegistry->disks[i] == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Cannot create a vboxSnapshotXmlHardDisk"));
+ goto cleanup;
+ }
+ }
+ n = 0;
+ VIR_FREE(nodes);
+
+ xPathContext->node = mediaRegistryNode;
+ n = virXPathNodeSet("./*[not(self::vbox:HardDisks)]",
+ xPathContext, &nodes);
+ if (n < 0)
+ goto cleanup;
+ if (n && VIR_ALLOC_N(mediaRegistry->otherMedia, n) < 0)
+ goto cleanup;
+ mediaRegistry->notherMedia = n;
+ for (i = 0; i < mediaRegistry->notherMedia; i++) {
+ mediaRegistry->otherMedia[i] = virXMLNodeToString(mediaRegistryNode->doc,
+ nodes[i]);
+ }
+
+ result = 0;
+
+ cleanup:
+ if (result < 0) {
+ virVBoxSnapshotConfMediaRegistryFree(mediaRegistry);
+ mediaRegistry = NULL;
+ }
+ VIR_FREE(nodes);
+ return mediaRegistry;
+}
+
+static virVBoxSnapshotConfSnapshotPtr
+virVBoxSnapshotConfRetrieveSnapshot(xmlNodePtr snapshotNode,
+ xmlXPathContextPtr xPathContext)
+{
+ virVBoxSnapshotConfSnapshotPtr snapshot = NULL;
+ xmlNodePtr hardwareNode = NULL;
+ xmlNodePtr descriptionNode = NULL;
+ xmlNodePtr storageControllerNode = NULL;
+ xmlNodePtr snapshotsNode = NULL;
+ xmlNodePtr *nodes = NULL;
+ char *uuid = NULL;
+ char **searchTabResult = NULL;
+ int resultSize = 0;
+ size_t i = 0;
+ int result = -1;
+ int n = 0;
+
+ if (VIR_ALLOC(snapshot) < 0)
+ goto cleanup;
+
+ uuid = virXMLPropString(snapshotNode, "uuid");
+ /*we use virStringSearch because the uuid is between brackets*/
+ resultSize = virStringSearch(uuid,
+ VBOX_UUID_REGEX,
+ 1,
+ &searchTabResult);
+ if (resultSize != 1) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse 'uuid' attribute"));
+ goto cleanup;
+ }
+ if (VIR_STRDUP(snapshot->uuid, searchTabResult[0]) < 0)
+ goto cleanup;
+
+ snapshot->name = virXMLPropString(snapshotNode, "name");
+ if (snapshot->name == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse 'name' attribute"));
+ goto cleanup;
+ }
+ snapshot->timeStamp = virXMLPropString(snapshotNode, "timeStamp");
+ if (snapshot->timeStamp == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse 'timeStamp' attribute"));
+ goto cleanup;
+ }
+
+ xPathContext->node = snapshotNode;
+ descriptionNode = virXPathNode("./vbox:Description", xPathContext);
+ if (descriptionNode != NULL) {
+ snapshot->description = virXMLNodeToString(descriptionNode->doc, descriptionNode);
+ }
+
+ hardwareNode = virXPathNode("./vbox:Hardware", xPathContext);
+ if (hardwareNode == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse node"));
+ goto cleanup;
+ }
+ snapshot->hardware = virXMLNodeToString(snapshotNode->doc, hardwareNode);
+
+ storageControllerNode = virXPathNode("./vbox:StorageControllers", xPathContext);
+ if (storageControllerNode == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse node"));
+ goto cleanup;
+ }
+ snapshot->storageController = virXMLNodeToString(snapshotNode->doc,
+ storageControllerNode);
+
+ snapshotsNode = virXPathNode("./vbox:Snapshots", xPathContext);
+
+ if (snapshotsNode != NULL) {
+ xPathContext->node = snapshotsNode;
+ n = virXPathNodeSet("./vbox:Snapshot", xPathContext, &nodes);
+ if (n < 0)
+ goto cleanup;
+ if (n && VIR_ALLOC_N(snapshot->children, n) < 0)
+ goto cleanup;
+ snapshot->nchildren = n;
+ for (i = 0; i < snapshot->nchildren; i++) {
+ snapshot->children[i] = virVBoxSnapshotConfRetrieveSnapshot(nodes[i], xPathContext);
+ if (snapshot->children[i] == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Cannot create a vboxSnapshotXmlSnapshotPtr"));
+ goto cleanup;
+ }
+ snapshot->children[i]->parent = snapshot;
+ }
+ }
+
+ result = 0;
+
+ cleanup:
+ if (result < 0) {
+ virVBoxSnapshotConfSnapshotFree(snapshot);
+ snapshot = NULL;
+ }
+ VIR_FREE(nodes);
+ VIR_FREE(uuid);
+ virStringFreeList(searchTabResult);
+ return snapshot;
+}
+
+virVBoxSnapshotConfSnapshotPtr
+virVBoxSnapshotConfSnapshotByName(virVBoxSnapshotConfSnapshotPtr snapshot,
+ const char *snapshotName)
+{
+ size_t i = 0;
+ virVBoxSnapshotConfSnapshotPtr ret = NULL;
+ if (STREQ(snapshot->name, snapshotName))
+ return snapshot;
+ for (i = 0; i < snapshot->nchildren; i++) {
+ ret = virVBoxSnapshotConfSnapshotByName(snapshot->children[i], snapshotName);
+ if (ret != NULL)
+ return ret;
+ }
+ return ret;
+}
+
+static virVBoxSnapshotConfHardDiskPtr
+virVBoxSnapshotConfHardDiskById(virVBoxSnapshotConfHardDiskPtr disk,
+ const char *parentHardDiskId)
+{
+ size_t i = 0;
+ virVBoxSnapshotConfHardDiskPtr ret = NULL;
+ if (STREQ(disk->uuid, parentHardDiskId))
+ return disk;
+ for (i = 0; i < disk->nchildren; i++) {
+ ret = virVBoxSnapshotConfHardDiskById(disk->children[i], parentHardDiskId);
+ if (ret != NULL)
+ return ret;
+ }
+ return ret;
+}
+
+static virVBoxSnapshotConfHardDiskPtr
+virVBoxSnapshotConfHardDiskByLocation(virVBoxSnapshotConfHardDiskPtr disk,
+ const char *parentLocation)
+{
+ size_t i = 0;
+ virVBoxSnapshotConfHardDiskPtr ret = NULL;
+ if (STREQ(disk->location, parentLocation))
+ return disk;
+ for (i = 0; i < disk->nchildren; i++) {
+ ret = virVBoxSnapshotConfHardDiskByLocation(disk->children[i], parentLocation);
+ if (ret != NULL)
+ return ret;
+ }
+ return ret;
+}
+
+static xmlNodePtr
+virVBoxSnapshotConfCreateHardDiskNode(virVBoxSnapshotConfHardDiskPtr hardDisk)
+{
+ int result = -1;
+ size_t i = 0;
+ char *uuid = NULL;
+ xmlNodePtr ret = xmlNewNode(NULL, BAD_CAST "HardDisk");
+ if (virAsprintf(&uuid, "{%s}", hardDisk->uuid) < 0)
+ goto cleanup;
+
+ if (xmlNewProp(ret, BAD_CAST "uuid", BAD_CAST uuid) == NULL)
+ goto cleanup;
+ if (xmlNewProp(ret, BAD_CAST "location", BAD_CAST hardDisk->location) == NULL)
+ goto cleanup;
+ if (xmlNewProp(ret, BAD_CAST "format", BAD_CAST hardDisk->format) == NULL)
+ goto cleanup;
+ if (hardDisk->type != NULL && xmlNewProp(ret, BAD_CAST "type", BAD_CAST hardDisk->type) == NULL)
+ goto cleanup;
+
+ for (i = 0; i < hardDisk->nchildren; i++) {
+ xmlNodePtr child = virVBoxSnapshotConfCreateHardDiskNode(hardDisk->children[i]);
+ if (child != NULL)
+ xmlAddChild(ret, child);
+ }
+
+ result = 0;
+ cleanup:
+ if (result < 0) {
+ xmlUnlinkNode(ret);
+ xmlFreeNode(ret);
+ }
+ VIR_FREE(uuid);
+ return ret;
+}
+
+static int
+virVBoxSnapshotConfSerializeSnapshot(xmlNodePtr node,
+ virVBoxSnapshotConfSnapshotPtr snapshot)
+{
+ int result = -1;
+ size_t i = 0;
+ xmlNodePtr descriptionNode = NULL;
+ xmlNodePtr snapshotsNode = NULL;
+ xmlNodePtr hardwareNode = NULL;
+ xmlNodePtr storageControllerNode = NULL;
+ xmlParserErrors parseError = XML_ERR_OK;
+ char *uuid = NULL;
+ char *timeStamp = NULL;
+
+ char **firstRegex = NULL;
+ int firstRegexResult = 0;
+ char **secondRegex = NULL;
+ int secondRegexResult = 0;
+
+ if (virAsprintf(&uuid, "{%s}", snapshot->uuid) < 0)
+ goto cleanup;
+
+ if (xmlNewProp(node, BAD_CAST "uuid", BAD_CAST uuid) == NULL)
+ goto cleanup;
+ if (xmlNewProp(node, BAD_CAST "name", BAD_CAST snapshot->name) == NULL)
+ goto cleanup;
+
+ /* We change the date format from "yyyy-MM-dd hh:mm:ss.msec+timeZone"
+ * to "yyyy-MM-ddThh:mm:ssZ"*/
+ firstRegexResult = virStringSearch(snapshot->timeStamp,
+ "([0-9]{4}-[0-9]{2}-[0-9]{2})",
+ 1,
+ &firstRegex);
+ secondRegexResult = virStringSearch(snapshot->timeStamp,
+ "([0-9]{2}:[0-9]{2}:[0-9]{2})",
+ 1,
+ &secondRegex);
+ if (firstRegexResult < 1)
+ goto cleanup;
+ if (secondRegexResult < 1)
+ goto cleanup;
+ if (virAsprintf(&timeStamp, "%sT%sZ", firstRegex[0], secondRegex[0]) < 0)
+ goto cleanup;
+
+ if (xmlNewProp(node, BAD_CAST "timeStamp", BAD_CAST timeStamp) == NULL)
+ goto cleanup;
+
+ /*node description*/
+ if (snapshot->description != NULL) {
+ descriptionNode = xmlNewNode(NULL, BAD_CAST "Description");
+ xmlNodeSetContent(descriptionNode, BAD_CAST snapshot->description);
+ xmlAddChild(node, descriptionNode);
+ }
+ /*hardware*/
+ parseError = xmlParseInNodeContext(node,
+ snapshot->hardware,
+ (int)strlen(snapshot->hardware),
+ 0,
+ &hardwareNode);
+ if (parseError != XML_ERR_OK) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Unable to add the snapshot hardware"));
+ goto cleanup;
+ }
+ xmlAddChild(node, hardwareNode);
+
+ /*storageController*/
+ if (xmlParseInNodeContext(node, snapshot->storageController,
+ (int)strlen(snapshot->storageController),
+ 0,
+ &storageControllerNode) != XML_ERR_OK) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Unable to add the snapshot storageController"));
+ goto cleanup;
+ }
+ xmlAddChild(node, storageControllerNode);
+
+ if (snapshot->nchildren > 0) {
+ snapshotsNode = xmlNewNode(NULL, BAD_CAST "Snapshots");
+ xmlAddChild(node, snapshotsNode);
+ for (i = 0; i < snapshot->nchildren; i++) {
+ xmlNodePtr child = xmlNewNode(NULL, BAD_CAST "Snapshot");
+ xmlAddChild(snapshotsNode, child);
+ if (virVBoxSnapshotConfSerializeSnapshot(child, snapshot->children[i]) < 0) {
+ goto cleanup;
+ }
+ }
+ }
+ result = 0;
+
+ cleanup:
+ if (result < 0) {
+ xmlFreeNode(descriptionNode);
+ xmlUnlinkNode(snapshotsNode);
+ xmlFreeNode(snapshotsNode);
+ }
+ virStringFreeList(firstRegex);
+ virStringFreeList(secondRegex);
+ VIR_FREE(uuid);
+ VIR_FREE(timeStamp);
+ return result;
+}
+
+static size_t
+virVBoxSnapshotConfAllChildren(virVBoxSnapshotConfHardDiskPtr disk,
+ virVBoxSnapshotConfHardDiskPtr **list)
+{
+ size_t returnSize = 0;
+ size_t tempSize = 0;
+ virVBoxSnapshotConfHardDiskPtr *ret = NULL;
+ virVBoxSnapshotConfHardDiskPtr *tempList = NULL;
+ size_t i = 0;
+ size_t j = 0;
+ if (VIR_ALLOC_N(ret, 0) < 0)
+ return 0;
+
+ for (i = 0; i < disk->nchildren; i++) {
+ tempSize = virVBoxSnapshotConfAllChildren(disk->children[i], &tempList);
+ if (VIR_EXPAND_N(ret, returnSize, tempSize) < 0)
+ return 0;
+
+ for (j = 0; j < tempSize; j++) {
+ ret[returnSize - tempSize + j] = tempList[j];
+ }
+ }
+ if (VIR_EXPAND_N(ret, returnSize, 1) < 0)
+ return 0;
+
+ ret[returnSize - 1] = disk;
+ *list = ret;
+ return returnSize;
+}
+
+void
+virVboxSnapshotConfHardDiskFree(virVBoxSnapshotConfHardDiskPtr disk)
+{
+ size_t i = 0;
+
+ if (!disk)
+ return;
+
+ VIR_FREE(disk->uuid);
+ VIR_FREE(disk->location);
+ VIR_FREE(disk->format);
+ VIR_FREE(disk->type);
+ for (i = 0; i < disk->nchildren; i++)
+ virVboxSnapshotConfHardDiskFree(disk->children[i]);
+ VIR_FREE(disk->children);
+ VIR_FREE(disk);
+}
+
+
+void
+virVBoxSnapshotConfMediaRegistryFree(virVBoxSnapshotConfMediaRegistryPtr mediaRegistry)
+{
+ size_t i = 0;
+
+ if (!mediaRegistry)
+ return;
+
+ for (i = 0; i < mediaRegistry->ndisks; i++)
+ virVboxSnapshotConfHardDiskFree(mediaRegistry->disks[i]);
+ VIR_FREE(mediaRegistry->disks);
+ for (i = 0; i < mediaRegistry->notherMedia; i++)
+ VIR_FREE(mediaRegistry->otherMedia[i]);
+ VIR_FREE(mediaRegistry->otherMedia);
+ VIR_FREE(mediaRegistry);
+}
+
+void
+virVBoxSnapshotConfSnapshotFree(virVBoxSnapshotConfSnapshotPtr snapshot)
+{
+ size_t i = 0;
+
+ if (!snapshot)
+ return;
+
+ VIR_FREE(snapshot->uuid);
+ VIR_FREE(snapshot->name);
+ VIR_FREE(snapshot->timeStamp);
+ VIR_FREE(snapshot->description);
+ VIR_FREE(snapshot->hardware);
+ VIR_FREE(snapshot->storageController);
+ for (i = 0; i < snapshot->nchildren; i++)
+ virVBoxSnapshotConfSnapshotFree(snapshot->children[i]);
+ VIR_FREE(snapshot->children);
+ VIR_FREE(snapshot);
+}
+
+void
+virVBoxSnapshotConfMachineFree(virVBoxSnapshotConfMachinePtr machine)
+{
+ if (!machine)
+ return;
+
+ VIR_FREE(machine->uuid);
+ VIR_FREE(machine->name);
+ VIR_FREE(machine->currentSnapshot);
+ VIR_FREE(machine->snapshotFolder);
+ VIR_FREE(machine->lastStateChange);
+ virVBoxSnapshotConfMediaRegistryFree(machine->mediaRegistry);
+ VIR_FREE(machine->hardware);
+ VIR_FREE(machine->extraData);
+ virVBoxSnapshotConfSnapshotFree(machine->snapshot);
+ VIR_FREE(machine->storageController);
+ VIR_FREE(machine);
+}
+
+/*
+ *vboxSnapshotLoadVboxFile: Create a vboxSnapshotXmlMachinePtr from a VirtualBoxl xml file.
+ *return an initialized vboxSnapshotXmlMachinePtr on success
+ *return NULL on failure
+ *filePath must not be NULL.
+ */
+virVBoxSnapshotConfMachinePtr
+virVBoxSnapshotConfLoadVboxFile(const char *filePath,
+ const char *machineLocation)
+{
+ int ret = -1;
+ virVBoxSnapshotConfMachinePtr machineDescription = NULL;
+ xmlDocPtr xml = NULL;
+ xmlNodePtr machineNode = NULL;
+ xmlNodePtr cur = NULL;
+ xmlXPathContextPtr xPathContext = NULL;
+ char *currentStateModifiedString = NULL;
+
+ char **searchResultTab = NULL;
+ ssize_t searchResultSize = 0;
+ char *currentSnapshotAttribute = NULL;
+
+ if (filePath == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Filepath is Null"));
+ goto cleanup;
+ }
+
+ if (VIR_ALLOC(machineDescription) < 0)
+ goto cleanup;
+
+ xml = virXMLParse(filePath, NULL, NULL);
+ if (xml == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Unable to parse the xml"));
+ goto cleanup;
+ }
+ if (!(xPathContext = xmlXPathNewContext(xml))) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ if (xmlXPathRegisterNs(xPathContext,
+ BAD_CAST "vbox",
+ BAD_CAST "http://www.innotek.de/VirtualBox-settings") < 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Failed to register xml namespace "
+ "'http://www.innotek.de/VirtualBox-settings'"));
+ goto cleanup;
+ }
+
+ /*Retrieve MachineNode*/
+ cur = xmlDocGetRootElement(xml);
+ xPathContext->node = cur;
+ machineNode = virXPathNode("./vbox:Machine", xPathContext);
+ if (machineNode == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse node"));
+ goto cleanup;
+ }
+
+ machineDescription->uuid = virXMLPropString(machineNode, "uuid");
+ if (machineDescription->uuid == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse 'uuid' attribute"));
+ goto cleanup;
+ }
+ machineDescription->name = virXMLPropString(machineNode, "name");
+ if (machineDescription->name == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse 'name' attribute"));
+ goto cleanup;
+ }
+
+ currentSnapshotAttribute = virXMLPropString(machineNode, "currentSnapshot");
+ if (currentSnapshotAttribute != NULL) {
+ /*we use virStringSearch because the uuid is between brackets*/
+ searchResultSize = virStringSearch(currentSnapshotAttribute,
+ VBOX_UUID_REGEX,
+ 1,
+ &searchResultTab);
+ if (searchResultSize != 1) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse 'currentSnapshot' attribute"));
+ goto cleanup;
+ }
+ if (VIR_STRDUP(machineDescription->currentSnapshot, searchResultTab[0]) < 0)
+ goto cleanup;
+ }
+
+ machineDescription->snapshotFolder = virXMLPropString(machineNode, "snapshotFolder");
+ if (machineDescription->snapshotFolder == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse 'snapshotFolder' attribute"));
+ goto cleanup;
+ }
+
+ currentStateModifiedString = virXMLPropString(machineNode, "currentStateModified");
+ if (currentStateModifiedString != NULL && STREQ(currentStateModifiedString, "true")) {
+ machineDescription->currentStateModified = 1;
+ } else {
+ machineDescription->currentStateModified = 0;
+ }
+ machineDescription->lastStateChange = virXMLPropString(machineNode, "lastStateChange");
+ if (machineDescription->lastStateChange == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse 'lastStateChange' attribute"));
+ goto cleanup;
+ }
+
+ xPathContext->node = machineNode;
+ cur = virXPathNode("./vbox:Hardware", xPathContext);
+ if (cur == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse node"));
+ goto cleanup;
+ }
+ machineDescription->hardware = virXMLNodeToString(xml, cur);
+
+ cur = virXPathNode("./vbox:ExtraData", xPathContext);
+ if (cur)
+ machineDescription->extraData = virXMLNodeToString(xml, cur);
+
+ cur = virXPathNode("./vbox:StorageControllers", xPathContext);
+ if (cur == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse node"));
+ goto cleanup;
+ }
+ machineDescription->storageController = virXMLNodeToString(xml, cur);
+
+ /*retrieve mediaRegistry*/
+ cur = virXPathNode("./vbox:MediaRegistry", xPathContext);
+ if (cur == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s"
+ , _("Cannot parse node"));
+ goto cleanup;
+ }
+ machineDescription->mediaRegistry = virVBoxSnapshotConfRetrieveMediaRegistry(cur, xPathContext, machineLocation);
+ if (machineDescription->mediaRegistry == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ ("Unable to create media registry"));
+ goto cleanup;
+ }
+
+ /*retrieve snapshot*/
+ xPathContext->node = machineNode;
+ cur = virXPathNode("./vbox:Snapshot", xPathContext);
+ if (cur != NULL) {
+ machineDescription->snapshot = virVBoxSnapshotConfRetrieveSnapshot(cur, xPathContext);
+ if (!machineDescription->snapshot)
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ xmlXPathFreeContext(xPathContext);
+ xmlFreeDoc(xml);
+
+ VIR_FREE(currentStateModifiedString);
+ VIR_FREE(currentSnapshotAttribute);
+ virStringFreeList(searchResultTab);
+ if (ret < 0) {
+ virVBoxSnapshotConfMachineFree(machineDescription);
+ machineDescription = NULL;
+ }
+ return machineDescription;
+}
+
+/*
+ *addSnapshotToXmlMachine: Add a vboxSnapshotXmlSnapshotPtr to a vboxSnapshotXmlMachinePtr.
+ *If 'snapshotParentName' is not NULL, the snapshot whose name is 'snapshotParentName'
+ *becomes the snapshot parent.
+ *return 0 on success
+ *return -1 on failure
+ */
+int
+virVBoxSnapshotConfAddSnapshotToXmlMachine(virVBoxSnapshotConfSnapshotPtr snapshot,
+ virVBoxSnapshotConfMachinePtr machine,
+ const char *snapshotParentName)
+{
+ int ret = -1;
+ virVBoxSnapshotConfSnapshotPtr parentSnapshot = NULL;
+
+ if (snapshot == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Snapshot is Null"));
+ goto cleanup;
+ }
+ if (machine == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Machine is Null"));
+ goto cleanup;
+ }
+
+ /*If parent is NULL and the machine has no snapshot yet,
+ *it means that the added snapshot is the first snapshot*/
+ if (snapshotParentName == NULL) {
+ if (machine->snapshot != NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to add this snapshot, there is already a snapshot "
+ "linked to the machine"));
+ goto cleanup;
+ }
+ machine->snapshot = snapshot;
+ ret = 0;
+ goto cleanup;
+ } else {
+ if (machine->snapshot == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("The machine has no snapshot and it should have it"));
+ goto cleanup;
+ }
+ parentSnapshot = virVBoxSnapshotConfSnapshotByName(machine->snapshot, snapshotParentName);
+ if (parentSnapshot == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to find the snapshot %s"), snapshotParentName);
+ goto cleanup;
+ }
+ if (VIR_EXPAND_N(parentSnapshot->children, parentSnapshot->nchildren, 1) < 0)
+ goto cleanup;
+
+ parentSnapshot->children[parentSnapshot->nchildren - 1] = snapshot;
+ ret = 0;
+ }
+
+ cleanup:
+ return ret;
+}
+
+/*
+ *addHardDisksToMediaRegistry: Add a vboxSnapshotXmlHardDiskPtr to the registry as a
+ *child of the disk whose uuid is 'parentHardDiskId'.
+ *return 0 on success
+ *return -1 on failure
+ */
+int
+virVBoxSnapshotConfAddHardDiskToMediaRegistry(virVBoxSnapshotConfHardDiskPtr hardDisk,
+ virVBoxSnapshotConfMediaRegistryPtr mediaRegistry,
+ const char *parentHardDiskId)
+{
+ int ret = -1;
+ size_t i = 0;
+ virVBoxSnapshotConfHardDiskPtr parentDisk = NULL;
+ if (hardDisk == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Hard disk is null"));
+ goto cleanup;
+ }
+ if (mediaRegistry == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Media Registry is null"));
+ goto cleanup;
+ }
+
+ for (i = 0; i < mediaRegistry->ndisks; i++) {
+ parentDisk = virVBoxSnapshotConfHardDiskById(mediaRegistry->disks[i], parentHardDiskId);
+ if (parentDisk != NULL)
+ break;
+ }
+ if (parentDisk == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to get the parent disk"));
+ goto cleanup;
+ }
+ /*Hard disk found*/
+ if (VIR_EXPAND_N(parentDisk->children, parentDisk->nchildren, 1) < 0)
+ goto cleanup;
+
+ parentDisk->children[parentDisk->nchildren - 1] = hardDisk;
+ if (hardDisk->parent == NULL) {
+ hardDisk->parent = parentDisk;
+ }
+ ret = 0;
+
+ cleanup:
+ return ret;
+}
+
+/*
+ *removeSnapshot: Remove the vboxSnapshotXmlSnapshotPtr whose name is 'snapshotName'
+ *from a vboxSnapshotXmlMachinePtr.
+ *return 0 on success
+ *return -1 on failure
+ */
+int
+virVBoxSnapshotConfRemoveSnapshot(virVBoxSnapshotConfMachinePtr machine,
+ const char *snapshotName)
+{
+ int ret = -1;
+ size_t i = 0;
+ virVBoxSnapshotConfSnapshotPtr snapshot = NULL;
+ virVBoxSnapshotConfSnapshotPtr parentSnapshot = NULL;
+ if (machine == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("machine is null"));
+ goto cleanup;
+ }
+ if (snapshotName == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("snapshotName is null"));
+ goto cleanup;
+ }
+ if (machine->snapshot == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("the machine has no snapshot"));
+ goto cleanup;
+ }
+ snapshot = virVBoxSnapshotConfSnapshotByName(machine->snapshot, snapshotName);
+ if (snapshot == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to find the snapshot with name %s"), snapshotName);
+ goto cleanup;
+ }
+ if (snapshot->nchildren > 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("This snapshot has children, "
+ "please delete theses snapshots before"));
+ goto cleanup;
+ }
+
+ if (snapshot->parent == NULL) {
+ if (machine->snapshot != snapshot) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("You are trying to remove a snapshot which does not exists"));
+ goto cleanup;
+ }
+ machine->snapshot = NULL;
+ virVBoxSnapshotConfSnapshotFree(snapshot);
+ ret = 0;
+ goto cleanup;
+ }
+ parentSnapshot = snapshot->parent;
+
+ snapshot->parent = NULL;
+ while (i < parentSnapshot->nchildren && parentSnapshot->children[i] != snapshot) {
+ ++i;
+ }
+ if (VIR_DELETE_ELEMENT(parentSnapshot->children, i, parentSnapshot->nchildren) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ return ret;
+}
+
+/*
+ *removeHardDisk: Remove the vboxSnapshotXmlHardDiskPtr whose uuid is 'uuid' from a
+ *vboxSnapshotXmlMediaRegistryPtr. The hard disk must not have any children.
+ *return 0 on success
+ *return -1 on failure
+ */
+int
+virVBoxSnapshotConfRemoveHardDisk(virVBoxSnapshotConfMediaRegistryPtr mediaRegistry,
+ const char *uuid)
+{
+ int ret = -1;
+ size_t i = 0;
+ virVBoxSnapshotConfHardDiskPtr hardDisk = NULL;
+ virVBoxSnapshotConfHardDiskPtr parentHardDisk = NULL;
+ if (mediaRegistry == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Media registry is null"));
+ goto cleanup;
+ }
+ if (uuid == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Uuid is null"));
+ goto cleanup;
+ }
+
+ for (i = 0; i < mediaRegistry->ndisks; i++) {
+ hardDisk = virVBoxSnapshotConfHardDiskById(mediaRegistry->disks[i], uuid);
+ if (hardDisk != NULL)
+ break;
+ }
+ if (hardDisk == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to find the hard disk with uuid %s"), uuid);
+ goto cleanup;
+ }
+ if (hardDisk->parent == NULL) {
+ //it means that the hard disk is in 'root'
+ for (i = 0; i < mediaRegistry->ndisks; i++) {
+ if (hardDisk == mediaRegistry->disks[i])
+ break;
+ }
+ if (VIR_DELETE_ELEMENT(mediaRegistry->disks, i, mediaRegistry->ndisks) < 0)
+ goto cleanup;
+ ret = 0;
+ goto cleanup;
+ }
+
+ parentHardDisk = hardDisk->parent;
+ i = 0;
+ while (i < parentHardDisk->nchildren && parentHardDisk->children[i] != hardDisk) {
+ ++i;
+ }
+ hardDisk->parent = NULL;
+ if (VIR_DELETE_ELEMENT(parentHardDisk->children, i, parentHardDisk->nchildren) < 0)
+ goto cleanup;
+ ret = 0;
+
+ cleanup:
+ return ret;
+}
+
+/*vboxSnapshotSaveVboxFile: Create a VirtualBox XML file from a vboxSnapshotXmlMachinePtr.
+ *The file is saved at 'filePath'.
+ *return 0 on success
+ *return -1 on failure
+ */
+int
+virVBoxSnapshotConfSaveVboxFile(virVBoxSnapshotConfMachinePtr machine,
+ const char *filePath)
+{
+ int ret = -1;
+ size_t i = 0;
+ xmlDocPtr xml = NULL;
+ xmlNodePtr mediaRegistryNode = NULL;
+ xmlNodePtr snapshotNode = NULL;
+ xmlNodePtr machineNode = NULL;
+ xmlNodePtr hardDisksNode = NULL;
+ xmlNodePtr cur = NULL;
+ xmlParserErrors parseError = XML_ERR_OK;
+ char *currentSnapshot = NULL;
+ char *timeStamp = NULL;
+
+ char **firstRegex = NULL;
+ int firstRegexResult = 0;
+ char **secondRegex = NULL;
+ int secondRegexResult = 0;
+
+ if (machine == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Machine is null"));
+ goto cleanup;
+ }
+ if (filePath == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Filepath is null"));
+ goto cleanup;
+ }
+ xml = xmlNewDoc(BAD_CAST "1.0");
+ if (!xml) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ cur = xmlNewNode(NULL, BAD_CAST "VirtualBox");
+ if (!cur) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (!xmlNewProp(cur, BAD_CAST "version", BAD_CAST "1.12-linux")) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Error in xmlNewProp"));
+ goto cleanup;
+ }
+ if (xmlNewProp(cur,
+ BAD_CAST "xmlns",
+ BAD_CAST "http://www.innotek.de/VirtualBox-settings") == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Error in xmlNewProp"));
+ goto cleanup;
+ }
+
+ xmlDocSetRootElement(xml, cur);
+
+ cur = xmlNewDocComment(xml,
+ BAD_CAST "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 (!cur) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (!xmlAddPrevSibling(xmlDocGetRootElement(xml), cur)) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Error in xmlAddPrevSibling"));
+ goto cleanup;
+ }
+
+ machineNode = xmlNewNode(NULL, BAD_CAST "Machine");
+ if (!machineNode) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (!xmlNewProp(machineNode, BAD_CAST "uuid", BAD_CAST machine->uuid)) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Error in xmlNewProp"));
+ goto cleanup;
+ }
+ if (!xmlNewProp(machineNode, BAD_CAST "name", BAD_CAST machine->name)) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Error in xmlNewProp"));
+ goto cleanup;
+ }
+
+ if (machine->currentSnapshot != NULL) {
+ if (virAsprintf(¤tSnapshot, "{%s}", machine->currentSnapshot) < 0)
+ goto cleanup;
+ if (!xmlNewProp(machineNode, BAD_CAST "currentSnapshot", BAD_CAST currentSnapshot)) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Error in xmlNewProp"));
+ goto cleanup;
+ }
+ }
+ if (!xmlNewProp(machineNode, BAD_CAST "snapshotFolder", BAD_CAST machine->snapshotFolder)) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Error in xmlNewProp"));
+ goto cleanup;
+ }
+ if (!xmlNewProp(machineNode, BAD_CAST "currentStateModified",
+ BAD_CAST(machine->currentStateModified == 0 ? "false" : "true"))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Error in xmlNewProp"));
+ goto cleanup;
+ }
+ if (!xmlNewProp(machineNode, BAD_CAST "OSType", BAD_CAST "Other")) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Error in xmlNewProp"));
+ goto cleanup;
+ }
+
+ firstRegexResult = virStringSearch(machine->lastStateChange,
+ "([0-9]{4}-[0-9]{2}-[0-9]{2})",
+ 1,
+ &firstRegex);
+ secondRegexResult = virStringSearch(machine->lastStateChange,
+ "([0-9]{2}:[0-9]{2}:[0-9]{2})",
+ 1,
+ &secondRegex);
+ if (firstRegexResult < 1)
+ goto cleanup;
+ if (secondRegexResult < 1)
+ goto cleanup;
+
+ if (virAsprintf(&timeStamp, "%sT%sZ", firstRegex[0], secondRegex[0]) < 0)
+ goto cleanup;
+ if (!xmlNewProp(machineNode, BAD_CAST "lastStateChange", BAD_CAST timeStamp)) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Error in xmlNewProp"));
+ goto cleanup;
+ }
+ xmlAddChild(xmlDocGetRootElement(xml), machineNode);
+
+ mediaRegistryNode = xmlNewNode(NULL, BAD_CAST "MediaRegistry");
+ if (!mediaRegistryNode) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ xmlAddChild(machineNode, mediaRegistryNode);
+ for (i = 0; i < machine->mediaRegistry->notherMedia; i++) {
+ parseError = xmlParseInNodeContext(mediaRegistryNode,
+ machine->mediaRegistry->otherMedia[i],
+ (int)strlen(machine->mediaRegistry->otherMedia[i]),
+ 0,
+ &cur);
+ if (parseError != XML_ERR_OK) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Unable to add media registry other media"));
+ goto cleanup;
+ }
+ xmlAddChild(mediaRegistryNode, cur);
+ }
+ hardDisksNode = xmlNewNode(NULL, BAD_CAST "HardDisks");
+ if (!hardDisksNode) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ for (i = 0; i < machine->mediaRegistry->ndisks; i++) {
+ xmlNodePtr child = virVBoxSnapshotConfCreateHardDiskNode(machine->mediaRegistry->disks[i]);
+ if (child != NULL)
+ xmlAddChild(hardDisksNode, child);
+ }
+ xmlAddChild(mediaRegistryNode, hardDisksNode);
+
+ parseError = xmlParseInNodeContext(machineNode,
+ machine->hardware,
+ (int)strlen(machine->hardware),
+ 0,
+ &cur);
+ if (parseError != XML_ERR_OK) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Unable to add hardware machine"));
+ goto cleanup;
+ }
+ xmlAddChild(machineNode, cur);
+
+ if (machine->extraData != NULL) {
+ parseError = xmlParseInNodeContext(xmlDocGetRootElement(xml),
+ machine->extraData,
+ (int)strlen(machine->extraData),
+ 0,
+ &cur);
+ if (parseError != XML_ERR_OK) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Unable to add extra data"));
+ goto cleanup;
+ }
+ xmlAddChild(machineNode, cur);
+ }
+
+ parseError = xmlParseInNodeContext(machineNode,
+ machine->storageController,
+ (int)strlen(machine->storageController),
+ 0,
+ &cur);
+ if (parseError != XML_ERR_OK) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Unable to add storage controller"));
+ goto cleanup;
+ }
+ xmlAddChild(machineNode, cur);
+
+ if (machine->snapshot != NULL) {
+ snapshotNode = xmlNewNode(NULL, BAD_CAST "Snapshot");
+ xmlAddChild(machineNode, snapshotNode);
+ if (virVBoxSnapshotConfSerializeSnapshot(snapshotNode, machine->snapshot) < 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Failed to serialize snapshot"));
+ goto cleanup;
+ }
+ }
+
+ if (xmlSaveFormatFileEnc(filePath, xml, "ISO-8859-1", -1) < 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Unable to save the xml"));
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ VIR_FREE(currentSnapshot);
+ VIR_FREE(timeStamp);
+
+ xmlUnlinkNode(hardDisksNode);
+ xmlFreeNode(hardDisksNode);
+
+ xmlUnlinkNode(mediaRegistryNode);
+ xmlFreeNode(mediaRegistryNode);
+
+ xmlUnlinkNode(snapshotNode);
+ xmlFreeNode(snapshotNode);
+
+ xmlUnlinkNode(cur);
+ xmlFreeNode(cur);
+
+ xmlUnlinkNode(machineNode);
+ xmlFreeNode(machineNode);
+
+ xmlFreeDoc(xml);
+
+ virStringFreeList(firstRegex);
+ virStringFreeList(secondRegex);
+ return ret;
+}
+
+/*
+ *isCurrentSnapshot: Return 1 if 'snapshotName' corresponds to the
+ *vboxSnapshotXmlMachinePtr's current snapshot, return 0 otherwise.
+ */
+int
+virVBoxSnapshotConfIsCurrentSnapshot(virVBoxSnapshotConfMachinePtr machine,
+ const char *snapshotName)
+{
+ virVBoxSnapshotConfSnapshotPtr snapshot = NULL;
+ if (machine == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Machine is null"));
+ goto cleanup;
+ }
+ if (snapshotName == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("snapshotName is null"));
+ goto cleanup;
+ }
+ snapshot = virVBoxSnapshotConfSnapshotByName(machine->snapshot, snapshotName);
+ return STREQ(snapshot->uuid, machine->currentSnapshot);
+
+ cleanup:
+ return 0;
+}
+
+/*
+ *getRWDisksPathsFromLibvirtXML: Parse a libvirt XML snapshot file, allocates and
+ *fills a list of read-write disk paths.
+ *return array length on success, -1 on failure.
+ */
+int
+virVBoxSnapshotConfGetRWDisksPathsFromLibvirtXML(const char *filePath,
+ char ***rwDisksPath)
+{
+ int result = -1;
+ size_t i = 0;
+ char **ret;
+ xmlDocPtr xml = NULL;
+ xmlXPathContextPtr xPathContext = NULL;
+ xmlNodePtr *nodes = NULL;
+ int nodeSize = 0;
+ *rwDisksPath = NULL;
+ if (filePath == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("filePath is null"));
+ goto cleanup;
+ }
+ xml = virXMLParse(filePath, NULL, NULL);
+ if (xml == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Unable to parse the xml"));
+ goto cleanup;
+ }
+ if (!(xPathContext = xmlXPathNewContext(xml))) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ xPathContext->node = xmlDocGetRootElement(xml);
+ nodeSize = virXPathNodeSet("/domainsnapshot/disks/disk", xPathContext, &nodes);
+
+ if (VIR_ALLOC_N(ret, nodeSize) < 0)
+ goto cleanup;
+
+ for (i = 0; i < nodeSize; i++) {
+ xmlNodePtr node = nodes[i];
+ xPathContext->node = node;
+ xmlNodePtr sourceNode = virXPathNode("./source", xPathContext);
+ if (sourceNode) {
+ ret[i] = virXMLPropString(sourceNode, "file");
+ }
+ }
+ result = 0;
+
+ cleanup:
+ xmlFreeDoc(xml);
+ xmlXPathFreeContext(xPathContext);
+ if (result < 0) {
+ for (i = 0; i < nodeSize; i++)
+ VIR_FREE(ret[i]);
+ VIR_FREE(ret);
+ nodeSize = -1;
+ } else {
+ *rwDisksPath = ret;
+ }
+ return nodeSize;
+}
+
+/*
+ *getRODisksPathsFromLibvirtXML: *Parse a libvirt XML snapshot file, allocates and fills
+ *a list of read-only disk paths (the parents of the read-write disks).
+ *return array length on success, -1 on failure.
+ */
+int
+virVBoxSnapshotConfGetRODisksPathsFromLibvirtXML(const char *filePath,
+ char ***roDisksPath)
+{
+ int result = -1;
+ size_t i = 0;
+ char **ret;
+ xmlDocPtr xml = NULL;
+ xmlXPathContextPtr xPathContext = NULL;
+ xmlNodePtr *nodes = NULL;
+ int nodeSize = 0;
+ if (filePath == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("filePath is null"));
+ goto cleanup;
+ }
+ xml = virXMLParse(filePath, NULL, NULL);
+ if (xml == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Unable to parse the xml"));
+ goto cleanup;
+ }
+ if (!(xPathContext = xmlXPathNewContext(xml))) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ xPathContext->node = xmlDocGetRootElement(xml);
+ nodeSize = virXPathNodeSet("/domainsnapshot/domain/devices/disk",
+ xPathContext,
+ &nodes);
+ if (VIR_ALLOC_N(ret, nodeSize) < 0)
+ goto cleanup;
+
+ for (i = 0; i < nodeSize; i++) {
+ xmlNodePtr node = nodes[i];
+ xPathContext->node = node;
+ xmlNodePtr sourceNode = virXPathNode("./source", xPathContext);
+ if (sourceNode) {
+ ret[i] = virXMLPropString(sourceNode, "file");
+ }
+ }
+ result = 0;
+
+ cleanup:
+ xmlFreeDoc(xml);
+ xmlXPathFreeContext(xPathContext);
+ if (result < 0) {
+ virStringFreeList(ret);
+ nodeSize = -1;
+ }
+ *roDisksPath = ret;
+ return nodeSize;
+}
+
+/*
+ *hardDiskUuidByLocation: Return the uuid of the hard disk whose location is 'location'
+ *return a valid uuid, or NULL on failure
+ */
+const char *
+virVBoxSnapshotConfHardDiskUuidByLocation(virVBoxSnapshotConfMachinePtr machine,
+ const char *location)
+{
+ size_t i = 0;
+ virVBoxSnapshotConfHardDiskPtr hardDisk = NULL;
+ for (i = 0; i < machine->mediaRegistry->ndisks; i++) {
+ hardDisk = virVBoxSnapshotConfHardDiskByLocation(machine->mediaRegistry->disks[i], location);
+ if (hardDisk != NULL)
+ break;
+ }
+ if (hardDisk == NULL)
+ return NULL;
+ return hardDisk->uuid;
+}
+
+/*Retreive the whole ancestry of the vboxSnapshotXmlHardDiskPtr whose location is
+ *'location', and store them in a newly allocated list of vboxSnapshotXmlHardDiskPtr.
+ *This list begins with the requested disk, and ends with the farthest ancestor.
+ *return array length on success, -1 on failure.*/
+
+size_t
+virVBoxSnapshotConfDiskListToOpen(virVBoxSnapshotConfMachinePtr machine,
+ virVBoxSnapshotConfHardDiskPtr **hardDiskToOpen,
+ const char *location)
+{
+ size_t i = 0;
+ size_t returnSize = 0;
+ virVBoxSnapshotConfHardDiskPtr *ret = NULL;
+ virVBoxSnapshotConfHardDiskPtr hardDisk = NULL;
+ for (i = 0; i < machine->mediaRegistry->ndisks; i++) {
+ hardDisk = virVBoxSnapshotConfHardDiskByLocation(machine->mediaRegistry->disks[i], location);
+ if (hardDisk != NULL)
+ break;
+ }
+ if (hardDisk == NULL)
+ return 0;
+ if (VIR_ALLOC_N(ret, 1) < 0)
+ return 0;
+
+ returnSize = 1;
+ ret[returnSize - 1] = hardDisk;
+
+ while (hardDisk->parent != NULL) {
+ if (VIR_EXPAND_N(ret, returnSize, 1) < 0)
+ return 0;
+ ret[returnSize - 1] = hardDisk->parent;
+ hardDisk = hardDisk->parent;
+ }
+ *hardDiskToOpen = ret;
+ return returnSize;
+}
+
+/*
+ *removeFakeDisks: Remove all fake disks from the machine's mediaRegistry
+ *return 0 on success
+ *return -1 on failure
+ */
+int
+virVBoxSnapshotConfRemoveFakeDisks(virVBoxSnapshotConfMachinePtr machine)
+{
+ int ret = -1;
+ size_t i = 0;
+ size_t j = 0;
+ size_t tempSize = 0;
+ size_t diskSize = 0;
+ virVBoxSnapshotConfHardDiskPtr *tempList = NULL;
+ virVBoxSnapshotConfHardDiskPtr *diskList = NULL;
+ if (VIR_ALLOC_N(diskList, 0) < 0)
+ return ret;
+
+ for (i = 0; i < machine->mediaRegistry->ndisks; i++) {
+ tempSize = virVBoxSnapshotConfAllChildren(machine->mediaRegistry->disks[i], &tempList);
+ if (VIR_EXPAND_N(diskList, diskSize, tempSize) < 0)
+ return ret;
+ for (j = 0; j < tempSize; j++) {
+ diskList[diskSize - tempSize + j] = tempList[j];
+ }
+ }
+ for (i = 0; i < diskSize; i++) {
+ if (strstr(diskList[i]->location, "fake") != NULL) {
+ if (virVBoxSnapshotConfRemoveHardDisk(machine->mediaRegistry, diskList[i]->uuid) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to remove hard disk %s from media registry"),
+ diskList[i]->location);
+ return ret;
+ }
+ }
+ }
+ ret = 0;
+ return ret;
+}
+
+/*
+ *diskIsInMediaRegistry: Check if the media registry contains the disk whose location is 'location'.
+ *return 0 if the disk is not in the media registry
+ *return 1 if the disk is in the media registry
+ *return -1 on failure
+ */
+int
+virVBoxSnapshotConfDiskIsInMediaRegistry(virVBoxSnapshotConfMachinePtr machine,
+ const char *location)
+{
+ int ret = -1;
+ size_t i = 0;
+ size_t j = 0;
+ size_t tempSize = 0;
+ size_t diskSize = 0;
+ virVBoxSnapshotConfHardDiskPtr *tempList = NULL;
+ virVBoxSnapshotConfHardDiskPtr *diskList = NULL;
+ if (VIR_ALLOC_N(diskList, 0) < 0)
+ return ret;
+
+ for (i = 0; i < machine->mediaRegistry->ndisks; i++) {
+ tempSize = virVBoxSnapshotConfAllChildren(machine->mediaRegistry->disks[i], &tempList);
+ if (VIR_EXPAND_N(diskList, diskSize, tempSize) < 0)
+ return ret;
+ for (j = 0; j < tempSize; j++) {
+ diskList[diskSize - tempSize + j] = tempList[j];
+ }
+ }
+ for (i = 0; i < diskSize; i++) {
+ if (STREQ(diskList[i]->location, location)) {
+ ret = 1;
+ return ret;
+ }
+ }
+ ret = 0;
+ return ret;
+}
+
+/*
+ *hardDisksPtrByLocation: Return a vboxSnapshotXmlHardDiskPtr whose location is 'location'
+ */
+virVBoxSnapshotConfHardDiskPtr
+virVBoxSnapshotConfHardDiskPtrByLocation(virVBoxSnapshotConfMachinePtr machine,
+ const char *location)
+{
+ int it = 0;
+ virVBoxSnapshotConfHardDiskPtr disk = NULL;
+ for (it = 0; it < machine->mediaRegistry->ndisks; it++) {
+ disk = virVBoxSnapshotConfHardDiskByLocation(machine->mediaRegistry->disks[it], location);
+ if (disk != NULL)
+ break;
+ }
+ return disk;
+}
diff --git a/src/vbox/vbox_snapshot_conf.h b/src/vbox/vbox_snapshot_conf.h
new file mode 100644
index 0000000000000000000000000000000000000000..3864b308470dc7ac997d3e1d49ab3e6d832fff9a
--- /dev/null
+++ b/src/vbox/vbox_snapshot_conf.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2014, diateam (www.diateam.net)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * .
+ */
+
+#ifndef VBOX_SNAPSHOT_CONF_H
+# define VBOX_SNAPSHOT_CONF_H
+
+# include "internal.h"
+
+# define VBOX_UUID_REGEX "([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})"
+
+/*Stores VirtualBox xml hard disk information
+A hard disk can have a parent and children*/
+typedef struct _virVBoxSnapshotConfHardDisk virVBoxSnapshotConfHardDisk;
+typedef virVBoxSnapshotConfHardDisk *virVBoxSnapshotConfHardDiskPtr;
+struct _virVBoxSnapshotConfHardDisk {
+ virVBoxSnapshotConfHardDiskPtr parent;
+ char *uuid;
+ char *location;
+ char *format;
+ char *type;
+ size_t nchildren;
+ virVBoxSnapshotConfHardDiskPtr *children;
+};
+
+/*Stores Virtualbox xml media registry information
+We separate disks from other media to manipulate them*/
+typedef struct _virVBoxSnapshotConfMediaRegistry virVBoxSnapshotConfMediaRegistry;
+typedef virVBoxSnapshotConfMediaRegistry *virVBoxSnapshotConfMediaRegistryPtr;
+struct _virVBoxSnapshotConfMediaRegistry {
+ size_t ndisks;
+ virVBoxSnapshotConfHardDiskPtr *disks;
+ size_t notherMedia;
+ char **otherMedia;
+};
+
+/*Stores VirtualBox xml snapshot information
+A snapshot can have a parent and children*/
+typedef struct _virVBoxSnapshotConfSnapshot virVBoxSnapshotConfSnapshot;
+typedef virVBoxSnapshotConfSnapshot *virVBoxSnapshotConfSnapshotPtr;
+struct _virVBoxSnapshotConfSnapshot {
+ virVBoxSnapshotConfSnapshotPtr parent;
+ char *uuid;
+ char *name;
+ char *timeStamp;
+ char *description;
+ char *hardware;
+ char *storageController;
+ size_t nchildren;
+ virVBoxSnapshotConfSnapshotPtr *children;
+};
+
+/*Stores VirtualBox xml Machine information*/
+typedef struct _virVBoxSnapshotConfMachine virVBoxSnapshotConfMachine;
+typedef virVBoxSnapshotConfMachine *virVBoxSnapshotConfMachinePtr;
+struct _virVBoxSnapshotConfMachine {
+ char *uuid;
+ char *name;
+ char *currentSnapshot;
+ char *snapshotFolder;
+ int currentStateModified;
+ char *lastStateChange;
+ virVBoxSnapshotConfMediaRegistryPtr mediaRegistry;
+ char *hardware;
+ char *extraData;
+ virVBoxSnapshotConfSnapshotPtr snapshot;
+ char *storageController;
+};
+
+void
+virVboxSnapshotConfHardDiskFree(virVBoxSnapshotConfHardDiskPtr disk);
+void
+virVBoxSnapshotConfMediaRegistryFree(virVBoxSnapshotConfMediaRegistryPtr mediaRegistry);
+void
+virVBoxSnapshotConfSnapshotFree(virVBoxSnapshotConfSnapshotPtr snapshot);
+void
+virVBoxSnapshotConfMachineFree(virVBoxSnapshotConfMachinePtr machine);
+
+virVBoxSnapshotConfMachinePtr
+virVBoxSnapshotConfLoadVboxFile(const char *filePath,
+ const char *machineLocation);
+int
+virVBoxSnapshotConfAddSnapshotToXmlMachine(virVBoxSnapshotConfSnapshotPtr snapshot,
+ virVBoxSnapshotConfMachinePtr machine,
+ const char *snapshotParentName);
+int
+virVBoxSnapshotConfAddHardDiskToMediaRegistry(virVBoxSnapshotConfHardDiskPtr hardDisk,
+ virVBoxSnapshotConfMediaRegistryPtr mediaRegistry,
+ const char *parentHardDiskId);
+int
+virVBoxSnapshotConfRemoveSnapshot(virVBoxSnapshotConfMachinePtr machine,
+ const char *snapshotName);
+int
+virVBoxSnapshotConfRemoveHardDisk(virVBoxSnapshotConfMediaRegistryPtr mediaRegistry,
+ const char *uuid);
+int
+virVBoxSnapshotConfSaveVboxFile(virVBoxSnapshotConfMachinePtr machine,
+ const char *filePath);
+int
+virVBoxSnapshotConfIsCurrentSnapshot(virVBoxSnapshotConfMachinePtr machine,
+ const char *snapshotName);
+int
+virVBoxSnapshotConfGetRWDisksPathsFromLibvirtXML(const char *filePath,
+ char ***realReadWriteDisksPath);
+int
+virVBoxSnapshotConfGetRODisksPathsFromLibvirtXML(const char *filePath,
+ char ***realReadOnlyDisksPath);
+const char *
+virVBoxSnapshotConfHardDiskUuidByLocation(virVBoxSnapshotConfMachinePtr machine,
+ const char *location);
+size_t
+virVBoxSnapshotConfDiskListToOpen(virVBoxSnapshotConfMachinePtr machine,
+ virVBoxSnapshotConfHardDiskPtr **hardDiskToOpen,
+ const char *location);
+int
+virVBoxSnapshotConfRemoveFakeDisks(virVBoxSnapshotConfMachinePtr machine);
+int
+virVBoxSnapshotConfDiskIsInMediaRegistry(virVBoxSnapshotConfMachinePtr machine,
+ const char *location);
+virVBoxSnapshotConfHardDiskPtr
+virVBoxSnapshotConfHardDiskPtrByLocation(virVBoxSnapshotConfMachinePtr machine,
+ const char *location);
+virVBoxSnapshotConfSnapshotPtr
+virVBoxSnapshotConfSnapshotByName(virVBoxSnapshotConfSnapshotPtr snapshot,
+ const char *snapshotName);
+
+#endif /*VBOX_SNAPSHOT_CONF_H*/
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9f4dff34a480612b27c265d0621261b937802a63..c99906158b11efcb7f49cbc1e4c765aee3df8c7d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -125,6 +125,7 @@ EXTRA_DIST = \
sysinfodata \
test-lib.sh \
vircaps2xmldata \
+ vboxsnapshotxmldata \
virsh-uriprecedence \
virfiledata \
virpcitestdata \
@@ -236,6 +237,10 @@ if WITH_ESX
test_programs += esxutilstest
endif WITH_ESX
+if WITH_VBOX
+test_programs += vboxsnapshotxmltest
+endif WITH_VBOX
+
if WITH_VMX
test_programs += vmx2xmltest xml2vmxtest
endif WITH_VMX
@@ -619,6 +624,16 @@ else ! WITH_ESX
EXTRA_DIST += esxutilstest.c
endif ! WITH_ESX
+if WITH_VBOX
+vboxsnapshotxmltest_SOURCES = \
+ vboxsnapshotxmltest.c \
+ testutils.c testutils.h
+vbox_LDADDS = ../src/libvirt_driver_vbox_impl.la
+vboxsnapshotxmltest_LDADD = $(LDADDS) $(vbox_LDADDS)
+else ! WITH_VBOX
+EXTRA_DIST += vboxsnapshotxmltest.c
+endif ! WITH_VBOX
+
if WITH_VMX
vmx2xmltest_SOURCES = \
vmx2xmltest.c \
diff --git a/tests/vboxsnapshotxmldata/2disks-1snap.vbox b/tests/vboxsnapshotxmldata/2disks-1snap.vbox
new file mode 100644
index 0000000000000000000000000000000000000000..f80c5af855fea8c10e718d24bfb5d1f38b01bdf1
--- /dev/null
+++ b/tests/vboxsnapshotxmldata/2disks-1snap.vbox
@@ -0,0 +1,322 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/vboxsnapshotxmldata/2disks-2snap.vbox b/tests/vboxsnapshotxmldata/2disks-2snap.vbox
new file mode 100644
index 0000000000000000000000000000000000000000..8277937df044fb99671c6bc844daf88c314df67c
--- /dev/null
+++ b/tests/vboxsnapshotxmldata/2disks-2snap.vbox
@@ -0,0 +1,478 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/vboxsnapshotxmldata/2disks-3snap-brother.vbox b/tests/vboxsnapshotxmldata/2disks-3snap-brother.vbox
new file mode 100644
index 0000000000000000000000000000000000000000..b5472f947e045700fd37f6916be3a7574422ea46
--- /dev/null
+++ b/tests/vboxsnapshotxmldata/2disks-3snap-brother.vbox
@@ -0,0 +1,786 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/vboxsnapshotxmldata/2disks-3snap.vbox b/tests/vboxsnapshotxmldata/2disks-3snap.vbox
new file mode 100644
index 0000000000000000000000000000000000000000..2fb6fe75e16e54c6ea2498875bb235591d612db6
--- /dev/null
+++ b/tests/vboxsnapshotxmldata/2disks-3snap.vbox
@@ -0,0 +1,636 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/vboxsnapshotxmldata/2disks-nosnap.vbox b/tests/vboxsnapshotxmldata/2disks-nosnap.vbox
new file mode 100644
index 0000000000000000000000000000000000000000..70375035dcd6083718d92156284497da6fa9dc1b
--- /dev/null
+++ b/tests/vboxsnapshotxmldata/2disks-nosnap.vbox
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/vboxsnapshotxmltest.c b/tests/vboxsnapshotxmltest.c
new file mode 100644
index 0000000000000000000000000000000000000000..7795688e61b82d373dd4d7df352308ac4f7f1257
--- /dev/null
+++ b/tests/vboxsnapshotxmltest.c
@@ -0,0 +1,161 @@
+#include
+
+#include "testutils.h"
+
+#ifdef WITH_VBOX
+
+# include
+# include
+# include
+# include "vbox/vbox_snapshot_conf.h"
+
+# define VIR_FROM_THIS VIR_FROM_NONE
+
+static const char *testSnapshotXMLVariableLineRegexStr =
+ "lastStateChange=[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z";
+
+regex_t *testSnapshotXMLVariableLineRegex = NULL;
+
+static char *
+testFilterXML(char *xml)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ char **xmlLines = NULL;
+ char **xmlLine;
+ char *ret = NULL;
+
+ if (!(xmlLines = virStringSplit(xml, "\n", 0))) {
+ VIR_FREE(xml);
+ goto cleanup;
+ }
+ VIR_FREE(xml);
+
+ for (xmlLine = xmlLines; *xmlLine; xmlLine++) {
+ if (regexec(testSnapshotXMLVariableLineRegex,
+ *xmlLine, 0, NULL, 0) == 0)
+ continue;
+
+ virBufferStrcat(&buf, *xmlLine, "\n", NULL);
+ }
+
+ if (virBufferError(&buf)) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ ret = virBufferContentAndReset(&buf);
+
+ cleanup:
+ virBufferFreeAndReset(&buf);
+ virStringFreeList(xmlLines);
+ return ret;
+}
+
+static int
+testCompareXMLtoXMLFiles(const char *xml)
+{
+ char *xmlData = NULL;
+ char *actual = NULL;
+ char *pathResult = NULL;
+ int ret = -1;
+ virVBoxSnapshotConfMachinePtr machine = NULL;
+ if (virAsprintf(&pathResult, "%s/vboxsnapshotxmldata/testResult.vbox",
+ abs_srcdir) < 0)
+ return -1;
+
+ if (virtTestLoadFile(xml, &xmlData) < 0)
+ goto fail;
+
+ if (!(machine = virVBoxSnapshotConfLoadVboxFile(xml, (char*)"")))
+ goto fail;
+
+ if (virVBoxSnapshotConfSaveVboxFile(machine, pathResult) < 0)
+ goto fail;
+
+ if (virtTestLoadFile(pathResult, &actual) < 0)
+ goto fail;
+
+ if (unlink(pathResult) < 0)
+ goto fail;
+
+ if (!(actual = testFilterXML(actual)))
+ goto fail;
+ if (!(xmlData = testFilterXML(xmlData)))
+ goto fail;
+
+ if (STRNEQ(actual, xmlData)) {
+ virtTestDifference(stderr, xmlData, actual);
+ goto fail;
+ }
+
+ ret = 0;
+
+ fail:
+ VIR_FREE(xmlData);
+ VIR_FREE(actual);
+ virVBoxSnapshotConfMachineFree(machine);
+ VIR_FREE(pathResult);
+
+ return ret;
+}
+
+static int
+testCompareXMLToXMLHelper(const void *data)
+{
+ int result = -1;
+ char *xml = NULL;
+
+ if (virAsprintf(&xml, "%s/vboxsnapshotxmldata/%s.vbox",
+ abs_srcdir, (const char*)data) < 0)
+ return -1;
+
+ result = testCompareXMLtoXMLFiles(xml);
+
+ VIR_FREE(xml);
+ return result;
+}
+
+static int
+mymain(void)
+{
+ int ret = 0;
+ if (VIR_ALLOC(testSnapshotXMLVariableLineRegex) < 0)
+ goto cleanup;
+
+ if (regcomp(testSnapshotXMLVariableLineRegex,
+ testSnapshotXMLVariableLineRegexStr,
+ REG_EXTENDED | REG_NOSUB) != 0) {
+ ret = -1;
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ "failed to compile test regex");
+ goto cleanup;
+ }
+
+# define DO_TEST(name) \
+ if (virtTestRun("VBox Snapshot XML-2-XML " name, \
+ testCompareXMLToXMLHelper, (name)) < 0) \
+ ret = -1
+
+ DO_TEST("2disks-nosnap");
+ DO_TEST("2disks-1snap");
+ DO_TEST("2disks-2snap");
+ DO_TEST("2disks-3snap");
+ DO_TEST("2disks-3snap-brother");
+
+ cleanup:
+ if (testSnapshotXMLVariableLineRegex)
+ regfree(testSnapshotXMLVariableLineRegex);
+ VIR_FREE(testSnapshotXMLVariableLineRegex);
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+VIRT_TEST_MAIN(mymain)
+
+#else
+
+int main(void)
+{
+ return EXIT_AM_SKIP;
+}
+
+#endif /*WITH_VBOX*/