[libvirt] [PATCH v8 0/4] Handling of undefine and re define snapshots with VirtualBox 4.2 or higher

Hello, This is a new series of patches in order to support undefining and redefining snapshots with VirtualBox 4.2 or higher. These patches are based on Manuel Vives' patches, taking into account Daniel P. Berrange's remarks. The VirtualBox API provides only high level operations to manipulate snapshots, so it not possible to support flags like VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE and VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY with only API calls. Following an IRC talk with Eric Blake, the decision was made to emulate these behaviours by manipulating directly the .vbox XML files. The first patch adds extra details in the snapshot XML returned by libvirt. We will need those details in order to redefine the snapshots. The second patch adds a new API to manipulate the VirtualBox XML file. It provides several structs describing the VirtualBox XML file nodes and functions which can manipulate these structs. The third patch adds support of the VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE and VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT flags in virDomainSnapshotCreateXML. The idea is to unregister the machine, add the snapshot in the Virtualbox XML file and re-register the machine. However, VirtualBox forbids a machine to have snapshots but no current snapshot. So, if the real current snapshot has not been redefined yet, we create fake disks, allowing us to have an intermediate state in order to not corrupt the snapshot's read-write disks. These fake disks will be deleted during the next redefine. The fourth and last patch adds support of the VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY flag in virDomainSnapshotDelete. As in the third patch, we also create fake disks to not corrupt the snapshot's read-write disks. The patches were only tested with VirtualBox 4.3.10 and VirtualBox 4.2.24. Regards Yohan BELLEGUIC v8: * Fix patches according to Daniel P. Berrange review (May 01, 2014) * Rename all the methods in vbox_snapshot.{c,h} * Add a test case for serialize and deserialize a virVBoxSnapshotConfMachine * Fix memory leaks v7: * Add vbox_snapshot_conf.{h,c} files to (de)serialize VirtualBox XML files * Update the code to use the API exposed by vbox_snapshot_conf.h * Handle the fact that VirtualBox forbids a machine to have snapshots but no current snapshot v6: * Rebased because of a massive change in vbox_tmpl.c due to changes in the handling of different versions of VirtualBox v5: * The patches are modified according to a first review by Laine Stump: * renamed virSearchUuid to virSearchRegex and moved it from viruuid.{c,h} to virstring.{c,h}. * Various fixes. v4: * The code is compliant with Virtualbox 4.3 API * Some minor modifications in order to satisfy "make syntax-check" v3: * Use of STREQ_NULLABLE instead of STREQ in one case * Fix the method for finding uuids according to Ján Tomko review v2: * Fix a licence problem with the method for string replacement Manuel VIVES (1): vbox_tmpl.c: Better XML description for snapshots Yohan BELLEGUIC (3): Add vbox_snapshot_conf struct vbox_tmpl.c: Patch for redefining snapshots vbox_tmpl.c: Add function for undefining snapshot po/POTFILES.in | 1 + src/Makefile.am | 1 + src/vbox/vbox_snapshot_conf.c | 1490 +++++++++++++++ src/vbox/vbox_snapshot_conf.h | 105 ++ src/vbox/vbox_tmpl.c | 1982 +++++++++++++++++++- tests/Makefile.am | 15 + tests/vboxsnapshotxmldata/2disks-1snap.vbox | 322 ++++ tests/vboxsnapshotxmldata/2disks-2snap.vbox | 478 +++++ .../vboxsnapshotxmldata/2disks-3snap-brother.vbox | 786 ++++++++ tests/vboxsnapshotxmldata/2disks-3snap.vbox | 636 +++++++ tests/vboxsnapshotxmldata/2disks-nosnap.vbox | 168 ++ tests/vboxsnapshotxmltest.c | 161 ++ 12 files changed, 6100 insertions(+), 45 deletions(-) create mode 100644 src/vbox/vbox_snapshot_conf.c create mode 100644 src/vbox/vbox_snapshot_conf.h create mode 100644 tests/vboxsnapshotxmldata/2disks-1snap.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-2snap.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-3snap-brother.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-3snap.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-nosnap.vbox create mode 100644 tests/vboxsnapshotxmltest.c -- 1.7.10.4

From: Manuel VIVES <manuel.vives@diateam.net> It will be needed for the future patches because we will redefine snapshots --- src/vbox/vbox_tmpl.c | 476 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 471 insertions(+), 5 deletions(-) diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index e124e69..5d4a7ba 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -58,6 +58,8 @@ #include "fdstream.h" #include "viruri.h" #include "virstring.h" +#include "virtime.h" +#include "virutil.h" /* This one changes from version to version. */ #if VBOX_API_VERSION == 2002000 @@ -6039,7 +6041,9 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA, NULL); if (!(def = virDomainSnapshotDefParseString(xmlDesc, data->caps, - data->xmlopt, 0, 0))) + data->xmlopt, -1, + VIR_DOMAIN_SNAPSHOT_PARSE_DISKS | + VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE))) goto cleanup; if (def->ndisks) { @@ -6130,6 +6134,431 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, return ret; } +#if VBOX_API_VERSION >=4002000 +static +int vboxSnapshotGetReadWriteDisks(virDomainSnapshotDefPtr def, + virDomainSnapshotPtr snapshot) +{ + virDomainPtr dom = snapshot->domain; + VBOX_OBJECT_CHECK(dom->conn, int, -1); + vboxIID domiid = VBOX_IID_INITIALIZER; + IMachine *machine = NULL; + ISnapshot *snap = NULL; + IMachine *snapMachine = NULL; + vboxArray mediumAttachments = VBOX_ARRAY_INITIALIZER; + PRUint32 maxPortPerInst[StorageBus_Floppy + 1] = {}; + PRUint32 maxSlotPerPort[StorageBus_Floppy + 1] = {}; + int diskCount = 0; + nsresult rc; + vboxIID snapIid = VBOX_IID_INITIALIZER; + char *snapshotUuidStr = NULL; + size_t i = 0; + + vboxIIDFromUUID(&domiid, dom->uuid); + rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name))) + goto cleanup; + + rc = snap->vtbl->GetId(snap, &snapIid.value); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not get snapshot id")); + goto cleanup; + } + + VBOX_UTF16_TO_UTF8(snapIid.value, &snapshotUuidStr); + rc = snap->vtbl->GetMachine(snap, &snapMachine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get machine")); + goto cleanup; + } + def->ndisks = 0; + rc = vboxArrayGet(&mediumAttachments, snapMachine, snapMachine->vtbl->GetMediumAttachments); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("no medium attachments")); + goto cleanup; + } + /* get the number of attachments */ + for (i = 0; i < mediumAttachments.count; i++) { + IMediumAttachment *imediumattach = mediumAttachments.items[i]; + if (imediumattach) { + IMedium *medium = NULL; + + rc = imediumattach->vtbl->GetMedium(imediumattach, &medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get medium")); + goto cleanup; + } + if (medium) { + def->ndisks++; + VBOX_RELEASE(medium); + } + } + } + /* Allocate mem, if fails return error */ + if (VIR_ALLOC_N(def->disks, def->ndisks) < 0) + goto cleanup; + + if (!vboxGetMaxPortSlotValues(data->vboxObj, maxPortPerInst, maxSlotPerPort)) + goto cleanup; + + /* get the attachment details here */ + for (i = 0; i < mediumAttachments.count && diskCount < def->ndisks; i++) { + IStorageController *storageController = NULL; + PRUnichar *storageControllerName = NULL; + PRUint32 deviceType = DeviceType_Null; + PRUint32 storageBus = StorageBus_Null; + IMedium *disk = NULL; + PRUnichar *childLocUtf16 = NULL; + char *childLocUtf8 = NULL; + PRUint32 deviceInst = 0; + PRInt32 devicePort = 0; + PRInt32 deviceSlot = 0; + vboxArray children = VBOX_ARRAY_INITIALIZER; + vboxArray snapshotIids = VBOX_ARRAY_INITIALIZER; + IMediumAttachment *imediumattach = mediumAttachments.items[i]; + size_t j = 0; + size_t k = 0; + if (!imediumattach) + continue; + rc = imediumattach->vtbl->GetMedium(imediumattach, &disk); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get medium")); + goto cleanup; + } + if (!disk) + continue; + rc = imediumattach->vtbl->GetController(imediumattach, &storageControllerName); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get controller")); + goto cleanup; + } + if (!storageControllerName) { + VBOX_RELEASE(disk); + continue; + } + rc = vboxArrayGet(&children, disk, disk->vtbl->GetChildren); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get children disk")); + goto cleanup; + } + rc = vboxArrayGetWithPtrArg(&snapshotIids, disk, disk->vtbl->GetSnapshotIds, domiid.value); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get snapshot ids")); + goto cleanup; + } + for (j = 0; j < children.count; ++j) { + IMedium *child = children.items[j]; + for (k = 0; k < snapshotIids.count; ++k) { + PRUnichar *diskSnapId = snapshotIids.items[k]; + char *diskSnapIdStr = NULL; + VBOX_UTF16_TO_UTF8(diskSnapId, &diskSnapIdStr); + if (STREQ(diskSnapIdStr, snapshotUuidStr)) { + rc = machine->vtbl->GetStorageControllerByName(machine, + storageControllerName, + &storageController); + VBOX_UTF16_FREE(storageControllerName); + if (!storageController) { + VBOX_RELEASE(child); + break; + } + rc = child->vtbl->GetLocation(child, &childLocUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get disk location")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(childLocUtf16, &childLocUtf8); + VBOX_UTF16_FREE(childLocUtf16); + if (VIR_STRDUP(def->disks[diskCount].src.path, childLocUtf8) < 0) { + VBOX_RELEASE(child); + VBOX_RELEASE(storageController); + goto cleanup; + } + VBOX_UTF8_FREE(childLocUtf8); + + rc = storageController->vtbl->GetBus(storageController, &storageBus); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get storage controller bus")); + goto cleanup; + } + rc = imediumattach->vtbl->GetType(imediumattach, &deviceType); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get medium attachment type")); + goto cleanup; + } + rc = imediumattach->vtbl->GetPort(imediumattach, &devicePort); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get medium attachchment type")); + goto cleanup; + } + rc = imediumattach->vtbl->GetDevice(imediumattach, &deviceSlot); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get medium attachment device")); + goto cleanup; + } + def->disks[diskCount].src.type = VIR_STORAGE_TYPE_FILE; + def->disks[diskCount].name = vboxGenerateMediumName(storageBus, + deviceInst, + devicePort, + deviceSlot, + maxPortPerInst, + maxSlotPerPort); + } + VBOX_UTF8_FREE(diskSnapIdStr); + } + } + VBOX_RELEASE(storageController); + VBOX_RELEASE(disk); + diskCount++; + } + vboxArrayRelease(&mediumAttachments); + + ret = 0; + cleanup: + if (ret < 0) { + for (i = 0; i < def->dom->ndisks; i++) + VIR_FREE(def->dom->disks[i]); + VIR_FREE(def->dom->disks); + def->dom->ndisks = 0; + ret = -1; + } + VBOX_RELEASE(snap); + return ret; +} + +static +int vboxSnapshotGetReadOnlyDisks(virDomainSnapshotPtr snapshot, + virDomainSnapshotDefPtr def) +{ + virDomainPtr dom = snapshot->domain; + VBOX_OBJECT_CHECK(dom->conn, int, -1); + vboxIID domiid = VBOX_IID_INITIALIZER; + ISnapshot *snap = NULL; + IMachine *machine = NULL; + IMachine *snapMachine = NULL; + IStorageController *storageController = NULL; + IMedium *disk = NULL; + nsresult rc; + vboxIIDFromUUID(&domiid, dom->uuid); + vboxArray mediumAttachments = VBOX_ARRAY_INITIALIZER; + size_t i = 0; + PRUint32 maxPortPerInst[StorageBus_Floppy + 1] = {}; + PRUint32 maxSlotPerPort[StorageBus_Floppy + 1] = {}; + int diskCount = 0; + + rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_NO_DOMAIN, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + + if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name))) + goto cleanup; + + rc = snap->vtbl->GetMachine(snap, &snapMachine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get machine")); + goto cleanup; + } + /* + * Get READ ONLY disks + * In the snapshot metadata, these are the disks written inside the <domain> node + */ + rc = vboxArrayGet(&mediumAttachments, snapMachine, snapMachine->vtbl->GetMediumAttachments); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get medium attachments")); + goto cleanup; + } + /* get the number of attachments */ + for (i = 0; i < mediumAttachments.count; i++) { + IMediumAttachment *imediumattach = mediumAttachments.items[i]; + if (imediumattach) { + IMedium *medium = NULL; + + rc = imediumattach->vtbl->GetMedium(imediumattach, &medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get medium")); + goto cleanup; + } + if (medium) { + def->dom->ndisks++; + VBOX_RELEASE(medium); + } + } + } + + /* Allocate mem, if fails return error */ + if (VIR_ALLOC_N(def->dom->disks, def->dom->ndisks) >= 0) { + for (i = 0; i < def->dom->ndisks; i++) { + if (VIR_ALLOC(def->dom->disks[i]) < 0) + goto cleanup; + } + } else { + goto cleanup; + } + + if (!vboxGetMaxPortSlotValues(data->vboxObj, maxPortPerInst, maxSlotPerPort)) + goto cleanup; + + /* get the attachment details here */ + for (i = 0; i < mediumAttachments.count && diskCount < def->dom->ndisks; i++) { + PRUnichar *storageControllerName = NULL; + PRUint32 deviceType = DeviceType_Null; + PRUint32 storageBus = StorageBus_Null; + PRBool readOnly = PR_FALSE; + PRUnichar *mediumLocUtf16 = NULL; + char *mediumLocUtf8 = NULL; + PRUint32 deviceInst = 0; + PRInt32 devicePort = 0; + PRInt32 deviceSlot = 0; + IMediumAttachment *imediumattach = mediumAttachments.items[i]; + if (!imediumattach) + continue; + rc = imediumattach->vtbl->GetMedium(imediumattach, &disk); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get medium")); + goto cleanup; + } + if (!disk) + continue; + rc = imediumattach->vtbl->GetController(imediumattach, &storageControllerName); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get storage controller name")); + goto cleanup; + } + if (!storageControllerName) + continue; + rc = machine->vtbl->GetStorageControllerByName(machine, + storageControllerName, + &storageController); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get storage controller")); + goto cleanup; + } + VBOX_UTF16_FREE(storageControllerName); + if (!storageController) + continue; + rc = disk->vtbl->GetLocation(disk, &mediumLocUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get disk location")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(mediumLocUtf16, &mediumLocUtf8); + VBOX_UTF16_FREE(mediumLocUtf16); + if (VIR_STRDUP(def->dom->disks[diskCount]->src.path, mediumLocUtf8) < 0) + goto cleanup; + + VBOX_UTF8_FREE(mediumLocUtf8); + + rc = storageController->vtbl->GetBus(storageController, &storageBus); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get storage controller bus")); + goto cleanup; + } + if (storageBus == StorageBus_IDE) { + def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_IDE; + } else if (storageBus == StorageBus_SATA) { + def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_SATA; + } else if (storageBus == StorageBus_SCSI) { + def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_SCSI; + } else if (storageBus == StorageBus_Floppy) { + def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_FDC; + } + + rc = imediumattach->vtbl->GetType(imediumattach, &deviceType); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get medium attachment type")); + goto cleanup; + } + if (deviceType == DeviceType_HardDisk) + def->dom->disks[diskCount]->device = VIR_DOMAIN_DISK_DEVICE_DISK; + else if (deviceType == DeviceType_Floppy) + def->dom->disks[diskCount]->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY; + else if (deviceType == DeviceType_DVD) + def->dom->disks[diskCount]->device = VIR_DOMAIN_DISK_DEVICE_CDROM; + + rc = imediumattach->vtbl->GetPort(imediumattach, &devicePort); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get medium attachment port")); + goto cleanup; + } + rc = imediumattach->vtbl->GetDevice(imediumattach, &deviceSlot); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get device")); + goto cleanup; + } + rc = disk->vtbl->GetReadOnly(disk, &readOnly); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get read only attribute")); + goto cleanup; + } + if (readOnly == PR_TRUE) + def->dom->disks[diskCount]->readonly = true; + def->dom->disks[diskCount]->src.type = VIR_STORAGE_TYPE_FILE; + def->dom->disks[diskCount]->dst = vboxGenerateMediumName(storageBus, + deviceInst, + devicePort, + deviceSlot, + maxPortPerInst, + maxSlotPerPort); + if (!def->dom->disks[diskCount]->dst) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not generate medium name for the disk " + "at: controller instance:%u, port:%d, slot:%d"), + deviceInst, devicePort, deviceSlot); + ret = -1; + goto cleanup; + } + diskCount ++; + } + /* cleanup on error */ + + ret = 0; + cleanup: + if (ret < 0) { + for (i = 0; i < def->dom->ndisks; i++) + VIR_FREE(def->dom->disks[i]); + VIR_FREE(def->dom->disks); + def->dom->ndisks = 0; + } + VBOX_RELEASE(disk); + VBOX_RELEASE(storageController); + vboxArrayRelease(&mediumAttachments); + VBOX_RELEASE(snap); + return ret; +} +#endif + static char * vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, unsigned int flags) @@ -6147,6 +6576,10 @@ vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, PRInt64 timestamp; PRBool online = PR_FALSE; char uuidstr[VIR_UUID_STRING_BUFLEN]; +#if VBOX_API_VERSION >=4002000 + PRUint32 memorySize = 0; + PRUint32 CPUCount = 0; +#endif virCheckFlags(0, NULL); @@ -6161,11 +6594,41 @@ vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name))) goto cleanup; - if (VIR_ALLOC(def) < 0) + if (VIR_ALLOC(def) < 0 || VIR_ALLOC(def->dom) < 0) goto cleanup; if (VIR_STRDUP(def->name, snapshot->name) < 0) goto cleanup; +#if VBOX_API_VERSION >=4002000 + /* Register def->dom properties for them to be saved inside the snapshot XMl + * Otherwise, there is a problem while parsing the xml + */ + def->dom->virtType = VIR_DOMAIN_VIRT_VBOX; + def->dom->id = dom->id; + memcpy(def->dom->uuid, dom->uuid, VIR_UUID_BUFLEN); + if (VIR_STRDUP(def->dom->name, dom->name) < 0) + goto cleanup; + machine->vtbl->GetMemorySize(machine, &memorySize); + def->dom->mem.cur_balloon = memorySize * 1024; + /* Currently setting memory and maxMemory as same, cause + * the notation here seems to be inconsistent while + * reading and while dumping xml + */ + def->dom->mem.max_balloon = memorySize * 1024; + if (VIR_STRDUP(def->dom->os.type, "hvm") < 0) + goto cleanup; + def->dom->os.arch = virArchFromHost(); + machine->vtbl->GetCPUCount(machine, &CPUCount); + def->dom->maxvcpus = def->dom->vcpus = CPUCount; + if (vboxSnapshotGetReadWriteDisks(def, snapshot) < 0) { + VIR_DEBUG("Could not get read write disks for snapshot"); + } + + if (vboxSnapshotGetReadOnlyDisks(snapshot, def) < 0) { + VIR_DEBUG("Could not get Readonly disks for snapshot"); + } +#endif /* VBOX_API_VERSION >= 4002000 */ + rc = snap->vtbl->GetDescription(snap, &str16); if (NS_FAILED(rc)) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -6230,6 +6693,7 @@ vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, def->state = VIR_DOMAIN_SHUTOFF; virUUIDFormat(dom->uuid, uuidstr); + memcpy(def->dom->uuid, dom->uuid, VIR_UUID_BUFLEN); ret = virDomainSnapshotDefFormat(uuidstr, def, flags, 0); cleanup: @@ -6936,6 +7400,8 @@ vboxDomainSnapshotDeleteTree(vboxGlobalData *data, return ret; } + + static int vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, unsigned int flags) @@ -6971,8 +7437,9 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, goto cleanup; } - /* VBOX snapshots do not require any libvirt metadata, making this - * flag trivial once we know we have a valid snapshot. */ + /* In case we just want to delete the metadata, we will edit the vbox file in order + *to remove the node concerning the snapshot + */ if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY) { ret = 0; goto cleanup; @@ -8989,7 +9456,6 @@ static int vboxStorageVolDelete(virStorageVolPtr vol, if (machineIdsSize == 0 || machineIdsSize == deregister) { IProgress *progress = NULL; - rc = hardDisk->vtbl->DeleteStorage(hardDisk, &progress); if (NS_SUCCEEDED(rc) && progress) { -- 1.7.10.4

On Mon, May 19, 2014 at 02:47:30PM +0200, Yohan BELLEGUIC wrote:
From: Manuel VIVES <manuel.vives@diateam.net>
It will be needed for the future patches because we will redefine snapshots --- src/vbox/vbox_tmpl.c | 476 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 471 insertions(+), 5 deletions(-)
ACK I had to change 'src.path' to 'src->path' in this patch since Eric's recently merged change broke it. Likewise for all other patches in this series. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

This structure contains the data to be saved in the VirtualBox XML file and can be manipulated with severals exposed functions. The structure is created by vboxSnapshotLoadVboxFile taking the machine XML file. It also can rewrite the XML by using vboxSnapshotSaveVboxFile. --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/vbox/vbox_snapshot_conf.c | 1490 ++++++++++++++++++++ src/vbox/vbox_snapshot_conf.h | 105 ++ tests/Makefile.am | 15 + tests/vboxsnapshotxmldata/2disks-1snap.vbox | 322 +++++ tests/vboxsnapshotxmldata/2disks-2snap.vbox | 478 +++++++ .../vboxsnapshotxmldata/2disks-3snap-brother.vbox | 786 +++++++++++ tests/vboxsnapshotxmldata/2disks-3snap.vbox | 636 +++++++++ tests/vboxsnapshotxmldata/2disks-nosnap.vbox | 168 +++ tests/vboxsnapshotxmltest.c | 161 +++ 11 files changed, 4163 insertions(+) create mode 100644 src/vbox/vbox_snapshot_conf.c create mode 100644 src/vbox/vbox_snapshot_conf.h create mode 100644 tests/vboxsnapshotxmldata/2disks-1snap.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-2snap.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-3snap-brother.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-3snap.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-nosnap.vbox create mode 100644 tests/vboxsnapshotxmltest.c diff --git a/po/POTFILES.in b/po/POTFILES.in index 6e8d465..52dbcfe 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -210,6 +210,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 cfb7097..065e7ea 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 0000000..9fee760 --- /dev/null +++ b/src/vbox/vbox_snapshot_conf.c @@ -0,0 +1,1490 @@ +/* + * 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, + 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); + virStringFreeList(searchTabResult); + if (result < 0) { + virVboxSnapshotConfHardDiskFree(hardDisk); + hardDisk = NULL; + } + return hardDisk; +} + +static virVBoxSnapshotConfMediaRegistryPtr virVBoxSnapshotConfRetrieveMediaRegistry(xmlNodePtr mediaRegistryNode, + xmlXPathContextPtr xPathContext, + 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); + virStringFreeList(searchTabResult); + return snapshot; +} + +virVBoxSnapshotConfSnapshotPtr virVBoxSnapshotConfSnapshotByName(virVBoxSnapshotConfSnapshotPtr snapshot, + 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, + 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, + 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); + virStringFreeList(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, 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); + } + + 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, + 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, + 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, 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, 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, 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(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; + 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) { + virStringFreeList(ret); + nodeSize = -1; + } + *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(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 + */ +char *virVBoxSnapshotConfHardDiskUuidByLocation(virVBoxSnapshotConfMachinePtr machine, 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, + 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, 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, 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 0000000..59353b5 --- /dev/null +++ b/src/vbox/vbox_snapshot_conf.h @@ -0,0 +1,105 @@ +/* + * 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/>. + */ + +#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, char *machineLocation); +int virVBoxSnapshotConfAddSnapshotToXmlMachine(virVBoxSnapshotConfSnapshotPtr snapshot, virVBoxSnapshotConfMachinePtr machine, char *snapshotParentName); +int virVBoxSnapshotConfAddHardDiskToMediaRegistry(virVBoxSnapshotConfHardDiskPtr hardDisk, virVBoxSnapshotConfMediaRegistryPtr mediaRegistry, char *parentHardDiskId); +int virVBoxSnapshotConfRemoveSnapshot(virVBoxSnapshotConfMachinePtr machine, char *snapshotName); +int virVBoxSnapshotConfRemoveHardDisk(virVBoxSnapshotConfMediaRegistryPtr mediaRegistry,char *uuid); +int virVBoxSnapshotConfSaveVboxFile(virVBoxSnapshotConfMachinePtr machine, const char *filePath); +int virVBoxSnapshotConfIsCurrentSnapshot(virVBoxSnapshotConfMachinePtr machine, char *snapshotName); +int virVBoxSnapshotConfGetRWDisksPathsFromLibvirtXML(char *filePath, char ***realReadWriteDisksPath); +int virVBoxSnapshotConfGetRODisksPathsFromLibvirtXML(char *filePath, char ***realReadOnlyDisksPath); +char *virVBoxSnapshotConfHardDiskUuidByLocation(virVBoxSnapshotConfMachinePtr machine, char *location); +size_t virVBoxSnapshotConfDiskListToOpen(virVBoxSnapshotConfMachinePtr machine, virVBoxSnapshotConfHardDiskPtr **hardDiskToOpen, char *location); +int virVBoxSnapshotConfRemoveFakeDisks(virVBoxSnapshotConfMachinePtr machine); +int virVBoxSnapshotConfDiskIsInMediaRegistry(virVBoxSnapshotConfMachinePtr machine, char *location); +virVBoxSnapshotConfHardDiskPtr virVBoxSnapshotConfHardDiskPtrByLocation(virVBoxSnapshotConfMachinePtr machine, char *location); +virVBoxSnapshotConfSnapshotPtr virVBoxSnapshotConfSnapshotByName(virVBoxSnapshotConfSnapshotPtr snapshot, char *snapshotName); + +#endif /*VBOX_SNAPSHOT_CONF_H*/ diff --git a/tests/Makefile.am b/tests/Makefile.am index 5ef8940..52ae46f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -124,6 +124,7 @@ EXTRA_DIST = \ storagevolxml2xmlout \ sysinfodata \ test-lib.sh \ + vboxsnapshotxmldata \ virsh-uriprecedence \ virfiledata \ virpcitestdata \ @@ -235,6 +236,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 @@ -617,6 +622,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.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 0000000..f80c5af --- /dev/null +++ b/tests/vboxsnapshotxmldata/2disks-1snap.vbox @@ -0,0 +1,322 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!--WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE +OVERWRITTEN AND LOST. +Changes to this xml configuration should be made using Virtualbox +or other application using the libvirt API--> +<VirtualBox version="1.12-linux" xmlns="http://www.innotek.de/VirtualBox-settings"> + <Machine uuid="{74864f52-a7b7-4172-a7a5-54b6f3e9cd21}" name="74864f52-a7b7-4172-a7a5-54b6f3e9cd21" currentSnapshot="{364c7314-b2d9-4fcf-b786-47b987449ef7}" snapshotFolder="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots" currentStateModified="false" OSType="Other" lastStateChange="2014-05-15T11:49:46Z"> + <MediaRegistry> + <DVDImages/> + <FloppyImages/> + <HardDisks> + <HardDisk uuid="{105122e9-f6d2-42c1-9c11-c01699c79517}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/libvirtTest-disk1.vdi" format="VDI" type="Normal"> + <HardDisk uuid="{a28723b5-8093-4548-bf87-e5fa4136f0a3}" location="/root/VirtualBox VMs/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/fakedisk-0.vdi" format="VDI"/> + </HardDisk> + <HardDisk uuid="{f113fdf8-9750-4557-a8be-87fd02319071}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/libvirtTest-disk2.vdi" format="VDI" type="Normal"> + <HardDisk uuid="{e1ff7f14-e6e5-40ac-8e05-8381a8ee5be9}" location="/root/VirtualBox VMs/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/fakedisk-1.vdi" format="VDI"/> + </HardDisk> + </HardDisks> + </MediaRegistry> + <Hardware version="2"> + <CPU count="1" hotplug="false"> + <HardwareVirtEx enabled="true"/> + <HardwareVirtExNestedPaging enabled="true"/> + <HardwareVirtExVPID enabled="true"/> + <HardwareVirtExUX enabled="true"/> + <PAE enabled="false"/> + <LongMode enabled="true"/> + <HardwareVirtExLargePages enabled="false"/> + <HardwareVirtForce enabled="false"/> + </CPU> + <Memory RAMSize="192" PageFusion="false"/> + <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/> + <HPET enabled="false"/> + <Chipset type="PIIX3"/> + <Boot> + <Order position="1" device="HardDisk"/> + <Order position="2" device="None"/> + <Order position="3" device="None"/> + <Order position="4" device="None"/> + </Boot> + <Display VRAMSize="8" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/> + <VideoCapture enabled="false" screens="18446744073709551615" horzRes="1024" vertRes="768" rate="512" fps="25"/> + <RemoteDisplay enabled="true" authType="Null" allowMultiConnection="true"> + <VRDEProperties> + <Property name="TCP/Ports" value="5900"/> + </VRDEProperties> + </RemoteDisplay> + <BIOS> + <ACPI enabled="true"/> + <IOAPIC enabled="true"/> + <Logo fadeIn="true" fadeOut="true" displayTime="0"/> + <BootMenu mode="MessageAndMenu"/> + <TimeOffset value="0"/> + <PXEDebug enabled="false"/> + </BIOS> + <USB> + <Controllers/> + <DeviceFilters/> + </USB> + <Network> + <Adapter slot="0" enabled="true" MACAddress="0800272A589D" cable="true" speed="0" type="82540EM"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + <InternalNetwork name="intnet"/> + <NATNetwork name="NatNetwork"/> + </DisabledModes> + <BridgedInterface name="hns0000000v"/> + </Adapter> + <Adapter slot="1" enabled="false" MACAddress="0800276B5197" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="2" enabled="false" MACAddress="080027A346C9" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="3" enabled="false" MACAddress="08002749A665" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="4" enabled="false" MACAddress="0800273246C6" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="5" enabled="false" MACAddress="080027B0B358" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="6" enabled="false" MACAddress="080027BF23E5" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="7" enabled="false" MACAddress="080027BEE078" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + </Network> + <UART> + <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + </UART> + <LPT> + <Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/> + <Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/> + </LPT> + <AudioAdapter controller="AC97" driver="Pulse" enabled="false"/> + <RTC localOrUTC="local"/> + <SharedFolders/> + <Clipboard mode="Disabled"/> + <DragAndDrop mode="Disabled"/> + <IO> + <IoCache enabled="true" size="5"/> + <BandwidthGroups/> + </IO> + <HostPci> + <Devices/> + </HostPci> + <EmulatedUSB> + <CardReader enabled="false"/> + </EmulatedUSB> + <Guest memoryBalloonSize="0"/> + <GuestProperties/> + </Hardware> + <ExtraData> + <ExtraDataItem name="FRONTEND/Type" value="vrdp"/> + </ExtraData> + <StorageControllers> + <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"> + <AttachedDevice type="HardDisk" port="0" device="0"> + <Image uuid="{a28723b5-8093-4548-bf87-e5fa4136f0a3}"/> + </AttachedDevice> + <AttachedDevice type="HardDisk" port="0" device="1"> + <Image uuid="{e1ff7f14-e6e5-40ac-8e05-8381a8ee5be9}"/> + </AttachedDevice> + </StorageController> + <StorageController name="SATA Controller" type="AHCI" PortCount="30" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3"/> + <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true"/> + <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true" Bootable="true"/> + </StorageControllers> + <Snapshot uuid="{364c7314-b2d9-4fcf-b786-47b987449ef7}" name="15052014-115228" timeStamp="2014-05-15T09:52:29Z"> + <Hardware version="2"> + <CPU count="1" hotplug="false"> + <HardwareVirtEx enabled="true"/> + <HardwareVirtExNestedPaging enabled="true"/> + <HardwareVirtExVPID enabled="true"/> + <HardwareVirtExUX enabled="true"/> + <PAE enabled="false"/> + <LongMode enabled="true"/> + <HardwareVirtExLargePages enabled="false"/> + <HardwareVirtForce enabled="false"/> + </CPU> + <Memory RAMSize="192" PageFusion="false"/> + <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/> + <HPET enabled="false"/> + <Chipset type="PIIX3"/> + <Boot> + <Order position="1" device="HardDisk"/> + <Order position="2" device="None"/> + <Order position="3" device="None"/> + <Order position="4" device="None"/> + </Boot> + <Display VRAMSize="8" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/> + <VideoCapture enabled="false" screens="18446744073709551615" horzRes="1024" vertRes="768" rate="512" fps="25"/> + <RemoteDisplay enabled="true" authType="Null" allowMultiConnection="true"> + <VRDEProperties> + <Property name="TCP/Ports" value="5900"/> + </VRDEProperties> + </RemoteDisplay> + <BIOS> + <ACPI enabled="true"/> + <IOAPIC enabled="true"/> + <Logo fadeIn="true" fadeOut="true" displayTime="0"/> + <BootMenu mode="MessageAndMenu"/> + <TimeOffset value="0"/> + <PXEDebug enabled="false"/> + </BIOS> + <USB> + <Controllers/> + <DeviceFilters/> + </USB> + <Network> + <Adapter slot="0" enabled="true" MACAddress="0800272A589D" cable="true" speed="0" type="82540EM"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + <InternalNetwork name="intnet"/> + <NATNetwork name="NatNetwork"/> + </DisabledModes> + <BridgedInterface name="hns0000000v"/> + </Adapter> + <Adapter slot="1" enabled="false" MACAddress="0800276B5197" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="2" enabled="false" MACAddress="080027A346C9" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="3" enabled="false" MACAddress="08002749A665" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="4" enabled="false" MACAddress="0800273246C6" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="5" enabled="false" MACAddress="080027B0B358" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="6" enabled="false" MACAddress="080027BF23E5" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="7" enabled="false" MACAddress="080027BEE078" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + </Network> + <UART> + <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + </UART> + <LPT> + <Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/> + <Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/> + </LPT> + <AudioAdapter controller="AC97" driver="Pulse" enabled="false"/> + <RTC localOrUTC="local"/> + <SharedFolders/> + <Clipboard mode="Disabled"/> + <DragAndDrop mode="Disabled"/> + <IO> + <IoCache enabled="true" size="5"/> + <BandwidthGroups/> + </IO> + <HostPci> + <Devices/> + </HostPci> + <EmulatedUSB> + <CardReader enabled="false"/> + </EmulatedUSB> + <Guest memoryBalloonSize="0"/> + <GuestProperties/> + </Hardware> + <StorageControllers> + <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"> + <AttachedDevice type="HardDisk" port="0" device="0"> + <Image uuid="{105122e9-f6d2-42c1-9c11-c01699c79517}"/> + </AttachedDevice> + <AttachedDevice type="HardDisk" port="0" device="1"> + <Image uuid="{f113fdf8-9750-4557-a8be-87fd02319071}"/> + </AttachedDevice> + </StorageController> + <StorageController name="SATA Controller" type="AHCI" PortCount="30" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3"/> + <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true"/> + <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true" Bootable="true"/> + </StorageControllers> + </Snapshot> + </Machine> +</VirtualBox> diff --git a/tests/vboxsnapshotxmldata/2disks-2snap.vbox b/tests/vboxsnapshotxmldata/2disks-2snap.vbox new file mode 100644 index 0000000..8277937 --- /dev/null +++ b/tests/vboxsnapshotxmldata/2disks-2snap.vbox @@ -0,0 +1,478 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!--WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE +OVERWRITTEN AND LOST. +Changes to this xml configuration should be made using Virtualbox +or other application using the libvirt API--> +<VirtualBox version="1.12-linux" xmlns="http://www.innotek.de/VirtualBox-settings"> + <Machine uuid="{74864f52-a7b7-4172-a7a5-54b6f3e9cd21}" name="74864f52-a7b7-4172-a7a5-54b6f3e9cd21" currentSnapshot="{94afa063-9281-4e15-b586-a5e1fc5e17bf}" snapshotFolder="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots" currentStateModified="false" OSType="Other" lastStateChange="2014-05-15T11:49:46Z"> + <MediaRegistry> + <DVDImages/> + <FloppyImages/> + <HardDisks> + <HardDisk uuid="{105122e9-f6d2-42c1-9c11-c01699c79517}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/libvirtTest-disk1.vdi" format="VDI" type="Normal"> + <HardDisk uuid="{de551924-a484-4f88-9897-c3c4986b8f75}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{de551924-a484-4f88-9897-c3c4986b8f75}.vdi" format="VDI"> + <HardDisk uuid="{df6194b0-7eec-4b3c-a838-8323dd9075eb}" location="/root/VirtualBox VMs/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/fakedisk-0.vdi" format="VDI"/> + </HardDisk> + </HardDisk> + <HardDisk uuid="{f113fdf8-9750-4557-a8be-87fd02319071}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/libvirtTest-disk2.vdi" format="VDI" type="Normal"> + <HardDisk uuid="{72638d05-918a-43e8-9994-dcc9a3e2f2fe}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{72638d05-918a-43e8-9994-dcc9a3e2f2fe}.vdi" format="VDI"> + <HardDisk uuid="{43fc566a-4c66-426b-9b43-3568fe653efd}" location="/root/VirtualBox VMs/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/fakedisk-1.vdi" format="VDI"/> + </HardDisk> + </HardDisk> + </HardDisks> + </MediaRegistry> + <Hardware version="2"> + <CPU count="1" hotplug="false"> + <HardwareVirtEx enabled="true"/> + <HardwareVirtExNestedPaging enabled="true"/> + <HardwareVirtExVPID enabled="true"/> + <HardwareVirtExUX enabled="true"/> + <PAE enabled="false"/> + <LongMode enabled="true"/> + <HardwareVirtExLargePages enabled="false"/> + <HardwareVirtForce enabled="false"/> + </CPU> + <Memory RAMSize="192" PageFusion="false"/> + <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/> + <HPET enabled="false"/> + <Chipset type="PIIX3"/> + <Boot> + <Order position="1" device="HardDisk"/> + <Order position="2" device="None"/> + <Order position="3" device="None"/> + <Order position="4" device="None"/> + </Boot> + <Display VRAMSize="8" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/> + <VideoCapture enabled="false" screens="18446744073709551615" horzRes="1024" vertRes="768" rate="512" fps="25"/> + <RemoteDisplay enabled="true" authType="Null" allowMultiConnection="true"> + <VRDEProperties> + <Property name="TCP/Ports" value="5900"/> + </VRDEProperties> + </RemoteDisplay> + <BIOS> + <ACPI enabled="true"/> + <IOAPIC enabled="true"/> + <Logo fadeIn="true" fadeOut="true" displayTime="0"/> + <BootMenu mode="MessageAndMenu"/> + <TimeOffset value="0"/> + <PXEDebug enabled="false"/> + </BIOS> + <USB> + <Controllers/> + <DeviceFilters/> + </USB> + <Network> + <Adapter slot="0" enabled="true" MACAddress="0800272A589D" cable="true" speed="0" type="82540EM"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + <InternalNetwork name="intnet"/> + <NATNetwork name="NatNetwork"/> + </DisabledModes> + <BridgedInterface name="hns0000000v"/> + </Adapter> + <Adapter slot="1" enabled="false" MACAddress="0800276B5197" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="2" enabled="false" MACAddress="080027A346C9" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="3" enabled="false" MACAddress="08002749A665" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="4" enabled="false" MACAddress="0800273246C6" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="5" enabled="false" MACAddress="080027B0B358" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="6" enabled="false" MACAddress="080027BF23E5" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="7" enabled="false" MACAddress="080027BEE078" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + </Network> + <UART> + <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + </UART> + <LPT> + <Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/> + <Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/> + </LPT> + <AudioAdapter controller="AC97" driver="Pulse" enabled="false"/> + <RTC localOrUTC="local"/> + <SharedFolders/> + <Clipboard mode="Disabled"/> + <DragAndDrop mode="Disabled"/> + <IO> + <IoCache enabled="true" size="5"/> + <BandwidthGroups/> + </IO> + <HostPci> + <Devices/> + </HostPci> + <EmulatedUSB> + <CardReader enabled="false"/> + </EmulatedUSB> + <Guest memoryBalloonSize="0"/> + <GuestProperties/> + </Hardware> + <ExtraData> + <ExtraDataItem name="FRONTEND/Type" value="vrdp"/> + </ExtraData> + <StorageControllers> + <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"> + <AttachedDevice type="HardDisk" port="0" device="0"> + <Image uuid="{df6194b0-7eec-4b3c-a838-8323dd9075eb}"/> + </AttachedDevice> + <AttachedDevice type="HardDisk" port="0" device="1"> + <Image uuid="{43fc566a-4c66-426b-9b43-3568fe653efd}"/> + </AttachedDevice> + </StorageController> + <StorageController name="SATA Controller" type="AHCI" PortCount="30" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3"/> + <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true"/> + <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true" Bootable="true"/> + </StorageControllers> + <Snapshot uuid="{364c7314-b2d9-4fcf-b786-47b987449ef7}" name="15052014-115228" timeStamp="2014-05-15T09:52:29Z"> + <Hardware version="2"> + <CPU count="1" hotplug="false"> + <HardwareVirtEx enabled="true"/> + <HardwareVirtExNestedPaging enabled="true"/> + <HardwareVirtExVPID enabled="true"/> + <HardwareVirtExUX enabled="true"/> + <PAE enabled="false"/> + <LongMode enabled="true"/> + <HardwareVirtExLargePages enabled="false"/> + <HardwareVirtForce enabled="false"/> + </CPU> + <Memory RAMSize="192" PageFusion="false"/> + <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/> + <HPET enabled="false"/> + <Chipset type="PIIX3"/> + <Boot> + <Order position="1" device="HardDisk"/> + <Order position="2" device="None"/> + <Order position="3" device="None"/> + <Order position="4" device="None"/> + </Boot> + <Display VRAMSize="8" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/> + <VideoCapture enabled="false" screens="18446744073709551615" horzRes="1024" vertRes="768" rate="512" fps="25"/> + <RemoteDisplay enabled="true" authType="Null" allowMultiConnection="true"> + <VRDEProperties> + <Property name="TCP/Ports" value="5900"/> + </VRDEProperties> + </RemoteDisplay> + <BIOS> + <ACPI enabled="true"/> + <IOAPIC enabled="true"/> + <Logo fadeIn="true" fadeOut="true" displayTime="0"/> + <BootMenu mode="MessageAndMenu"/> + <TimeOffset value="0"/> + <PXEDebug enabled="false"/> + </BIOS> + <USB> + <Controllers/> + <DeviceFilters/> + </USB> + <Network> + <Adapter slot="0" enabled="true" MACAddress="0800272A589D" cable="true" speed="0" type="82540EM"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + <InternalNetwork name="intnet"/> + <NATNetwork name="NatNetwork"/> + </DisabledModes> + <BridgedInterface name="hns0000000v"/> + </Adapter> + <Adapter slot="1" enabled="false" MACAddress="0800276B5197" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="2" enabled="false" MACAddress="080027A346C9" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="3" enabled="false" MACAddress="08002749A665" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="4" enabled="false" MACAddress="0800273246C6" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="5" enabled="false" MACAddress="080027B0B358" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="6" enabled="false" MACAddress="080027BF23E5" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="7" enabled="false" MACAddress="080027BEE078" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + </Network> + <UART> + <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + </UART> + <LPT> + <Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/> + <Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/> + </LPT> + <AudioAdapter controller="AC97" driver="Pulse" enabled="false"/> + <RTC localOrUTC="local"/> + <SharedFolders/> + <Clipboard mode="Disabled"/> + <DragAndDrop mode="Disabled"/> + <IO> + <IoCache enabled="true" size="5"/> + <BandwidthGroups/> + </IO> + <HostPci> + <Devices/> + </HostPci> + <EmulatedUSB> + <CardReader enabled="false"/> + </EmulatedUSB> + <Guest memoryBalloonSize="0"/> + <GuestProperties/> + </Hardware> + <StorageControllers> + <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"> + <AttachedDevice type="HardDisk" port="0" device="0"> + <Image uuid="{105122e9-f6d2-42c1-9c11-c01699c79517}"/> + </AttachedDevice> + <AttachedDevice type="HardDisk" port="0" device="1"> + <Image uuid="{f113fdf8-9750-4557-a8be-87fd02319071}"/> + </AttachedDevice> + </StorageController> + <StorageController name="SATA Controller" type="AHCI" PortCount="30" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3"/> + <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true"/> + <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true" Bootable="true"/> + </StorageControllers> + <Snapshots> + <Snapshot uuid="{94afa063-9281-4e15-b586-a5e1fc5e17bf}" name="15052014-115248" timeStamp="2014-05-15T09:52:49Z"> + <Hardware version="2"> + <CPU count="1" hotplug="false"> + <HardwareVirtEx enabled="true"/> + <HardwareVirtExNestedPaging enabled="true"/> + <HardwareVirtExVPID enabled="true"/> + <HardwareVirtExUX enabled="true"/> + <PAE enabled="false"/> + <LongMode enabled="true"/> + <HardwareVirtExLargePages enabled="false"/> + <HardwareVirtForce enabled="false"/> + </CPU> + <Memory RAMSize="192" PageFusion="false"/> + <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/> + <HPET enabled="false"/> + <Chipset type="PIIX3"/> + <Boot> + <Order position="1" device="HardDisk"/> + <Order position="2" device="None"/> + <Order position="3" device="None"/> + <Order position="4" device="None"/> + </Boot> + <Display VRAMSize="8" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/> + <VideoCapture enabled="false" screens="18446744073709551615" horzRes="1024" vertRes="768" rate="512" fps="25"/> + <RemoteDisplay enabled="true" authType="Null" allowMultiConnection="true"> + <VRDEProperties> + <Property name="TCP/Ports" value="5900"/> + </VRDEProperties> + </RemoteDisplay> + <BIOS> + <ACPI enabled="true"/> + <IOAPIC enabled="true"/> + <Logo fadeIn="true" fadeOut="true" displayTime="0"/> + <BootMenu mode="MessageAndMenu"/> + <TimeOffset value="0"/> + <PXEDebug enabled="false"/> + </BIOS> + <USB> + <Controllers/> + <DeviceFilters/> + </USB> + <Network> + <Adapter slot="0" enabled="true" MACAddress="0800272A589D" cable="true" speed="0" type="82540EM"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + <InternalNetwork name="intnet"/> + <NATNetwork name="NatNetwork"/> + </DisabledModes> + <BridgedInterface name="hns0000000v"/> + </Adapter> + <Adapter slot="1" enabled="false" MACAddress="0800276B5197" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="2" enabled="false" MACAddress="080027A346C9" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="3" enabled="false" MACAddress="08002749A665" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="4" enabled="false" MACAddress="0800273246C6" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="5" enabled="false" MACAddress="080027B0B358" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="6" enabled="false" MACAddress="080027BF23E5" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="7" enabled="false" MACAddress="080027BEE078" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + </Network> + <UART> + <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + </UART> + <LPT> + <Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/> + <Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/> + </LPT> + <AudioAdapter controller="AC97" driver="Pulse" enabled="false"/> + <RTC localOrUTC="local"/> + <SharedFolders/> + <Clipboard mode="Disabled"/> + <DragAndDrop mode="Disabled"/> + <IO> + <IoCache enabled="true" size="5"/> + <BandwidthGroups/> + </IO> + <HostPci> + <Devices/> + </HostPci> + <EmulatedUSB> + <CardReader enabled="false"/> + </EmulatedUSB> + <Guest memoryBalloonSize="0"/> + <GuestProperties/> + </Hardware> + <StorageControllers> + <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"> + <AttachedDevice type="HardDisk" port="0" device="0"> + <Image uuid="{de551924-a484-4f88-9897-c3c4986b8f75}"/> + </AttachedDevice> + <AttachedDevice type="HardDisk" port="0" device="1"> + <Image uuid="{72638d05-918a-43e8-9994-dcc9a3e2f2fe}"/> + </AttachedDevice> + </StorageController> + <StorageController name="SATA Controller" type="AHCI" PortCount="30" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3"/> + <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true"/> + <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true" Bootable="true"/> + </StorageControllers> + </Snapshot> + </Snapshots> + </Snapshot> + </Machine> +</VirtualBox> diff --git a/tests/vboxsnapshotxmldata/2disks-3snap-brother.vbox b/tests/vboxsnapshotxmldata/2disks-3snap-brother.vbox new file mode 100644 index 0000000..b5472f9 --- /dev/null +++ b/tests/vboxsnapshotxmldata/2disks-3snap-brother.vbox @@ -0,0 +1,786 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!--WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE +OVERWRITTEN AND LOST. +Changes to this xml configuration should be made using Virtualbox +or other application using the libvirt API--> +<VirtualBox version="1.12-linux" xmlns="http://www.innotek.de/VirtualBox-settings"> + <Machine uuid="{74864f52-a7b7-4172-a7a5-54b6f3e9cd21}" name="74864f52-a7b7-4172-a7a5-54b6f3e9cd21" currentSnapshot="{5d8b609b-f5af-4025-8aca-220480fa6394}" snapshotFolder="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots" currentStateModified="false" OSType="Other" lastStateChange="2014-05-15T11:49:46Z"> + <MediaRegistry> + <DVDImages/> + <FloppyImages/> + <HardDisks> + <HardDisk uuid="{105122e9-f6d2-42c1-9c11-c01699c79517}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/libvirtTest-disk1.vdi" format="VDI" type="Normal"> + <HardDisk uuid="{de551924-a484-4f88-9897-c3c4986b8f75}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{de551924-a484-4f88-9897-c3c4986b8f75}.vdi" format="VDI"> + <HardDisk uuid="{55c4d273-380b-43c6-b394-b2636051cfc7}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{55c4d273-380b-43c6-b394-b2636051cfc7}.vdi" format="VDI"/> + <HardDisk uuid="{bf3ea5d4-fcb3-4cfe-b289-9d605fc94c3b}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{bf3ea5d4-fcb3-4cfe-b289-9d605fc94c3b}.vdi" format="VDI"> + <HardDisk uuid="{f79158b5-258d-4afa-a0ad-2e7c113da30c}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{f79158b5-258d-4afa-a0ad-2e7c113da30c}.vdi" format="VDI"/> + </HardDisk> + </HardDisk> + </HardDisk> + <HardDisk uuid="{f113fdf8-9750-4557-a8be-87fd02319071}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/libvirtTest-disk2.vdi" format="VDI" type="Normal"> + <HardDisk uuid="{72638d05-918a-43e8-9994-dcc9a3e2f2fe}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{72638d05-918a-43e8-9994-dcc9a3e2f2fe}.vdi" format="VDI"> + <HardDisk uuid="{f9f1a347-e9b0-4ba3-bf5d-cfbccccf8c27}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{f9f1a347-e9b0-4ba3-bf5d-cfbccccf8c27}.vdi" format="VDI"/> + <HardDisk uuid="{18394ad4-fad9-4cb7-9de9-7477a93aab08}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{18394ad4-fad9-4cb7-9de9-7477a93aab08}.vdi" format="VDI"> + <HardDisk uuid="{71030c24-a668-41c6-be9c-7704d39e2618}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{71030c24-a668-41c6-be9c-7704d39e2618}.vdi" format="VDI"/> + </HardDisk> + </HardDisk> + </HardDisk> + </HardDisks> + </MediaRegistry> + <Hardware version="2"> + <CPU count="1" hotplug="false"> + <HardwareVirtEx enabled="true"/> + <HardwareVirtExNestedPaging enabled="true"/> + <HardwareVirtExVPID enabled="true"/> + <HardwareVirtExUX enabled="true"/> + <PAE enabled="false"/> + <LongMode enabled="true"/> + <HardwareVirtExLargePages enabled="false"/> + <HardwareVirtForce enabled="false"/> + </CPU> + <Memory RAMSize="192" PageFusion="false"/> + <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/> + <HPET enabled="false"/> + <Chipset type="PIIX3"/> + <Boot> + <Order position="1" device="HardDisk"/> + <Order position="2" device="None"/> + <Order position="3" device="None"/> + <Order position="4" device="None"/> + </Boot> + <Display VRAMSize="8" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/> + <VideoCapture enabled="false" screens="18446744073709551615" horzRes="1024" vertRes="768" rate="512" fps="25"/> + <RemoteDisplay enabled="true" authType="Null" authTimeout="5000" allowMultiConnection="true"> + <VRDEProperties> + <Property name="TCP/Ports" value="5900"/> + </VRDEProperties> + </RemoteDisplay> + <BIOS> + <ACPI enabled="true"/> + <IOAPIC enabled="true"/> + <Logo fadeIn="true" fadeOut="true" displayTime="0"/> + <BootMenu mode="MessageAndMenu"/> + <TimeOffset value="0"/> + <PXEDebug enabled="false"/> + </BIOS> + <USB> + <Controllers/> + <DeviceFilters/> + </USB> + <Network> + <Adapter slot="0" enabled="true" MACAddress="0800272A589D" cable="true" speed="0" type="82540EM"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + <InternalNetwork name="intnet"/> + <NATNetwork name="NatNetwork"/> + </DisabledModes> + <BridgedInterface name="hns0000000v"/> + </Adapter> + <Adapter slot="1" enabled="false" MACAddress="0800276B5197" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="2" enabled="false" MACAddress="080027A346C9" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="3" enabled="false" MACAddress="08002749A665" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="4" enabled="false" MACAddress="0800273246C6" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="5" enabled="false" MACAddress="080027B0B358" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="6" enabled="false" MACAddress="080027BF23E5" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="7" enabled="false" MACAddress="080027BEE078" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + </Network> + <UART> + <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + </UART> + <LPT> + <Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/> + <Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/> + </LPT> + <AudioAdapter controller="AC97" driver="Pulse" enabled="false"/> + <RTC localOrUTC="local"/> + <SharedFolders/> + <Clipboard mode="Disabled"/> + <DragAndDrop mode="Disabled"/> + <IO> + <IoCache enabled="true" size="5"/> + <BandwidthGroups/> + </IO> + <HostPci> + <Devices/> + </HostPci> + <EmulatedUSB> + <CardReader enabled="false"/> + </EmulatedUSB> + <Guest memoryBalloonSize="0"/> + <GuestProperties/> + </Hardware> + <ExtraData> + <ExtraDataItem name="FRONTEND/Type" value="vrdp"/> + </ExtraData> + <StorageControllers> + <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"> + <AttachedDevice type="HardDisk" port="0" device="0"> + <Image uuid="{f79158b5-258d-4afa-a0ad-2e7c113da30c}"/> + </AttachedDevice> + <AttachedDevice type="HardDisk" port="0" device="1"> + <Image uuid="{71030c24-a668-41c6-be9c-7704d39e2618}"/> + </AttachedDevice> + </StorageController> + <StorageController name="SATA Controller" type="AHCI" PortCount="30" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3"/> + <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true"/> + <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true" Bootable="true"/> + </StorageControllers> + <Snapshot uuid="{364c7314-b2d9-4fcf-b786-47b987449ef7}" name="15052014-115228" timeStamp="2014-05-15T09:52:29Z"> + <Hardware version="2"> + <CPU count="1" hotplug="false"> + <HardwareVirtEx enabled="true"/> + <HardwareVirtExNestedPaging enabled="true"/> + <HardwareVirtExVPID enabled="true"/> + <HardwareVirtExUX enabled="true"/> + <PAE enabled="false"/> + <LongMode enabled="true"/> + <HardwareVirtExLargePages enabled="false"/> + <HardwareVirtForce enabled="false"/> + </CPU> + <Memory RAMSize="192" PageFusion="false"/> + <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/> + <HPET enabled="false"/> + <Chipset type="PIIX3"/> + <Boot> + <Order position="1" device="HardDisk"/> + <Order position="2" device="None"/> + <Order position="3" device="None"/> + <Order position="4" device="None"/> + </Boot> + <Display VRAMSize="8" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/> + <VideoCapture enabled="false" screens="18446744073709551615" horzRes="1024" vertRes="768" rate="512" fps="25"/> + <RemoteDisplay enabled="true" authType="Null" authTimeout="5000" allowMultiConnection="true"> + <VRDEProperties> + <Property name="TCP/Ports" value="5900"/> + </VRDEProperties> + </RemoteDisplay> + <BIOS> + <ACPI enabled="true"/> + <IOAPIC enabled="true"/> + <Logo fadeIn="true" fadeOut="true" displayTime="0"/> + <BootMenu mode="MessageAndMenu"/> + <TimeOffset value="0"/> + <PXEDebug enabled="false"/> + </BIOS> + <USB> + <Controllers/> + <DeviceFilters/> + </USB> + <Network> + <Adapter slot="0" enabled="true" MACAddress="0800272A589D" cable="true" speed="0" type="82540EM"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + <InternalNetwork name="intnet"/> + <NATNetwork name="NatNetwork"/> + </DisabledModes> + <BridgedInterface name="hns0000000v"/> + </Adapter> + <Adapter slot="1" enabled="false" MACAddress="0800276B5197" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="2" enabled="false" MACAddress="080027A346C9" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="3" enabled="false" MACAddress="08002749A665" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="4" enabled="false" MACAddress="0800273246C6" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="5" enabled="false" MACAddress="080027B0B358" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="6" enabled="false" MACAddress="080027BF23E5" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="7" enabled="false" MACAddress="080027BEE078" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + </Network> + <UART> + <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + </UART> + <LPT> + <Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/> + <Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/> + </LPT> + <AudioAdapter controller="AC97" driver="Pulse" enabled="false"/> + <RTC localOrUTC="local"/> + <SharedFolders/> + <Clipboard mode="Disabled"/> + <DragAndDrop mode="Disabled"/> + <IO> + <IoCache enabled="true" size="5"/> + <BandwidthGroups/> + </IO> + <HostPci> + <Devices/> + </HostPci> + <EmulatedUSB> + <CardReader enabled="false"/> + </EmulatedUSB> + <Guest memoryBalloonSize="0"/> + <GuestProperties/> + </Hardware> + <StorageControllers> + <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"> + <AttachedDevice type="HardDisk" port="0" device="0"> + <Image uuid="{105122e9-f6d2-42c1-9c11-c01699c79517}"/> + </AttachedDevice> + <AttachedDevice type="HardDisk" port="0" device="1"> + <Image uuid="{f113fdf8-9750-4557-a8be-87fd02319071}"/> + </AttachedDevice> + </StorageController> + <StorageController name="SATA Controller" type="AHCI" PortCount="30" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3"/> + <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true"/> + <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true" Bootable="true"/> + </StorageControllers> + <Snapshots> + <Snapshot uuid="{94afa063-9281-4e15-b586-a5e1fc5e17bf}" name="15052014-115248" timeStamp="2014-05-15T09:52:49Z"> + <Hardware version="2"> + <CPU count="1" hotplug="false"> + <HardwareVirtEx enabled="true"/> + <HardwareVirtExNestedPaging enabled="true"/> + <HardwareVirtExVPID enabled="true"/> + <HardwareVirtExUX enabled="true"/> + <PAE enabled="false"/> + <LongMode enabled="true"/> + <HardwareVirtExLargePages enabled="false"/> + <HardwareVirtForce enabled="false"/> + </CPU> + <Memory RAMSize="192" PageFusion="false"/> + <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/> + <HPET enabled="false"/> + <Chipset type="PIIX3"/> + <Boot> + <Order position="1" device="HardDisk"/> + <Order position="2" device="None"/> + <Order position="3" device="None"/> + <Order position="4" device="None"/> + </Boot> + <Display VRAMSize="8" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/> + <VideoCapture enabled="false" screens="18446744073709551615" horzRes="1024" vertRes="768" rate="512" fps="25"/> + <RemoteDisplay enabled="true" authType="Null" authTimeout="5000" allowMultiConnection="true"> + <VRDEProperties> + <Property name="TCP/Ports" value="5900"/> + </VRDEProperties> + </RemoteDisplay> + <BIOS> + <ACPI enabled="true"/> + <IOAPIC enabled="true"/> + <Logo fadeIn="true" fadeOut="true" displayTime="0"/> + <BootMenu mode="MessageAndMenu"/> + <TimeOffset value="0"/> + <PXEDebug enabled="false"/> + </BIOS> + <USB> + <Controllers/> + <DeviceFilters/> + </USB> + <Network> + <Adapter slot="0" enabled="true" MACAddress="0800272A589D" cable="true" speed="0" type="82540EM"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + <InternalNetwork name="intnet"/> + <NATNetwork name="NatNetwork"/> + </DisabledModes> + <BridgedInterface name="hns0000000v"/> + </Adapter> + <Adapter slot="1" enabled="false" MACAddress="0800276B5197" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="2" enabled="false" MACAddress="080027A346C9" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="3" enabled="false" MACAddress="08002749A665" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="4" enabled="false" MACAddress="0800273246C6" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="5" enabled="false" MACAddress="080027B0B358" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="6" enabled="false" MACAddress="080027BF23E5" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="7" enabled="false" MACAddress="080027BEE078" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + </Network> + <UART> + <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + </UART> + <LPT> + <Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/> + <Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/> + </LPT> + <AudioAdapter controller="AC97" driver="Pulse" enabled="false"/> + <RTC localOrUTC="local"/> + <SharedFolders/> + <Clipboard mode="Disabled"/> + <DragAndDrop mode="Disabled"/> + <IO> + <IoCache enabled="true" size="5"/> + <BandwidthGroups/> + </IO> + <HostPci> + <Devices/> + </HostPci> + <EmulatedUSB> + <CardReader enabled="false"/> + </EmulatedUSB> + <Guest memoryBalloonSize="0"/> + <GuestProperties/> + </Hardware> + <StorageControllers> + <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"> + <AttachedDevice type="HardDisk" port="0" device="0"> + <Image uuid="{de551924-a484-4f88-9897-c3c4986b8f75}"/> + </AttachedDevice> + <AttachedDevice type="HardDisk" port="0" device="1"> + <Image uuid="{72638d05-918a-43e8-9994-dcc9a3e2f2fe}"/> + </AttachedDevice> + </StorageController> + <StorageController name="SATA Controller" type="AHCI" PortCount="30" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3"/> + <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true"/> + <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true" Bootable="true"/> + </StorageControllers> + <Snapshots> + <Snapshot uuid="{574d0edf-0f9d-47ef-82e3-1b6d2d54a537}" name="15052014-115306" timeStamp="2014-05-15T09:53:06Z"> + <Hardware version="2"> + <CPU count="1" hotplug="false"> + <HardwareVirtEx enabled="true"/> + <HardwareVirtExNestedPaging enabled="true"/> + <HardwareVirtExVPID enabled="true"/> + <HardwareVirtExUX enabled="true"/> + <PAE enabled="false"/> + <LongMode enabled="true"/> + <HardwareVirtExLargePages enabled="false"/> + <HardwareVirtForce enabled="false"/> + </CPU> + <Memory RAMSize="192" PageFusion="false"/> + <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/> + <HPET enabled="false"/> + <Chipset type="PIIX3"/> + <Boot> + <Order position="1" device="HardDisk"/> + <Order position="2" device="None"/> + <Order position="3" device="None"/> + <Order position="4" device="None"/> + </Boot> + <Display VRAMSize="8" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/> + <VideoCapture enabled="false" screens="18446744073709551615" horzRes="1024" vertRes="768" rate="512" fps="25"/> + <RemoteDisplay enabled="true" authType="Null" authTimeout="5000" allowMultiConnection="true"> + <VRDEProperties> + <Property name="TCP/Ports" value="5900"/> + </VRDEProperties> + </RemoteDisplay> + <BIOS> + <ACPI enabled="true"/> + <IOAPIC enabled="true"/> + <Logo fadeIn="true" fadeOut="true" displayTime="0"/> + <BootMenu mode="MessageAndMenu"/> + <TimeOffset value="0"/> + <PXEDebug enabled="false"/> + </BIOS> + <USB> + <Controllers/> + <DeviceFilters/> + </USB> + <Network> + <Adapter slot="0" enabled="true" MACAddress="0800272A589D" cable="true" speed="0" type="82540EM"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + <InternalNetwork name="intnet"/> + <NATNetwork name="NatNetwork"/> + </DisabledModes> + <BridgedInterface name="hns0000000v"/> + </Adapter> + <Adapter slot="1" enabled="false" MACAddress="0800276B5197" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="2" enabled="false" MACAddress="080027A346C9" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="3" enabled="false" MACAddress="08002749A665" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="4" enabled="false" MACAddress="0800273246C6" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="5" enabled="false" MACAddress="080027B0B358" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="6" enabled="false" MACAddress="080027BF23E5" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="7" enabled="false" MACAddress="080027BEE078" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + </Network> + <UART> + <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + </UART> + <LPT> + <Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/> + <Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/> + </LPT> + <AudioAdapter controller="AC97" driver="Pulse" enabled="false"/> + <RTC localOrUTC="local"/> + <SharedFolders/> + <Clipboard mode="Disabled"/> + <DragAndDrop mode="Disabled"/> + <IO> + <IoCache enabled="true" size="5"/> + <BandwidthGroups/> + </IO> + <HostPci> + <Devices/> + </HostPci> + <EmulatedUSB> + <CardReader enabled="false"/> + </EmulatedUSB> + <Guest memoryBalloonSize="0"/> + <GuestProperties/> + </Hardware> + <StorageControllers> + <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"> + <AttachedDevice type="HardDisk" port="0" device="0"> + <Image uuid="{55c4d273-380b-43c6-b394-b2636051cfc7}"/> + </AttachedDevice> + <AttachedDevice type="HardDisk" port="0" device="1"> + <Image uuid="{f9f1a347-e9b0-4ba3-bf5d-cfbccccf8c27}"/> + </AttachedDevice> + </StorageController> + <StorageController name="SATA Controller" type="AHCI" PortCount="30" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3"/> + <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true"/> + <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true" Bootable="true"/> + </StorageControllers> + </Snapshot> + <Snapshot uuid="{5d8b609b-f5af-4025-8aca-220480fa6394}" name="15052014-115334" timeStamp="2014-05-15T09:53:35Z"> + <Hardware version="2"> + <CPU count="1" hotplug="false"> + <HardwareVirtEx enabled="true"/> + <HardwareVirtExNestedPaging enabled="true"/> + <HardwareVirtExVPID enabled="true"/> + <HardwareVirtExUX enabled="true"/> + <PAE enabled="false"/> + <LongMode enabled="true"/> + <HardwareVirtExLargePages enabled="false"/> + <HardwareVirtForce enabled="false"/> + </CPU> + <Memory RAMSize="192" PageFusion="false"/> + <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/> + <HPET enabled="false"/> + <Chipset type="PIIX3"/> + <Boot> + <Order position="1" device="HardDisk"/> + <Order position="2" device="None"/> + <Order position="3" device="None"/> + <Order position="4" device="None"/> + </Boot> + <Display VRAMSize="8" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/> + <VideoCapture enabled="false" screens="18446744073709551615" horzRes="1024" vertRes="768" rate="512" fps="25"/> + <RemoteDisplay enabled="true" authType="Null" authTimeout="5000" allowMultiConnection="true"> + <VRDEProperties> + <Property name="TCP/Ports" value="5900"/> + </VRDEProperties> + </RemoteDisplay> + <BIOS> + <ACPI enabled="true"/> + <IOAPIC enabled="true"/> + <Logo fadeIn="true" fadeOut="true" displayTime="0"/> + <BootMenu mode="MessageAndMenu"/> + <TimeOffset value="0"/> + <PXEDebug enabled="false"/> + </BIOS> + <USB> + <Controllers/> + <DeviceFilters/> + </USB> + <Network> + <Adapter slot="0" enabled="true" MACAddress="0800272A589D" cable="true" speed="0" type="82540EM"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + <InternalNetwork name="intnet"/> + <NATNetwork name="NatNetwork"/> + </DisabledModes> + <BridgedInterface name="hns0000000v"/> + </Adapter> + <Adapter slot="1" enabled="false" MACAddress="0800276B5197" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="2" enabled="false" MACAddress="080027A346C9" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="3" enabled="false" MACAddress="08002749A665" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="4" enabled="false" MACAddress="0800273246C6" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="5" enabled="false" MACAddress="080027B0B358" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="6" enabled="false" MACAddress="080027BF23E5" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="7" enabled="false" MACAddress="080027BEE078" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + </Network> + <UART> + <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + </UART> + <LPT> + <Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/> + <Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/> + </LPT> + <AudioAdapter controller="AC97" driver="Pulse" enabled="false"/> + <RTC localOrUTC="local"/> + <SharedFolders/> + <Clipboard mode="Disabled"/> + <DragAndDrop mode="Disabled"/> + <IO> + <IoCache enabled="true" size="5"/> + <BandwidthGroups/> + </IO> + <HostPci> + <Devices/> + </HostPci> + <EmulatedUSB> + <CardReader enabled="false"/> + </EmulatedUSB> + <Guest memoryBalloonSize="0"/> + <GuestProperties/> + </Hardware> + <StorageControllers> + <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"> + <AttachedDevice type="HardDisk" port="0" device="0"> + <Image uuid="{bf3ea5d4-fcb3-4cfe-b289-9d605fc94c3b}"/> + </AttachedDevice> + <AttachedDevice type="HardDisk" port="0" device="1"> + <Image uuid="{18394ad4-fad9-4cb7-9de9-7477a93aab08}"/> + </AttachedDevice> + </StorageController> + <StorageController name="SATA Controller" type="AHCI" PortCount="30" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3"/> + <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true"/> + <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true" Bootable="true"/> + </StorageControllers> + </Snapshot> + </Snapshots> + </Snapshot> + </Snapshots> + </Snapshot> + </Machine> +</VirtualBox> diff --git a/tests/vboxsnapshotxmldata/2disks-3snap.vbox b/tests/vboxsnapshotxmldata/2disks-3snap.vbox new file mode 100644 index 0000000..2fb6fe7 --- /dev/null +++ b/tests/vboxsnapshotxmldata/2disks-3snap.vbox @@ -0,0 +1,636 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!--WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE +OVERWRITTEN AND LOST. +Changes to this xml configuration should be made using Virtualbox +or other application using the libvirt API--> +<VirtualBox version="1.12-linux" xmlns="http://www.innotek.de/VirtualBox-settings"> + <Machine uuid="{74864f52-a7b7-4172-a7a5-54b6f3e9cd21}" name="74864f52-a7b7-4172-a7a5-54b6f3e9cd21" currentSnapshot="{574d0edf-0f9d-47ef-82e3-1b6d2d54a537}" snapshotFolder="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots" currentStateModified="false" OSType="Other" lastStateChange="2014-05-15T11:49:46Z"> + <MediaRegistry> + <DVDImages/> + <FloppyImages/> + <HardDisks> + <HardDisk uuid="{105122e9-f6d2-42c1-9c11-c01699c79517}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/libvirtTest-disk1.vdi" format="VDI" type="Normal"> + <HardDisk uuid="{de551924-a484-4f88-9897-c3c4986b8f75}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{de551924-a484-4f88-9897-c3c4986b8f75}.vdi" format="VDI"> + <HardDisk uuid="{bf3ea5d4-fcb3-4cfe-b289-9d605fc94c3b}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{bf3ea5d4-fcb3-4cfe-b289-9d605fc94c3b}.vdi" format="VDI"/> + <HardDisk uuid="{55c4d273-380b-43c6-b394-b2636051cfc7}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{55c4d273-380b-43c6-b394-b2636051cfc7}.vdi" format="VDI"> + <HardDisk uuid="{64c31740-9498-4c90-866b-650819686ef5}" location="/root/VirtualBox VMs/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/fakedisk-0.vdi" format="VDI"/> + </HardDisk> + </HardDisk> + </HardDisk> + <HardDisk uuid="{f113fdf8-9750-4557-a8be-87fd02319071}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/libvirtTest-disk2.vdi" format="VDI" type="Normal"> + <HardDisk uuid="{72638d05-918a-43e8-9994-dcc9a3e2f2fe}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{72638d05-918a-43e8-9994-dcc9a3e2f2fe}.vdi" format="VDI"> + <HardDisk uuid="{18394ad4-fad9-4cb7-9de9-7477a93aab08}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{18394ad4-fad9-4cb7-9de9-7477a93aab08}.vdi" format="VDI"/> + <HardDisk uuid="{f9f1a347-e9b0-4ba3-bf5d-cfbccccf8c27}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots/{f9f1a347-e9b0-4ba3-bf5d-cfbccccf8c27}.vdi" format="VDI"> + <HardDisk uuid="{ec8ccae3-adc5-4bfd-b381-5a5eca357699}" location="/root/VirtualBox VMs/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/fakedisk-1.vdi" format="VDI"/> + </HardDisk> + </HardDisk> + </HardDisk> + </HardDisks> + </MediaRegistry> + <Hardware version="2"> + <CPU count="1" hotplug="false"> + <HardwareVirtEx enabled="true"/> + <HardwareVirtExNestedPaging enabled="true"/> + <HardwareVirtExVPID enabled="true"/> + <HardwareVirtExUX enabled="true"/> + <PAE enabled="false"/> + <LongMode enabled="true"/> + <HardwareVirtExLargePages enabled="false"/> + <HardwareVirtForce enabled="false"/> + </CPU> + <Memory RAMSize="192" PageFusion="false"/> + <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/> + <HPET enabled="false"/> + <Chipset type="PIIX3"/> + <Boot> + <Order position="1" device="HardDisk"/> + <Order position="2" device="None"/> + <Order position="3" device="None"/> + <Order position="4" device="None"/> + </Boot> + <Display VRAMSize="8" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/> + <VideoCapture enabled="false" screens="18446744073709551615" horzRes="1024" vertRes="768" rate="512" fps="25"/> + <RemoteDisplay enabled="true" authType="Null" allowMultiConnection="true"> + <VRDEProperties> + <Property name="TCP/Ports" value="5900"/> + </VRDEProperties> + </RemoteDisplay> + <BIOS> + <ACPI enabled="true"/> + <IOAPIC enabled="true"/> + <Logo fadeIn="true" fadeOut="true" displayTime="0"/> + <BootMenu mode="MessageAndMenu"/> + <TimeOffset value="0"/> + <PXEDebug enabled="false"/> + </BIOS> + <USB> + <Controllers/> + <DeviceFilters/> + </USB> + <Network> + <Adapter slot="0" enabled="true" MACAddress="0800272A589D" cable="true" speed="0" type="82540EM"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + <InternalNetwork name="intnet"/> + <NATNetwork name="NatNetwork"/> + </DisabledModes> + <BridgedInterface name="hns0000000v"/> + </Adapter> + <Adapter slot="1" enabled="false" MACAddress="0800276B5197" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="2" enabled="false" MACAddress="080027A346C9" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="3" enabled="false" MACAddress="08002749A665" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="4" enabled="false" MACAddress="0800273246C6" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="5" enabled="false" MACAddress="080027B0B358" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="6" enabled="false" MACAddress="080027BF23E5" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="7" enabled="false" MACAddress="080027BEE078" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + </Network> + <UART> + <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + </UART> + <LPT> + <Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/> + <Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/> + </LPT> + <AudioAdapter controller="AC97" driver="Pulse" enabled="false"/> + <RTC localOrUTC="local"/> + <SharedFolders/> + <Clipboard mode="Disabled"/> + <DragAndDrop mode="Disabled"/> + <IO> + <IoCache enabled="true" size="5"/> + <BandwidthGroups/> + </IO> + <HostPci> + <Devices/> + </HostPci> + <EmulatedUSB> + <CardReader enabled="false"/> + </EmulatedUSB> + <Guest memoryBalloonSize="0"/> + <GuestProperties/> + </Hardware> + <ExtraData> + <ExtraDataItem name="FRONTEND/Type" value="vrdp"/> + </ExtraData> + <StorageControllers> + <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"> + <AttachedDevice type="HardDisk" port="0" device="0"> + <Image uuid="{64c31740-9498-4c90-866b-650819686ef5}"/> + </AttachedDevice> + <AttachedDevice type="HardDisk" port="0" device="1"> + <Image uuid="{ec8ccae3-adc5-4bfd-b381-5a5eca357699}"/> + </AttachedDevice> + </StorageController> + <StorageController name="SATA Controller" type="AHCI" PortCount="30" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3"/> + <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true"/> + <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true" Bootable="true"/> + </StorageControllers> + <Snapshot uuid="{364c7314-b2d9-4fcf-b786-47b987449ef7}" name="15052014-115228" timeStamp="2014-05-15T09:52:29Z"> + <Hardware version="2"> + <CPU count="1" hotplug="false"> + <HardwareVirtEx enabled="true"/> + <HardwareVirtExNestedPaging enabled="true"/> + <HardwareVirtExVPID enabled="true"/> + <HardwareVirtExUX enabled="true"/> + <PAE enabled="false"/> + <LongMode enabled="true"/> + <HardwareVirtExLargePages enabled="false"/> + <HardwareVirtForce enabled="false"/> + </CPU> + <Memory RAMSize="192" PageFusion="false"/> + <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/> + <HPET enabled="false"/> + <Chipset type="PIIX3"/> + <Boot> + <Order position="1" device="HardDisk"/> + <Order position="2" device="None"/> + <Order position="3" device="None"/> + <Order position="4" device="None"/> + </Boot> + <Display VRAMSize="8" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/> + <VideoCapture enabled="false" screens="18446744073709551615" horzRes="1024" vertRes="768" rate="512" fps="25"/> + <RemoteDisplay enabled="true" authType="Null" allowMultiConnection="true"> + <VRDEProperties> + <Property name="TCP/Ports" value="5900"/> + </VRDEProperties> + </RemoteDisplay> + <BIOS> + <ACPI enabled="true"/> + <IOAPIC enabled="true"/> + <Logo fadeIn="true" fadeOut="true" displayTime="0"/> + <BootMenu mode="MessageAndMenu"/> + <TimeOffset value="0"/> + <PXEDebug enabled="false"/> + </BIOS> + <USB> + <Controllers/> + <DeviceFilters/> + </USB> + <Network> + <Adapter slot="0" enabled="true" MACAddress="0800272A589D" cable="true" speed="0" type="82540EM"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + <InternalNetwork name="intnet"/> + <NATNetwork name="NatNetwork"/> + </DisabledModes> + <BridgedInterface name="hns0000000v"/> + </Adapter> + <Adapter slot="1" enabled="false" MACAddress="0800276B5197" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="2" enabled="false" MACAddress="080027A346C9" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="3" enabled="false" MACAddress="08002749A665" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="4" enabled="false" MACAddress="0800273246C6" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="5" enabled="false" MACAddress="080027B0B358" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="6" enabled="false" MACAddress="080027BF23E5" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="7" enabled="false" MACAddress="080027BEE078" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + </Network> + <UART> + <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + </UART> + <LPT> + <Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/> + <Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/> + </LPT> + <AudioAdapter controller="AC97" driver="Pulse" enabled="false"/> + <RTC localOrUTC="local"/> + <SharedFolders/> + <Clipboard mode="Disabled"/> + <DragAndDrop mode="Disabled"/> + <IO> + <IoCache enabled="true" size="5"/> + <BandwidthGroups/> + </IO> + <HostPci> + <Devices/> + </HostPci> + <EmulatedUSB> + <CardReader enabled="false"/> + </EmulatedUSB> + <Guest memoryBalloonSize="0"/> + <GuestProperties/> + </Hardware> + <StorageControllers> + <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"> + <AttachedDevice type="HardDisk" port="0" device="0"> + <Image uuid="{105122e9-f6d2-42c1-9c11-c01699c79517}"/> + </AttachedDevice> + <AttachedDevice type="HardDisk" port="0" device="1"> + <Image uuid="{f113fdf8-9750-4557-a8be-87fd02319071}"/> + </AttachedDevice> + </StorageController> + <StorageController name="SATA Controller" type="AHCI" PortCount="30" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3"/> + <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true"/> + <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true" Bootable="true"/> + </StorageControllers> + <Snapshots> + <Snapshot uuid="{94afa063-9281-4e15-b586-a5e1fc5e17bf}" name="15052014-115248" timeStamp="2014-05-15T09:52:49Z"> + <Hardware version="2"> + <CPU count="1" hotplug="false"> + <HardwareVirtEx enabled="true"/> + <HardwareVirtExNestedPaging enabled="true"/> + <HardwareVirtExVPID enabled="true"/> + <HardwareVirtExUX enabled="true"/> + <PAE enabled="false"/> + <LongMode enabled="true"/> + <HardwareVirtExLargePages enabled="false"/> + <HardwareVirtForce enabled="false"/> + </CPU> + <Memory RAMSize="192" PageFusion="false"/> + <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/> + <HPET enabled="false"/> + <Chipset type="PIIX3"/> + <Boot> + <Order position="1" device="HardDisk"/> + <Order position="2" device="None"/> + <Order position="3" device="None"/> + <Order position="4" device="None"/> + </Boot> + <Display VRAMSize="8" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/> + <VideoCapture enabled="false" screens="18446744073709551615" horzRes="1024" vertRes="768" rate="512" fps="25"/> + <RemoteDisplay enabled="true" authType="Null" allowMultiConnection="true"> + <VRDEProperties> + <Property name="TCP/Ports" value="5900"/> + </VRDEProperties> + </RemoteDisplay> + <BIOS> + <ACPI enabled="true"/> + <IOAPIC enabled="true"/> + <Logo fadeIn="true" fadeOut="true" displayTime="0"/> + <BootMenu mode="MessageAndMenu"/> + <TimeOffset value="0"/> + <PXEDebug enabled="false"/> + </BIOS> + <USB> + <Controllers/> + <DeviceFilters/> + </USB> + <Network> + <Adapter slot="0" enabled="true" MACAddress="0800272A589D" cable="true" speed="0" type="82540EM"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + <InternalNetwork name="intnet"/> + <NATNetwork name="NatNetwork"/> + </DisabledModes> + <BridgedInterface name="hns0000000v"/> + </Adapter> + <Adapter slot="1" enabled="false" MACAddress="0800276B5197" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="2" enabled="false" MACAddress="080027A346C9" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="3" enabled="false" MACAddress="08002749A665" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="4" enabled="false" MACAddress="0800273246C6" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="5" enabled="false" MACAddress="080027B0B358" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="6" enabled="false" MACAddress="080027BF23E5" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="7" enabled="false" MACAddress="080027BEE078" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + </Network> + <UART> + <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + </UART> + <LPT> + <Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/> + <Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/> + </LPT> + <AudioAdapter controller="AC97" driver="Pulse" enabled="false"/> + <RTC localOrUTC="local"/> + <SharedFolders/> + <Clipboard mode="Disabled"/> + <DragAndDrop mode="Disabled"/> + <IO> + <IoCache enabled="true" size="5"/> + <BandwidthGroups/> + </IO> + <HostPci> + <Devices/> + </HostPci> + <EmulatedUSB> + <CardReader enabled="false"/> + </EmulatedUSB> + <Guest memoryBalloonSize="0"/> + <GuestProperties/> + </Hardware> + <StorageControllers> + <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"> + <AttachedDevice type="HardDisk" port="0" device="0"> + <Image uuid="{de551924-a484-4f88-9897-c3c4986b8f75}"/> + </AttachedDevice> + <AttachedDevice type="HardDisk" port="0" device="1"> + <Image uuid="{72638d05-918a-43e8-9994-dcc9a3e2f2fe}"/> + </AttachedDevice> + </StorageController> + <StorageController name="SATA Controller" type="AHCI" PortCount="30" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3"/> + <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true"/> + <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true" Bootable="true"/> + </StorageControllers> + <Snapshots> + <Snapshot uuid="{574d0edf-0f9d-47ef-82e3-1b6d2d54a537}" name="15052014-115306" timeStamp="2014-05-15T09:53:06Z"> + <Hardware version="2"> + <CPU count="1" hotplug="false"> + <HardwareVirtEx enabled="true"/> + <HardwareVirtExNestedPaging enabled="true"/> + <HardwareVirtExVPID enabled="true"/> + <HardwareVirtExUX enabled="true"/> + <PAE enabled="false"/> + <LongMode enabled="true"/> + <HardwareVirtExLargePages enabled="false"/> + <HardwareVirtForce enabled="false"/> + </CPU> + <Memory RAMSize="192" PageFusion="false"/> + <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/> + <HPET enabled="false"/> + <Chipset type="PIIX3"/> + <Boot> + <Order position="1" device="HardDisk"/> + <Order position="2" device="None"/> + <Order position="3" device="None"/> + <Order position="4" device="None"/> + </Boot> + <Display VRAMSize="8" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/> + <VideoCapture enabled="false" screens="18446744073709551615" horzRes="1024" vertRes="768" rate="512" fps="25"/> + <RemoteDisplay enabled="true" authType="Null" allowMultiConnection="true"> + <VRDEProperties> + <Property name="TCP/Ports" value="5900"/> + </VRDEProperties> + </RemoteDisplay> + <BIOS> + <ACPI enabled="true"/> + <IOAPIC enabled="true"/> + <Logo fadeIn="true" fadeOut="true" displayTime="0"/> + <BootMenu mode="MessageAndMenu"/> + <TimeOffset value="0"/> + <PXEDebug enabled="false"/> + </BIOS> + <USB> + <Controllers/> + <DeviceFilters/> + </USB> + <Network> + <Adapter slot="0" enabled="true" MACAddress="0800272A589D" cable="true" speed="0" type="82540EM"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + <InternalNetwork name="intnet"/> + <NATNetwork name="NatNetwork"/> + </DisabledModes> + <BridgedInterface name="hns0000000v"/> + </Adapter> + <Adapter slot="1" enabled="false" MACAddress="0800276B5197" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="2" enabled="false" MACAddress="080027A346C9" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="3" enabled="false" MACAddress="08002749A665" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="4" enabled="false" MACAddress="0800273246C6" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="5" enabled="false" MACAddress="080027B0B358" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="6" enabled="false" MACAddress="080027BF23E5" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="7" enabled="false" MACAddress="080027BEE078" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + </Network> + <UART> + <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + </UART> + <LPT> + <Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/> + <Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/> + </LPT> + <AudioAdapter controller="AC97" driver="Pulse" enabled="false"/> + <RTC localOrUTC="local"/> + <SharedFolders/> + <Clipboard mode="Disabled"/> + <DragAndDrop mode="Disabled"/> + <IO> + <IoCache enabled="true" size="5"/> + <BandwidthGroups/> + </IO> + <HostPci> + <Devices/> + </HostPci> + <EmulatedUSB> + <CardReader enabled="false"/> + </EmulatedUSB> + <Guest memoryBalloonSize="0"/> + <GuestProperties/> + </Hardware> + <StorageControllers> + <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"> + <AttachedDevice type="HardDisk" port="0" device="0"> + <Image uuid="{55c4d273-380b-43c6-b394-b2636051cfc7}"/> + </AttachedDevice> + <AttachedDevice type="HardDisk" port="0" device="1"> + <Image uuid="{f9f1a347-e9b0-4ba3-bf5d-cfbccccf8c27}"/> + </AttachedDevice> + </StorageController> + <StorageController name="SATA Controller" type="AHCI" PortCount="30" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3"/> + <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true"/> + <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true" Bootable="true"/> + </StorageControllers> + </Snapshot> + </Snapshots> + </Snapshot> + </Snapshots> + </Snapshot> + </Machine> +</VirtualBox> diff --git a/tests/vboxsnapshotxmldata/2disks-nosnap.vbox b/tests/vboxsnapshotxmldata/2disks-nosnap.vbox new file mode 100644 index 0000000..7037503 --- /dev/null +++ b/tests/vboxsnapshotxmldata/2disks-nosnap.vbox @@ -0,0 +1,168 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!--WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE +OVERWRITTEN AND LOST. +Changes to this xml configuration should be made using Virtualbox +or other application using the libvirt API--> +<VirtualBox version="1.12-linux" xmlns="http://www.innotek.de/VirtualBox-settings"> + <Machine uuid="{74864f52-a7b7-4172-a7a5-54b6f3e9cd21}" name="74864f52-a7b7-4172-a7a5-54b6f3e9cd21" snapshotFolder="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/Snapshots" currentStateModified="false" OSType="Other" lastStateChange="2014-05-15T11:49:46Z"> + <MediaRegistry> + <DVDImages/> + <FloppyImages/> + <HardDisks> + <HardDisk uuid="{105122e9-f6d2-42c1-9c11-c01699c79517}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/libvirtTest-disk1.vdi" format="VDI" type="Normal"/> + <HardDisk uuid="{f113fdf8-9750-4557-a8be-87fd02319071}" location="/home/libvirt/vbox/74864f52-a7b7-4172-a7a5-54b6f3e9cd21/libvirtTest-disk2.vdi" format="VDI" type="Normal"/> + </HardDisks> + </MediaRegistry> + <Hardware version="2"> + <CPU count="1" hotplug="false"> + <HardwareVirtEx enabled="true"/> + <HardwareVirtExNestedPaging enabled="true"/> + <HardwareVirtExVPID enabled="true"/> + <HardwareVirtExUX enabled="true"/> + <PAE enabled="false"/> + <LongMode enabled="true"/> + <HardwareVirtExLargePages enabled="false"/> + <HardwareVirtForce enabled="false"/> + </CPU> + <Memory RAMSize="192" PageFusion="false"/> + <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/> + <HPET enabled="false"/> + <Chipset type="PIIX3"/> + <Boot> + <Order position="1" device="HardDisk"/> + <Order position="2" device="None"/> + <Order position="3" device="None"/> + <Order position="4" device="None"/> + </Boot> + <Display VRAMSize="8" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/> + <VideoCapture enabled="false" screens="18446744073709551615" horzRes="1024" vertRes="768" rate="512" fps="25"/> + <RemoteDisplay enabled="true" authType="Null" allowMultiConnection="true"> + <VRDEProperties> + <Property name="TCP/Ports" value="5900"/> + </VRDEProperties> + </RemoteDisplay> + <BIOS> + <ACPI enabled="true"/> + <IOAPIC enabled="true"/> + <Logo fadeIn="true" fadeOut="true" displayTime="0"/> + <BootMenu mode="MessageAndMenu"/> + <TimeOffset value="0"/> + <PXEDebug enabled="false"/> + </BIOS> + <USB> + <Controllers/> + <DeviceFilters/> + </USB> + <Network> + <Adapter slot="0" enabled="true" MACAddress="0800272A589D" cable="true" speed="0" type="82540EM"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + <InternalNetwork name="intnet"/> + <NATNetwork name="NatNetwork"/> + </DisabledModes> + <BridgedInterface name="hns0000000v"/> + </Adapter> + <Adapter slot="1" enabled="false" MACAddress="0800276B5197" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="2" enabled="false" MACAddress="080027A346C9" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="3" enabled="false" MACAddress="08002749A665" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="4" enabled="false" MACAddress="0800273246C6" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="5" enabled="false" MACAddress="080027B0B358" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="6" enabled="false" MACAddress="080027BF23E5" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + <Adapter slot="7" enabled="false" MACAddress="080027BEE078" cable="true" speed="0" type="Am79C973"> + <DisabledModes> + <NAT> + <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/> + <Alias logging="false" proxy-only="false" use-same-ports="false"/> + </NAT> + </DisabledModes> + </Adapter> + </Network> + <UART> + <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/> + </UART> + <LPT> + <Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/> + <Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/> + </LPT> + <AudioAdapter controller="AC97" driver="Pulse" enabled="false"/> + <RTC localOrUTC="local"/> + <SharedFolders/> + <Clipboard mode="Disabled"/> + <DragAndDrop mode="Disabled"/> + <IO> + <IoCache enabled="true" size="5"/> + <BandwidthGroups/> + </IO> + <HostPci> + <Devices/> + </HostPci> + <EmulatedUSB> + <CardReader enabled="false"/> + </EmulatedUSB> + <Guest memoryBalloonSize="0"/> + <GuestProperties/> + </Hardware> + <ExtraData> + <ExtraDataItem name="FRONTEND/Type" value="vrdp"/> + </ExtraData> + <StorageControllers> + <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"> + <AttachedDevice type="HardDisk" port="0" device="0"> + <Image uuid="{105122e9-f6d2-42c1-9c11-c01699c79517}"/> + </AttachedDevice> + <AttachedDevice type="HardDisk" port="0" device="1"> + <Image uuid="{f113fdf8-9750-4557-a8be-87fd02319071}"/> + </AttachedDevice> + </StorageController> + <StorageController name="SATA Controller" type="AHCI" PortCount="30" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3"/> + <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true"/> + <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true" Bootable="true"/> + </StorageControllers> + </Machine> +</VirtualBox> diff --git a/tests/vboxsnapshotxmltest.c b/tests/vboxsnapshotxmltest.c new file mode 100644 index 0000000..7795688 --- /dev/null +++ b/tests/vboxsnapshotxmltest.c @@ -0,0 +1,161 @@ +#include <config.h> + +#include "testutils.h" + +#ifdef WITH_VBOX + +# include <stdio.h> +# include <stdlib.h> +# include <regex.h> +# 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*/ -- 1.7.10.4

On Mon, May 19, 2014 at 02:47:31PM +0200, Yohan BELLEGUIC wrote:
This structure contains the data to be saved in the VirtualBox XML file and can be manipulated with severals exposed functions. The structure is created by vboxSnapshotLoadVboxFile taking the machine XML file. It also can rewrite the XML by using vboxSnapshotSaveVboxFile. --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/vbox/vbox_snapshot_conf.c | 1490 ++++++++++++++++++++ src/vbox/vbox_snapshot_conf.h | 105 ++ tests/Makefile.am | 15 + tests/vboxsnapshotxmldata/2disks-1snap.vbox | 322 +++++ tests/vboxsnapshotxmldata/2disks-2snap.vbox | 478 +++++++ .../vboxsnapshotxmldata/2disks-3snap-brother.vbox | 786 +++++++++++ tests/vboxsnapshotxmldata/2disks-3snap.vbox | 636 +++++++++ tests/vboxsnapshotxmldata/2disks-nosnap.vbox | 168 +++ tests/vboxsnapshotxmltest.c | 161 +++ 11 files changed, 4163 insertions(+) create mode 100644 src/vbox/vbox_snapshot_conf.c create mode 100644 src/vbox/vbox_snapshot_conf.h create mode 100644 tests/vboxsnapshotxmldata/2disks-1snap.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-2snap.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-3snap-brother.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-3snap.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-nosnap.vbox create mode 100644 tests/vboxsnapshotxmltest.c
ACK I made a few changes to this - primarily to add 'const' to all the 'char *' strings, tweak line breaks, and remove some bogus use of virStringFreeList on non-NULL terminated arrays So the change I'm pushing includes this: diff --git a/src/vbox/vbox_snapshot_conf.c b/src/vbox/vbox_snapshot_conf.c index 9fee760..4909665 100644 --- a/src/vbox/vbox_snapshot_conf.c +++ b/src/vbox/vbox_snapshot_conf.c @@ -30,9 +30,10 @@ #define VIR_FROM_THIS VIR_FROM_VBOX VIR_LOG_INIT("vbox.vbox_snapshot_conf"); -static virVBoxSnapshotConfHardDiskPtr virVBoxSnapshotConfCreateVBoxSnapshotConfHardDiskPtr(xmlNodePtr diskNode, - xmlXPathContextPtr xPathContext, - char *machineLocation) +static virVBoxSnapshotConfHardDiskPtr +virVBoxSnapshotConfCreateVBoxSnapshotConfHardDiskPtr(xmlNodePtr diskNode, + xmlXPathContextPtr xPathContext, + const char *machineLocation) { virVBoxSnapshotConfHardDiskPtr hardDisk = NULL; xmlNodePtr *nodes = NULL; @@ -114,9 +115,10 @@ static virVBoxSnapshotConfHardDiskPtr virVBoxSnapshotConfCreateVBoxSnapshotConfH return hardDisk; } -static virVBoxSnapshotConfMediaRegistryPtr virVBoxSnapshotConfRetrieveMediaRegistry(xmlNodePtr mediaRegistryNode, - xmlXPathContextPtr xPathContext, - char *machineLocation) +static virVBoxSnapshotConfMediaRegistryPtr +virVBoxSnapshotConfRetrieveMediaRegistry(xmlNodePtr mediaRegistryNode, + xmlXPathContextPtr xPathContext, + const char *machineLocation) { virVBoxSnapshotConfMediaRegistryPtr mediaRegistry = NULL; xmlNodePtr hardDisksNode = NULL; @@ -175,8 +177,9 @@ static virVBoxSnapshotConfMediaRegistryPtr virVBoxSnapshotConfRetrieveMediaRegis return mediaRegistry; } -static virVBoxSnapshotConfSnapshotPtr virVBoxSnapshotConfRetrieveSnapshot(xmlNodePtr snapshotNode, - xmlXPathContextPtr xPathContext) +static virVBoxSnapshotConfSnapshotPtr +virVBoxSnapshotConfRetrieveSnapshot(xmlNodePtr snapshotNode, + xmlXPathContextPtr xPathContext) { virVBoxSnapshotConfSnapshotPtr snapshot = NULL; xmlNodePtr hardwareNode = NULL; @@ -278,8 +281,9 @@ static virVBoxSnapshotConfSnapshotPtr virVBoxSnapshotConfRetrieveSnapshot(xmlNod return snapshot; } -virVBoxSnapshotConfSnapshotPtr virVBoxSnapshotConfSnapshotByName(virVBoxSnapshotConfSnapshotPtr snapshot, - char *snapshotName) +virVBoxSnapshotConfSnapshotPtr +virVBoxSnapshotConfSnapshotByName(virVBoxSnapshotConfSnapshotPtr snapshot, + const char *snapshotName) { size_t i = 0; virVBoxSnapshotConfSnapshotPtr ret = NULL; @@ -293,8 +297,9 @@ virVBoxSnapshotConfSnapshotPtr virVBoxSnapshotConfSnapshotByName(virVBoxSnapshot return ret; } -static virVBoxSnapshotConfHardDiskPtr virVBoxSnapshotConfHardDiskById(virVBoxSnapshotConfHardDiskPtr disk, - char *parentHardDiskId) +static virVBoxSnapshotConfHardDiskPtr +virVBoxSnapshotConfHardDiskById(virVBoxSnapshotConfHardDiskPtr disk, + const char *parentHardDiskId) { size_t i = 0; virVBoxSnapshotConfHardDiskPtr ret = NULL; @@ -308,8 +313,9 @@ static virVBoxSnapshotConfHardDiskPtr virVBoxSnapshotConfHardDiskById(virVBoxSna return ret; } -static virVBoxSnapshotConfHardDiskPtr virVBoxSnapshotConfHardDiskByLocation(virVBoxSnapshotConfHardDiskPtr disk, - char *parentLocation) +static virVBoxSnapshotConfHardDiskPtr +virVBoxSnapshotConfHardDiskByLocation(virVBoxSnapshotConfHardDiskPtr disk, + const char *parentLocation) { size_t i = 0; virVBoxSnapshotConfHardDiskPtr ret = NULL; @@ -323,7 +329,8 @@ static virVBoxSnapshotConfHardDiskPtr virVBoxSnapshotConfHardDiskByLocation(virV return ret; } -static xmlNodePtr virVBoxSnapshotConfCreateHardDiskNode(virVBoxSnapshotConfHardDiskPtr hardDisk) +static xmlNodePtr +virVBoxSnapshotConfCreateHardDiskNode(virVBoxSnapshotConfHardDiskPtr hardDisk) { int result = -1; size_t i = 0; @@ -357,7 +364,9 @@ static xmlNodePtr virVBoxSnapshotConfCreateHardDiskNode(virVBoxSnapshotConfHardD return ret; } -static int virVBoxSnapshotConfSerializeSnapshot(xmlNodePtr node, virVBoxSnapshotConfSnapshotPtr snapshot) +static int +virVBoxSnapshotConfSerializeSnapshot(xmlNodePtr node, + virVBoxSnapshotConfSnapshotPtr snapshot) { int result = -1; size_t i = 0; @@ -458,7 +467,9 @@ static int virVBoxSnapshotConfSerializeSnapshot(xmlNodePtr node, virVBoxSnapshot return result; } -static size_t virVBoxSnapshotConfAllChildren(virVBoxSnapshotConfHardDiskPtr disk, virVBoxSnapshotConfHardDiskPtr **list) +static size_t +virVBoxSnapshotConfAllChildren(virVBoxSnapshotConfHardDiskPtr disk, + virVBoxSnapshotConfHardDiskPtr **list) { size_t returnSize = 0; size_t tempSize = 0; @@ -486,7 +497,8 @@ static size_t virVBoxSnapshotConfAllChildren(virVBoxSnapshotConfHardDiskPtr disk return returnSize; } -void virVboxSnapshotConfHardDiskFree(virVBoxSnapshotConfHardDiskPtr disk) +void +virVboxSnapshotConfHardDiskFree(virVBoxSnapshotConfHardDiskPtr disk) { size_t i = 0; @@ -504,7 +516,8 @@ void virVboxSnapshotConfHardDiskFree(virVBoxSnapshotConfHardDiskPtr disk) } -void virVBoxSnapshotConfMediaRegistryFree(virVBoxSnapshotConfMediaRegistryPtr mediaRegistry) +void +virVBoxSnapshotConfMediaRegistryFree(virVBoxSnapshotConfMediaRegistryPtr mediaRegistry) { size_t i = 0; @@ -514,11 +527,14 @@ void virVBoxSnapshotConfMediaRegistryFree(virVBoxSnapshotConfMediaRegistryPtr me for (i = 0; i < mediaRegistry->ndisks; i++) virVboxSnapshotConfHardDiskFree(mediaRegistry->disks[i]); VIR_FREE(mediaRegistry->disks); - virStringFreeList(mediaRegistry->otherMedia); + for (i = 0; i < mediaRegistry->notherMedia; i++) + VIR_FREE(mediaRegistry->otherMedia[i]); + VIR_FREE(mediaRegistry->otherMedia); VIR_FREE(mediaRegistry); } -void virVBoxSnapshotConfSnapshotFree(virVBoxSnapshotConfSnapshotPtr snapshot) +void +virVBoxSnapshotConfSnapshotFree(virVBoxSnapshotConfSnapshotPtr snapshot) { size_t i = 0; @@ -537,7 +553,8 @@ void virVBoxSnapshotConfSnapshotFree(virVBoxSnapshotConfSnapshotPtr snapshot) VIR_FREE(snapshot); } -void virVBoxSnapshotConfMachineFree(virVBoxSnapshotConfMachinePtr machine) +void +virVBoxSnapshotConfMachineFree(virVBoxSnapshotConfMachinePtr machine) { if (!machine) return; @@ -561,7 +578,9 @@ void virVBoxSnapshotConfMachineFree(virVBoxSnapshotConfMachinePtr machine) *return NULL on failure *filePath must not be NULL. */ -virVBoxSnapshotConfMachinePtr virVBoxSnapshotConfLoadVboxFile(const char *filePath, char *machineLocation) +virVBoxSnapshotConfMachinePtr +virVBoxSnapshotConfLoadVboxFile(const char *filePath, + const char *machineLocation) { int ret = -1; virVBoxSnapshotConfMachinePtr machineDescription = NULL; @@ -702,6 +721,8 @@ virVBoxSnapshotConfMachinePtr virVBoxSnapshotConfLoadVboxFile(const char *filePa cur = virXPathNode("./vbox:Snapshot", xPathContext); if (cur != NULL) { machineDescription->snapshot = virVBoxSnapshotConfRetrieveSnapshot(cur, xPathContext); + if (!machineDescription->snapshot) + goto cleanup; } ret = 0; @@ -727,9 +748,10 @@ virVBoxSnapshotConfMachinePtr virVBoxSnapshotConfLoadVboxFile(const char *filePa *return 0 on success *return -1 on failure */ -int virVBoxSnapshotConfAddSnapshotToXmlMachine(virVBoxSnapshotConfSnapshotPtr snapshot, - virVBoxSnapshotConfMachinePtr machine, - char *snapshotParentName) +int +virVBoxSnapshotConfAddSnapshotToXmlMachine(virVBoxSnapshotConfSnapshotPtr snapshot, + virVBoxSnapshotConfMachinePtr machine, + const char *snapshotParentName) { int ret = -1; virVBoxSnapshotConfSnapshotPtr parentSnapshot = NULL; @@ -786,9 +808,10 @@ int virVBoxSnapshotConfAddSnapshotToXmlMachine(virVBoxSnapshotConfSnapshotPtr sn *return 0 on success *return -1 on failure */ -int virVBoxSnapshotConfAddHardDiskToMediaRegistry(virVBoxSnapshotConfHardDiskPtr hardDisk, - virVBoxSnapshotConfMediaRegistryPtr mediaRegistry, - char *parentHardDiskId) +int +virVBoxSnapshotConfAddHardDiskToMediaRegistry(virVBoxSnapshotConfHardDiskPtr hardDisk, + virVBoxSnapshotConfMediaRegistryPtr mediaRegistry, + const char *parentHardDiskId) { int ret = -1; size_t i = 0; @@ -834,7 +857,9 @@ int virVBoxSnapshotConfAddHardDiskToMediaRegistry(virVBoxSnapshotConfHardDiskPtr *return 0 on success *return -1 on failure */ -int virVBoxSnapshotConfRemoveSnapshot(virVBoxSnapshotConfMachinePtr machine, char *snapshotName) +int +virVBoxSnapshotConfRemoveSnapshot(virVBoxSnapshotConfMachinePtr machine, + const char *snapshotName) { int ret = -1; size_t i = 0; @@ -899,7 +924,9 @@ int virVBoxSnapshotConfRemoveSnapshot(virVBoxSnapshotConfMachinePtr machine, cha *return 0 on success *return -1 on failure */ -int virVBoxSnapshotConfRemoveHardDisk(virVBoxSnapshotConfMediaRegistryPtr mediaRegistry, char *uuid) +int +virVBoxSnapshotConfRemoveHardDisk(virVBoxSnapshotConfMediaRegistryPtr mediaRegistry, + const char *uuid) { int ret = -1; size_t i = 0; @@ -957,7 +984,9 @@ int virVBoxSnapshotConfRemoveHardDisk(virVBoxSnapshotConfMediaRegistryPtr mediaR *return 0 on success *return -1 on failure */ -int virVBoxSnapshotConfSaveVboxFile(virVBoxSnapshotConfMachinePtr machine, const char *filePath) +int +virVBoxSnapshotConfSaveVboxFile(virVBoxSnapshotConfMachinePtr machine, + const char *filePath) { int ret = -1; size_t i = 0; @@ -1212,7 +1241,9 @@ int virVBoxSnapshotConfSaveVboxFile(virVBoxSnapshotConfMachinePtr machine, const *isCurrentSnapshot: Return 1 if 'snapshotName' corresponds to the *vboxSnapshotXmlMachinePtr's current snapshot, return 0 otherwise. */ -int virVBoxSnapshotConfIsCurrentSnapshot(virVBoxSnapshotConfMachinePtr machine, char *snapshotName) +int +virVBoxSnapshotConfIsCurrentSnapshot(virVBoxSnapshotConfMachinePtr machine, + const char *snapshotName) { virVBoxSnapshotConfSnapshotPtr snapshot = NULL; if (machine == NULL) { @@ -1237,7 +1268,9 @@ int virVBoxSnapshotConfIsCurrentSnapshot(virVBoxSnapshotConfMachinePtr machine, *fills a list of read-write disk paths. *return array length on success, -1 on failure. */ -int virVBoxSnapshotConfGetRWDisksPathsFromLibvirtXML(char *filePath, char ***rwDisksPath) +int +virVBoxSnapshotConfGetRWDisksPathsFromLibvirtXML(const char *filePath, + char ***rwDisksPath) { int result = -1; size_t i = 0; @@ -1246,6 +1279,7 @@ int virVBoxSnapshotConfGetRWDisksPathsFromLibvirtXML(char *filePath, char ***rwD xmlXPathContextPtr xPathContext = NULL; xmlNodePtr *nodes = NULL; int nodeSize = 0; + *rwDisksPath = NULL; if (filePath == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("filePath is null")); @@ -1281,10 +1315,13 @@ int virVBoxSnapshotConfGetRWDisksPathsFromLibvirtXML(char *filePath, char ***rwD xmlFreeDoc(xml); xmlXPathFreeContext(xPathContext); if (result < 0) { - virStringFreeList(ret); + for (i = 0; i < nodeSize; i++) + VIR_FREE(ret[i]); + VIR_FREE(ret); nodeSize = -1; + } else { + *rwDisksPath = ret; } - *rwDisksPath = ret; return nodeSize; } @@ -1293,7 +1330,9 @@ int virVBoxSnapshotConfGetRWDisksPathsFromLibvirtXML(char *filePath, char ***rwD *a list of read-only disk paths (the parents of the read-write disks). *return array length on success, -1 on failure. */ -int virVBoxSnapshotConfGetRODisksPathsFromLibvirtXML(char *filePath, char ***roDisksPath) +int +virVBoxSnapshotConfGetRODisksPathsFromLibvirtXML(const char *filePath, + char ***roDisksPath) { int result = -1; size_t i = 0; @@ -1349,7 +1388,9 @@ int virVBoxSnapshotConfGetRODisksPathsFromLibvirtXML(char *filePath, char ***roD *hardDiskUuidByLocation: Return the uuid of the hard disk whose location is 'location' *return a valid uuid, or NULL on failure */ -char *virVBoxSnapshotConfHardDiskUuidByLocation(virVBoxSnapshotConfMachinePtr machine, char *location) +const char * +virVBoxSnapshotConfHardDiskUuidByLocation(virVBoxSnapshotConfMachinePtr machine, + const char *location) { size_t i = 0; virVBoxSnapshotConfHardDiskPtr hardDisk = NULL; @@ -1368,9 +1409,10 @@ char *virVBoxSnapshotConfHardDiskUuidByLocation(virVBoxSnapshotConfMachinePtr ma *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, - char *location) +size_t +virVBoxSnapshotConfDiskListToOpen(virVBoxSnapshotConfMachinePtr machine, + virVBoxSnapshotConfHardDiskPtr **hardDiskToOpen, + const char *location) { size_t i = 0; size_t returnSize = 0; @@ -1404,7 +1446,8 @@ size_t virVBoxSnapshotConfDiskListToOpen(virVBoxSnapshotConfMachinePtr machine, *return 0 on success *return -1 on failure */ -int virVBoxSnapshotConfRemoveFakeDisks(virVBoxSnapshotConfMachinePtr machine) +int +virVBoxSnapshotConfRemoveFakeDisks(virVBoxSnapshotConfMachinePtr machine) { int ret = -1; size_t i = 0; @@ -1444,7 +1487,9 @@ int virVBoxSnapshotConfRemoveFakeDisks(virVBoxSnapshotConfMachinePtr machine) *return 1 if the disk is in the media registry *return -1 on failure */ -int virVBoxSnapshotConfDiskIsInMediaRegistry(virVBoxSnapshotConfMachinePtr machine, char *location) +int +virVBoxSnapshotConfDiskIsInMediaRegistry(virVBoxSnapshotConfMachinePtr machine, + const char *location) { int ret = -1; size_t i = 0; @@ -1477,7 +1522,9 @@ int virVBoxSnapshotConfDiskIsInMediaRegistry(virVBoxSnapshotConfMachinePtr machi /* *hardDisksPtrByLocation: Return a vboxSnapshotXmlHardDiskPtr whose location is 'location' */ -virVBoxSnapshotConfHardDiskPtr virVBoxSnapshotConfHardDiskPtrByLocation(virVBoxSnapshotConfMachinePtr machine, char *location) +virVBoxSnapshotConfHardDiskPtr +virVBoxSnapshotConfHardDiskPtrByLocation(virVBoxSnapshotConfMachinePtr machine, + const char *location) { int it = 0; virVBoxSnapshotConfHardDiskPtr disk = NULL; diff --git a/src/vbox/vbox_snapshot_conf.h b/src/vbox/vbox_snapshot_conf.h index 59353b5..3864b30 100644 --- a/src/vbox/vbox_snapshot_conf.h +++ b/src/vbox/vbox_snapshot_conf.h @@ -81,25 +81,61 @@ struct _virVBoxSnapshotConfMachine { char *storageController; }; -void virVboxSnapshotConfHardDiskFree(virVBoxSnapshotConfHardDiskPtr disk); -void virVBoxSnapshotConfMediaRegistryFree(virVBoxSnapshotConfMediaRegistryPtr mediaRegistry); -void virVBoxSnapshotConfSnapshotFree(virVBoxSnapshotConfSnapshotPtr snapshot); -void virVBoxSnapshotConfMachineFree(virVBoxSnapshotConfMachinePtr machine); +void +virVboxSnapshotConfHardDiskFree(virVBoxSnapshotConfHardDiskPtr disk); +void +virVBoxSnapshotConfMediaRegistryFree(virVBoxSnapshotConfMediaRegistryPtr mediaRegistry); +void +virVBoxSnapshotConfSnapshotFree(virVBoxSnapshotConfSnapshotPtr snapshot); +void +virVBoxSnapshotConfMachineFree(virVBoxSnapshotConfMachinePtr machine); -virVBoxSnapshotConfMachinePtr virVBoxSnapshotConfLoadVboxFile(const char *filePath, char *machineLocation); -int virVBoxSnapshotConfAddSnapshotToXmlMachine(virVBoxSnapshotConfSnapshotPtr snapshot, virVBoxSnapshotConfMachinePtr machine, char *snapshotParentName); -int virVBoxSnapshotConfAddHardDiskToMediaRegistry(virVBoxSnapshotConfHardDiskPtr hardDisk, virVBoxSnapshotConfMediaRegistryPtr mediaRegistry, char *parentHardDiskId); -int virVBoxSnapshotConfRemoveSnapshot(virVBoxSnapshotConfMachinePtr machine, char *snapshotName); -int virVBoxSnapshotConfRemoveHardDisk(virVBoxSnapshotConfMediaRegistryPtr mediaRegistry,char *uuid); -int virVBoxSnapshotConfSaveVboxFile(virVBoxSnapshotConfMachinePtr machine, const char *filePath); -int virVBoxSnapshotConfIsCurrentSnapshot(virVBoxSnapshotConfMachinePtr machine, char *snapshotName); -int virVBoxSnapshotConfGetRWDisksPathsFromLibvirtXML(char *filePath, char ***realReadWriteDisksPath); -int virVBoxSnapshotConfGetRODisksPathsFromLibvirtXML(char *filePath, char ***realReadOnlyDisksPath); -char *virVBoxSnapshotConfHardDiskUuidByLocation(virVBoxSnapshotConfMachinePtr machine, char *location); -size_t virVBoxSnapshotConfDiskListToOpen(virVBoxSnapshotConfMachinePtr machine, virVBoxSnapshotConfHardDiskPtr **hardDiskToOpen, char *location); -int virVBoxSnapshotConfRemoveFakeDisks(virVBoxSnapshotConfMachinePtr machine); -int virVBoxSnapshotConfDiskIsInMediaRegistry(virVBoxSnapshotConfMachinePtr machine, char *location); -virVBoxSnapshotConfHardDiskPtr virVBoxSnapshotConfHardDiskPtrByLocation(virVBoxSnapshotConfMachinePtr machine, char *location); -virVBoxSnapshotConfSnapshotPtr virVBoxSnapshotConfSnapshotByName(virVBoxSnapshotConfSnapshotPtr snapshot, char *snapshotName); +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 498e2af..c999061 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -628,7 +628,7 @@ if WITH_VBOX vboxsnapshotxmltest_SOURCES = \ vboxsnapshotxmltest.c \ testutils.c testutils.h -vbox_LDADDS = ../src/libvirt_driver_vbox.la +vbox_LDADDS = ../src/libvirt_driver_vbox_impl.la vboxsnapshotxmltest_LDADD = $(LDADDS) $(vbox_LDADDS) else ! WITH_VBOX EXTRA_DIST += vboxsnapshotxmltest.c Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

This patch has resulted in many new Coverity errors - mostly resource leaks as a result of the virVBoxSnapshotConfAllChildren() recursive function. I would clean them up, but I'm a bit leary of missing some nuance in the original design. There were also a couple of issues in virVBoxSnapshotConfGetRWDisksPathsFromLibvirtXML and virVBoxSnapshotConfGetRODisksPathsFromLibvirtXML Details of the issues and some thoughts are inline below in the functions These should be cleaned up before the next release... John On 05/19/2014 08:47 AM, Yohan BELLEGUIC wrote:
This structure contains the data to be saved in the VirtualBox XML file and can be manipulated with severals exposed functions. The structure is created by vboxSnapshotLoadVboxFile taking the machine XML file. It also can rewrite the XML by using vboxSnapshotSaveVboxFile. --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/vbox/vbox_snapshot_conf.c | 1490 ++++++++++++++++++++ src/vbox/vbox_snapshot_conf.h | 105 ++ tests/Makefile.am | 15 + tests/vboxsnapshotxmldata/2disks-1snap.vbox | 322 +++++ tests/vboxsnapshotxmldata/2disks-2snap.vbox | 478 +++++++ .../vboxsnapshotxmldata/2disks-3snap-brother.vbox | 786 +++++++++++ tests/vboxsnapshotxmldata/2disks-3snap.vbox | 636 +++++++++ tests/vboxsnapshotxmldata/2disks-nosnap.vbox | 168 +++ tests/vboxsnapshotxmltest.c | 161 +++ 11 files changed, 4163 insertions(+) create mode 100644 src/vbox/vbox_snapshot_conf.c create mode 100644 src/vbox/vbox_snapshot_conf.h create mode 100644 tests/vboxsnapshotxmldata/2disks-1snap.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-2snap.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-3snap-brother.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-3snap.vbox create mode 100644 tests/vboxsnapshotxmldata/2disks-nosnap.vbox create mode 100644 tests/vboxsnapshotxmltest.c
diff --git a/po/POTFILES.in b/po/POTFILES.in index 6e8d465..52dbcfe 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -210,6 +210,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 cfb7097..065e7ea 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 0000000..9fee760 --- /dev/null +++ b/src/vbox/vbox_snapshot_conf.c @@ -0,0 +1,1490 @@ +/* + * 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, + 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); + virStringFreeList(searchTabResult); + if (result < 0) { + virVboxSnapshotConfHardDiskFree(hardDisk); + hardDisk = NULL; + } + return hardDisk; +} + +static virVBoxSnapshotConfMediaRegistryPtr virVBoxSnapshotConfRetrieveMediaRegistry(xmlNodePtr mediaRegistryNode, + xmlXPathContextPtr xPathContext, + 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); + virStringFreeList(searchTabResult); + return snapshot; +} + +virVBoxSnapshotConfSnapshotPtr virVBoxSnapshotConfSnapshotByName(virVBoxSnapshotConfSnapshotPtr snapshot, + 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, + 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, + 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;
Unusual to allocate an array of 0 elements - why start at 0? Other code starts at 1 and increments from there. Does this even work the way you expect?
+ + for (i = 0; i < disk->nchildren; i++) { + tempSize = virVBoxSnapshotConfAllChildren(disk->children[i], &tempList);
This is a recursive call - which perhaps might lead you down into a path of doom. If tempSize returned is 0 (which it could be), then the following EXPAND isn't going to be happy and the callers are going to be failing. Failed callers do not go through any of the free allocated memory logic in failure path - resulting in potentially memory leaks.
+ if (VIR_EXPAND_N(ret, returnSize, tempSize) < 0) + return 0;
If you're failing to EXPAND then "theoretically" you should FAIL here and thus VIR_FREE() each of the existing elements of the array followed by a VIR_FREE() of the array itself
+ + for (j = 0; j < tempSize; j++) { + ret[returnSize - tempSize + j] = tempList[j];
If you're going to handle failures, then a "tempList[j] = NULL;" would be added in order to ensure a future VIR_FREE(tempList[j]); works properly.
+ } + } + if (VIR_EXPAND_N(ret, returnSize, 1) < 0) + return 0;
Again another failure scenario....
+ + 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); + virStringFreeList(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, 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); + } + + 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, + 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, + 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, 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, 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, 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(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; + 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); Coverity comlains that nodeSize can be a negative number. Other code in
So this is where there's typically a cleanup section such as: cleanup: for (i = 0; i < tempSize; i++) VIR_FREE(tempList[i]); VIR_FREE(tempList); libvirt will do something like: 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) { + virStringFreeList(ret); + nodeSize = -1; + } + *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(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);
Same issue here as the RW code - you need to handle nodeSize return: 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) { + 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 + */ +char *virVBoxSnapshotConfHardDiskUuidByLocation(virVBoxSnapshotConfMachinePtr machine, 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, + 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);
Another place where dealing with failures is necessary
+ if (VIR_EXPAND_N(diskList, diskSize, tempSize) < 0)
Failure causes tempList to be lost
+ 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;
Same comment
+ } + } + } + ret = 0;
Again, typically a cleanup: section goes here
+ 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, 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);
Similar issues in this function as well as the previous one
+ 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, 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; +}

Ug... and trying to "locally" fix the RW/RO discovered two more issues... On 06/11/2014 08:21 AM, John Ferlan wrote:
This patch has resulted in many new Coverity errors - mostly resource leaks as a result of the virVBoxSnapshotConfAllChildren() recursive function. I would clean them up, but I'm a bit leary of missing some nuance in the original design.
There were also a couple of issues in virVBoxSnapshotConfGetRWDisksPathsFromLibvirtXML and virVBoxSnapshotConfGetRODisksPathsFromLibvirtXML
Details of the issues and some thoughts are inline below in the functions
These should be cleaned up before the next release...
John
<...snip...>
+ +/* + *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(char *filePath, char ***rwDisksPath) +{ + int result = -1; + size_t i = 0; + char **ret;
Needs to be initialized to NULL (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); + nodeSize = virXPathNodeSet("/domainsnapshot/disks/disk", xPathContext, &nodes); Coverity comlains that nodeSize can be a negative number. Other code in libvirt will do something like:
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) { + virStringFreeList(ret); + nodeSize = -1; + } + *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(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);
Same issue here as the RW code - you need to handle nodeSize return:
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) { + virStringFreeList(ret); + nodeSize = -1; + } + *roDisksPath = ret;
Use: + } else { + *roDisksPath = ret; }
+ return nodeSize; +} +

One more here too - missed it in my last pass... On 05/19/2014 08:47 AM, Yohan BELLEGUIC wrote: <...snip...>
+ +/* + *isCurrentSnapshot: Return 1 if 'snapshotName' corresponds to the + *vboxSnapshotXmlMachinePtr's current snapshot, return 0 otherwise. + */ +int virVBoxSnapshotConfIsCurrentSnapshot(virVBoxSnapshotConfMachinePtr machine, 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);
Coverity complains: (5) Event returned_null: "virVBoxSnapshotConfSnapshotByName" returns null (checked 4 out of 5 times). [details] (14) Event var_assigned: Assigning: "snapshot" = null return value from "virVBoxSnapshotConfSnapshotByName". You will need a: if (snapshot == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to find the snapshot %s"), snapshotName); goto cleanup; }
+ return STREQ(snapshot->uuid, machine->currentSnapshot);
(15) Event dereference: Dereferencing a null pointer "snapshot".
+ + cleanup: + return 0; +} + +/*

The machine is unregistered and its vbox XML file is changed in order to add snapshot information. The machine is then registered with the snapshot to redefine. --- src/vbox/vbox_tmpl.c | 976 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 970 insertions(+), 6 deletions(-) diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 5d4a7ba..a7f15d4 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -43,6 +43,7 @@ #include "datatypes.h" #include "domain_conf.h" #include "snapshot_conf.h" +#include "vbox_snapshot_conf.h" #include "network_conf.h" #include "virerror.h" #include "domain_event.h" @@ -6015,6 +6016,958 @@ vboxDomainSnapshotGet(vboxGlobalData *data, return snapshot; } +#if VBOX_API_VERSION >= 4002000 +static int vboxCloseDisksRecursively(virDomainPtr dom, char *location) +{ + VBOX_OBJECT_CHECK(dom->conn, int, -1); + nsresult rc; + size_t i = 0; + PRUnichar *locationUtf = NULL; + IMedium *medium = NULL; + IMedium **children = NULL; + PRUint32 childrenSize = 0; + VBOX_UTF8_TO_UTF16(location, &locationUtf); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + rc = medium->vtbl->GetChildren(medium, &childrenSize, &children); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s" + , _("Unable to get disk children")); + goto cleanup; + } + for (i = 0; i < childrenSize; i++) { + IMedium *childMedium = children[i]; + if (childMedium) { + PRUnichar *childLocationUtf = NULL; + char *childLocation = NULL; + rc = childMedium->vtbl->GetLocation(childMedium, &childLocationUtf); + VBOX_UTF16_TO_UTF8(childLocationUtf, &childLocation); + VBOX_UTF16_FREE(childLocationUtf); + if (vboxCloseDisksRecursively(dom, childLocation) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s" + , _("Unable to close disk children")); + goto cleanup; + } + VIR_FREE(childLocation); + } + } + rc = medium->vtbl->Close(medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to close HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + + ret = 0; + cleanup: + VBOX_UTF16_FREE(locationUtf); + return ret; +} + +static int +vboxSnapshotRedefine(virDomainPtr dom, + virDomainSnapshotDefPtr def, + bool isCurrent) +{ + /* + * If your snapshot has a parent, + * it will only be redefined if you have already + * redefined the parent. + * + * The general algorithm of this function is below : + * First of all, we are going to create our vboxSnapshotXmlMachinePtr struct from + * the machine settings path. + * Then, if the machine current snapshot xml file is saved in the machine location, + * it means that this snapshot was previously modified by us and has fake disks. + * Fake disks are added when the flag VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT was not set + * yet, in order to not corrupt read-only disks. The first thing to do is to remove those + * disks and restore the read-write disks, if any, in the vboxSnapshotXmlMachinePtr struct. + * We also delete the current snapshot xml file. + * + * After that, we are going to register the snapshot read-only disks that we want to redefine, + * if they are not in the media registry struct. + * + * The next step is to unregister the machine and close all disks. + * + * Then, we check if the flag VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE has already been set. + * If this flag was set, we just add read-write disks to the media registry + * struct. Otherwise, we save the snapshot xml file into the machine location in order + * to recover the read-write disks during the next redefine and we create differential disks + * from the snapshot read-only disks and add them to the media registry struct. + * + * Finally, we register the machine with the new virtualbox description file. + */ + VBOX_OBJECT_CHECK(dom->conn, int, -1); + vboxIID domiid = VBOX_IID_INITIALIZER; + IMachine *machine = NULL; + nsresult rc; + PRUnichar *settingsFilePath = NULL; + char *settingsFilePath_Utf8 = NULL; + virVBoxSnapshotConfMachinePtr snapshotMachineDesc = NULL; + char *currentSnapshotXmlFilePath = NULL; + PRUnichar *machineNameUtf16 = NULL; + char *machineName = NULL; + char **realReadWriteDisksPath = NULL; + int realReadWriteDisksPathSize = 0; + char **realReadOnlyDisksPath = NULL; + int realReadOnlyDisksPathSize = 0; + virVBoxSnapshotConfSnapshotPtr newSnapshotPtr = NULL; + unsigned char snapshotUuid[VIR_UUID_BUFLEN]; + int it = 0; + int jt = 0; + PRUint32 aMediaSize = 0; + IMedium **aMedia = NULL; + char *machineLocationPath = NULL; + char *nameTmpUse = NULL; + bool snapshotFileExists = false; + bool needToChangeStorageController = false; + + vboxIIDFromUUID(&domiid, dom->uuid); + rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_NO_DOMAIN, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + + rc = machine->vtbl->SaveSettings(machine); + /*It may failed when the machine is not mutable.*/ + rc = machine->vtbl->GetSettingsFilePath(machine, &settingsFilePath); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get settings file path")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(settingsFilePath, &settingsFilePath_Utf8); + + /*Getting the machine name to retrieve the machine location path.*/ + rc = machine->vtbl->GetName(machine, &machineNameUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get machine name")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineName); + + if (virAsprintf(&nameTmpUse, "%s.vbox", machineName) < 0) + goto cleanup; + machineLocationPath = virStringReplace(settingsFilePath_Utf8, nameTmpUse, ""); + if (machineLocationPath == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get the machine location path")); + goto cleanup; + } + + /*We create the xml struct with the settings file path.*/ + snapshotMachineDesc = virVBoxSnapshotConfLoadVboxFile(settingsFilePath_Utf8, machineLocationPath); + if (snapshotMachineDesc == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot create a vboxSnapshotXmlPtr")); + goto cleanup; + } + if (snapshotMachineDesc->currentSnapshot != NULL) { + if (virAsprintf(¤tSnapshotXmlFilePath, "%s%s.xml", machineLocationPath, + snapshotMachineDesc->currentSnapshot) < 0) + goto cleanup; + snapshotFileExists = virFileExists(currentSnapshotXmlFilePath); + } + + if (snapshotFileExists) { + /* + * We have created fake disks, so we have to remove them and replace them with + * the read-write disks if there are any. The fake disks will be closed during + * the machine unregistration. + */ + if (virVBoxSnapshotConfRemoveFakeDisks(snapshotMachineDesc) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to remove Fake Disks")); + goto cleanup; + } + realReadWriteDisksPathSize = virVBoxSnapshotConfGetRWDisksPathsFromLibvirtXML(currentSnapshotXmlFilePath, + &realReadWriteDisksPath); + realReadOnlyDisksPathSize = virVBoxSnapshotConfGetRODisksPathsFromLibvirtXML(currentSnapshotXmlFilePath, + &realReadOnlyDisksPath); + /*The read-only disk number is necessarily greater or equal to the + *read-write disk number*/ + if (realReadOnlyDisksPathSize < realReadWriteDisksPathSize) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("The read only disk number must be greater or equal to the " + " read write disk number")); + goto cleanup; + } + for (it = 0; it < realReadWriteDisksPathSize; it++) { + virVBoxSnapshotConfHardDiskPtr readWriteDisk = NULL; + PRUnichar *locationUtf = NULL; + IMedium *readWriteMedium = NULL; + PRUnichar *uuidUtf = NULL; + char *uuid = NULL; + PRUnichar *formatUtf = NULL; + char *format = NULL; + char *parentUuid = NULL; + + VBOX_UTF8_TO_UTF16(realReadWriteDisksPath[it], &locationUtf); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &readWriteMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + VBOX_UTF16_FREE(locationUtf); + goto cleanup; + } + VBOX_UTF16_FREE(locationUtf); + + rc = readWriteMedium->vtbl->GetId(readWriteMedium, &uuidUtf); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get the read write medium id")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(uuidUtf, &uuid); + VBOX_UTF16_FREE(uuidUtf); + + rc = readWriteMedium->vtbl->GetFormat(readWriteMedium, &formatUtf); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get the read write medium format")); + VIR_FREE(uuid); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(formatUtf, &format); + VBOX_UTF16_FREE(formatUtf); + + if (VIR_ALLOC(readWriteDisk) < 0) { + VIR_FREE(uuid); + VIR_FREE(formatUtf); + goto cleanup; + } + + readWriteDisk->format = format; + readWriteDisk->uuid = uuid; + readWriteDisk->location = realReadWriteDisksPath[it]; + /* + * We get the current snapshot's read-only disk uuid in order to add the + * read-write disk to the media registry as it's child. The read-only disk + * is already in the media registry because it is the fake disk's parent. + */ + parentUuid = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc, + realReadOnlyDisksPath[it]); + if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(readWriteDisk, + snapshotMachineDesc->mediaRegistry, + parentUuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to add hard disk to media Registry")); + VIR_FREE(readWriteDisk); + goto cleanup; + } + rc = readWriteMedium->vtbl->Close(readWriteMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to close HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + } + /* + * Now we have done this swap, we remove the snapshot xml file from the + * current machine location. + */ + if (unlink(currentSnapshotXmlFilePath) < 0) { + virReportSystemError(errno, + _("Unable to delete file %s"), currentSnapshotXmlFilePath); + goto cleanup; + } + } + /* + * Before unregistering the machine, while all disks are still open, ensure that all + * read-only disks are in the redefined snapshot's media registry (the disks need to + * be open to query their uuid). + */ + for (it = 0; it < def->dom->ndisks; it++) { + int diskInMediaRegistry = 0; + IMedium *readOnlyMedium = NULL; + PRUnichar *locationUtf = NULL; + PRUnichar *uuidUtf = NULL; + char *uuid = NULL; + PRUnichar *formatUtf = NULL; + char *format = NULL; + PRUnichar *parentUuidUtf = NULL; + char *parentUuid = NULL; + virVBoxSnapshotConfHardDiskPtr readOnlyDisk = NULL; + + diskInMediaRegistry = virVBoxSnapshotConfDiskIsInMediaRegistry(snapshotMachineDesc, + def->dom->disks[it]->src.path); + if (diskInMediaRegistry == -1) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to know if disk is in media registry")); + goto cleanup; + } + if (diskInMediaRegistry == 1) /*Nothing to do.*/ + continue; + /*The read only disk is not in the media registry*/ + + VBOX_UTF8_TO_UTF16(def->dom->disks[it]->src.path, &locationUtf); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &readOnlyMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + VBOX_UTF16_FREE(locationUtf); + goto cleanup; + } + VBOX_UTF16_FREE(locationUtf); + + rc = readOnlyMedium->vtbl->GetId(readOnlyMedium, &uuidUtf); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get hard disk id")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(uuidUtf, &uuid); + VBOX_UTF16_FREE(uuidUtf); + + rc = readOnlyMedium->vtbl->GetFormat(readOnlyMedium, &formatUtf); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get hard disk format")); + VIR_FREE(uuid); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(formatUtf, &format); + VBOX_UTF16_FREE(formatUtf); + + /*This disk is already in the media registry*/ + IMedium *parentReadOnlyMedium = NULL; + rc = readOnlyMedium->vtbl->GetParent(readOnlyMedium, &parentReadOnlyMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get parent hard disk")); + VIR_FREE(uuid); + goto cleanup; + } + + rc = parentReadOnlyMedium->vtbl->GetId(parentReadOnlyMedium, &parentUuidUtf); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to get hard disk id, rc=%08x"), + (unsigned)rc); + VIR_FREE(uuid); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(parentUuidUtf, &parentUuid); + VBOX_UTF16_FREE(parentUuidUtf); + + rc = readOnlyMedium->vtbl->Close(readOnlyMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to close HardDisk, rc=%08x"), + (unsigned)rc); + VIR_FREE(uuid); + VIR_FREE(parentUuid); + goto cleanup; + } + + if (VIR_ALLOC(readOnlyDisk) < 0) { + VIR_FREE(uuid); + VIR_FREE(parentUuid); + goto cleanup; + } + + readOnlyDisk->format = format; + readOnlyDisk->uuid = uuid; + if (VIR_STRDUP(readOnlyDisk->location, def->dom->disks[it]->src.path) < 0) { + VIR_FREE(readOnlyDisk); + goto cleanup; + } + + if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(readOnlyDisk, snapshotMachineDesc->mediaRegistry, + parentUuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to add hard disk to media registry")); + VIR_FREE(readOnlyDisk); + goto cleanup; + } + } + + /*Now, we can unregister the machine*/ + rc = machine->vtbl->Unregister(machine, + CleanupMode_DetachAllReturnHardDisksOnly, + &aMediaSize, + &aMedia); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to unregister machine, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_RELEASE(machine); + + /* + * Unregister the machine, and then close all disks returned by the unregister method. + * Some close operations will fail because some disks that need to be closed will not + * be returned by virtualbox. We will close them just after. We have to use this + * solution because it is the only way to delete fake disks. + */ + for (it = 0; it < aMediaSize; it++) { + IMedium *medium = aMedia[it]; + if (medium) { + PRUnichar *locationUtf16 = NULL; + char *locationUtf8 = NULL; + rc = medium->vtbl->GetLocation(medium, &locationUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get medium location")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(locationUtf16, &locationUtf8); + VBOX_UTF16_FREE(locationUtf16); + if (strstr(locationUtf8, "fake") != NULL) { + /*we delete the fake disk because we don't need it anymore*/ + IProgress *progress = NULL; + PRInt32 resultCode = -1; + rc = medium->vtbl->DeleteStorage(medium, &progress); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to delete medium, rc=%08x"), + (unsigned)rc); + VIR_FREE(locationUtf8); + goto cleanup; + } + progress->vtbl->WaitForCompletion(progress, -1); + progress->vtbl->GetResultCode(progress, &resultCode); + if (NS_FAILED(resultCode)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Error while closing medium, rc=%08x"), + (unsigned)resultCode); + VIR_FREE(locationUtf8); + goto cleanup; + } + VBOX_RELEASE(progress); + } else { + /* + * This a comment from vboxmanage code in the handleUnregisterVM + * function in VBoxManageMisc.cpp : + * Note that the IMachine::Unregister method will return the medium + * reference in a sane order, which means that closing will normally + * succeed, unless there is still another machine which uses the + * medium. No harm done if we ignore the error. + */ + rc = medium->vtbl->Close(medium); + } + VBOX_UTF8_FREE(locationUtf8); + } + } + /*Close all disks that failed to close normally.*/ + for (it = 0; it < snapshotMachineDesc->mediaRegistry->ndisks; it++) { + if (vboxCloseDisksRecursively(dom, snapshotMachineDesc->mediaRegistry->disks[it]->location) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to close recursively all disks")); + goto cleanup; + } + } + /*Here, all disks are closed or deleted*/ + + /*We are now going to create and fill the Snapshot xml struct*/ + if (VIR_ALLOC(newSnapshotPtr) < 0) + goto cleanup; + + if (virUUIDGenerate(snapshotUuid) < 0) + goto cleanup; + + char uuidtmp[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(snapshotUuid, uuidtmp); + if (VIR_STRDUP(newSnapshotPtr->uuid, uuidtmp) < 0) + goto cleanup; + + VIR_DEBUG("New snapshot UUID: %s", newSnapshotPtr->uuid); + if (VIR_STRDUP(newSnapshotPtr->name, def->name) < 0) + goto cleanup; + + newSnapshotPtr->timeStamp = virTimeStringThen(def->creationTime * 1000); + + if (VIR_STRDUP(newSnapshotPtr->description, def->description) < 0) + goto cleanup; + + if (VIR_STRDUP(newSnapshotPtr->hardware, snapshotMachineDesc->hardware) < 0) + goto cleanup; + + if (VIR_STRDUP(newSnapshotPtr->storageController, snapshotMachineDesc->storageController) < 0) + goto cleanup; + + /*We get the parent disk uuid from the parent disk location to correctly fill the storage controller.*/ + for (it = 0; it < def->dom->ndisks; it++) { + char *location = NULL; + char *uuidReplacing = NULL; + char **searchResultTab = NULL; + ssize_t resultSize = 0; + char *tmp = NULL; + + location = def->dom->disks[it]->src.path; + if (!location) + goto cleanup; + /*Replacing the uuid*/ + uuidReplacing = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc, location); + if (uuidReplacing == NULL) + goto cleanup; + + resultSize = virStringSearch(newSnapshotPtr->storageController, + VBOX_UUID_REGEX, + it + 1, + &searchResultTab); + if (resultSize != it + 1) + goto cleanup; + + tmp = virStringReplace(newSnapshotPtr->storageController, + searchResultTab[it], + uuidReplacing); + virStringFreeList(searchResultTab); + VIR_FREE(newSnapshotPtr->storageController); + if (!tmp) + goto cleanup; + if (VIR_STRDUP(newSnapshotPtr->storageController, tmp) < 0) + goto cleanup; + + VIR_FREE(tmp); + } + if (virVBoxSnapshotConfAddSnapshotToXmlMachine(newSnapshotPtr, snapshotMachineDesc, def->parent) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to add the snapshot to the machine description")); + goto cleanup; + } + /* + * We change the current snapshot only if there is no current snapshot or if the + * snapshotFile exists, otherwise, it means that the correct current snapshot is + * already set. + */ + + if (snapshotMachineDesc->currentSnapshot == NULL || snapshotFileExists) { + snapshotMachineDesc->currentSnapshot = newSnapshotPtr->uuid; + needToChangeStorageController = true; + } + + /* + * Open the snapshot's read-write disk's full ancestry to allow opening the + * read-write disk itself. + */ + for (it = 0; it < def->dom->ndisks; it++) { + char *location = NULL; + virVBoxSnapshotConfHardDiskPtr *hardDiskToOpen = NULL; + size_t hardDiskToOpenSize = 0; + + location = def->dom->disks[it]->src.path; + if (!location) + goto cleanup; + + hardDiskToOpenSize = virVBoxSnapshotConfDiskListToOpen(snapshotMachineDesc, + &hardDiskToOpen, location); + for (jt = hardDiskToOpenSize -1; jt >= 0; jt--) { + IMedium *medium = NULL; + PRUnichar *locationUtf16 = NULL; + VBOX_UTF8_TO_UTF16(hardDiskToOpen[jt]->location, &locationUtf16); + + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf16, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &medium); + VBOX_UTF16_FREE(locationUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + } + } + if (isCurrent || !needToChangeStorageController) { + /* We don't create a differential hard disk because either the current snapshot + * has already been defined or the snapshot to redefine is the current snapshot. + * If the snapshot to redefine is the current snapshot, we add read-write disks in + * the machine storage controllers. + */ + for (it = 0; it < def->ndisks; it++) { + IMedium *medium = NULL; + PRUnichar *locationUtf16 = NULL; + virVBoxSnapshotConfHardDiskPtr disk = NULL; + PRUnichar *formatUtf16 = NULL; + char *format = NULL; + PRUnichar *uuidUtf16 = NULL; + char *uuid = NULL; + IMedium *parentDisk = NULL; + PRUnichar *parentUuidUtf16 = NULL; + char *parentUuid = NULL; + + VBOX_UTF8_TO_UTF16(def->disks[it].src.path, &locationUtf16); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf16, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_UTF16_FREE(locationUtf16); + + if (VIR_ALLOC(disk) < 0) + goto cleanup; + + rc = medium->vtbl->GetFormat(medium, &formatUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get disk format")); + VIR_FREE(disk); + goto cleanup; + } + + VBOX_UTF16_TO_UTF8(formatUtf16, &format); + disk->format = format; + VBOX_UTF16_FREE(formatUtf16); + + if (VIR_STRDUP(disk->location, def->disks[it].src.path) < 0) { + VIR_FREE(disk); + goto cleanup; + } + + rc = medium->vtbl->GetId(medium, &uuidUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get disk uuid")); + VIR_FREE(disk); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(uuidUtf16, &uuid); + disk->uuid = uuid; + VBOX_UTF16_FREE(uuidUtf16); + + rc = medium->vtbl->GetParent(medium, &parentDisk); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get disk parent")); + VIR_FREE(disk); + goto cleanup; + } + + parentDisk->vtbl->GetId(parentDisk, &parentUuidUtf16); + VBOX_UTF16_TO_UTF8(parentUuidUtf16, &parentUuid); + VBOX_UTF16_FREE(parentUuidUtf16); + if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(disk, + snapshotMachineDesc->mediaRegistry, + parentUuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to add hard disk to the media registry")); + VIR_FREE(disk); + goto cleanup; + } + + if (needToChangeStorageController) { + /*We need to append this disk in the storage controller*/ + char **searchResultTab = NULL; + ssize_t resultSize = 0; + char *tmp = NULL; + resultSize = virStringSearch(snapshotMachineDesc->storageController, + VBOX_UUID_REGEX, + it + 1, + &searchResultTab); + if (resultSize != it + 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find UUID %s"), searchResultTab[it]); + goto cleanup; + } + + tmp = virStringReplace(snapshotMachineDesc->storageController, + searchResultTab[it], + disk->uuid); + virStringFreeList(searchResultTab); + VIR_FREE(snapshotMachineDesc->storageController); + if (!tmp) + goto cleanup; + if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0) + goto cleanup; + + VIR_FREE(tmp); + } + /*Close disk*/ + rc = medium->vtbl->Close(medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to close HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + } + } else { + /*Create a "fake" disk to avoid corrupting children snapshot disks.*/ + for (it = 0; it < def->dom->ndisks; it++) { + IMedium *medium = NULL; + PRUnichar *locationUtf16 = NULL; + PRUnichar *parentUuidUtf16 = NULL; + char *parentUuid = NULL; + IMedium *newMedium = NULL; + PRUnichar *formatUtf16 = NULL; + PRUnichar *newLocation = NULL; + char *newLocationUtf8 = NULL; + PRInt32 resultCode = -1; + virVBoxSnapshotConfHardDiskPtr disk = NULL; + PRUnichar *uuidUtf16 = NULL; + char *uuid = NULL; + char *format = NULL; + char **searchResultTab = NULL; + ssize_t resultSize = 0; + char *tmp = NULL; + + VBOX_UTF8_TO_UTF16(def->dom->disks[it]->src.path, &locationUtf16); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf16, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + VBOX_UTF16_FREE(locationUtf16); + goto cleanup; + } + VBOX_UTF16_FREE(locationUtf16); + + rc = medium->vtbl->GetId(medium, &parentUuidUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to get hardDisk Id, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(parentUuidUtf16, &parentUuid); + VBOX_UTF16_FREE(parentUuidUtf16); + VBOX_UTF8_TO_UTF16("VDI", &formatUtf16); + + if (virAsprintf(&newLocationUtf8, "%sfakedisk-%d.vdi", machineLocationPath, it) < 0) + goto cleanup; + VBOX_UTF8_TO_UTF16(newLocationUtf8, &newLocation); + rc = data->vboxObj->vtbl->CreateHardDisk(data->vboxObj, + formatUtf16, + newLocation, + &newMedium); + VBOX_UTF16_FREE(newLocation); + VBOX_UTF16_FREE(formatUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to create HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + + IProgress *progress = NULL; +# if VBOX_API_VERSION < 4003000 + medium->vtbl->CreateDiffStorage(medium, newMedium, MediumVariant_Diff, &progress); +# else + PRUint32 tab[1]; + tab[0] = MediumVariant_Diff; + medium->vtbl->CreateDiffStorage(medium, newMedium, 1, tab, &progress); +# endif + + progress->vtbl->WaitForCompletion(progress, -1); + progress->vtbl->GetResultCode(progress, &resultCode); + if (NS_FAILED(resultCode)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Error while creating diff storage, rc=%08x"), + (unsigned)resultCode); + goto cleanup; + } + VBOX_RELEASE(progress); + /* + * The differential disk is created, we add it to the media registry and the + * machine storage controllers. + */ + + if (VIR_ALLOC(disk) < 0) + goto cleanup; + + rc = newMedium->vtbl->GetId(newMedium, &uuidUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to get medium uuid, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(uuidUtf16, &uuid); + disk->uuid = uuid; + VBOX_UTF16_FREE(uuidUtf16); + + if (VIR_STRDUP(disk->location, newLocationUtf8) < 0) + goto cleanup; + + rc = newMedium->vtbl->GetFormat(newMedium, &formatUtf16); + VBOX_UTF16_TO_UTF8(formatUtf16, &format); + disk->format = format; + VBOX_UTF16_FREE(formatUtf16); + + if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(disk, + snapshotMachineDesc->mediaRegistry, + parentUuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to add hard disk to the media registry")); + goto cleanup; + } + /*Adding the fake disk to the machine storage controllers*/ + + resultSize = virStringSearch(snapshotMachineDesc->storageController, + VBOX_UUID_REGEX, + it + 1, + &searchResultTab); + if (resultSize != it + 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find UUID %s"), searchResultTab[it]); + goto cleanup; + } + + tmp = virStringReplace(snapshotMachineDesc->storageController, + searchResultTab[it], + disk->uuid); + virStringFreeList(searchResultTab); + VIR_FREE(snapshotMachineDesc->storageController); + if (!tmp) + goto cleanup; + if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0) + goto cleanup; + + VIR_FREE(tmp); + /*Closing the "fake" disk*/ + rc = newMedium->vtbl->Close(newMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to close the new medium, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + } + /* + * We save the snapshot xml file to retrieve the real read-write disk during the + * next define. This file is saved as "'machineLocation'/snapshot-'uuid'.xml" + */ + VIR_FREE(currentSnapshotXmlFilePath); + if (virAsprintf(¤tSnapshotXmlFilePath, "%s%s.xml", machineLocationPath, snapshotMachineDesc->currentSnapshot) < 0) + goto cleanup; + char *snapshotContent = virDomainSnapshotDefFormat(NULL, def, VIR_DOMAIN_XML_SECURE, 0); + if (snapshotContent == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get snapshot content")); + goto cleanup; + } + if (virFileWriteStr(currentSnapshotXmlFilePath, snapshotContent, 0644) < 0) { + virReportSystemError(errno, "%s", + _("Unable to save new snapshot xml file")); + goto cleanup; + } + VIR_FREE(snapshotContent); + } + /* + * All the snapshot structure manipulation is done, we close the disks we have + * previously opened. + */ + for (it = 0; it < def->dom->ndisks; it++) { + char *location = def->dom->disks[it]->src.path; + if (!location) + goto cleanup; + + virVBoxSnapshotConfHardDiskPtr *hardDiskToOpen = NULL; + size_t hardDiskToOpenSize = virVBoxSnapshotConfDiskListToOpen(snapshotMachineDesc, + &hardDiskToOpen, location); + for (jt = 0; jt < hardDiskToOpenSize; jt++) { + IMedium *medium = NULL; + PRUnichar *locationUtf16 = NULL; + VBOX_UTF8_TO_UTF16(hardDiskToOpen[jt]->location, &locationUtf16); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf16, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + rc = medium->vtbl->Close(medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to close HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_UTF16_FREE(locationUtf16); + } + } + + /*Now, we rewrite the 'machineName'.vbox file to redefine the machine.*/ + if (virVBoxSnapshotConfSaveVboxFile(snapshotMachineDesc, settingsFilePath_Utf8) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to serialize the machine description")); + goto cleanup; + } + rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj, + settingsFilePath, + &machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open Machine, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + + rc = data->vboxObj->vtbl->RegisterMachine(data->vboxObj, machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to register Machine, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + + ret = 0; + cleanup: + VBOX_RELEASE(machine); + VBOX_UTF16_FREE(settingsFilePath); + VBOX_UTF8_FREE(settingsFilePath_Utf8); + VIR_FREE(snapshotMachineDesc); + VIR_FREE(currentSnapshotXmlFilePath); + VBOX_UTF16_FREE(machineNameUtf16); + VBOX_UTF8_FREE(machineName); + virStringFreeList(realReadOnlyDisksPath); + virStringFreeList(realReadWriteDisksPath); + VIR_FREE(newSnapshotPtr); + VIR_FREE(machineLocationPath); + VIR_FREE(nameTmpUse); + return ret; +} +#endif + static virDomainSnapshotPtr vboxDomainSnapshotCreateXML(virDomainPtr dom, const char *xmlDesc, @@ -6036,9 +6989,15 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, #else PRInt32 result; #endif +#if VBOX_API_VERSION >= 4002000 + bool isCurrent = false; +#endif + /* VBox has no snapshot metadata, so this flag is trivial. */ - virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA, NULL); + virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA | + VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE | + VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT, NULL); if (!(def = virDomainSnapshotDefParseString(xmlDesc, data->caps, data->xmlopt, -1, @@ -6046,11 +7005,6 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE))) goto cleanup; - if (def->ndisks) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("disk snapshots not supported yet")); - goto cleanup; - } vboxIIDFromUUID(&domiid, dom->uuid); rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); @@ -6060,6 +7014,16 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, goto cleanup; } +#if VBOX_API_VERSION >= 4002000 + isCurrent = flags & VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT; + if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) { + if (vboxSnapshotRedefine(dom, def, isCurrent) < 0) + goto cleanup; + ret = virGetDomainSnapshot(dom, def->name); + goto cleanup; + } +#endif + rc = machine->vtbl->GetState(machine, &state); if (NS_FAILED(rc)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", -- 1.7.10.4

On Mon, May 19, 2014 at 02:47:32PM +0200, Yohan BELLEGUIC wrote:
The machine is unregistered and its vbox XML file is changed in order to add snapshot information. The machine is then registered with the snapshot to redefine. --- src/vbox/vbox_tmpl.c | 976 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 970 insertions(+), 6 deletions(-)
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Another coverity error as listed in-line below in vboxSnapshotRedefine() John On 05/19/2014 08:47 AM, Yohan BELLEGUIC wrote:
The machine is unregistered and its vbox XML file is changed in order to add snapshot information. The machine is then registered with the snapshot to redefine. --- src/vbox/vbox_tmpl.c | 976 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 970 insertions(+), 6 deletions(-)
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 5d4a7ba..a7f15d4 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -43,6 +43,7 @@ #include "datatypes.h" #include "domain_conf.h" #include "snapshot_conf.h" +#include "vbox_snapshot_conf.h" #include "network_conf.h" #include "virerror.h" #include "domain_event.h" @@ -6015,6 +6016,958 @@ vboxDomainSnapshotGet(vboxGlobalData *data, return snapshot; }
+#if VBOX_API_VERSION >= 4002000 +static int vboxCloseDisksRecursively(virDomainPtr dom, char *location) +{ + VBOX_OBJECT_CHECK(dom->conn, int, -1); + nsresult rc; + size_t i = 0; + PRUnichar *locationUtf = NULL; + IMedium *medium = NULL; + IMedium **children = NULL; + PRUint32 childrenSize = 0; + VBOX_UTF8_TO_UTF16(location, &locationUtf); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + rc = medium->vtbl->GetChildren(medium, &childrenSize, &children); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s" + , _("Unable to get disk children")); + goto cleanup; + } + for (i = 0; i < childrenSize; i++) { + IMedium *childMedium = children[i]; + if (childMedium) { + PRUnichar *childLocationUtf = NULL; + char *childLocation = NULL; + rc = childMedium->vtbl->GetLocation(childMedium, &childLocationUtf); + VBOX_UTF16_TO_UTF8(childLocationUtf, &childLocation); + VBOX_UTF16_FREE(childLocationUtf); + if (vboxCloseDisksRecursively(dom, childLocation) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s" + , _("Unable to close disk children")); + goto cleanup; + } + VIR_FREE(childLocation); + } + } + rc = medium->vtbl->Close(medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to close HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + + ret = 0; + cleanup: + VBOX_UTF16_FREE(locationUtf); + return ret; +} + +static int +vboxSnapshotRedefine(virDomainPtr dom, + virDomainSnapshotDefPtr def, + bool isCurrent) +{ + /* + * If your snapshot has a parent, + * it will only be redefined if you have already + * redefined the parent. + * + * The general algorithm of this function is below : + * First of all, we are going to create our vboxSnapshotXmlMachinePtr struct from + * the machine settings path. + * Then, if the machine current snapshot xml file is saved in the machine location, + * it means that this snapshot was previously modified by us and has fake disks. + * Fake disks are added when the flag VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT was not set + * yet, in order to not corrupt read-only disks. The first thing to do is to remove those + * disks and restore the read-write disks, if any, in the vboxSnapshotXmlMachinePtr struct. + * We also delete the current snapshot xml file. + * + * After that, we are going to register the snapshot read-only disks that we want to redefine, + * if they are not in the media registry struct. + * + * The next step is to unregister the machine and close all disks. + * + * Then, we check if the flag VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE has already been set. + * If this flag was set, we just add read-write disks to the media registry + * struct. Otherwise, we save the snapshot xml file into the machine location in order + * to recover the read-write disks during the next redefine and we create differential disks + * from the snapshot read-only disks and add them to the media registry struct. + * + * Finally, we register the machine with the new virtualbox description file. + */ + VBOX_OBJECT_CHECK(dom->conn, int, -1); + vboxIID domiid = VBOX_IID_INITIALIZER; + IMachine *machine = NULL; + nsresult rc; + PRUnichar *settingsFilePath = NULL; + char *settingsFilePath_Utf8 = NULL; + virVBoxSnapshotConfMachinePtr snapshotMachineDesc = NULL; + char *currentSnapshotXmlFilePath = NULL; + PRUnichar *machineNameUtf16 = NULL; + char *machineName = NULL; + char **realReadWriteDisksPath = NULL; + int realReadWriteDisksPathSize = 0; + char **realReadOnlyDisksPath = NULL; + int realReadOnlyDisksPathSize = 0; + virVBoxSnapshotConfSnapshotPtr newSnapshotPtr = NULL; + unsigned char snapshotUuid[VIR_UUID_BUFLEN]; + int it = 0; + int jt = 0; + PRUint32 aMediaSize = 0; + IMedium **aMedia = NULL; + char *machineLocationPath = NULL; + char *nameTmpUse = NULL; + bool snapshotFileExists = false; + bool needToChangeStorageController = false; + + vboxIIDFromUUID(&domiid, dom->uuid); + rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_NO_DOMAIN, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + + rc = machine->vtbl->SaveSettings(machine); + /*It may failed when the machine is not mutable.*/ + rc = machine->vtbl->GetSettingsFilePath(machine, &settingsFilePath); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get settings file path")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(settingsFilePath, &settingsFilePath_Utf8); + + /*Getting the machine name to retrieve the machine location path.*/ + rc = machine->vtbl->GetName(machine, &machineNameUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get machine name")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineName); + + if (virAsprintf(&nameTmpUse, "%s.vbox", machineName) < 0) + goto cleanup; + machineLocationPath = virStringReplace(settingsFilePath_Utf8, nameTmpUse, ""); + if (machineLocationPath == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get the machine location path")); + goto cleanup; + } + + /*We create the xml struct with the settings file path.*/ + snapshotMachineDesc = virVBoxSnapshotConfLoadVboxFile(settingsFilePath_Utf8, machineLocationPath); + if (snapshotMachineDesc == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot create a vboxSnapshotXmlPtr")); + goto cleanup; + } + if (snapshotMachineDesc->currentSnapshot != NULL) { + if (virAsprintf(¤tSnapshotXmlFilePath, "%s%s.xml", machineLocationPath, + snapshotMachineDesc->currentSnapshot) < 0) + goto cleanup; + snapshotFileExists = virFileExists(currentSnapshotXmlFilePath); + } + + if (snapshotFileExists) { + /* + * We have created fake disks, so we have to remove them and replace them with + * the read-write disks if there are any. The fake disks will be closed during + * the machine unregistration. + */ + if (virVBoxSnapshotConfRemoveFakeDisks(snapshotMachineDesc) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to remove Fake Disks")); + goto cleanup; + } + realReadWriteDisksPathSize = virVBoxSnapshotConfGetRWDisksPathsFromLibvirtXML(currentSnapshotXmlFilePath, + &realReadWriteDisksPath); + realReadOnlyDisksPathSize = virVBoxSnapshotConfGetRODisksPathsFromLibvirtXML(currentSnapshotXmlFilePath, + &realReadOnlyDisksPath); + /*The read-only disk number is necessarily greater or equal to the + *read-write disk number*/ + if (realReadOnlyDisksPathSize < realReadWriteDisksPathSize) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("The read only disk number must be greater or equal to the " + " read write disk number")); + goto cleanup; + } + for (it = 0; it < realReadWriteDisksPathSize; it++) { + virVBoxSnapshotConfHardDiskPtr readWriteDisk = NULL; + PRUnichar *locationUtf = NULL; + IMedium *readWriteMedium = NULL; + PRUnichar *uuidUtf = NULL; + char *uuid = NULL; + PRUnichar *formatUtf = NULL; + char *format = NULL; + char *parentUuid = NULL; + + VBOX_UTF8_TO_UTF16(realReadWriteDisksPath[it], &locationUtf); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &readWriteMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + VBOX_UTF16_FREE(locationUtf); + goto cleanup; + } + VBOX_UTF16_FREE(locationUtf); + + rc = readWriteMedium->vtbl->GetId(readWriteMedium, &uuidUtf); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get the read write medium id")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(uuidUtf, &uuid); + VBOX_UTF16_FREE(uuidUtf); + + rc = readWriteMedium->vtbl->GetFormat(readWriteMedium, &formatUtf); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get the read write medium format")); + VIR_FREE(uuid); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(formatUtf, &format); + VBOX_UTF16_FREE(formatUtf); + + if (VIR_ALLOC(readWriteDisk) < 0) { + VIR_FREE(uuid); + VIR_FREE(formatUtf); + goto cleanup; + } + + readWriteDisk->format = format; + readWriteDisk->uuid = uuid; + readWriteDisk->location = realReadWriteDisksPath[it]; + /* + * We get the current snapshot's read-only disk uuid in order to add the + * read-write disk to the media registry as it's child. The read-only disk + * is already in the media registry because it is the fake disk's parent. + */ + parentUuid = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc, + realReadOnlyDisksPath[it]);
(35) Event returned_null: "virVBoxSnapshotConfHardDiskUuidByLocation" returns null (checked 4 out of 5 times). [details] (44) Event var_assigned: Assigning: "parentUuid" = null return value from "virVBoxSnapshotConfHardDiskUuidByLocation". Looks like a if (parentUuid == NULL) { VIR_FREE(readWriteDisk); goto cleanup; } Will resolve this.
+ if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(readWriteDisk, + snapshotMachineDesc->mediaRegistry, + parentUuid) < 0) {
(45) Event dereference: Dereferencing a pointer that might be null "parentUuid" when calling "virVBoxSnapshotConfAddHardDiskToMediaRegistry". [details]
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to add hard disk to media Registry")); + VIR_FREE(readWriteDisk); + goto cleanup; + } + rc = readWriteMedium->vtbl->Close(readWriteMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to close HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + } + /* + * Now we have done this swap, we remove the snapshot xml file from the + * current machine location. + */ + if (unlink(currentSnapshotXmlFilePath) < 0) { + virReportSystemError(errno, + _("Unable to delete file %s"), currentSnapshotXmlFilePath); + goto cleanup; + } + } + /* + * Before unregistering the machine, while all disks are still open, ensure that all + * read-only disks are in the redefined snapshot's media registry (the disks need to + * be open to query their uuid). + */ + for (it = 0; it < def->dom->ndisks; it++) { + int diskInMediaRegistry = 0; + IMedium *readOnlyMedium = NULL; + PRUnichar *locationUtf = NULL; + PRUnichar *uuidUtf = NULL; + char *uuid = NULL; + PRUnichar *formatUtf = NULL; + char *format = NULL; + PRUnichar *parentUuidUtf = NULL; + char *parentUuid = NULL; + virVBoxSnapshotConfHardDiskPtr readOnlyDisk = NULL; + + diskInMediaRegistry = virVBoxSnapshotConfDiskIsInMediaRegistry(snapshotMachineDesc, + def->dom->disks[it]->src.path); + if (diskInMediaRegistry == -1) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to know if disk is in media registry")); + goto cleanup; + } + if (diskInMediaRegistry == 1) /*Nothing to do.*/ + continue; + /*The read only disk is not in the media registry*/ + + VBOX_UTF8_TO_UTF16(def->dom->disks[it]->src.path, &locationUtf); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &readOnlyMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + VBOX_UTF16_FREE(locationUtf); + goto cleanup; + } + VBOX_UTF16_FREE(locationUtf); + + rc = readOnlyMedium->vtbl->GetId(readOnlyMedium, &uuidUtf); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get hard disk id")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(uuidUtf, &uuid); + VBOX_UTF16_FREE(uuidUtf); + + rc = readOnlyMedium->vtbl->GetFormat(readOnlyMedium, &formatUtf); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get hard disk format")); + VIR_FREE(uuid); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(formatUtf, &format); + VBOX_UTF16_FREE(formatUtf); + + /*This disk is already in the media registry*/ + IMedium *parentReadOnlyMedium = NULL; + rc = readOnlyMedium->vtbl->GetParent(readOnlyMedium, &parentReadOnlyMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get parent hard disk")); + VIR_FREE(uuid); + goto cleanup; + } + + rc = parentReadOnlyMedium->vtbl->GetId(parentReadOnlyMedium, &parentUuidUtf); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to get hard disk id, rc=%08x"), + (unsigned)rc); + VIR_FREE(uuid); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(parentUuidUtf, &parentUuid); + VBOX_UTF16_FREE(parentUuidUtf); + + rc = readOnlyMedium->vtbl->Close(readOnlyMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to close HardDisk, rc=%08x"), + (unsigned)rc); + VIR_FREE(uuid); + VIR_FREE(parentUuid); + goto cleanup; + } + + if (VIR_ALLOC(readOnlyDisk) < 0) { + VIR_FREE(uuid); + VIR_FREE(parentUuid); + goto cleanup; + } + + readOnlyDisk->format = format; + readOnlyDisk->uuid = uuid; + if (VIR_STRDUP(readOnlyDisk->location, def->dom->disks[it]->src.path) < 0) { + VIR_FREE(readOnlyDisk); + goto cleanup; + } + + if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(readOnlyDisk, snapshotMachineDesc->mediaRegistry, + parentUuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to add hard disk to media registry")); + VIR_FREE(readOnlyDisk); + goto cleanup; + } + } + + /*Now, we can unregister the machine*/ + rc = machine->vtbl->Unregister(machine, + CleanupMode_DetachAllReturnHardDisksOnly, + &aMediaSize, + &aMedia); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to unregister machine, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_RELEASE(machine); + + /* + * Unregister the machine, and then close all disks returned by the unregister method. + * Some close operations will fail because some disks that need to be closed will not + * be returned by virtualbox. We will close them just after. We have to use this + * solution because it is the only way to delete fake disks. + */ + for (it = 0; it < aMediaSize; it++) { + IMedium *medium = aMedia[it]; + if (medium) { + PRUnichar *locationUtf16 = NULL; + char *locationUtf8 = NULL; + rc = medium->vtbl->GetLocation(medium, &locationUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get medium location")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(locationUtf16, &locationUtf8); + VBOX_UTF16_FREE(locationUtf16); + if (strstr(locationUtf8, "fake") != NULL) { + /*we delete the fake disk because we don't need it anymore*/ + IProgress *progress = NULL; + PRInt32 resultCode = -1; + rc = medium->vtbl->DeleteStorage(medium, &progress); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to delete medium, rc=%08x"), + (unsigned)rc); + VIR_FREE(locationUtf8); + goto cleanup; + } + progress->vtbl->WaitForCompletion(progress, -1); + progress->vtbl->GetResultCode(progress, &resultCode); + if (NS_FAILED(resultCode)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Error while closing medium, rc=%08x"), + (unsigned)resultCode); + VIR_FREE(locationUtf8); + goto cleanup; + } + VBOX_RELEASE(progress); + } else { + /* + * This a comment from vboxmanage code in the handleUnregisterVM + * function in VBoxManageMisc.cpp : + * Note that the IMachine::Unregister method will return the medium + * reference in a sane order, which means that closing will normally + * succeed, unless there is still another machine which uses the + * medium. No harm done if we ignore the error. + */ + rc = medium->vtbl->Close(medium); + } + VBOX_UTF8_FREE(locationUtf8); + } + } + /*Close all disks that failed to close normally.*/ + for (it = 0; it < snapshotMachineDesc->mediaRegistry->ndisks; it++) { + if (vboxCloseDisksRecursively(dom, snapshotMachineDesc->mediaRegistry->disks[it]->location) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to close recursively all disks")); + goto cleanup; + } + } + /*Here, all disks are closed or deleted*/ + + /*We are now going to create and fill the Snapshot xml struct*/ + if (VIR_ALLOC(newSnapshotPtr) < 0) + goto cleanup; + + if (virUUIDGenerate(snapshotUuid) < 0) + goto cleanup; + + char uuidtmp[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(snapshotUuid, uuidtmp); + if (VIR_STRDUP(newSnapshotPtr->uuid, uuidtmp) < 0) + goto cleanup; + + VIR_DEBUG("New snapshot UUID: %s", newSnapshotPtr->uuid); + if (VIR_STRDUP(newSnapshotPtr->name, def->name) < 0) + goto cleanup; + + newSnapshotPtr->timeStamp = virTimeStringThen(def->creationTime * 1000); + + if (VIR_STRDUP(newSnapshotPtr->description, def->description) < 0) + goto cleanup; + + if (VIR_STRDUP(newSnapshotPtr->hardware, snapshotMachineDesc->hardware) < 0) + goto cleanup; + + if (VIR_STRDUP(newSnapshotPtr->storageController, snapshotMachineDesc->storageController) < 0) + goto cleanup; + + /*We get the parent disk uuid from the parent disk location to correctly fill the storage controller.*/ + for (it = 0; it < def->dom->ndisks; it++) { + char *location = NULL; + char *uuidReplacing = NULL; + char **searchResultTab = NULL; + ssize_t resultSize = 0; + char *tmp = NULL; + + location = def->dom->disks[it]->src.path; + if (!location) + goto cleanup; + /*Replacing the uuid*/ + uuidReplacing = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc, location); + if (uuidReplacing == NULL) + goto cleanup; + + resultSize = virStringSearch(newSnapshotPtr->storageController, + VBOX_UUID_REGEX, + it + 1, + &searchResultTab); + if (resultSize != it + 1) + goto cleanup; + + tmp = virStringReplace(newSnapshotPtr->storageController, + searchResultTab[it], + uuidReplacing); + virStringFreeList(searchResultTab); + VIR_FREE(newSnapshotPtr->storageController); + if (!tmp) + goto cleanup; + if (VIR_STRDUP(newSnapshotPtr->storageController, tmp) < 0) + goto cleanup; + + VIR_FREE(tmp); + } + if (virVBoxSnapshotConfAddSnapshotToXmlMachine(newSnapshotPtr, snapshotMachineDesc, def->parent) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to add the snapshot to the machine description")); + goto cleanup; + } + /* + * We change the current snapshot only if there is no current snapshot or if the + * snapshotFile exists, otherwise, it means that the correct current snapshot is + * already set. + */ + + if (snapshotMachineDesc->currentSnapshot == NULL || snapshotFileExists) { + snapshotMachineDesc->currentSnapshot = newSnapshotPtr->uuid; + needToChangeStorageController = true; + } + + /* + * Open the snapshot's read-write disk's full ancestry to allow opening the + * read-write disk itself. + */ + for (it = 0; it < def->dom->ndisks; it++) { + char *location = NULL; + virVBoxSnapshotConfHardDiskPtr *hardDiskToOpen = NULL; + size_t hardDiskToOpenSize = 0; + + location = def->dom->disks[it]->src.path; + if (!location) + goto cleanup; + + hardDiskToOpenSize = virVBoxSnapshotConfDiskListToOpen(snapshotMachineDesc, + &hardDiskToOpen, location); + for (jt = hardDiskToOpenSize -1; jt >= 0; jt--) { + IMedium *medium = NULL; + PRUnichar *locationUtf16 = NULL; + VBOX_UTF8_TO_UTF16(hardDiskToOpen[jt]->location, &locationUtf16); + + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf16, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &medium); + VBOX_UTF16_FREE(locationUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + } + } + if (isCurrent || !needToChangeStorageController) { + /* We don't create a differential hard disk because either the current snapshot + * has already been defined or the snapshot to redefine is the current snapshot. + * If the snapshot to redefine is the current snapshot, we add read-write disks in + * the machine storage controllers. + */ + for (it = 0; it < def->ndisks; it++) { + IMedium *medium = NULL; + PRUnichar *locationUtf16 = NULL; + virVBoxSnapshotConfHardDiskPtr disk = NULL; + PRUnichar *formatUtf16 = NULL; + char *format = NULL; + PRUnichar *uuidUtf16 = NULL; + char *uuid = NULL; + IMedium *parentDisk = NULL; + PRUnichar *parentUuidUtf16 = NULL; + char *parentUuid = NULL; + + VBOX_UTF8_TO_UTF16(def->disks[it].src.path, &locationUtf16); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf16, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_UTF16_FREE(locationUtf16); + + if (VIR_ALLOC(disk) < 0) + goto cleanup; + + rc = medium->vtbl->GetFormat(medium, &formatUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get disk format")); + VIR_FREE(disk); + goto cleanup; + } + + VBOX_UTF16_TO_UTF8(formatUtf16, &format); + disk->format = format; + VBOX_UTF16_FREE(formatUtf16); + + if (VIR_STRDUP(disk->location, def->disks[it].src.path) < 0) { + VIR_FREE(disk); + goto cleanup; + } + + rc = medium->vtbl->GetId(medium, &uuidUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get disk uuid")); + VIR_FREE(disk); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(uuidUtf16, &uuid); + disk->uuid = uuid; + VBOX_UTF16_FREE(uuidUtf16); + + rc = medium->vtbl->GetParent(medium, &parentDisk); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get disk parent")); + VIR_FREE(disk); + goto cleanup; + } + + parentDisk->vtbl->GetId(parentDisk, &parentUuidUtf16); + VBOX_UTF16_TO_UTF8(parentUuidUtf16, &parentUuid); + VBOX_UTF16_FREE(parentUuidUtf16); + if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(disk, + snapshotMachineDesc->mediaRegistry, + parentUuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to add hard disk to the media registry")); + VIR_FREE(disk); + goto cleanup; + } + + if (needToChangeStorageController) { + /*We need to append this disk in the storage controller*/ + char **searchResultTab = NULL; + ssize_t resultSize = 0; + char *tmp = NULL; + resultSize = virStringSearch(snapshotMachineDesc->storageController, + VBOX_UUID_REGEX, + it + 1, + &searchResultTab); + if (resultSize != it + 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find UUID %s"), searchResultTab[it]); + goto cleanup; + } + + tmp = virStringReplace(snapshotMachineDesc->storageController, + searchResultTab[it], + disk->uuid); + virStringFreeList(searchResultTab); + VIR_FREE(snapshotMachineDesc->storageController); + if (!tmp) + goto cleanup; + if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0) + goto cleanup; + + VIR_FREE(tmp); + } + /*Close disk*/ + rc = medium->vtbl->Close(medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to close HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + } + } else { + /*Create a "fake" disk to avoid corrupting children snapshot disks.*/ + for (it = 0; it < def->dom->ndisks; it++) { + IMedium *medium = NULL; + PRUnichar *locationUtf16 = NULL; + PRUnichar *parentUuidUtf16 = NULL; + char *parentUuid = NULL; + IMedium *newMedium = NULL; + PRUnichar *formatUtf16 = NULL; + PRUnichar *newLocation = NULL; + char *newLocationUtf8 = NULL; + PRInt32 resultCode = -1; + virVBoxSnapshotConfHardDiskPtr disk = NULL; + PRUnichar *uuidUtf16 = NULL; + char *uuid = NULL; + char *format = NULL; + char **searchResultTab = NULL; + ssize_t resultSize = 0; + char *tmp = NULL; + + VBOX_UTF8_TO_UTF16(def->dom->disks[it]->src.path, &locationUtf16); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf16, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + VBOX_UTF16_FREE(locationUtf16); + goto cleanup; + } + VBOX_UTF16_FREE(locationUtf16); + + rc = medium->vtbl->GetId(medium, &parentUuidUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to get hardDisk Id, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(parentUuidUtf16, &parentUuid); + VBOX_UTF16_FREE(parentUuidUtf16); + VBOX_UTF8_TO_UTF16("VDI", &formatUtf16); + + if (virAsprintf(&newLocationUtf8, "%sfakedisk-%d.vdi", machineLocationPath, it) < 0) + goto cleanup; + VBOX_UTF8_TO_UTF16(newLocationUtf8, &newLocation); + rc = data->vboxObj->vtbl->CreateHardDisk(data->vboxObj, + formatUtf16, + newLocation, + &newMedium); + VBOX_UTF16_FREE(newLocation); + VBOX_UTF16_FREE(formatUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to create HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + + IProgress *progress = NULL; +# if VBOX_API_VERSION < 4003000 + medium->vtbl->CreateDiffStorage(medium, newMedium, MediumVariant_Diff, &progress); +# else + PRUint32 tab[1]; + tab[0] = MediumVariant_Diff; + medium->vtbl->CreateDiffStorage(medium, newMedium, 1, tab, &progress); +# endif + + progress->vtbl->WaitForCompletion(progress, -1); + progress->vtbl->GetResultCode(progress, &resultCode); + if (NS_FAILED(resultCode)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Error while creating diff storage, rc=%08x"), + (unsigned)resultCode); + goto cleanup; + } + VBOX_RELEASE(progress); + /* + * The differential disk is created, we add it to the media registry and the + * machine storage controllers. + */ + + if (VIR_ALLOC(disk) < 0) + goto cleanup; + + rc = newMedium->vtbl->GetId(newMedium, &uuidUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to get medium uuid, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(uuidUtf16, &uuid); + disk->uuid = uuid; + VBOX_UTF16_FREE(uuidUtf16); + + if (VIR_STRDUP(disk->location, newLocationUtf8) < 0) + goto cleanup; + + rc = newMedium->vtbl->GetFormat(newMedium, &formatUtf16); + VBOX_UTF16_TO_UTF8(formatUtf16, &format); + disk->format = format; + VBOX_UTF16_FREE(formatUtf16); + + if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(disk, + snapshotMachineDesc->mediaRegistry, + parentUuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to add hard disk to the media registry")); + goto cleanup; + } + /*Adding the fake disk to the machine storage controllers*/ + + resultSize = virStringSearch(snapshotMachineDesc->storageController, + VBOX_UUID_REGEX, + it + 1, + &searchResultTab); + if (resultSize != it + 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find UUID %s"), searchResultTab[it]); + goto cleanup; + } + + tmp = virStringReplace(snapshotMachineDesc->storageController, + searchResultTab[it], + disk->uuid); + virStringFreeList(searchResultTab); + VIR_FREE(snapshotMachineDesc->storageController); + if (!tmp) + goto cleanup; + if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0) + goto cleanup; + + VIR_FREE(tmp); + /*Closing the "fake" disk*/ + rc = newMedium->vtbl->Close(newMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to close the new medium, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + } + /* + * We save the snapshot xml file to retrieve the real read-write disk during the + * next define. This file is saved as "'machineLocation'/snapshot-'uuid'.xml" + */ + VIR_FREE(currentSnapshotXmlFilePath); + if (virAsprintf(¤tSnapshotXmlFilePath, "%s%s.xml", machineLocationPath, snapshotMachineDesc->currentSnapshot) < 0) + goto cleanup; + char *snapshotContent = virDomainSnapshotDefFormat(NULL, def, VIR_DOMAIN_XML_SECURE, 0); + if (snapshotContent == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get snapshot content")); + goto cleanup; + } + if (virFileWriteStr(currentSnapshotXmlFilePath, snapshotContent, 0644) < 0) { + virReportSystemError(errno, "%s", + _("Unable to save new snapshot xml file")); + goto cleanup; + } + VIR_FREE(snapshotContent); + } + /* + * All the snapshot structure manipulation is done, we close the disks we have + * previously opened. + */ + for (it = 0; it < def->dom->ndisks; it++) { + char *location = def->dom->disks[it]->src.path; + if (!location) + goto cleanup; + + virVBoxSnapshotConfHardDiskPtr *hardDiskToOpen = NULL; + size_t hardDiskToOpenSize = virVBoxSnapshotConfDiskListToOpen(snapshotMachineDesc, + &hardDiskToOpen, location); + for (jt = 0; jt < hardDiskToOpenSize; jt++) { + IMedium *medium = NULL; + PRUnichar *locationUtf16 = NULL; + VBOX_UTF8_TO_UTF16(hardDiskToOpen[jt]->location, &locationUtf16); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf16, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + rc = medium->vtbl->Close(medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to close HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_UTF16_FREE(locationUtf16); + } + } + + /*Now, we rewrite the 'machineName'.vbox file to redefine the machine.*/ + if (virVBoxSnapshotConfSaveVboxFile(snapshotMachineDesc, settingsFilePath_Utf8) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to serialize the machine description")); + goto cleanup; + } + rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj, + settingsFilePath, + &machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open Machine, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + + rc = data->vboxObj->vtbl->RegisterMachine(data->vboxObj, machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to register Machine, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + + ret = 0; + cleanup: + VBOX_RELEASE(machine); + VBOX_UTF16_FREE(settingsFilePath); + VBOX_UTF8_FREE(settingsFilePath_Utf8); + VIR_FREE(snapshotMachineDesc); + VIR_FREE(currentSnapshotXmlFilePath); + VBOX_UTF16_FREE(machineNameUtf16); + VBOX_UTF8_FREE(machineName); + virStringFreeList(realReadOnlyDisksPath); + virStringFreeList(realReadWriteDisksPath); + VIR_FREE(newSnapshotPtr); + VIR_FREE(machineLocationPath); + VIR_FREE(nameTmpUse); + return ret; +} +#endif + static virDomainSnapshotPtr vboxDomainSnapshotCreateXML(virDomainPtr dom, const char *xmlDesc, @@ -6036,9 +6989,15 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, #else PRInt32 result; #endif +#if VBOX_API_VERSION >= 4002000 + bool isCurrent = false; +#endif +
/* VBox has no snapshot metadata, so this flag is trivial. */ - virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA, NULL); + virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA | + VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE | + VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT, NULL);
if (!(def = virDomainSnapshotDefParseString(xmlDesc, data->caps, data->xmlopt, -1, @@ -6046,11 +7005,6 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE))) goto cleanup;
- if (def->ndisks) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("disk snapshots not supported yet")); - goto cleanup; - }
vboxIIDFromUUID(&domiid, dom->uuid); rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); @@ -6060,6 +7014,16 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom, goto cleanup; }
+#if VBOX_API_VERSION >= 4002000 + isCurrent = flags & VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT; + if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) { + if (vboxSnapshotRedefine(dom, def, isCurrent) < 0) + goto cleanup; + ret = virGetDomainSnapshot(dom, def->name); + goto cleanup; + } +#endif + rc = machine->vtbl->GetState(machine, &state); if (NS_FAILED(rc)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s",

All snapshots information will be deleted from the vbox XML, but differencing disks will be kept so the user will be able to redefine the snapshot. --- src/vbox/vbox_tmpl.c | 464 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 463 insertions(+), 1 deletion(-) diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index a7f15d4..eb577f4 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -8364,7 +8364,454 @@ vboxDomainSnapshotDeleteTree(vboxGlobalData *data, return ret; } +#if VBOX_API_VERSION >= 4002000 +static int +vboxDomainSnapshotDeleteMetadataOnly(virDomainSnapshotPtr snapshot) +{ + /* + * This function will remove the node in the vbox xml corresponding to the snapshot. + * It is usually called by vboxDomainSnapshotDelete() with the flag + * VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY. + * If you want to use it anywhere else, be careful, if the snapshot you want to delete + * has children, the result is not granted, they will probably will be deleted in the + * xml, but you may have a problem with hard drives. + * + * If the snapshot which is being deleted is the current one, we will set the current + * snapshot of the machine to the parent of this snapshot. Before writing the modified + * xml file, we undefine the machine from vbox. After writing the file, we redefine + * the machine with the new file. + */ + + virDomainPtr dom = snapshot->domain; + VBOX_OBJECT_CHECK(dom->conn, int, -1); + virDomainSnapshotDefPtr def= NULL; + char *defXml = NULL; + vboxIID domiid = VBOX_IID_INITIALIZER; + nsresult rc; + IMachine *machine = NULL; + PRUnichar *settingsFilePathUtf16 = NULL; + char *settingsFilepath = NULL; + virVBoxSnapshotConfMachinePtr snapshotMachineDesc = NULL; + int isCurrent = -1; + int it = 0; + PRUnichar *machineNameUtf16 = NULL; + char *machineName = NULL; + char *nameTmpUse = NULL; + char *machineLocationPath = NULL; + PRUint32 aMediaSize = 0; + IMedium **aMedia = NULL; + defXml = vboxDomainSnapshotGetXMLDesc(snapshot, 0); + if (!defXml) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get XML Desc of snapshot")); + goto cleanup; + } + def = virDomainSnapshotDefParseString(defXml, + data->caps, + data->xmlopt, + -1, + VIR_DOMAIN_SNAPSHOT_PARSE_DISKS | + VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE); + if (!def) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get a virDomainSnapshotDefPtr")); + goto cleanup; + } + + vboxIIDFromUUID(&domiid, dom->uuid); + rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_NO_DOMAIN, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + rc = machine->vtbl->GetSettingsFilePath(machine, &settingsFilePathUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get settings file path")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(settingsFilePathUtf16, &settingsFilepath); + + /*Getting the machine name to retrieve the machine location path.*/ + rc = machine->vtbl->GetName(machine, &machineNameUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get machine name")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineName); + if (virAsprintf(&nameTmpUse, "%s.vbox", machineName) < 0) + goto cleanup; + machineLocationPath = virStringReplace(settingsFilepath, nameTmpUse, ""); + if (machineLocationPath == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get the machine location path")); + goto cleanup; + } + snapshotMachineDesc = virVBoxSnapshotConfLoadVboxFile(settingsFilepath, machineLocationPath); + if (!snapshotMachineDesc) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot create a vboxSnapshotXmlPtr")); + goto cleanup; + } + + isCurrent = virVBoxSnapshotConfIsCurrentSnapshot(snapshotMachineDesc, def->name); + if (isCurrent < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to know if the snapshot is the current snapshot")); + goto cleanup; + } + if (isCurrent) { + /* + * If the snapshot is the current snapshot, it means that the machine has read-write + * disks. The first thing to do is to manipulate VirtualBox API to create + * differential read-write disks if the parent snapshot is not null. + */ + if (def->parent != NULL) { + for (it = 0; it < def->dom->ndisks; it++) { + virVBoxSnapshotConfHardDiskPtr readOnly = NULL; + IMedium *medium = NULL; + PRUnichar *locationUtf16 = NULL; + PRUnichar *parentUuidUtf16 = NULL; + char *parentUuid = NULL; + IMedium *newMedium = NULL; + PRUnichar *formatUtf16 = NULL; + PRUnichar *newLocation = NULL; + char *newLocationUtf8 = NULL; + IProgress *progress = NULL; + PRInt32 resultCode = -1; + virVBoxSnapshotConfHardDiskPtr disk = NULL; + PRUnichar *uuidUtf16 = NULL; + char *uuid = NULL; + char *format = NULL; + char **searchResultTab = NULL; + ssize_t resultSize = 0; + char *tmp = NULL; + + readOnly = virVBoxSnapshotConfHardDiskPtrByLocation(snapshotMachineDesc, + def->dom->disks[it]->src.path); + if (!readOnly) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot get hard disk by location")); + goto cleanup; + } + if (readOnly->parent == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("The read only disk has no parent")); + goto cleanup; + } + + VBOX_UTF8_TO_UTF16(readOnly->parent->location, &locationUtf16); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf16, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + + rc = medium->vtbl->GetId(medium, &parentUuidUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to get hardDisk Id, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(parentUuidUtf16, &parentUuid); + VBOX_UTF16_FREE(parentUuidUtf16); + VBOX_UTF16_FREE(locationUtf16); + VBOX_UTF8_TO_UTF16("VDI", &formatUtf16); + + if (virAsprintf(&newLocationUtf8, "%sfakedisk-%s-%d.vdi", + machineLocationPath, def->parent, it) < 0) + goto cleanup; + VBOX_UTF8_TO_UTF16(newLocationUtf8, &newLocation); + rc = data->vboxObj->vtbl->CreateHardDisk(data->vboxObj, + formatUtf16, + newLocation, + &newMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to create HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_UTF16_FREE(formatUtf16); + VBOX_UTF16_FREE(newLocation); + +# if VBOX_API_VERSION < 4003000 + medium->vtbl->CreateDiffStorage(medium, newMedium, MediumVariant_Diff, &progress); +# else + PRUint32 tab[1]; + tab[0] = MediumVariant_Diff; + medium->vtbl->CreateDiffStorage(medium, newMedium, 1, tab, &progress); +# endif + + progress->vtbl->WaitForCompletion(progress, -1); + progress->vtbl->GetResultCode(progress, &resultCode); + if (NS_FAILED(resultCode)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Error while creating diff storage, rc=%08x"), + (unsigned)resultCode); + goto cleanup; + } + VBOX_RELEASE(progress); + /* + * The differential disk is created, we add it to the media registry and + * the machine storage controller. + */ + + if (VIR_ALLOC(disk) < 0) + goto cleanup; + + rc = newMedium->vtbl->GetId(newMedium, &uuidUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to get medium uuid, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(uuidUtf16, &uuid); + disk->uuid = uuid; + VBOX_UTF16_FREE(uuidUtf16); + + if (VIR_STRDUP(disk->location, newLocationUtf8) < 0) + goto cleanup; + + rc = newMedium->vtbl->GetFormat(newMedium, &formatUtf16); + VBOX_UTF16_TO_UTF8(formatUtf16, &format); + disk->format = format; + VBOX_UTF16_FREE(formatUtf16); + + if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(disk, + snapshotMachineDesc->mediaRegistry, + parentUuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to add hard disk to the media registry")); + goto cleanup; + } + /*Adding fake disks to the machine storage controllers*/ + + resultSize = virStringSearch(snapshotMachineDesc->storageController, + VBOX_UUID_REGEX, + it + 1, + &searchResultTab); + if (resultSize != it + 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find UUID %s"), searchResultTab[it]); + goto cleanup; + } + + tmp = virStringReplace(snapshotMachineDesc->storageController, + searchResultTab[it], + disk->uuid); + virStringFreeList(searchResultTab); + VIR_FREE(snapshotMachineDesc->storageController); + if (!tmp) + goto cleanup; + if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0) + goto cleanup; + + VIR_FREE(tmp); + /*Closing the "fake" disk*/ + rc = newMedium->vtbl->Close(newMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to close the new medium, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + } + } else { + for (it = 0; it < def->dom->ndisks; it++) { + char *uuidRO = NULL; + char **searchResultTab = NULL; + ssize_t resultSize = 0; + char *tmp = NULL; + uuidRO = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc, + def->dom->disks[it]->src.path); + if (!uuidRO) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("No such disk in media registry %s"), + def->dom->disks[it]->src.path); + goto cleanup; + } + + resultSize = virStringSearch(snapshotMachineDesc->storageController, + VBOX_UUID_REGEX, + it + 1, + &searchResultTab); + if (resultSize != it + 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find UUID %s"), + searchResultTab[it]); + goto cleanup; + } + + tmp = virStringReplace(snapshotMachineDesc->storageController, + searchResultTab[it], + uuidRO); + virStringFreeList(searchResultTab); + VIR_FREE(snapshotMachineDesc->storageController); + if (!tmp) + goto cleanup; + if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0) + goto cleanup; + + VIR_FREE(tmp); + } + } + } + /*We remove the read write disks from the media registry*/ + for (it = 0; it < def->ndisks; it++) { + char *uuidRW = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc, def->disks[it].src.path); + if (!uuidRW) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find UUID for location %s"), def->disks[it].src.path); + goto cleanup; + } + if (virVBoxSnapshotConfRemoveHardDisk(snapshotMachineDesc->mediaRegistry, uuidRW) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to remove disk from media registry. uuid = %s"), uuidRW); + goto cleanup; + } + } + /*If the parent snapshot is not NULL, we remove the-read only disks from the media registry*/ + if (def->parent != NULL) { + for (it = 0; it < def->dom->ndisks; it++) { + char *uuidRO = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc, def->dom->disks[it]->src.path); + if (!uuidRO) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find UUID for location %s"), def->dom->disks[it]->src.path); + goto cleanup; + } + if (virVBoxSnapshotConfRemoveHardDisk(snapshotMachineDesc->mediaRegistry, uuidRO) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to remove disk from media registry. uuid = %s"), uuidRO); + goto cleanup; + } + } + } + rc = machine->vtbl->Unregister(machine, + CleanupMode_DetachAllReturnHardDisksOnly, + &aMediaSize, + &aMedia); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to unregister machine, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_RELEASE(machine); + for (it = 0; it < aMediaSize; it++) { + IMedium *medium = aMedia[it]; + if (medium) { + PRUnichar *locationUtf16 = NULL; + char *locationUtf8 = NULL; + rc = medium->vtbl->GetLocation(medium, &locationUtf16); + VBOX_UTF16_TO_UTF8(locationUtf16, &locationUtf8); + if (isCurrent && strstr(locationUtf8, "fake") != NULL) { + /*we delete the fake disk because we don't need it anymore*/ + IProgress *progress = NULL; + PRInt32 resultCode = -1; + rc = medium->vtbl->DeleteStorage(medium, &progress); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to delete medium, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + progress->vtbl->WaitForCompletion(progress, -1); + progress->vtbl->GetResultCode(progress, &resultCode); + if (NS_FAILED(resultCode)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Error while closing medium, rc=%08x"), + (unsigned)resultCode); + goto cleanup; + } + VBOX_RELEASE(progress); + } else { + /* This a comment from vboxmanage code in the handleUnregisterVM + * function in VBoxManageMisc.cpp : + * Note that the IMachine::Unregister method will return the medium + * reference in a sane order, which means that closing will normally + * succeed, unless there is still another machine which uses the + * medium. No harm done if we ignore the error. */ + rc = medium->vtbl->Close(medium); + } + VBOX_UTF16_FREE(locationUtf16); + VBOX_UTF8_FREE(locationUtf8); + } + } + + /*removing the snapshot*/ + if (virVBoxSnapshotConfRemoveSnapshot(snapshotMachineDesc, def->name) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to remove snapshot %s"), def->name); + goto cleanup; + } + + if (isCurrent) { + VIR_FREE(snapshotMachineDesc->currentSnapshot); + if (def->parent != NULL) { + virVBoxSnapshotConfSnapshotPtr snap = virVBoxSnapshotConfSnapshotByName(snapshotMachineDesc->snapshot, def->parent); + if (!snap) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get the snapshot to remove")); + goto cleanup; + } + if (VIR_STRDUP(snapshotMachineDesc->currentSnapshot, snap->uuid) < 0) + goto cleanup; + } + } + + /*Registering the machine*/ + if (virVBoxSnapshotConfSaveVboxFile(snapshotMachineDesc, settingsFilepath) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to serialize the machine description")); + goto cleanup; + } + rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj, + settingsFilePathUtf16, + &machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open Machine, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + + rc = data->vboxObj->vtbl->RegisterMachine(data->vboxObj, machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to register Machine, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + + ret = 0; + cleanup: + VIR_FREE(def); + VIR_FREE(defXml); + VBOX_RELEASE(machine); + VBOX_UTF16_FREE(settingsFilePathUtf16); + VBOX_UTF8_FREE(settingsFilepath); + VIR_FREE(snapshotMachineDesc); + VBOX_UTF16_FREE(machineNameUtf16); + VBOX_UTF8_FREE(machineName); + VIR_FREE(machineLocationPath); + VIR_FREE(nameTmpUse); + + return ret; +} +#endif static int vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, @@ -8378,6 +8825,7 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, IConsole *console = NULL; PRUint32 state; nsresult rc; + vboxArray snapChildren = VBOX_ARRAY_INITIALIZER; virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN | VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY, -1); @@ -8405,7 +8853,21 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, *to remove the node concerning the snapshot */ if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY) { - ret = 0; + rc = vboxArrayGet(&snapChildren, snap, snap->vtbl->GetChildren); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get snapshot children")); + goto cleanup; + } + if (snapChildren.count != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot delete metadata of a snapshot with children")); + goto cleanup; + } else { +#if VBOX_API_VERSION >= 4002000 + ret = vboxDomainSnapshotDeleteMetadataOnly(snapshot); +#endif + } goto cleanup; } -- 1.7.10.4

On Mon, May 19, 2014 at 02:47:33PM +0200, Yohan BELLEGUIC wrote:
All snapshots information will be deleted from the vbox XML, but differencing disks will be kept so the user will be able to redefine the snapshot. --- src/vbox/vbox_tmpl.c | 464 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 463 insertions(+), 1 deletion(-)
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Another module where Coverity was less than enthusiastic about the changes - although less issues than the snapshot code... This one's a bit easier - add a VIR_FREE(disk); to the two places shown below and you're good. John On 05/19/2014 08:47 AM, Yohan BELLEGUIC wrote:
All snapshots information will be deleted from the vbox XML, but differencing disks will be kept so the user will be able to redefine the snapshot. --- src/vbox/vbox_tmpl.c | 464 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 463 insertions(+), 1 deletion(-)
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index a7f15d4..eb577f4 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -8364,7 +8364,454 @@ vboxDomainSnapshotDeleteTree(vboxGlobalData *data, return ret; }
+#if VBOX_API_VERSION >= 4002000 +static int +vboxDomainSnapshotDeleteMetadataOnly(virDomainSnapshotPtr snapshot) +{ + /* + * This function will remove the node in the vbox xml corresponding to the snapshot. + * It is usually called by vboxDomainSnapshotDelete() with the flag + * VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY. + * If you want to use it anywhere else, be careful, if the snapshot you want to delete + * has children, the result is not granted, they will probably will be deleted in the + * xml, but you may have a problem with hard drives. + * + * If the snapshot which is being deleted is the current one, we will set the current + * snapshot of the machine to the parent of this snapshot. Before writing the modified + * xml file, we undefine the machine from vbox. After writing the file, we redefine + * the machine with the new file. + */ + + virDomainPtr dom = snapshot->domain; + VBOX_OBJECT_CHECK(dom->conn, int, -1); + virDomainSnapshotDefPtr def= NULL; + char *defXml = NULL; + vboxIID domiid = VBOX_IID_INITIALIZER; + nsresult rc; + IMachine *machine = NULL; + PRUnichar *settingsFilePathUtf16 = NULL; + char *settingsFilepath = NULL; + virVBoxSnapshotConfMachinePtr snapshotMachineDesc = NULL; + int isCurrent = -1; + int it = 0; + PRUnichar *machineNameUtf16 = NULL; + char *machineName = NULL; + char *nameTmpUse = NULL; + char *machineLocationPath = NULL; + PRUint32 aMediaSize = 0; + IMedium **aMedia = NULL;
+ defXml = vboxDomainSnapshotGetXMLDesc(snapshot, 0); + if (!defXml) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get XML Desc of snapshot")); + goto cleanup; + } + def = virDomainSnapshotDefParseString(defXml, + data->caps, + data->xmlopt, + -1, + VIR_DOMAIN_SNAPSHOT_PARSE_DISKS | + VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE); + if (!def) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get a virDomainSnapshotDefPtr")); + goto cleanup; + } + + vboxIIDFromUUID(&domiid, dom->uuid); + rc = VBOX_OBJECT_GET_MACHINE(domiid.value, &machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_NO_DOMAIN, "%s", + _("no domain with matching UUID")); + goto cleanup; + } + rc = machine->vtbl->GetSettingsFilePath(machine, &settingsFilePathUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get settings file path")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(settingsFilePathUtf16, &settingsFilepath); + + /*Getting the machine name to retrieve the machine location path.*/ + rc = machine->vtbl->GetName(machine, &machineNameUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get machine name")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineName); + if (virAsprintf(&nameTmpUse, "%s.vbox", machineName) < 0) + goto cleanup; + machineLocationPath = virStringReplace(settingsFilepath, nameTmpUse, ""); + if (machineLocationPath == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get the machine location path")); + goto cleanup; + } + snapshotMachineDesc = virVBoxSnapshotConfLoadVboxFile(settingsFilepath, machineLocationPath); + if (!snapshotMachineDesc) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot create a vboxSnapshotXmlPtr")); + goto cleanup; + } + + isCurrent = virVBoxSnapshotConfIsCurrentSnapshot(snapshotMachineDesc, def->name); + if (isCurrent < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to know if the snapshot is the current snapshot")); + goto cleanup; + } + if (isCurrent) { + /* + * If the snapshot is the current snapshot, it means that the machine has read-write + * disks. The first thing to do is to manipulate VirtualBox API to create + * differential read-write disks if the parent snapshot is not null. + */ + if (def->parent != NULL) { + for (it = 0; it < def->dom->ndisks; it++) { + virVBoxSnapshotConfHardDiskPtr readOnly = NULL; + IMedium *medium = NULL; + PRUnichar *locationUtf16 = NULL; + PRUnichar *parentUuidUtf16 = NULL; + char *parentUuid = NULL; + IMedium *newMedium = NULL; + PRUnichar *formatUtf16 = NULL; + PRUnichar *newLocation = NULL; + char *newLocationUtf8 = NULL; + IProgress *progress = NULL; + PRInt32 resultCode = -1; + virVBoxSnapshotConfHardDiskPtr disk = NULL; + PRUnichar *uuidUtf16 = NULL; + char *uuid = NULL; + char *format = NULL; + char **searchResultTab = NULL; + ssize_t resultSize = 0; + char *tmp = NULL; + + readOnly = virVBoxSnapshotConfHardDiskPtrByLocation(snapshotMachineDesc, + def->dom->disks[it]->src.path); + if (!readOnly) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot get hard disk by location")); + goto cleanup; + } + if (readOnly->parent == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("The read only disk has no parent")); + goto cleanup; + } + + VBOX_UTF8_TO_UTF16(readOnly->parent->location, &locationUtf16); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, + locationUtf16, + DeviceType_HardDisk, + AccessMode_ReadWrite, + false, + &medium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + + rc = medium->vtbl->GetId(medium, &parentUuidUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to get hardDisk Id, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(parentUuidUtf16, &parentUuid); + VBOX_UTF16_FREE(parentUuidUtf16); + VBOX_UTF16_FREE(locationUtf16); + VBOX_UTF8_TO_UTF16("VDI", &formatUtf16); + + if (virAsprintf(&newLocationUtf8, "%sfakedisk-%s-%d.vdi", + machineLocationPath, def->parent, it) < 0) + goto cleanup; + VBOX_UTF8_TO_UTF16(newLocationUtf8, &newLocation); + rc = data->vboxObj->vtbl->CreateHardDisk(data->vboxObj, + formatUtf16, + newLocation, + &newMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to create HardDisk, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_UTF16_FREE(formatUtf16); + VBOX_UTF16_FREE(newLocation); + +# if VBOX_API_VERSION < 4003000 + medium->vtbl->CreateDiffStorage(medium, newMedium, MediumVariant_Diff, &progress); +# else + PRUint32 tab[1]; + tab[0] = MediumVariant_Diff; + medium->vtbl->CreateDiffStorage(medium, newMedium, 1, tab, &progress); +# endif + + progress->vtbl->WaitForCompletion(progress, -1); + progress->vtbl->GetResultCode(progress, &resultCode); + if (NS_FAILED(resultCode)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Error while creating diff storage, rc=%08x"), + (unsigned)resultCode); + goto cleanup; + } + VBOX_RELEASE(progress); + /* + * The differential disk is created, we add it to the media registry and + * the machine storage controller. + */ + + if (VIR_ALLOC(disk) < 0) + goto cleanup;
Coverity complains: (43) Event alloc_arg: "virAlloc" allocates memory that is stored into "disk". [details]
+ + rc = newMedium->vtbl->GetId(newMedium, &uuidUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to get medium uuid, rc=%08x"), + (unsigned)rc);
(47) Event goto: Jumping to label "cleanup" (48) Event leaked_storage: Variable "disk" going out of scope leaks the storage it points to.
+ goto cleanup; + } + VBOX_UTF16_TO_UTF8(uuidUtf16, &uuid); + disk->uuid = uuid; + VBOX_UTF16_FREE(uuidUtf16); + + if (VIR_STRDUP(disk->location, newLocationUtf8) < 0)
(50) Event goto: Jumping to label "cleanup" (51) Event leaked_storage: Variable "disk" going out of scope leaks the storage it points to.
+ goto cleanup; + + rc = newMedium->vtbl->GetFormat(newMedium, &formatUtf16); + VBOX_UTF16_TO_UTF8(formatUtf16, &format); + disk->format = format; + VBOX_UTF16_FREE(formatUtf16); + + if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(disk, + snapshotMachineDesc->mediaRegistry, + parentUuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to add hard disk to the media registry")); + goto cleanup; + } + /*Adding fake disks to the machine storage controllers*/ + + resultSize = virStringSearch(snapshotMachineDesc->storageController, + VBOX_UUID_REGEX, + it + 1, + &searchResultTab); + if (resultSize != it + 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find UUID %s"), searchResultTab[it]); + goto cleanup; + } + + tmp = virStringReplace(snapshotMachineDesc->storageController, + searchResultTab[it], + disk->uuid); + virStringFreeList(searchResultTab); + VIR_FREE(snapshotMachineDesc->storageController); + if (!tmp) + goto cleanup; + if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0) + goto cleanup; + + VIR_FREE(tmp); + /*Closing the "fake" disk*/ + rc = newMedium->vtbl->Close(newMedium); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to close the new medium, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + } + } else { + for (it = 0; it < def->dom->ndisks; it++) { + char *uuidRO = NULL; + char **searchResultTab = NULL; + ssize_t resultSize = 0; + char *tmp = NULL; + uuidRO = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc, + def->dom->disks[it]->src.path); + if (!uuidRO) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("No such disk in media registry %s"), + def->dom->disks[it]->src.path); + goto cleanup; + } + + resultSize = virStringSearch(snapshotMachineDesc->storageController, + VBOX_UUID_REGEX, + it + 1, + &searchResultTab); + if (resultSize != it + 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find UUID %s"), + searchResultTab[it]); + goto cleanup; + } + + tmp = virStringReplace(snapshotMachineDesc->storageController, + searchResultTab[it], + uuidRO); + virStringFreeList(searchResultTab); + VIR_FREE(snapshotMachineDesc->storageController); + if (!tmp) + goto cleanup; + if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0) + goto cleanup; + + VIR_FREE(tmp); + } + } + } + /*We remove the read write disks from the media registry*/ + for (it = 0; it < def->ndisks; it++) { + char *uuidRW = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc, def->disks[it].src.path); + if (!uuidRW) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find UUID for location %s"), def->disks[it].src.path); + goto cleanup; + } + if (virVBoxSnapshotConfRemoveHardDisk(snapshotMachineDesc->mediaRegistry, uuidRW) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to remove disk from media registry. uuid = %s"), uuidRW); + goto cleanup; + } + } + /*If the parent snapshot is not NULL, we remove the-read only disks from the media registry*/ + if (def->parent != NULL) { + for (it = 0; it < def->dom->ndisks; it++) { + char *uuidRO = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc, def->dom->disks[it]->src.path); + if (!uuidRO) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find UUID for location %s"), def->dom->disks[it]->src.path); + goto cleanup; + } + if (virVBoxSnapshotConfRemoveHardDisk(snapshotMachineDesc->mediaRegistry, uuidRO) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to remove disk from media registry. uuid = %s"), uuidRO); + goto cleanup; + } + } + } + rc = machine->vtbl->Unregister(machine, + CleanupMode_DetachAllReturnHardDisksOnly, + &aMediaSize, + &aMedia); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to unregister machine, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + VBOX_RELEASE(machine); + for (it = 0; it < aMediaSize; it++) { + IMedium *medium = aMedia[it]; + if (medium) { + PRUnichar *locationUtf16 = NULL; + char *locationUtf8 = NULL; + rc = medium->vtbl->GetLocation(medium, &locationUtf16); + VBOX_UTF16_TO_UTF8(locationUtf16, &locationUtf8); + if (isCurrent && strstr(locationUtf8, "fake") != NULL) { + /*we delete the fake disk because we don't need it anymore*/ + IProgress *progress = NULL; + PRInt32 resultCode = -1; + rc = medium->vtbl->DeleteStorage(medium, &progress); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to delete medium, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + progress->vtbl->WaitForCompletion(progress, -1); + progress->vtbl->GetResultCode(progress, &resultCode); + if (NS_FAILED(resultCode)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Error while closing medium, rc=%08x"), + (unsigned)resultCode); + goto cleanup; + } + VBOX_RELEASE(progress); + } else { + /* This a comment from vboxmanage code in the handleUnregisterVM + * function in VBoxManageMisc.cpp : + * Note that the IMachine::Unregister method will return the medium + * reference in a sane order, which means that closing will normally + * succeed, unless there is still another machine which uses the + * medium. No harm done if we ignore the error. */ + rc = medium->vtbl->Close(medium); + } + VBOX_UTF16_FREE(locationUtf16); + VBOX_UTF8_FREE(locationUtf8); + } + } + + /*removing the snapshot*/ + if (virVBoxSnapshotConfRemoveSnapshot(snapshotMachineDesc, def->name) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to remove snapshot %s"), def->name); + goto cleanup; + } + + if (isCurrent) { + VIR_FREE(snapshotMachineDesc->currentSnapshot); + if (def->parent != NULL) { + virVBoxSnapshotConfSnapshotPtr snap = virVBoxSnapshotConfSnapshotByName(snapshotMachineDesc->snapshot, def->parent); + if (!snap) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to get the snapshot to remove")); + goto cleanup; + } + if (VIR_STRDUP(snapshotMachineDesc->currentSnapshot, snap->uuid) < 0) + goto cleanup; + } + } + + /*Registering the machine*/ + if (virVBoxSnapshotConfSaveVboxFile(snapshotMachineDesc, settingsFilepath) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to serialize the machine description")); + goto cleanup; + } + rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj, + settingsFilePathUtf16, + &machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to open Machine, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + + rc = data->vboxObj->vtbl->RegisterMachine(data->vboxObj, machine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to register Machine, rc=%08x"), + (unsigned)rc); + goto cleanup; + } + + ret = 0; + cleanup: + VIR_FREE(def); + VIR_FREE(defXml); + VBOX_RELEASE(machine); + VBOX_UTF16_FREE(settingsFilePathUtf16); + VBOX_UTF8_FREE(settingsFilepath); + VIR_FREE(snapshotMachineDesc); + VBOX_UTF16_FREE(machineNameUtf16); + VBOX_UTF8_FREE(machineName); + VIR_FREE(machineLocationPath); + VIR_FREE(nameTmpUse); + + return ret; +} +#endif
static int vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, @@ -8378,6 +8825,7 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, IConsole *console = NULL; PRUint32 state; nsresult rc; + vboxArray snapChildren = VBOX_ARRAY_INITIALIZER;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN | VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY, -1); @@ -8405,7 +8853,21 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, *to remove the node concerning the snapshot */ if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY) { - ret = 0; + rc = vboxArrayGet(&snapChildren, snap, snap->vtbl->GetChildren); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get snapshot children")); + goto cleanup; + } + if (snapChildren.count != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot delete metadata of a snapshot with children")); + goto cleanup; + } else { +#if VBOX_API_VERSION >= 4002000 + ret = vboxDomainSnapshotDeleteMetadataOnly(snapshot); +#endif + } goto cleanup; }

On Mon, May 19, 2014 at 02:47:29PM +0200, Yohan BELLEGUIC wrote:
Hello,
This is a new series of patches in order to support undefining and redefining snapshots with VirtualBox 4.2 or higher. These patches are based on Manuel Vives' patches, taking into account Daniel P. Berrange's remarks.
Thanks for working through this series. It is at long last all merged into libvirt GIT master. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (3)
-
Daniel P. Berrange
-
John Ferlan
-
Yohan BELLEGUIC