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