mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-01 10:51:12 +00:00
c2a5a4e7ea
We have couple of functions that operate over NULL terminated lits of strings. However, our naming sucks: virStringJoin virStringFreeList virStringFreeListCount virStringArrayHasString virStringGetFirstWithPrefix We can do better: virStringListJoin virStringListFree virStringListFreeCount virStringListHasString virStringListGetFirstWithPrefix Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
1568 lines
51 KiB
C
1568 lines
51 KiB
C
/*
|
|
* 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
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "vbox_snapshot_conf.h"
|
|
#include "virerror.h"
|
|
#include "viralloc.h"
|
|
#include "virlog.h"
|
|
#include "virstring.h"
|
|
#include "virxml.h"
|
|
|
|
#include <libxml/xpathInternals.h>
|
|
|
|
#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 <HardDisk> '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 <HardDisk> '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 <HardDisk> 'format' attribute"));
|
|
goto cleanup;
|
|
}
|
|
hardDisk->type = virXMLPropString(diskNode, "type");
|
|
result = 0;
|
|
|
|
cleanup:
|
|
VIR_FREE(uuid);
|
|
VIR_FREE(nodes);
|
|
VIR_FREE(location);
|
|
VIR_FREE(tmp);
|
|
virStringListFree(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 <Snapshot> '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 <Snapshot> 'name' attribute"));
|
|
goto cleanup;
|
|
}
|
|
snapshot->timeStamp = virXMLPropString(snapshotNode, "timeStamp");
|
|
if (snapshot->timeStamp == NULL) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("Cannot parse <Snapshot> '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 <Snapshot> <Hardware> 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 <Snapshot> <StorageControllers> 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);
|
|
virStringListFree(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);
|
|
}
|
|
virStringListFree(firstRegex);
|
|
virStringListFree(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)
|
|
goto error;
|
|
|
|
for (j = 0; j < tempSize; j++)
|
|
ret[returnSize - tempSize + j] = tempList[j];
|
|
|
|
VIR_FREE(tempList);
|
|
}
|
|
|
|
if (VIR_EXPAND_N(ret, returnSize, 1) < 0)
|
|
goto error;
|
|
|
|
ret[returnSize - 1] = disk;
|
|
*list = ret;
|
|
return returnSize;
|
|
|
|
error:
|
|
VIR_FREE(tempList);
|
|
VIR_FREE(ret);
|
|
return 0;
|
|
}
|
|
|
|
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 <VirtualBox> <Machine> node"));
|
|
goto cleanup;
|
|
}
|
|
|
|
machineDescription->uuid = virXMLPropString(machineNode, "uuid");
|
|
if (machineDescription->uuid == NULL) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("Cannot parse <Machine> 'uuid' attribute"));
|
|
goto cleanup;
|
|
}
|
|
machineDescription->name = virXMLPropString(machineNode, "name");
|
|
if (machineDescription->name == NULL) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("Cannot parse <Machine> '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 <Machine> '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 <Machine> '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 <Machine> 'lastStateChange' attribute"));
|
|
goto cleanup;
|
|
}
|
|
|
|
xPathContext->node = machineNode;
|
|
cur = virXPathNode("./vbox:Hardware", xPathContext);
|
|
if (cur == NULL) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("Cannot parse <Machine> <Hardware> 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 <Machine> <StorageControllers> 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 <Machine> <MediaRegistry> 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);
|
|
virStringListFree(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);
|
|
|
|
virStringListFree(firstRegex);
|
|
virStringListFree(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);
|
|
if (snapshot == NULL) {
|
|
virReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT,
|
|
_("Unable to find the snapshot %s"), snapshotName);
|
|
goto cleanup;
|
|
}
|
|
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 = NULL;
|
|
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);
|
|
if ((nodeSize = virXPathNodeSet("/domainsnapshot/disks/disk",
|
|
xPathContext, &nodes)) < 0)
|
|
goto cleanup;
|
|
|
|
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) {
|
|
virStringListFree(ret);
|
|
nodeSize = -1;
|
|
} else {
|
|
*rwDisksPath = ret;
|
|
}
|
|
VIR_FREE(nodes);
|
|
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 = NULL;
|
|
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);
|
|
if ((nodeSize = virXPathNodeSet("/domainsnapshot/domain/devices/disk",
|
|
xPathContext,
|
|
&nodes)) < 0)
|
|
goto cleanup;
|
|
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) {
|
|
virStringListFree(ret);
|
|
nodeSize = -1;
|
|
} else {
|
|
*roDisksPath = ret;
|
|
}
|
|
VIR_FREE(nodes);
|
|
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 -1;
|
|
|
|
for (i = 0; i < machine->mediaRegistry->ndisks; i++) {
|
|
tempSize = virVBoxSnapshotConfAllChildren(machine->mediaRegistry->disks[i], &tempList);
|
|
if (VIR_EXPAND_N(diskList, diskSize, tempSize) < 0)
|
|
goto cleanup;
|
|
|
|
for (j = 0; j < tempSize; j++)
|
|
diskList[diskSize - tempSize + j] = tempList[j];
|
|
|
|
VIR_FREE(tempList);
|
|
}
|
|
|
|
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);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
VIR_FREE(diskList);
|
|
VIR_FREE(tempList);
|
|
|
|
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 -1;
|
|
|
|
for (i = 0; i < machine->mediaRegistry->ndisks; i++) {
|
|
tempSize = virVBoxSnapshotConfAllChildren(machine->mediaRegistry->disks[i], &tempList);
|
|
if (VIR_EXPAND_N(diskList, diskSize, tempSize) < 0)
|
|
goto cleanup;
|
|
|
|
for (j = 0; j < tempSize; j++)
|
|
diskList[diskSize - tempSize + j] = tempList[j];
|
|
|
|
VIR_FREE(tempList);
|
|
}
|
|
|
|
for (i = 0; i < diskSize; i++) {
|
|
if (STREQ(diskList[i]->location, location)) {
|
|
ret = 1;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
VIR_FREE(diskList);
|
|
VIR_FREE(tempList);
|
|
|
|
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;
|
|
}
|