[libvirt] [PATCHv5 0/5] Handling of undefine and redefine snapshots with VirtualBox 4.2

Hi, This is a serie of patches in order to support undefining and redefining snapshots with VirtualBox 4.2. The serie of patches is rather big, and adds among other things some utility functions unrelated to VirtualBox in patches 1 & 2. The code review could be done in several parts: e.g. patches 1 & 2 separately to validate the utility functions. 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 taken to emulate these behaviours by manipulating directly the .vbox XML files. The first two patches are some util methods for handling regexp and strings that will be used after. The third patch brings more details in the snapshot XML returned by libvirt. We will need those modifications in order to redefine the snapshots. The fourth patch brings the support of the VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE and VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT flags in virDomainSnapshotCreateXML. The fifth and last patch brings the support of the VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY flag in virDomainSnapshotDelete. The patches are only tested with Virtualbox 4.2 but the code is compliant with Virtualbox 4.3 API. Regards, Manuel VIVES 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 (5): virstring.h/c: Util method for finding regexp patterns in some strings virstring.h/c: Util method for making some find and replace in strings vbox_tmpl.c: Better XML description for snapshots vbox_tmpl.c: Patch for redefining snapshots vbox_tmpl.c: Add methods for undefining snapshots po/POTFILES.in | 1 + src/libvirt_private.syms | 2 + src/util/virstring.c | 163 +++- src/util/virstring.h | 4 + src/vbox/vbox_tmpl.c | 2347 ++++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 2346 insertions(+), 171 deletions(-) -- 1.7.10.4

--- po/POTFILES.in | 1 + src/libvirt_private.syms | 1 + src/util/virstring.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/virstring.h | 3 ++ 4 files changed, 102 insertions(+) diff --git a/po/POTFILES.in b/po/POTFILES.in index 49dfc9c..ed09fc1 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -189,6 +189,7 @@ src/util/virscsi.c src/util/virsocketaddr.c src/util/virstatslinux.c src/util/virstoragefile.c +src/util/virstring.c src/util/virsysinfo.c src/util/virerror.c src/util/virerror.h diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2dbb8f8..14ad8f4 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1751,6 +1751,7 @@ virStorageFileResize; # util/virstring.h virArgvToString; virAsprintfInternal; +virSearchRegex; virSkipSpaces; virSkipSpacesAndBackslash; virSkipSpacesBackwards; diff --git a/src/util/virstring.c b/src/util/virstring.c index 8d0ca70..3c93450 100644 --- a/src/util/virstring.c +++ b/src/util/virstring.c @@ -23,6 +23,7 @@ #include <stdlib.h> #include <stdio.h> +#include <regex.h> #include "c-ctype.h" #include "virstring.h" @@ -645,3 +646,99 @@ int virStringSortRevCompare(const void *a, const void *b) return strcmp(*sb, *sa); } + +/** + * virSearchRegex: + * Allows you to get the nth occurrence of a substring in sourceString which matches + * a POSIX Extended regular expression pattern. + * If there is no substring, result is not modified. + * return -1 on error, 0 if not found and 1 if found. + * + * @sourceString: String to parse + * @occurrence: return occurrence 'n' (starting from 0) of a sub-string that + * matches the pattern. + * @regexp: POSIX Extended regular expression pattern used for matching + * @result: nth occurrence substring matching the @regexp pattern + * @code + char *source = "6853a496-1c10-472e-867a-8244937bd6f0 + 773ab075-4cd7-4fc2-8b6e-21c84e9cb391 + bbb3c75c-d60f-43b0-b802-fd56b84a4222 + 60c04aa1-0375-4654-8d9f-e149d9885273 + 4548d465-9891-4c34-a184-3b1c34a26aa8"; + char *ret1=NULL; + char *ret2=NULL; + char *ret3=NULL; + virSearchRegex(source, + 4, + "([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})", + &ret1); + //ret1 = "4548d465-9891-4c34-a184-3b1c34a26aa8" + virSearchRegex(source, + 0, + "([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})", + &ret2); + //ret2 = "6853a496-1c10-472e-867a-8244937bd6f0" + virSearchRegex(source, + 1, + "([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})", + &ret3); + //ret3 = "773ab075-4cd7-4fc2-8b6e-21c84e9cb391" + * @endcode + */ + +int +virSearchRegex(const char *sourceString, + unsigned int occurrence, + const char *regexp, + char **result) +{ + regex_t pregUuidBracket; + size_t i = 0; + size_t nmatch = 0; + regmatch_t *pmatch = NULL; + int ret = -1; + int regError = -1; + + regError = regcomp(&pregUuidBracket, regexp, REG_EXTENDED); + if (regError != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Error while compiling regular expression: %d"), + regError); + goto cleanup; + } + nmatch = pregUuidBracket.re_nsub; + if (VIR_ALLOC_N(pmatch, nmatch) < 0) + goto cleanup; + + while (i < (occurrence+1)) { + if (regexec(&pregUuidBracket, sourceString, nmatch, pmatch, 0) == 0) { + regoff_t start = pmatch[0].rm_so; + regoff_t end = pmatch[0].rm_eo; + if (i == occurrence || + (occurrence > i && regexec(&pregUuidBracket, &sourceString[end], + nmatch, pmatch, 0) != 0)) { + /* We copy only if i == position (so that it is the uuid we're looking for), + * or position > i AND there is no matches left in the rest of the string + * (this is the case where we give a biggest @occurence than the + * number of matches and we want to return the last one) + */ + if (VIR_STRNDUP(*result, sourceString + start, end - start) < 0) + goto cleanup; + + ret = 1; + goto cleanup; + } + sourceString = &sourceString[end]; + } else { + break; + ret = 0; + goto cleanup; + } + ++i; + } + +cleanup: + regfree(&pregUuidBracket); + VIR_FREE(pmatch); + return ret; +} diff --git a/src/util/virstring.h b/src/util/virstring.h index 13a6e5a..fe05403 100644 --- a/src/util/virstring.h +++ b/src/util/virstring.h @@ -226,4 +226,7 @@ size_t virStringListLength(char **strings); int virStringSortCompare(const void *a, const void *b); int virStringSortRevCompare(const void *a, const void *b); +int virSearchRegex(const char *sourceString, unsigned int occurrence, const char *regexp, char **result); + + #endif /* __VIR_STRING_H__ */ -- 1.7.10.4

--- src/libvirt_private.syms | 1 + src/util/virstring.c | 66 +++++++++++++++++++++++++++++++++++++++++++++- src/util/virstring.h | 1 + 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 14ad8f4..17aadfe 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1766,6 +1766,7 @@ virStringSortRevCompare; virStringSplit; virStrncpy; virStrndup; +virStrReplace; virStrToDouble; virStrToLong_i; virStrToLong_l; diff --git a/src/util/virstring.c b/src/util/virstring.c index 3c93450..b6f6192 100644 --- a/src/util/virstring.c +++ b/src/util/virstring.c @@ -618,7 +618,6 @@ size_t virStringListLength(char **strings) return i; } - /** * virStringSortCompare: * @@ -742,3 +741,68 @@ cleanup: VIR_FREE(pmatch); return ret; } + +/* + virStrReplace(haystack, oldneedle, newneedle) -- + Search haystack and replace all occurences of oldneedle with newneedle. + Return a string with all the replacements in case of success, NULL in case + of failure +*/ +char * +virStrReplace(char *haystack, + const char *oldneedle, const char *newneedle) +{ + char *ret = NULL; + size_t i = strlen(haystack); + size_t count = 0; + size_t newneedle_len = strlen(newneedle); + size_t oldneedle_len = strlen(oldneedle); + int diff_len = newneedle_len - oldneedle_len; + size_t totalLength = 0; + size_t numberOfElementsToAllocate = 0; + if (!haystack || !oldneedle || !newneedle) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("null value for haystack, oldneedle or newneedle")); + goto cleanup; + } + if (oldneedle_len == 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("oldneedle cannot be empty")); + goto cleanup; + } + if (diff_len == 0 && memcmp(oldneedle, newneedle, newneedle_len) == 0) { + ignore_value(VIR_STRDUP(ret, haystack)); + goto cleanup; + } + + for (i = 0; haystack[i] != '\0'; i++) { + if (memcmp(&haystack[i], oldneedle, oldneedle_len) == 0) { + count ++; + i += oldneedle_len - 1; + } + } + numberOfElementsToAllocate = (i + count * (newneedle_len - oldneedle_len)); + + if (VIR_ALLOC_N(ret, numberOfElementsToAllocate) < 0) + goto cleanup; + + totalLength = sizeof(char) * numberOfElementsToAllocate; + i = 0; + while (*haystack) { + if (memcmp(haystack, oldneedle, oldneedle_len) == 0) { + if (virStrcpy(&ret[i], newneedle, totalLength) == NULL) { + VIR_FREE(ret); + goto cleanup; + } + totalLength -= newneedle_len; + i += newneedle_len; + haystack += oldneedle_len; + } else { + ret[i++] = *haystack++; + totalLength --; + } + } + ret[i] = '\0'; +cleanup: + return ret; +} diff --git a/src/util/virstring.h b/src/util/virstring.h index fe05403..2df0695 100644 --- a/src/util/virstring.h +++ b/src/util/virstring.h @@ -227,6 +227,7 @@ int virStringSortCompare(const void *a, const void *b); int virStringSortRevCompare(const void *a, const void *b); int virSearchRegex(const char *sourceString, unsigned int occurrence, const char *regexp, char **result); +char * virStrReplace(char *haystack, const char *oldneedle, const char *newneedle); #endif /* __VIR_STRING_H__ */ -- 1.7.10.4

It will be needed for the future patches because we will redefine snapshots --- src/vbox/vbox_tmpl.c | 510 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 502 insertions(+), 8 deletions(-) diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 385ee54..98724c1 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -38,6 +38,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> +#include <libxml/xmlwriter.h> #include "internal.h" #include "datatypes.h" @@ -58,6 +59,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 == 2002 @@ -285,6 +288,12 @@ static void vboxDriverUnlock(vboxGlobalData *data) { virMutexUnlock(&data->lock); } +typedef enum { + VBOX_STORAGE_DELETE_FLAG = 0, +#if VBOX_API_VERSION >= 4002 + VBOX_STORAGE_CLOSE_FLAG = 1, +#endif +} vboxStorageDeleteOrCloseFlags; #if VBOX_API_VERSION == 2002 static void nsIDtoChar(unsigned char *uuid, const nsID *iid) { @@ -5968,7 +5977,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) { @@ -6059,6 +6070,436 @@ cleanup: return ret; } +#if VBOX_API_VERSION >=4002 +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].file, 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].type = VIR_DOMAIN_DISK_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, 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]->type = VIR_DOMAIN_DISK_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; + ret = -1; + goto cleanup; + } + VBOX_RELEASE(disk); + VBOX_RELEASE(storageController); + vboxArrayRelease(&mediumAttachments); + VBOX_RELEASE(snap); + return ret; +} +#endif + static char * vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, unsigned int flags) @@ -6076,6 +6517,10 @@ vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, PRInt64 timestamp; PRBool online = PR_FALSE; char uuidstr[VIR_UUID_STRING_BUFLEN]; +#if VBOX_API_VERSION >=4002 + PRUint32 memorySize = 0; + PRUint32 CPUCount = 0; +#endif virCheckFlags(0, NULL); @@ -6090,11 +6535,40 @@ 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 >=4002 + /* 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); + ignore_value(VIR_STRDUP(def->dom->name, dom->name)); + 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; + ignore_value(VIR_STRDUP(def->dom->os.type, "hvm")); + 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 >= 4002 */ + rc = snap->vtbl->GetDescription(snap, &str16); if (NS_FAILED(rc)) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -6159,8 +6633,8 @@ 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: virDomainSnapshotDefFree(def); VBOX_RELEASE(parent); @@ -6865,6 +7339,8 @@ cleanup: return ret; } + + static int vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, unsigned int flags) @@ -6900,8 +7376,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; @@ -8727,8 +9204,9 @@ cleanup: return ret; } -static int vboxStorageVolDelete(virStorageVolPtr vol, - unsigned int flags) +static int vboxStorageDeleteOrClose(virStorageVolPtr vol, + unsigned int flags, + unsigned int flagDeleteOrClose) { VBOX_OBJECT_CHECK(vol->conn, int, -1); vboxIID hddIID = VBOX_IID_INITIALIZER; @@ -8883,8 +9361,18 @@ static int vboxStorageVolDelete(virStorageVolPtr vol, if (machineIdsSize == 0 || machineIdsSize == deregister) { IProgress *progress = NULL; - +#if VBOX_API_VERSION >= 4002 + if (flagDeleteOrClose & VBOX_STORAGE_CLOSE_FLAG) { + rc = hardDisk->vtbl->Close(hardDisk); + if (NS_SUCCEEDED(rc)) { + DEBUGIID("HardDisk closed, UUID", hddIID.value); + ret = 0; + } + } +#endif + if (flagDeleteOrClose & VBOX_STORAGE_DELETE_FLAG){ rc = hardDisk->vtbl->DeleteStorage(hardDisk, &progress); + } if (NS_SUCCEEDED(rc) && progress) { progress->vtbl->WaitForCompletion(progress, -1); @@ -8903,6 +9391,12 @@ static int vboxStorageVolDelete(virStorageVolPtr vol, return ret; } +static int vboxStorageVolDelete(virStorageVolPtr vol, + unsigned int flags) +{ + return vboxStorageDeleteOrClose(vol, flags, VBOX_STORAGE_DELETE_FLAG); +} + static int vboxStorageVolGetInfo(virStorageVolPtr vol, virStorageVolInfoPtr info) { VBOX_OBJECT_CHECK(vol->conn, int, -1); IHardDisk *hardDisk = NULL; -- 1.7.10.4

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

All the informations concerning snapshots (and snapshot disks) will be deleted from the vbox xml. But the differencing disks will be kept so you will be able to redefine the snapshots. --- src/vbox/vbox_tmpl.c | 448 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 447 insertions(+), 1 deletion(-) diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index a43a875..009ff3a 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -6171,6 +6171,68 @@ vboxSnapshotXmlAppendDiskToMediaRegistry(xmlNodePtr *inMediaRegistry, } } + +static void +vboxRemoveAllDisksExceptParentFromMediaRegistry(xmlNodePtr mediaRegistryNode){ + xmlNodePtr cur_node = NULL; + for (cur_node = mediaRegistryNode; cur_node; cur_node = cur_node->next) { + if (cur_node) { + if (cur_node->type == XML_ELEMENT_NODE + && !xmlStrcmp(cur_node->name, (const xmlChar *) "HardDisk")) { + xmlNodePtr child = NULL; + for (child = cur_node->children; child; child = child->next) { + /*We look over all the children + *If there is a node element, we delete it + */ + if (child->type == XML_ELEMENT_NODE) { + xmlUnlinkNode(child); + xmlFreeNode(child); + } + } + } + } + if ((cur_node->children)) + vboxRemoveAllDisksExceptParentFromMediaRegistry((cur_node->children)); + } +} + +static void +vboxRemoveDiskFromMediaRegistryIfNoChildren(xmlNodePtr mediaRegistryNode, + char *diskLocation) +{ + /* + *This function will remove a disk from the media registry only if it doesn't + *have any children + */ + xmlNodePtr cur_node = NULL; + for (cur_node = mediaRegistryNode; cur_node; cur_node = cur_node->next) { + if (cur_node) { + if (cur_node->type == XML_ELEMENT_NODE + && !xmlStrcmp(cur_node->name, (const xmlChar *) "HardDisk") + && xmlHasProp(cur_node, BAD_CAST "location") != NULL + && strstr(diskLocation, (char *)xmlHasProp(cur_node, BAD_CAST "location")->children->content) != NULL) { + + xmlNodePtr child = NULL; + bool deleteNode = true; + for (child = cur_node->children; child; child = child->next) { + /*We look over all the children + *If there is a node element, we don't delete it + */ + if (child->type == XML_ELEMENT_NODE) + deleteNode = false; + } + if (deleteNode) { + xmlUnlinkNode(cur_node); + xmlFreeNode(cur_node); + } + return; + } + } + if ((cur_node->children)) + vboxRemoveDiskFromMediaRegistryIfNoChildren((cur_node->children), diskLocation); + } +} + static int vboxSnapshotGenerateVboxXML(xmlNodePtr rootElementVboxXML, char *storageControllerString, @@ -8407,8 +8469,377 @@ cleanup: vboxArrayRelease(&children); return ret; } +#if VBOX_API_VERSION >= 4002 +static int +vboxCloseDisk(virDomainPtr dom, + IMedium *baseDisk) { + VBOX_OBJECT_CHECK(dom->conn, int, -1); + nsresult rc; + vboxArray childrenDiskArray = VBOX_ARRAY_INITIALIZER; + size_t i = 0; + if (!baseDisk) + goto cleanup; + + rc = vboxArrayGet(&childrenDiskArray, baseDisk, baseDisk->vtbl->GetChildren); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get children disks")); + goto cleanup; + } + for (i=0; i < childrenDiskArray.count; ++i) + vboxCloseDisk(dom, childrenDiskArray.items[i]); + + baseDisk->vtbl->Close(baseDisk); + ret = 0; +cleanup: + vboxArrayRelease(&childrenDiskArray); + return ret; +} 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, we will set the current snapshot of the machine to + *its parent. + * + *Before the writing of the modified xml file, we undefine the machine from vbox + *After the modification, we redefine the machine + */ + + virDomainPtr dom = snapshot->domain; + VBOX_OBJECT_CHECK(dom->conn, int, -1); + IMachine *machine = NULL; + IMachine *newMachine = NULL; + nsresult rc; + ISnapshot *snap = NULL; + vboxIID domiid = VBOX_IID_INITIALIZER; + vboxArray machineDisks = VBOX_ARRAY_INITIALIZER; + IMedium *dummyArray[] = { NULL }; + PRUnichar *settingsFilePath = NULL; + char *settingsFilePathUtf8 = NULL; + xmlDocPtr xml = NULL; + xmlNodePtr xmlRootElement = NULL; + xmlNodePtr snapshotToDelete = NULL; + IProgress *progress = NULL; + vboxIID snapIid = VBOX_IID_INITIALIZER; + char *snapshotUuidStr = NULL; + char *snapshotUuidStrWithBrackets = NULL; + ISnapshot *parentSnapshot = NULL; + vboxIID parentIId = VBOX_IID_INITIALIZER; + char *parentSnapshotUuidStr = NULL; + char *parentSnapshotUuidStrWithBrackets = NULL; + virDomainSnapshotDefPtr def = NULL; + char *snapXmlDesc = NULL; + xmlNodePtr mediaRegistryNode = NULL; + xmlNodePtr storageControllersNode = NULL; + xmlBufferPtr buf = xmlBufferCreate(); + xmlNodePtr newStorageControllersNode = NULL; + xmlNodePtr machineNode = NULL; + bool snapshotHasParent = false; + char *snapshotStorageControllersString = NULL; + size_t i = 0; + unsigned int numberOfDisks = 0; + unsigned int failedCounter = 10; + + + VIR_DEBUG("vboxDomainSnapshotDeleteMetadataOnly()"); + 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; + } + + /*Get snapshot*/ + snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name); + if (!snap) + goto cleanup; + rc = snap->vtbl->GetId(snap, &snapIid.value); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get snapshot uuid")); + goto cleanup; + } + snapXmlDesc = vboxDomainSnapshotGetXMLDesc(snapshot, 0); + if (!(def = virDomainSnapshotDefParseString(snapXmlDesc, data->caps, + data->xmlopt, -1, VIR_DOMAIN_SNAPSHOT_PARSE_DISKS | VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE))) + goto cleanup; + snapshotHasParent = !(def->parent == NULL); + /*Get its parent because it's possible that we have to put the parent uuid in + *the current snapshot attribute*/ + rc = snap->vtbl->GetParent(snap, &parentSnapshot); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get parent snapshot")); + goto cleanup; + } + if (parentSnapshot) { + rc = parentSnapshot->vtbl->GetId(parentSnapshot, &parentIId.value); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get parent snapshot id")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(parentIId.value, &parentSnapshotUuidStr); + if (virAsprintf(&parentSnapshotUuidStrWithBrackets, "{%s}", parentSnapshotUuidStr) < 0) { + VIR_DEBUG("problem while using virasprintf"); + goto cleanup; + } + } + + rc = machine->vtbl->GetSettingsFilePath(machine, &settingsFilePath); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get settings file path")); + goto cleanup; + } + + /*We ask libxml2 to parse the xmlfile*/ + VBOX_UTF16_TO_UTF8(settingsFilePath, &settingsFilePathUtf8); + xml = virXMLParse(settingsFilePathUtf8, NULL, NULL); + xmlKeepBlanksDefault(1); + if (!xml) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot parse settings file")); + goto cleanup; + } + + xmlRootElement = xmlDocGetRootElement(xml); + if (!xmlRootElement) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot find any root element in the xml")); + goto cleanup; + } + + vboxSnapshotXmlRetrieveSnapshotNodeByName(xmlRootElement->children, + snapshot->name, + &snapshotToDelete); + if (!snapshotToDelete) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot find any snapshot to delete")); + goto cleanup; + } + /*We manage the case where the snapshot we want to delete is the current one */ + VBOX_UTF16_TO_UTF8(snapIid.value, &snapshotUuidStr); + if (virAsprintf(&snapshotUuidStrWithBrackets, "{%s}", snapshotUuidStr) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("problem while using virasprintf")); + goto cleanup; + } + vboxSnapshotXmlRetrieveMachineNode(xmlRootElement, &machineNode); + /*we change the current snapshot if necessary*/ + if (STREQ(virXMLPropString(machineNode, "currentSnapshot"), snapshotUuidStrWithBrackets)) { + if (parentSnapshotUuidStrWithBrackets != NULL) { + xmlSetProp(machineNode, + BAD_CAST "currentSnapshot", + BAD_CAST parentSnapshotUuidStrWithBrackets); + } else { + xmlUnsetProp(machineNode, BAD_CAST "currentSnapshot"); + } + } + vboxSnapshotXmlRetrieveRootNodeByName(xmlRootElement, + "MediaRegistry", + &mediaRegistryNode); + if (!mediaRegistryNode) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("problem while retrieving MediaRegistry node")); + goto cleanup; + } + vboxSnapshotXmlRetrieveRootNodeByName(xmlRootElement, "StorageControllers", &storageControllersNode); + if (!storageControllersNode) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("problem while retrieving StorageControllers node")); + goto cleanup; + } + xmlNodeDump(buf, xml, storageControllersNode, 0, 0); + if (VIR_STRDUP(snapshotStorageControllersString, (char *)xmlBufferContent(buf)) < 0) + goto cleanup; + + xmlBufferFree(buf); + if (snapshotHasParent) + numberOfDisks = def->dom->ndisks; + else + numberOfDisks = def->ndisks; + + for (i = 0; i < numberOfDisks; ++i) { + IMedium *diskSnapshot = NULL; + IMedium *baseDiskSnapshot = NULL; + PRUnichar *locationDiskSnapshotUtf16 = NULL; + PRUnichar *baseDiskIdUtf16 = NULL; + char *baseDiskId = NULL; + char *diskPath = NULL; + char *uuidToReplace = NULL; + if (snapshotHasParent) + diskPath = def->dom->disks[i]->src; + else + diskPath = def->disks[i].file; + VBOX_UTF8_TO_UTF16(diskPath, &locationDiskSnapshotUtf16); + + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, locationDiskSnapshotUtf16, DeviceType_HardDisk, AccessMode_ReadWrite, false, &diskSnapshot); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot open medium")); + goto cleanup; + } + if (!diskSnapshot) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("could not open: %s"), + def->dom->disks[i]->src); + goto cleanup; + } + rc = diskSnapshot->vtbl->GetBase(diskSnapshot, &baseDiskSnapshot); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get base disk")); + goto cleanup; + } + if (!baseDiskSnapshot) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("could not get base medium")); + goto cleanup; + } + rc = baseDiskSnapshot->vtbl->GetId(baseDiskSnapshot, &baseDiskIdUtf16); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get base disk id")); + goto cleanup; + } + VBOX_UTF16_TO_UTF8(baseDiskIdUtf16, &baseDiskId); + VIR_DEBUG("Removing disk %s from media registry", diskPath); + if (snapshotHasParent) { + vboxRemoveDiskFromMediaRegistryIfNoChildren(mediaRegistryNode->children, diskPath); + } else { + vboxRemoveAllDisksExceptParentFromMediaRegistry(mediaRegistryNode->children); + virSearchRegex(snapshotStorageControllersString, + i, + "([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})", + &uuidToReplace); + char *tmp = NULL; + tmp = virStrReplace(snapshotStorageControllersString, uuidToReplace, baseDiskId); + VIR_FREE(snapshotStorageControllersString); + if (!tmp) + goto cleanup; + if (VIR_STRDUP(snapshotStorageControllersString, tmp) < 0) { + VIR_FREE(tmp); + goto cleanup; + } + VIR_FREE(tmp); + + xmlParseInNodeContext(machineNode, snapshotStorageControllersString, (int)strlen(snapshotStorageControllersString), 0, &newStorageControllersNode); + } + VBOX_UTF16_FREE(locationDiskSnapshotUtf16); + VBOX_UTF16_FREE(baseDiskIdUtf16); + } + /*we remove the machine from vbox*/ + rc = vboxArrayGetWithUintArg(&machineDisks, machine, machine->vtbl->Unregister, CleanupMode_DetachAllReturnNone); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot unregister machine")); + goto cleanup; + } +# if VBOX_API_VERSION == 4002 + rc = machine->vtbl->Delete(machine, 0, dummyArray, &progress); +# elif VBOX_API_VERSION >= 4003 + rc = machine->vtbl->DeleteConfig(machine, 0, dummyArray, &progress); +# endif + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot delete machine")); + goto cleanup; + } + if (progress != NULL) { + progress->vtbl->WaitForCompletion(progress, -1); + VBOX_RELEASE(progress); + } + + for (i = 0; i < numberOfDisks; ++i) { + IMedium *diskSnapshot= NULL; + PRUnichar *locationDiskSnapshotUtf16 = NULL; + IMedium *baseDiskSnapshot = NULL; + + VBOX_UTF8_TO_UTF16(def->dom->disks[i]->src, &locationDiskSnapshotUtf16); + rc = data->vboxObj->vtbl->OpenMedium(data->vboxObj, locationDiskSnapshotUtf16, DeviceType_HardDisk, AccessMode_ReadWrite, false, &diskSnapshot); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot open medium")); + goto cleanup; + } + rc = diskSnapshot->vtbl->GetBase(diskSnapshot, &baseDiskSnapshot); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot get disk base")); + goto cleanup; + } + vboxCloseDisk(dom, baseDiskSnapshot); + } + + /*we delete the xml part corresponding to the snapshot*/ + xmlUnlinkNode(snapshotToDelete); + xmlFreeNode(snapshotToDelete); + + /*We delete the part corresponding to the storagecontrollers if we need to*/ + if (numberOfDisks > 0 && !snapshotHasParent) { + xmlUnlinkNode(storageControllersNode); + xmlFreeNode(storageControllersNode); + xmlAddChild(machineNode, newStorageControllersNode); + } + + /*we save the modified xml*/ + if (virFileMakePath(mdir_name(settingsFilePathUtf8)) < 0) + virReportError(VIR_ERR_INTERNAL_ERROR, "%s" + " %s", + _("Cannot create path :"), + settingsFilePathUtf8); + if (xmlSaveFormatFileEnc(settingsFilePathUtf8, xml, "ISO-8859-1", 1) <= 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s" + " %s", + _("Cannot save xml file to:"), + settingsFilePathUtf8); + } + rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj, settingsFilePath, &newMachine); + while (NS_FAILED(rc) && (rc == 0x80004005 /*NSCOM standard error*/) && failedCounter != 0) { + /*There is some random fails happening and they are not the error + *supposed to happen. + *I didn't have some solution on #vbox-dev + *but it appears that with another try it works so that's why we are trying 10 times maximum + * + */ + rc = data->vboxObj->vtbl->OpenMachine(data->vboxObj, settingsFilePath, &newMachine); + failedCounter--; + } + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot open machine")); + goto cleanup; + } + if (newMachine) { + rc = data->vboxObj->vtbl->RegisterMachine(data->vboxObj, newMachine); + if (NS_FAILED(rc)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot register machine")); + goto cleanup; + } + } + else + goto cleanup; + + ret = 0; +cleanup: + vboxArrayRelease(&machineDisks); + VBOX_UTF16_FREE(settingsFilePath); + return ret; + +} +#endif +static int vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, unsigned int flags) { @@ -8420,6 +8851,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); @@ -8447,7 +8879,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 >= 4002 + ret = vboxDomainSnapshotDeleteMetadataOnly(snapshot); +#endif + } goto cleanup; } -- 1.7.10.4
participants (1)
-
Manuel VIVES