diff options
Diffstat (limited to 'src/VBox/Main/src-server/ApplianceImplImport.cpp')
-rw-r--r-- | src/VBox/Main/src-server/ApplianceImplImport.cpp | 1867 |
1 files changed, 1427 insertions, 440 deletions
diff --git a/src/VBox/Main/src-server/ApplianceImplImport.cpp b/src/VBox/Main/src-server/ApplianceImplImport.cpp index 447d717f..1eada736 100644 --- a/src/VBox/Main/src-server/ApplianceImplImport.cpp +++ b/src/VBox/Main/src-server/ApplianceImplImport.cpp @@ -1,11 +1,10 @@ /* $Id: ApplianceImplImport.cpp $ */ /** @file - * * IAppliance and IVirtualSystem COM class implementations. */ /* - * Copyright (C) 2008-2012 Oracle Corporation + * Copyright (C) 2008-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -47,6 +46,8 @@ #include <VBox/version.h> #include <VBox/settings.h> +#include <set> + using namespace std; //////////////////////////////////////////////////////////////////////////////// @@ -116,7 +117,8 @@ STDMETHODIMP Appliance::Read(IN_BSTR path, IProgress **aProgress) STDMETHODIMP Appliance::Interpret() { // @todo: - // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk)) + // - don't use COM methods but the methods directly (faster, but needs appropriate + // locking of that objects itself (s. HardDisk)) // - Appropriate handle errors like not supported file formats AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); @@ -160,21 +162,21 @@ STDMETHODIMP Appliance::Interpret() // if the virtual system in OVF had a <vbox:Machine> element, have the // VirtualBox settings code parse that XML now - if (vsysThis.pelmVboxMachine) - pNewDesc->importVboxMachineXML(*vsysThis.pelmVboxMachine); + if (vsysThis.pelmVBoxMachine) + pNewDesc->importVBoxMachineXML(*vsysThis.pelmVBoxMachine); // Guest OS type // This is taken from one of three places, in this order: Utf8Str strOsTypeVBox; Utf8StrFmt strCIMOSType("%RU32", (uint32_t)vsysThis.cimos); // 1) If there is a <vbox:Machine>, then use the type from there. - if ( vsysThis.pelmVboxMachine + if ( vsysThis.pelmVBoxMachine && pNewDesc->m->pConfig->machineUserData.strOsType.isNotEmpty() ) strOsTypeVBox = pNewDesc->m->pConfig->machineUserData.strOsType; // 2) Otherwise, if there is OperatingSystemSection/vbox:OSType, use that one. - else if (vsysThis.strTypeVbox.isNotEmpty()) // OVFReader has found vbox:OSType - strOsTypeVBox = vsysThis.strTypeVbox; + else if (vsysThis.strTypeVBox.isNotEmpty()) // OVFReader has found vbox:OSType + strOsTypeVBox = vsysThis.strTypeVBox; // 3) Otherwise, make a best guess what the vbox type is from the OVF (CIM) OS type. else convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos, vsysThis.strCimosDesc); @@ -186,7 +188,7 @@ STDMETHODIMP Appliance::Interpret() /* VM name */ Utf8Str nameVBox; /* If there is a <vbox:Machine>, we always prefer the setting from there. */ - if ( vsysThis.pelmVboxMachine + if ( vsysThis.pelmVBoxMachine && pNewDesc->m->pConfig->machineUserData.strName.isNotEmpty()) nameVBox = pNewDesc->m->pConfig->machineUserData.strName; else @@ -269,7 +271,7 @@ STDMETHODIMP Appliance::Interpret() /* CPU count */ ULONG cpuCountVBox; /* If there is a <vbox:Machine>, we always prefer the setting from there. */ - if ( vsysThis.pelmVboxMachine + if ( vsysThis.pelmVBoxMachine && pNewDesc->m->pConfig->hardwareMachine.cCPUs) cpuCountVBox = pNewDesc->m->pConfig->hardwareMachine.cCPUs; else @@ -277,7 +279,8 @@ STDMETHODIMP Appliance::Interpret() /* Check for the constraints */ if (cpuCountVBox > SchemaDefs::MaxCPUCount) { - addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."), + addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for " + "max %u CPU's only."), vsysThis.strName.c_str(), cpuCountVBox, SchemaDefs::MaxCPUCount); cpuCountVBox = SchemaDefs::MaxCPUCount; } @@ -291,7 +294,7 @@ STDMETHODIMP Appliance::Interpret() /* RAM */ uint64_t ullMemSizeVBox; /* If there is a <vbox:Machine>, we always prefer the setting from there. */ - if ( vsysThis.pelmVboxMachine + if ( vsysThis.pelmVBoxMachine && pNewDesc->m->pConfig->hardwareMachine.ulMemorySizeMB) ullMemSizeVBox = pNewDesc->m->pConfig->hardwareMachine.ulMemorySizeMB; else @@ -303,7 +306,8 @@ STDMETHODIMP Appliance::Interpret() ) ) { - addWarning(tr("The virtual system \"%s\" claims support for %llu MB RAM size, but VirtualBox has support for min %u & max %u MB RAM size only."), + addWarning(tr("The virtual system \"%s\" claims support for %llu MB RAM size, but VirtualBox has " + "support for min %u & max %u MB RAM size only."), vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB); ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB); } @@ -325,9 +329,12 @@ STDMETHODIMP Appliance::Interpret() Utf8Str strSoundCard; Utf8Str strSoundCardOrig; /* If there is a <vbox:Machine>, we always prefer the setting from there. */ - if ( vsysThis.pelmVboxMachine + if ( vsysThis.pelmVBoxMachine && pNewDesc->m->pConfig->hardwareMachine.audioAdapter.fEnabled) - strSoundCard = Utf8StrFmt("%RU32", (uint32_t)pNewDesc->m->pConfig->hardwareMachine.audioAdapter.controllerType); + { + strSoundCard = Utf8StrFmt("%RU32", + (uint32_t)pNewDesc->m->pConfig->hardwareMachine.audioAdapter.controllerType); + } else if (vsysThis.strSoundCardType.isNotEmpty()) { /* Set the AC97 always for the simple OVF case. @@ -344,22 +351,23 @@ STDMETHODIMP Appliance::Interpret() #ifdef VBOX_WITH_USB /* USB Controller */ /* If there is a <vbox:Machine>, we always prefer the setting from there. */ - if ( ( vsysThis.pelmVboxMachine - && pNewDesc->m->pConfig->hardwareMachine.usbController.fEnabled) + if ( ( vsysThis.pelmVBoxMachine + && pNewDesc->m->pConfig->hardwareMachine.usbSettings.llUSBControllers.size() > 0) || vsysThis.fHasUsbController) pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", ""); #endif /* VBOX_WITH_USB */ /* Network Controller */ /* If there is a <vbox:Machine>, we always prefer the setting from there. */ - if (vsysThis.pelmVboxMachine) + if (vsysThis.pelmVBoxMachine) { uint32_t maxNetworkAdapters = Global::getMaxNetworkAdapters(pNewDesc->m->pConfig->hardwareMachine.chipsetType); const settings::NetworkAdaptersList &llNetworkAdapters = pNewDesc->m->pConfig->hardwareMachine.llNetworkAdapters; /* Check for the constrains */ if (llNetworkAdapters.size() > maxNetworkAdapters) - addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."), + addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox " + "has support for max %u network adapter only."), vsysThis.strName.c_str(), llNetworkAdapters.size(), maxNetworkAdapters); /* Iterate through all network adapters. */ settings::NetworkAdaptersList::const_iterator it1; @@ -387,7 +395,8 @@ STDMETHODIMP Appliance::Interpret() /* Check for the constrains */ if (cEthernetAdapters > maxNetworkAdapters) - addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."), + addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox " + "has support for max %u network adapter only."), vsysThis.strName.c_str(), cEthernetAdapters, maxNetworkAdapters); /* Get the default network adapter type for the selected guest OS */ @@ -462,7 +471,7 @@ STDMETHODIMP Appliance::Interpret() /* If there is a <vbox:Machine>, we always prefer the setting from there. */ bool fFloppy = false; bool fDVD = false; - if (vsysThis.pelmVboxMachine) + if (vsysThis.pelmVBoxMachine) { settings::StorageControllersList &llControllers = pNewDesc->m->pConfig->storageMachine.llStorageControllers; settings::StorageControllersList::iterator it3; @@ -526,12 +535,13 @@ STDMETHODIMP Appliance::Interpret() pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE, strControllerID, // strRef hdc.strControllerType, // aOvfValue - strType); // aVboxValue + strType); // aVBoxValue } else /* Warn only once */ if (cIDEused == 2) - addWarning(tr("The virtual \"%s\" system requests support for more than two IDE controller channels, but VirtualBox supports only two."), + addWarning(tr("The virtual \"%s\" system requests support for more than two " + "IDE controller channels, but VirtualBox supports only two."), vsysThis.strName.c_str()); ++cIDEused; @@ -552,7 +562,8 @@ STDMETHODIMP Appliance::Interpret() { /* Warn only once */ if (cSATAused == 1) - addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"), + addWarning(tr("The virtual system \"%s\" requests support for more than one " + "SATA controller, but VirtualBox has support for only one"), vsysThis.strName.c_str()); } @@ -579,7 +590,9 @@ STDMETHODIMP Appliance::Interpret() hdcController); } else - addWarning(tr("The virtual system \"%s\" requests support for an additional SCSI controller of type \"%s\" with ID %s, but VirtualBox presently supports only one SCSI controller."), + addWarning(tr("The virtual system \"%s\" requests support for an additional " + "SCSI controller of type \"%s\" with ID %s, but VirtualBox presently " + "supports only one SCSI controller."), vsysThis.strName.c_str(), hdc.strControllerType.c_str(), strControllerID.c_str()); @@ -599,22 +612,96 @@ STDMETHODIMP Appliance::Interpret() { const ovf::VirtualDisk &hd = itVD->second; /* Get the associated disk image */ - const ovf::DiskImage &di = m->pReader->m_mapDisks[hd.strDiskId]; + ovf::DiskImage di; + std::map<RTCString, ovf::DiskImage>::iterator foundDisk; + + foundDisk = m->pReader->m_mapDisks.find(hd.strDiskId); + if (foundDisk == m->pReader->m_mapDisks.end()) + continue; + else + { + di = foundDisk->second; + } + + /* + * Figure out from URI which format the image of disk has. + * URI must have inside section <Disk> . + * But there aren't strong requirements about correspondence one URI for one disk virtual format. + * So possibly, we aren't able to recognize some URIs. + */ + Utf8Str vdf = typeOfVirtualDiskFormatFromURI(di.strFormat); + + /* + * fallback, if we can't determine virtual disk format using URI from the attribute ovf:format + * in the corresponding section <Disk> in the OVF file. + */ + if (vdf.isEmpty()) + { + /* Figure out from extension which format the image of disk has. */ + { + char *pszExt = RTPathExt(di.strHref.c_str()); + /* Get the system properties. */ + SystemProperties *pSysProps = mVirtualBox->getSystemProperties(); + ComObjPtr<MediumFormat> trgFormat = pSysProps->mediumFormatFromExtension(&pszExt[1]); + if (trgFormat.isNull()) + { + throw setError(E_FAIL, + tr("Internal inconsistency looking up medium format for the disk image '%s'"), + di.strHref.c_str()); + } + + Bstr bstrFormatName; + rc = trgFormat->COMGETTER(Name)(bstrFormatName.asOutParam()); + if (FAILED(rc)) + throw rc; + + vdf = Utf8Str(bstrFormatName); + } + } // @todo: // - figure out all possible vmdk formats we also support // - figure out if there is a url specifier for vhd already // - we need a url specifier for the vdi format - if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive) - || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized", Utf8Str::CaseInsensitive) - || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive) - || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive) - ) + + if (vdf.compare("VMDK", Utf8Str::CaseInsensitive) == 0) { /* If the href is empty use the VM name as filename */ Utf8Str strFilename = di.strHref; if (!strFilename.length()) - strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str()); + strFilename = Utf8StrFmt("%s.vmdk", hd.strDiskId.c_str()); + + Utf8Str strTargetPath = Utf8Str(strMachineFolder); + strTargetPath.append(RTPATH_DELIMITER).append(di.strHref); + searchUniqueDiskImageFilePath(strTargetPath); + + /* find the description for the hard disk controller + * that has the same ID as hd.idController */ + const VirtualSystemDescriptionEntry *pController; + if (!(pController = pNewDesc->findControllerFromID(hd.idController))) + throw setError(E_FAIL, + tr("Cannot find hard disk controller with OVF instance ID %RI32 " + "to which disk \"%s\" should be attached"), + hd.idController, + di.strHref.c_str()); + + /* controller to attach to, and the bus within that controller */ + Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16", + pController->ulIndex, + hd.ulAddressOnParent); + pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage, + hd.strDiskId, + di.strHref, + strTargetPath, + di.ulSuggestedSizeMB, + strExtraConfig); + } + else if (vdf.compare("RAW", Utf8Str::CaseInsensitive) == 0) + { + /* If the href is empty use the VM name as filename */ + Utf8Str strFilename = di.strHref; + if (!strFilename.length()) + strFilename = Utf8StrFmt("%s.iso", hd.strDiskId.c_str()); Utf8Str strTargetPath = Utf8Str(strMachineFolder) .append(RTPATH_DELIMITER) @@ -626,7 +713,8 @@ STDMETHODIMP Appliance::Interpret() const VirtualSystemDescriptionEntry *pController; if (!(pController = pNewDesc->findControllerFromID(hd.idController))) throw setError(E_FAIL, - tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"), + tr("Cannot find disk controller with OVF instance ID %RI32 " + "to which disk \"%s\" should be attached"), hd.idController, di.strHref.c_str()); @@ -643,7 +731,9 @@ STDMETHODIMP Appliance::Interpret() } else throw setError(VBOX_E_FILE_ERROR, - tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str())); + tr("Unsupported format for virtual disk image %s in OVF: \"%s\""), + di.strHref.c_str(), + di.strFormat.c_str()); } } @@ -678,12 +768,12 @@ STDMETHODIMP Appliance::ImportMachines(ComSafeArrayIn(ImportOptions_T, options), AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - if (options != NULL) - m->optList = com::SafeArray<ImportOptions_T>(ComSafeArrayInArg(options)).toList(); + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - AssertReturn(!(m->optList.contains(ImportOptions_KeepAllMACs) && m->optList.contains(ImportOptions_KeepNATMACs)), E_INVALIDARG); + if (options != NULL) + m->optListImport = com::SafeArray<ImportOptions_T>(ComSafeArrayInArg(options)).toList(); - AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + AssertReturn(!(m->optListImport.contains(ImportOptions_KeepAllMACs) && m->optListImport.contains(ImportOptions_KeepNATMACs)), E_INVALIDARG); // do not allow entering this method if the appliance is busy reading or writing if (!isApplianceIdle()) @@ -717,6 +807,35 @@ STDMETHODIMP Appliance::ImportMachines(ComSafeArrayIn(ImportOptions_T, options), // //////////////////////////////////////////////////////////////////////////////// +HRESULT Appliance::preCheckImageAvailability(PSHASTORAGE pSHAStorage, + RTCString &availableImage) +{ + HRESULT rc = S_OK; + RTTAR tar = (RTTAR)pSHAStorage->pVDImageIfaces->pvUser; + char *pszFilename = 0; + + int vrc = RTTarCurrentFile(tar, &pszFilename); + + if (RT_FAILURE(vrc)) + { + throw setError(VBOX_E_FILE_ERROR, + tr("Could not open the current file in the OVA package (%Rrc)"), vrc); + } + else + { + if (vrc == VINF_TAR_DIR_PATH) + { + throw setError(VBOX_E_FILE_ERROR, + tr("Empty directory folder (%s) isn't allowed in the OVA package (%Rrc)"), + pszFilename, + vrc); + } + } + + availableImage = pszFilename; + + return rc; +} /******************************************************************************* * Read stuff @@ -818,36 +937,125 @@ HRESULT Appliance::readFSOVF(TaskOVF *pTask) LogFlowFuncEnter(); HRESULT rc = S_OK; + int vrc = VINF_SUCCESS; PVDINTERFACEIO pShaIo = 0; PVDINTERFACEIO pFileIo = 0; do { - pShaIo = ShaCreateInterface(); - if (!pShaIo) - { - rc = E_OUTOFMEMORY; - break; - } - pFileIo = FileCreateInterface(); - if (!pFileIo) + try { - rc = E_OUTOFMEMORY; - break; + /* Create the necessary file access interfaces. */ + pFileIo = FileCreateInterface(); + if (!pFileIo) + { + rc = E_OUTOFMEMORY; + break; + } + + Utf8Str strMfFile = Utf8Str(pTask->locInfo.strPath).stripExt().append(".mf"); + + SHASTORAGE storage; + RT_ZERO(storage); + + if (RTFileExists(strMfFile.c_str())) + { + pShaIo = ShaCreateInterface(); + if (!pShaIo) + { + rc = E_OUTOFMEMORY; + break; + } + + //read the manifest file and find a type of used digest + RTFILE pFile = NULL; + vrc = RTFileOpen(&pFile, strMfFile.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE); + if (RT_SUCCESS(vrc) && pFile != NULL) + { + uint64_t cbFile = 0; + uint64_t maxFileSize = _1M; + size_t cbRead = 0; + void *pBuf; /** @todo r=bird: You leak this buffer! throwing stuff is evil. */ + + vrc = RTFileGetSize(pFile, &cbFile); + if (cbFile > maxFileSize) + throw setError(VBOX_E_FILE_ERROR, + tr("Size of the manifest file '%s' is bigger than 1Mb. Check it, please."), + RTPathFilename(strMfFile.c_str())); + + if (RT_SUCCESS(vrc)) + pBuf = RTMemAllocZ(cbFile); + else + throw setError(VBOX_E_FILE_ERROR, + tr("Could not get size of the manifest file '%s' "), + RTPathFilename(strMfFile.c_str())); + + vrc = RTFileRead(pFile, pBuf, cbFile, &cbRead); + + if (RT_FAILURE(vrc)) + { + if (pBuf) + RTMemFree(pBuf); + throw setError(VBOX_E_FILE_ERROR, + tr("Could not read the manifest file '%s' (%Rrc)"), + RTPathFilename(strMfFile.c_str()), vrc); + } + + RTFileClose(pFile); + + RTDIGESTTYPE digestType; + vrc = RTManifestVerifyDigestType(pBuf, cbRead, &digestType); + + if (pBuf) + RTMemFree(pBuf); + + if (RT_FAILURE(vrc)) + { + throw setError(VBOX_E_FILE_ERROR, + tr("Could not verify supported digest types in the manifest file '%s' (%Rrc)"), + RTPathFilename(strMfFile.c_str()), vrc); + } + + storage.fCreateDigest = true; + + if (digestType == RTDIGESTTYPE_SHA256) + { + storage.fSha256 = true; + } + + Utf8Str name = applianceIOName(applianceIOFile); + + vrc = VDInterfaceAdd(&pFileIo->Core, name.c_str(), + VDINTERFACETYPE_IO, 0, sizeof(VDINTERFACEIO), + &storage.pVDImageIfaces); + if (RT_FAILURE(vrc)) + throw setError(VBOX_E_IPRT_ERROR, "Creation of the VD interface failed (%Rrc)", vrc); + + rc = readFSImpl(pTask, pTask->locInfo.strPath, pShaIo, &storage); + if (FAILED(rc)) + break; + } + else + { + throw setError(VBOX_E_FILE_ERROR, + tr("Could not open the manifest file '%s' (%Rrc)"), + RTPathFilename(strMfFile.c_str()), vrc); + } + } + else + { + storage.fCreateDigest = false; + rc = readFSImpl(pTask, pTask->locInfo.strPath, pFileIo, &storage); + if (FAILED(rc)) + break; + } } - SHASTORAGE storage; - RT_ZERO(storage); - int vrc = VDInterfaceAdd(&pFileIo->Core, "Appliance::IOFile", - VDINTERFACETYPE_IO, 0, sizeof(VDINTERFACEIO), - &storage.pVDImageIfaces); - if (RT_FAILURE(vrc)) + catch (HRESULT rc2) { - rc = setError(VBOX_E_IPRT_ERROR, "Creation of the VD interface failed (%Rrc)", vrc); - break; + rc = rc2; } - rc = readFSImpl(pTask, pTask->locInfo.strPath, pShaIo, &storage); - }while(0); + }while (0); /* Cleanup */ if (pShaIo) @@ -866,51 +1074,79 @@ HRESULT Appliance::readFSOVA(TaskOVF *pTask) LogFlowFuncEnter(); RTTAR tar; - int vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, true); - if (RT_FAILURE(vrc)) - return setError(VBOX_E_FILE_ERROR, - tr("Could not open OVA file '%s' (%Rrc)"), - pTask->locInfo.strPath.c_str(), vrc); - HRESULT rc = S_OK; - + int vrc = 0; PVDINTERFACEIO pShaIo = 0; PVDINTERFACEIO pTarIo = 0; char *pszFilename = 0; - do + SHASTORAGE storage; + + RT_ZERO(storage); + + vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, true); + if (RT_FAILURE(vrc)) + rc = setError(VBOX_E_FILE_ERROR, + tr("Could not open the OVA file '%s' (%Rrc)"), + pTask->locInfo.strPath.c_str(), vrc); + else { - vrc = RTTarCurrentFile(tar, &pszFilename); - if (RT_FAILURE(vrc)) - { - rc = VBOX_E_FILE_ERROR; - break; - } - pShaIo = ShaCreateInterface(); - if (!pShaIo) - { - rc = E_OUTOFMEMORY; - break; - } - pTarIo = TarCreateInterface(); - if (!pTarIo) - { - rc = E_OUTOFMEMORY; - break; - } - SHASTORAGE storage; - RT_ZERO(storage); - vrc = VDInterfaceAdd(&pTarIo->Core, "Appliance::IOTar", - VDINTERFACETYPE_IO, tar, sizeof(VDINTERFACEIO), - &storage.pVDImageIfaces); - if (RT_FAILURE(vrc)) + do { - rc = setError(VBOX_E_IPRT_ERROR, "Creation of the VD interface failed (%Rrc)", vrc); - break; - } - rc = readFSImpl(pTask, pszFilename, pShaIo, &storage); - }while(0); + vrc = RTTarCurrentFile(tar, &pszFilename); + if (RT_FAILURE(vrc)) + { + rc = VBOX_E_FILE_ERROR; + break; + } + + Utf8Str extension(RTPathExt(pszFilename)); + + if (!extension.endsWith(".ovf",Utf8Str::CaseInsensitive)) + { + vrc = VERR_FILE_NOT_FOUND; + rc = setError(VBOX_E_FILE_ERROR, + tr("First file in the OVA package must have the extension 'ovf'. " + "But the file '%s' has the different extension (%Rrc)"), + pszFilename, + vrc); + break; + } + + pTarIo = TarCreateInterface(); + if (!pTarIo) + { + rc = E_OUTOFMEMORY; + break; + } + + pShaIo = ShaCreateInterface(); + if (!pShaIo) + { + rc = E_OUTOFMEMORY; + break ; + } + + Utf8Str name = applianceIOName(applianceIOTar); + + vrc = VDInterfaceAdd(&pTarIo->Core, name.c_str(), + VDINTERFACETYPE_IO, tar, sizeof(VDINTERFACEIO), + &storage.pVDImageIfaces); + if (RT_FAILURE(vrc)) + { + rc = setError(VBOX_E_IPRT_ERROR, "Creation of the VD interface failed (%Rrc)", vrc); + break; + } + + rc = readFSImpl(pTask, pszFilename, pShaIo, &storage); + if (FAILED(rc)) + break; + + } while (0); + + RTTarClose(tar); + } + - RTTarClose(tar); /* Cleanup */ if (pszFilename) @@ -940,15 +1176,49 @@ HRESULT Appliance::readFSImpl(TaskOVF *pTask, const RTCString &strFilename, PVDI /* Read the OVF into a memory buffer */ size_t cbSize = 0; int vrc = ShaReadBuf(strFilename.c_str(), &pvTmpBuf, &cbSize, pIfIo, pStorage); - if ( RT_FAILURE(vrc) + if (RT_FAILURE(vrc) || !pvTmpBuf) throw setError(VBOX_E_FILE_ERROR, tr("Could not read OVF file '%s' (%Rrc)"), RTPathFilename(strFilename.c_str()), vrc); - /* Copy the SHA1/SHA256 sum of the OVF file for later validation */ - m->strOVFSHADigest = pStorage->strDigest; + /* Read & parse the XML structure of the OVF file */ m->pReader = new ovf::OVFReader(pvTmpBuf, cbSize, pTask->locInfo.strPath); + + if (m->pReader->m_envelopeData.getOVFVersion() == ovf::OVFVersion_2_0) + { + m->fSha256 = true; + + uint8_t digest[RTSHA256_HASH_SIZE]; + size_t cbDigest = RTSHA256_DIGEST_LEN; + char *pszDigest; + + RTSha256(pvTmpBuf, cbSize, &digest[0]); + + vrc = RTStrAllocEx(&pszDigest, cbDigest + 1); + if (RT_SUCCESS(vrc)) + vrc = RTSha256ToString(digest, pszDigest, cbDigest + 1); + else + throw setError(VBOX_E_FILE_ERROR, + tr("Could not allocate string for SHA256 digest (%Rrc)"), vrc); + + if (RT_SUCCESS(vrc)) + /* Copy the SHA256 sum of the OVF file for later validation */ + m->strOVFSHADigest = pszDigest; + else + throw setError(VBOX_E_FILE_ERROR, + tr("Converting SHA256 digest to a string was failed (%Rrc)"), vrc); + + RTStrFree(pszDigest); + + } + else + { + m->fSha256 = false; + /* Copy the SHA1 sum of the OVF file for later validation */ + m->strOVFSHADigest = pStorage->strDigest; + } + } catch (RTCError &x) // includes all XML exceptions { @@ -1017,7 +1287,11 @@ HRESULT Appliance::readS3(TaskOVF *pTask) strTmpOvf = Utf8StrFmt("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str())); /* Next we have to download the OVF */ - vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING); + vrc = RTS3Create(&hS3, + pTask->locInfo.strUsername.c_str(), + pTask->locInfo.strPassword.c_str(), + pTask->locInfo.strHostname.c_str(), + "virtualbox-agent/"VBOX_VERSION_STRING); if (RT_FAILURE(vrc)) throw setError(VBOX_E_IPRT_ERROR, tr("Cannot create S3 service handler")); @@ -1032,7 +1306,8 @@ HRESULT Appliance::readS3(TaskOVF *pTask) throw S_OK; /* todo: !!!!!!!!!!!!! */ else if (vrc == VERR_S3_ACCESS_DENIED) throw setError(E_ACCESSDENIED, - tr("Cannot download file '%s' from S3 storage server (Access denied). Make sure that your credentials are right." + tr("Cannot download file '%s' from S3 storage server (Access denied). Make sure that " + "your credentials are right. " "Also check that your host clock is properly synced"), pszFilename); else if (vrc == VERR_S3_NOT_FOUND) @@ -1148,9 +1423,12 @@ HRESULT Appliance::importImpl(const LocationInfo &locInfo, } /** - * Actual worker code for importing OVF data into VirtualBox. This is called from Appliance::taskThreadImportOrExport() - * and therefore runs on the OVF import worker thread. This creates one or more new machines according to the - * VirtualSystemScription instances created by Appliance::Interpret(). + * Actual worker code for importing OVF data into VirtualBox. + * + * This is called from Appliance::taskThreadImportOrExport() and therefore runs + * on the OVF import worker thread. This creates one or more new machines + * according to the VirtualSystemScription instances created by + * Appliance::Interpret(). * * This runs in three contexts: * @@ -1162,8 +1440,8 @@ HRESULT Appliance::importImpl(const LocationInfo &locInfo, * 3) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which * called Appliance::importS3(), which called Appliance::importImpl(), which then called this again. * - * @param pTask - * @return + * @param pTask The OVF task data. + * @return COM status code. */ HRESULT Appliance::importFS(TaskOVF *pTask) { @@ -1199,6 +1477,7 @@ HRESULT Appliance::importFS(TaskOVF *pTask) /* With _whatever_ error we've had, do a complete roll-back of * machines and disks we've created */ writeLock.release(); + ErrorInfoKeeper eik; for (list<Guid>::iterator itID = m->llGuidsMachinesCreated.begin(); itID != m->llGuidsMachinesCreated.end(); ++itID) @@ -1212,7 +1491,7 @@ HRESULT Appliance::importFS(TaskOVF *pTask) SafeIfaceArray<IMedium> aMedia; rc2 = failedMachine->Unregister(CleanupMode_DetachAllReturnHardDisksOnly, ComSafeArrayAsOutParam(aMedia)); ComPtr<IProgress> pProgress2; - rc2 = failedMachine->Delete(ComSafeArrayAsInParam(aMedia), pProgress2.asOutParam()); + rc2 = failedMachine->DeleteConfig(ComSafeArrayAsInParam(aMedia), pProgress2.asOutParam()); pProgress2->WaitForCompletion(-1); } } @@ -1237,6 +1516,7 @@ HRESULT Appliance::importFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock) PVDINTERFACEIO pShaIo = NULL; PVDINTERFACEIO pFileIo = NULL; void *pvMfBuf = NULL; + void *pvCertBuf = NULL; writeLock.release(); try { @@ -1246,39 +1526,69 @@ HRESULT Appliance::importFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock) throw setError(E_OUTOFMEMORY); Utf8Str strMfFile = Utf8Str(pTask->locInfo.strPath).stripExt().append(".mf"); + + SHASTORAGE storage; + RT_ZERO(storage); + + Utf8Str name = applianceIOName(applianceIOFile); + + int vrc = VDInterfaceAdd(&pFileIo->Core, name.c_str(), + VDINTERFACETYPE_IO, 0, sizeof(VDINTERFACEIO), + &storage.pVDImageIfaces); + if (RT_FAILURE(vrc)) + throw setError(VBOX_E_IPRT_ERROR, "Creation of the VD interface failed (%Rrc)", vrc); + /* Create the import stack for the rollback on errors. */ ImportStack stack(pTask->locInfo, m->pReader->m_mapDisks, pTask->pProgress); if (RTFileExists(strMfFile.c_str())) { - SHASTORAGE storage; - RT_ZERO(storage); - pShaIo = ShaCreateInterface(); if (!pShaIo) throw setError(E_OUTOFMEMORY); + Utf8Str nameSha = applianceIOName(applianceIOSha); + /* Fill out interface descriptor. */ + pShaIo->Core.u32Magic = VDINTERFACE_MAGIC; + pShaIo->Core.cbSize = sizeof(VDINTERFACEIO); + pShaIo->Core.pszInterfaceName = nameSha.c_str(); + pShaIo->Core.enmInterface = VDINTERFACETYPE_IO; + pShaIo->Core.pvUser = &storage; + pShaIo->Core.pNext = NULL; + storage.fCreateDigest = true; - int vrc = VDInterfaceAdd(&pFileIo->Core, "Appliance::IOFile", - VDINTERFACETYPE_IO, 0, sizeof(VDINTERFACEIO), - &storage.pVDImageIfaces); - if (RT_FAILURE(vrc)) - throw setError(VBOX_E_IPRT_ERROR, "Creation of the VD interface failed (%Rrc)", vrc); size_t cbMfSize = 0; - storage.fCreateDigest = true; + /* Now import the appliance. */ importMachines(stack, pShaIo, &storage); /* Read & verify the manifest file. */ /* Add the ovf file to the digest list. */ stack.llSrcDisksDigest.push_front(STRPAIR(pTask->locInfo.strPath, m->strOVFSHADigest)); - rc = readManifestFile(strMfFile, &pvMfBuf, &cbMfSize, pShaIo, &storage); + rc = readFileToBuf(strMfFile, &pvMfBuf, &cbMfSize, true, pShaIo, &storage); if (FAILED(rc)) throw rc; rc = verifyManifestFile(strMfFile, stack, pvMfBuf, cbMfSize); if (FAILED(rc)) throw rc; + + size_t cbCertSize = 0; + + /* Save the SHA digest of the manifest file for the next validation */ + Utf8Str manifestShaDigest = storage.strDigest; + + Utf8Str strCertFile = Utf8Str(pTask->locInfo.strPath).stripExt().append(".cert"); + if (RTFileExists(strCertFile.c_str())) + { + rc = readFileToBuf(strCertFile, &pvCertBuf, &cbCertSize, false, pShaIo, &storage); + if (FAILED(rc)) throw rc; + + /* verify Certificate */ + } } else - importMachines(stack, pFileIo, NULL); + { + storage.fCreateDigest = false; + importMachines(stack, pFileIo, &storage); + } } catch (HRESULT rc2) { @@ -1289,6 +1599,8 @@ HRESULT Appliance::importFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock) /* Cleanup */ if (pvMfBuf) RTMemFree(pvMfBuf); + if (pvCertBuf) + RTMemFree(pvCertBuf); if (pShaIo) RTMemFree(pShaIo); if (pFileIo) @@ -1305,7 +1617,9 @@ HRESULT Appliance::importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock) LogFlowFuncEnter(); RTTAR tar; - int vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, true); + int vrc = RTTarOpen(&tar, + pTask->locInfo.strPath.c_str(), + RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, true); if (RT_FAILURE(vrc)) return setError(VBOX_E_FILE_ERROR, tr("Could not open OVA file '%s' (%Rrc)"), @@ -1317,6 +1631,9 @@ HRESULT Appliance::importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock) PVDINTERFACEIO pTarIo = 0; char *pszFilename = 0; void *pvMfBuf = 0; + void *pvCertBuf = 0; + Utf8Str OVFfilename; + writeLock.release(); try { @@ -1330,25 +1647,66 @@ HRESULT Appliance::importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock) SHASTORAGE storage; RT_ZERO(storage); - vrc = VDInterfaceAdd(&pTarIo->Core, "Appliance::IOTar", + + Utf8Str nameTar = applianceIOName(applianceIOTar); + + vrc = VDInterfaceAdd(&pTarIo->Core, nameTar.c_str(), VDINTERFACETYPE_IO, tar, sizeof(VDINTERFACEIO), &storage.pVDImageIfaces); if (RT_FAILURE(vrc)) throw setError(VBOX_E_IPRT_ERROR, tr("Creation of the VD interface failed (%Rrc)"), vrc); + Utf8Str nameSha = applianceIOName(applianceIOSha); + /* Fill out interface descriptor. */ + pShaIo->Core.u32Magic = VDINTERFACE_MAGIC; + pShaIo->Core.cbSize = sizeof(VDINTERFACEIO); + pShaIo->Core.pszInterfaceName = nameSha.c_str(); + pShaIo->Core.enmInterface = VDINTERFACETYPE_IO; + pShaIo->Core.pvUser = &storage; + pShaIo->Core.pNext = NULL; + /* Read the file name of the first file (need to be the ovf file). This * is how all internal files are named. */ vrc = RTTarCurrentFile(tar, &pszFilename); if (RT_FAILURE(vrc)) throw setError(VBOX_E_IPRT_ERROR, - tr("Getting the current file within the archive failed (%Rrc)"), vrc); + tr("Getting the OVF file within the archive failed (%Rrc)"), vrc); + else + { + if (vrc == VINF_TAR_DIR_PATH) + { + throw setError(VBOX_E_FILE_ERROR, + tr("Empty directory folder (%s) isn't allowed in the OVA package (%Rrc)"), + pszFilename, + vrc); + } + } + + /* save original OVF filename */ + OVFfilename = pszFilename; + size_t cbMfSize = 0; + size_t cbCertSize = 0; + Utf8Str strMfFile = (Utf8Str(pszFilename)).stripExt().append(".mf"); + Utf8Str strCertFile = (Utf8Str(pszFilename)).stripExt().append(".cert"); + /* Skip the OVF file, cause this was read in IAppliance::Read already. */ vrc = RTTarSeekNextFile(tar); if ( RT_FAILURE(vrc) && vrc != VERR_TAR_END_OF_FILE) throw setError(VBOX_E_IPRT_ERROR, tr("Seeking within the archive failed (%Rrc)"), vrc); + else + { + RTTarCurrentFile(tar, &pszFilename); + if (vrc == VINF_TAR_DIR_PATH) + { + throw setError(VBOX_E_FILE_ERROR, + tr("Empty directory folder (%s) isn't allowed in the OVA package (%Rrc)"), + pszFilename, + vrc); + } + } PVDINTERFACEIO pCallbacks = pShaIo; PSHASTORAGE pStorage = &storage; @@ -1357,8 +1715,6 @@ HRESULT Appliance::importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock) * is a manifest file in the stream. */ pStorage->fCreateDigest = true; - size_t cbMfSize = 0; - Utf8Str strMfFile = Utf8Str(pszFilename).stripExt().append(".mf"); /* Create the import stack for the rollback on errors. */ ImportStack stack(pTask->locInfo, m->pReader->m_mapDisks, pTask->pProgress); /* @@ -1373,23 +1729,68 @@ HRESULT Appliance::importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock) * searching for it. We have to try to open it on all possible places. * If it fails here, we will try it again after all disks where read. */ - rc = readTarManifestFile(tar, strMfFile, &pvMfBuf, &cbMfSize, pCallbacks, pStorage); + rc = readTarFileToBuf(tar, strMfFile, &pvMfBuf, &cbMfSize, true, pCallbacks, pStorage); if (FAILED(rc)) throw rc; + + /* + * Try to read the certificate file. First try. + * Logic is the same as with manifest file + * Only if the manifest file had been read successfully before + */ + vrc = RTTarCurrentFile(tar, &pszFilename); + if (RT_SUCCESS(vrc)) + { + if (pvMfBuf) + { + if (strCertFile.compare(pszFilename) == 0) + { + rc = readTarFileToBuf(tar, strCertFile, &pvCertBuf, &cbCertSize, false, pCallbacks, pStorage); + if (FAILED(rc)) throw rc; + + if (pvCertBuf) + { + /* verify the certificate */ + } + } + } + } + /* Now import the appliance. */ importMachines(stack, pCallbacks, pStorage); /* Try to read the manifest file. Second try. */ if (!pvMfBuf) { - rc = readTarManifestFile(tar, strMfFile, &pvMfBuf, &cbMfSize, pCallbacks, pStorage); - if (FAILED(rc)) throw rc; - } - /* If we were able to read a manifest file we can check it now. */ - if (pvMfBuf) - { - /* Add the ovf file to the digest list. */ - stack.llSrcDisksDigest.push_front(STRPAIR(Utf8Str(pszFilename).stripExt().append(".ovf"), m->strOVFSHADigest)); - rc = verifyManifestFile(strMfFile, stack, pvMfBuf, cbMfSize); + rc = readTarFileToBuf(tar, strMfFile, &pvMfBuf, &cbMfSize, true, pCallbacks, pStorage); if (FAILED(rc)) throw rc; + + /* If we were able to read a manifest file we can check it now. */ + if (pvMfBuf) + { + /* Add the ovf file to the digest list. */ + stack.llSrcDisksDigest.push_front(STRPAIR(OVFfilename, m->strOVFSHADigest)); + rc = verifyManifestFile(strMfFile, stack, pvMfBuf, cbMfSize); + if (FAILED(rc)) throw rc; + + /* + * Try to read the certificate file. Second try. + * Only if the manifest file had been read successfully before + */ + + vrc = RTTarCurrentFile(tar, &pszFilename); + if (RT_SUCCESS(vrc)) + { + if (strCertFile.compare(pszFilename) == 0) + { + rc = readTarFileToBuf(tar, strCertFile, &pvCertBuf, &cbCertSize, false, pCallbacks, pStorage); + if (FAILED(rc)) throw rc; + + if (pvCertBuf) + { + /* verify the certificate */ + } + } + } + } } } catch (HRESULT rc2) @@ -1409,6 +1810,8 @@ HRESULT Appliance::importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock) RTMemFree(pShaIo); if (pTarIo) RTMemFree(pTarIo); + if (pvCertBuf) + RTMemFree(pvCertBuf); LogFlowFunc(("rc=%Rhrc\n", rc)); LogFlowFuncLeave(); @@ -1481,7 +1884,11 @@ HRESULT Appliance::importS3(TaskOVF *pTask) } /* Next we have to download the disk images */ - vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING); + vrc = RTS3Create(&hS3, + pTask->locInfo.strUsername.c_str(), + pTask->locInfo.strPassword.c_str(), + pTask->locInfo.strHostname.c_str(), + "virtualbox-agent/"VBOX_VERSION_STRING); if (RT_FAILURE(vrc)) throw setError(VBOX_E_IPRT_ERROR, tr("Cannot create S3 service handler")); @@ -1506,7 +1913,8 @@ HRESULT Appliance::importS3(TaskOVF *pTask) else if (vrc == VERR_S3_ACCESS_DENIED) throw setError(E_ACCESSDENIED, tr("Cannot download file '%s' from S3 storage server (Access denied). " - "Make sure that your credentials are right. Also check that your host clock is properly synced"), + "Make sure that your credentials are right. Also check that your host clock is " + "properly synced"), pszFilename); else if (vrc == VERR_S3_NOT_FOUND) throw setError(VBOX_E_FILE_ERROR, @@ -1542,7 +1950,8 @@ HRESULT Appliance::importS3(TaskOVF *pTask) else if (vrc == VERR_S3_ACCESS_DENIED) throw setError(E_ACCESSDENIED, tr("Cannot download file '%s' from S3 storage server (Access denied)." - "Make sure that your credentials are right. Also check that your host clock is properly synced"), + "Make sure that your credentials are right. " + "Also check that your host clock is properly synced"), pszFilename); else throw setError(VBOX_E_IPRT_ERROR, @@ -1609,24 +2018,35 @@ HRESULT Appliance::importS3(TaskOVF *pTask) } #endif /* VBOX_WITH_S3 */ -HRESULT Appliance::readManifestFile(const Utf8Str &strFile, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, PSHASTORAGE pStorage) +HRESULT Appliance::readFileToBuf(const Utf8Str &strFile, + void **ppvBuf, + size_t *pcbSize, + bool fCreateDigest, + PVDINTERFACEIO pCallbacks, + PSHASTORAGE pStorage) { HRESULT rc = S_OK; - bool fOldDigest = pStorage->fCreateDigest; - pStorage->fCreateDigest = false; /* No digest for the manifest file */ + bool fOldDigest = pStorage->fCreateDigest;/* Save the old digest property */ + pStorage->fCreateDigest = fCreateDigest; int vrc = ShaReadBuf(strFile.c_str(), ppvBuf, pcbSize, pCallbacks, pStorage); if ( RT_FAILURE(vrc) && vrc != VERR_FILE_NOT_FOUND) rc = setError(VBOX_E_FILE_ERROR, - tr("Could not read manifest file '%s' (%Rrc)"), + tr("Could not read file '%s' (%Rrc)"), RTPathFilename(strFile.c_str()), vrc); pStorage->fCreateDigest = fOldDigest; /* Restore the old digest creation behavior again. */ return rc; } -HRESULT Appliance::readTarManifestFile(RTTAR tar, const Utf8Str &strFile, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, PSHASTORAGE pStorage) +HRESULT Appliance::readTarFileToBuf(RTTAR tar, + const Utf8Str &strFile, + void **ppvBuf, + size_t *pcbSize, + bool fCreateDigest, + PVDINTERFACEIO pCallbacks, + PSHASTORAGE pStorage) { HRESULT rc = S_OK; @@ -1634,9 +2054,19 @@ HRESULT Appliance::readTarManifestFile(RTTAR tar, const Utf8Str &strFile, void * int vrc = RTTarCurrentFile(tar, &pszCurFile); if (RT_SUCCESS(vrc)) { - if (!strcmp(pszCurFile, RTPathFilename(strFile.c_str()))) - rc = readManifestFile(strFile, ppvBuf, pcbSize, pCallbacks, pStorage); - RTStrFree(pszCurFile); + if (vrc == VINF_TAR_DIR_PATH) + { + rc = setError(VBOX_E_FILE_ERROR, + tr("Empty directory folder (%s) isn't allowed in the OVA package (%Rrc)"), + pszCurFile, + vrc); + } + else + { + if (!strcmp(pszCurFile, RTPathFilename(strFile.c_str()))) + rc = readFileToBuf(strFile, ppvBuf, pcbSize, fCreateDigest, pCallbacks, pStorage); + RTStrFree(pszCurFile); + } } else if (vrc != VERR_TAR_END_OF_FILE) rc = setError(VBOX_E_IPRT_ERROR, "Seeking within the archive failed (%Rrc)", vrc); @@ -1665,7 +2095,7 @@ HRESULT Appliance::verifyManifestFile(const Utf8Str &strFile, ImportStack &stack int vrc = RTManifestVerifyFilesBuf(pvBuf, cbSize, paTests, stack.llSrcDisksDigest.size(), &iFailed); if (RT_UNLIKELY(vrc == VERR_MANIFEST_DIGEST_MISMATCH)) rc = setError(VBOX_E_FILE_ERROR, - tr("The SHA1 digest of '%s' does not match the one in '%s' (%Rrc)"), + tr("The SHA digest of '%s' does not match the one in '%s' (%Rrc)"), RTPathFilename(paTests[iFailed].pszTestFile), RTPathFilename(strFile.c_str()), vrc); else if (RT_FAILURE(vrc)) rc = setError(VBOX_E_FILE_ERROR, @@ -1677,7 +2107,6 @@ HRESULT Appliance::verifyManifestFile(const Utf8Str &strFile, ImportStack &stack return rc; } - /** * Helper that converts VirtualSystem attachment values into VirtualBox attachment values. * Throws HRESULT values on errors! @@ -1694,7 +2123,10 @@ void Appliance::convertDiskAttachmentValues(const ovf::HardDiskController &hdc, int32_t &lControllerPort, int32_t &lDevice) { - Log(("Appliance::convertDiskAttachmentValues: hdc.system=%d, hdc.fPrimary=%d, ulAddressOnParent=%d\n", hdc.system, hdc.fPrimary, ulAddressOnParent)); + Log(("Appliance::convertDiskAttachmentValues: hdc.system=%d, hdc.fPrimary=%d, ulAddressOnParent=%d\n", + hdc.system, + hdc.fPrimary, + ulAddressOnParent)); switch (hdc.system) { @@ -1790,31 +2222,57 @@ void Appliance::convertDiskAttachmentValues(const ovf::HardDiskController &hdc, * This advances stack.pProgress by one operation with the disk's weight. * * @param di ovfreader.cpp structure describing the disk image from the OVF that is to be imported - * @param ulSizeMB Size of the disk image (for progress reporting) * @param strTargetPath Where to create the target image. * @param pTargetHD out: The newly created target disk. This also gets pushed on stack.llHardDisksCreated for cleanup. * @param stack */ void Appliance::importOneDiskImage(const ovf::DiskImage &di, - const Utf8Str &strTargetPath, + Utf8Str *strTargetPath, ComObjPtr<Medium> &pTargetHD, ImportStack &stack, PVDINTERFACEIO pCallbacks, PSHASTORAGE pStorage) { + SHASTORAGE finalStorage; + PSHASTORAGE pRealUsedStorage = pStorage;/* may be changed later to finalStorage */ + PVDINTERFACEIO pFileIo = NULL;/* used in GZIP case*/ ComObjPtr<Progress> pProgress; pProgress.createObject(); - HRESULT rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this), BstrFmt(tr("Creating medium '%s'"), strTargetPath.c_str()).raw(), TRUE); + HRESULT rc = pProgress->init(mVirtualBox, + static_cast<IAppliance*>(this), + BstrFmt(tr("Creating medium '%s'"), + strTargetPath->c_str()).raw(), + TRUE); if (FAILED(rc)) throw rc; /* Get the system properties. */ SystemProperties *pSysProps = mVirtualBox->getSystemProperties(); + /* + * we put strSourceOVF into the stack.llSrcDisksDigest in the end of this + * function like a key for a later validation of the SHA digests + */ + const Utf8Str &strSourceOVF = di.strHref; + + Utf8Str strSrcFilePath(stack.strSourceDir); + Utf8Str strTargetDir(*strTargetPath); + + /* Construct source file path */ + Utf8Str name = applianceIOName(applianceIOTar); + + if (RTStrNICmp(pStorage->pVDImageIfaces->pszInterfaceName, name.c_str(), name.length()) == 0) + strSrcFilePath = strSourceOVF; + else + { + strSrcFilePath.append(RTPATH_SLASH_STR); + strSrcFilePath.append(strSourceOVF); + } + /* First of all check if the path is an UUID. If so, the user like to * import the disk into an existing path. This is useful for iSCSI for * example. */ RTUUID uuid; - int vrc = RTUuidFromStr(&uuid, strTargetPath.c_str()); + int vrc = RTUuidFromStr(&uuid, strTargetPath->c_str()); if (vrc == VINF_SUCCESS) { rc = mVirtualBox->findHardDiskById(Guid(uuid), true, &pTargetHD); @@ -1822,102 +2280,248 @@ void Appliance::importOneDiskImage(const ovf::DiskImage &di, } else { - Utf8Str strTrgFormat = "VMDK"; - if (RTPathHaveExt(strTargetPath.c_str())) + bool fGzipUsed = !(di.strCompression.compare("gzip",Utf8Str::CaseInsensitive)); + /* check read file to GZIP compression */ + try { - char *pszExt = RTPathExt(strTargetPath.c_str()); - /* Figure out which format the user like to have. Default is VMDK. */ - ComObjPtr<MediumFormat> trgFormat = pSysProps->mediumFormatFromExtension(&pszExt[1]); - if (trgFormat.isNull()) - throw setError(VBOX_E_NOT_SUPPORTED, - tr("Could not find a valid medium format for the target disk '%s'"), - strTargetPath.c_str()); - /* Check the capabilities. We need create capabilities. */ + if (fGzipUsed == true) + { + /* + * Create the necessary file access interfaces. + * For the next step: + * We need to replace the previously created chain of SHA-TAR or SHA-FILE interfaces + * with simple FILE interface because we don't need SHA or TAR interfaces here anymore. + * But we mustn't delete the chain of SHA-TAR or SHA-FILE interfaces. + */ + + /* Decompress the GZIP file and save a new file in the target path */ + strTargetDir = strTargetDir.stripFilename(); + strTargetDir.append("/temp_"); + + Utf8Str strTempTargetFilename(*strTargetPath); + strTempTargetFilename = strTempTargetFilename.stripPath(); + strTempTargetFilename = strTempTargetFilename.stripExt(); + Utf8Str vdf = typeOfVirtualDiskFormatFromURI(di.strFormat); + + strTargetDir.append(strTempTargetFilename); + + vrc = decompressImageAndSave(strSrcFilePath.c_str(), strTargetDir.c_str(), pCallbacks, pStorage); + + if (RT_FAILURE(vrc)) + throw setError(VBOX_E_FILE_ERROR, + tr("Could not read the file '%s' (%Rrc)"), + RTPathFilename(strSrcFilePath.c_str()), vrc); + + /* Create the necessary file access interfaces. */ + pFileIo = FileCreateInterface(); + if (!pFileIo) + throw setError(E_OUTOFMEMORY); + + name = applianceIOName(applianceIOFile); + + vrc = VDInterfaceAdd(&pFileIo->Core, name.c_str(), + VDINTERFACETYPE_IO, NULL, sizeof(VDINTERFACEIO), + &finalStorage.pVDImageIfaces); + if (RT_FAILURE(vrc)) + throw setError(VBOX_E_IPRT_ERROR, + tr("Creation of the VD interface failed (%Rrc)"), vrc); + + /* Correct the source and the target with the actual values */ + strSrcFilePath = strTargetDir; + strTargetDir = strTargetDir.stripFilename(); + strTargetDir.append(RTPATH_SLASH_STR); + strTargetDir.append(strTempTargetFilename.c_str()); + *strTargetPath = strTargetDir.c_str(); + + pRealUsedStorage = &finalStorage; + } + + Utf8Str strTrgFormat = "VMDK"; ULONG lCabs = 0; - rc = trgFormat->COMGETTER(Capabilities)(&lCabs); - if (FAILED(rc)) throw rc; - if (!( ((lCabs & MediumFormatCapabilities_CreateFixed) == MediumFormatCapabilities_CreateFixed) - || ((lCabs & MediumFormatCapabilities_CreateDynamic) == MediumFormatCapabilities_CreateDynamic))) - throw setError(VBOX_E_NOT_SUPPORTED, - tr("Could not find a valid medium format for the target disk '%s'"), - strTargetPath.c_str()); - Bstr bstrFormatName; - rc = trgFormat->COMGETTER(Name)(bstrFormatName.asOutParam()); - if (FAILED(rc)) throw rc; - strTrgFormat = Utf8Str(bstrFormatName); - } + char *pszExt = NULL; - /* Create an IMedium object. */ - pTargetHD.createObject(); - rc = pTargetHD->init(mVirtualBox, - strTrgFormat, - strTargetPath, - Guid::Empty /* media registry: none yet */); - if (FAILED(rc)) throw rc; + if (RTPathHaveExt(strTargetPath->c_str())) + { + pszExt = RTPathExt(strTargetPath->c_str()); + /* Figure out which format the user like to have. Default is VMDK. */ + ComObjPtr<MediumFormat> trgFormat = pSysProps->mediumFormatFromExtension(&pszExt[1]); + if (trgFormat.isNull()) + throw setError(VBOX_E_NOT_SUPPORTED, + tr("Could not find a valid medium format for the target disk '%s'"), + strTargetPath->c_str()); + /* Check the capabilities. We need create capabilities. */ + lCabs = 0; + com::SafeArray <MediumFormatCapabilities_T> mediumFormatCap; + rc = trgFormat->COMGETTER(Capabilities)(ComSafeArrayAsOutParam(mediumFormatCap)); + + if (FAILED(rc)) + throw rc; + else + { + for (ULONG j = 0; j < mediumFormatCap.size(); j++) + lCabs |= mediumFormatCap[j]; + } - /* Now create an empty hard disk. */ - rc = mVirtualBox->CreateHardDisk(NULL, - Bstr(strTargetPath).raw(), - ComPtr<IMedium>(pTargetHD).asOutParam()); - if (FAILED(rc)) throw rc; - } + if (!( ((lCabs & MediumFormatCapabilities_CreateFixed) == MediumFormatCapabilities_CreateFixed) + || ((lCabs & MediumFormatCapabilities_CreateDynamic) == MediumFormatCapabilities_CreateDynamic))) + throw setError(VBOX_E_NOT_SUPPORTED, + tr("Could not find a valid medium format for the target disk '%s'"), + strTargetPath->c_str()); + Bstr bstrFormatName; + rc = trgFormat->COMGETTER(Name)(bstrFormatName.asOutParam()); + if (FAILED(rc)) throw rc; + strTrgFormat = Utf8Str(bstrFormatName); + } + else + { + throw setError(VBOX_E_FILE_ERROR, + tr("The target disk '%s' has no extension "), + strTargetPath->c_str(), VERR_INVALID_NAME); + } - const Utf8Str &strSourceOVF = di.strHref; - /* Construct source file path */ - Utf8StrFmt strSrcFilePath("%s%c%s", stack.strSourceDir.c_str(), RTPATH_DELIMITER, strSourceOVF.c_str()); + /* Create an IMedium object. */ + pTargetHD.createObject(); - /* If strHref is empty we have to create a new file. */ - if (strSourceOVF.isEmpty()) - { - /* Create a dynamic growing disk image with the given capacity. */ - rc = pTargetHD->CreateBaseStorage(di.iCapacity / _1M, MediumVariant_Standard, ComPtr<IProgress>(pProgress).asOutParam()); - if (FAILED(rc)) throw rc; + /*CD/DVD case*/ + if (strTrgFormat.compare("RAW", Utf8Str::CaseInsensitive) == 0) + { + try + { + if (fGzipUsed == true) + { + /* + * The source and target pathes are the same. + * It means that we have the needed file already. + * For example, in GZIP case, we decompress the file and save it in the target path, + * but with some prefix like "temp_". See part "check read file to GZIP compression" earlier + * in this function. + * Just rename the file by deleting "temp_" from it's name + */ + vrc = RTFileRename(strSrcFilePath.c_str(), strTargetPath->c_str(), RTPATHRENAME_FLAGS_NO_REPLACE); + if (RT_FAILURE(vrc)) + throw setError(VBOX_E_FILE_ERROR, + tr("Could not rename the file '%s' (%Rrc)"), + RTPathFilename(strSourceOVF.c_str()), vrc); + } + else + { + /* Calculating SHA digest for ISO file while copying one */ + vrc = copyFileAndCalcShaDigest(strSrcFilePath.c_str(), + strTargetPath->c_str(), + pCallbacks, + pRealUsedStorage); + + if (RT_FAILURE(vrc)) + throw setError(VBOX_E_FILE_ERROR, + tr("Could not copy ISO file '%s' listed in the OVF file (%Rrc)"), + RTPathFilename(strSourceOVF.c_str()), vrc); + } + } + catch (HRESULT /*arc*/) + { + throw; + } - /* Advance to the next operation. */ - stack.pProgress->SetNextOperation(BstrFmt(tr("Creating disk image '%s'"), strTargetPath.c_str()).raw(), - di.ulSuggestedSizeMB); // operation's weight, as set up with the IProgress originally - } - else - { - /* We need a proper source format description */ - ComObjPtr<MediumFormat> srcFormat; - /* Which format to use? */ - Utf8Str strSrcFormat = "VDI"; - if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive) - || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized", Utf8Str::CaseInsensitive) - || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive) - || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive) - ) - strSrcFormat = "VMDK"; - srcFormat = pSysProps->mediumFormat(strSrcFormat); - if (srcFormat.isNull()) - throw setError(VBOX_E_NOT_SUPPORTED, - tr("Could not find a valid medium format for the source disk '%s'"), - RTPathFilename(strSrcFilePath.c_str())); - - /* Clone the source disk image */ - ComObjPtr<Medium> nullParent; - rc = pTargetHD->importFile(strSrcFilePath.c_str(), - srcFormat, - MediumVariant_Standard, - pCallbacks, pStorage, - nullParent, - pProgress); - if (FAILED(rc)) throw rc; + /* Advance to the next operation. */ + /* operation's weight, as set up with the IProgress originally */ + stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), + RTPathFilename(strSourceOVF.c_str())).raw(), + di.ulSuggestedSizeMB); + } + else/* HDD case*/ + { + rc = pTargetHD->init(mVirtualBox, + strTrgFormat, + *strTargetPath, + Guid::Empty /* media registry: none yet */); + if (FAILED(rc)) throw rc; - /* Advance to the next operation. */ - stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), RTPathFilename(strSrcFilePath.c_str())).raw(), - di.ulSuggestedSizeMB); // operation's weight, as set up with the IProgress originally); + /* Now create an empty hard disk. */ + rc = mVirtualBox->CreateHardDisk(Bstr(strTrgFormat).raw(), + Bstr(*strTargetPath).raw(), + ComPtr<IMedium>(pTargetHD).asOutParam()); + if (FAILED(rc)) throw rc; + + /* If strHref is empty we have to create a new file. */ + if (strSourceOVF.isEmpty()) + { + com::SafeArray<MediumVariant_T> mediumVariant; + mediumVariant.push_back(MediumVariant_Standard); + /* Create a dynamic growing disk image with the given capacity. */ + rc = pTargetHD->CreateBaseStorage(di.iCapacity / _1M, + ComSafeArrayAsInParam(mediumVariant), + ComPtr<IProgress>(pProgress).asOutParam()); + if (FAILED(rc)) throw rc; + + /* Advance to the next operation. */ + /* operation's weight, as set up with the IProgress originally */ + stack.pProgress->SetNextOperation(BstrFmt(tr("Creating disk image '%s'"), + strTargetPath->c_str()).raw(), + di.ulSuggestedSizeMB); + } + else + { + /* We need a proper source format description */ + /* Which format to use? */ + Utf8Str strSrcFormat = typeOfVirtualDiskFormatFromURI(di.strFormat); + + ComObjPtr<MediumFormat> srcFormat = pSysProps->mediumFormat(strSrcFormat); + if (srcFormat.isNull()) + throw setError(VBOX_E_NOT_SUPPORTED, + tr("Could not find a valid medium format for the source disk '%s' " + "Check correctness of the image format URL in the OVF description file."), + RTPathFilename(strSourceOVF.c_str())); + + /* Clone the source disk image */ + ComObjPtr<Medium> nullParent; + rc = pTargetHD->importFile(strSrcFilePath.c_str(), + srcFormat, + MediumVariant_Standard, + pCallbacks, pRealUsedStorage, + nullParent, + pProgress); + if (FAILED(rc)) throw rc; + + /* Advance to the next operation. */ + /* operation's weight, as set up with the IProgress originally */ + stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), + RTPathFilename(strSourceOVF.c_str())).raw(), + di.ulSuggestedSizeMB); + } + + /* Now wait for the background disk operation to complete; this throws + * HRESULTs on error. */ + ComPtr<IProgress> pp(pProgress); + waitForAsyncProgress(stack.pProgress, pp); + + if (fGzipUsed == true) + { + /* + * Just delete the temporary file + */ + vrc = RTFileDelete(strSrcFilePath.c_str()); + if (RT_FAILURE(vrc)) + setWarning(VBOX_E_FILE_ERROR, + tr("Could not delete the file '%s' (%Rrc)"), + RTPathFilename(strSrcFilePath.c_str()), vrc); + } + } + } + catch (...) + { + if (pFileIo) + RTMemFree(pFileIo); + + throw; + } } - /* Now wait for the background disk operation to complete; this throws - * HRESULTs on error. */ - ComPtr<IProgress> pp(pProgress); - waitForAsyncProgress(stack.pProgress, pp); + if (pFileIo) + RTMemFree(pFileIo); /* Add the newly create disk path + a corresponding digest the our list for * later manifest verification. */ - stack.llSrcDisksDigest.push_back(STRPAIR(strSrcFilePath, pStorage ? pStorage->strDigest : "")); + stack.llSrcDisksDigest.push_back(STRPAIR(strSourceOVF, pStorage ? pStorage->strDigest : "")); } /** @@ -1940,6 +2544,7 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, PVDINTERFACEIO pCallbacks, PSHASTORAGE pStorage) { + LogFlowFuncEnter(); HRESULT rc; // Get the instance of IGuestOSType which matches our string guest OS type so we @@ -2027,11 +2632,12 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, #ifdef VBOX_WITH_USB /* USB Controller */ - ComPtr<IUSBController> usbController; - rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam()); - if (FAILED(rc)) throw rc; - rc = usbController->COMSETTER(Enabled)(stack.fUSBEnabled); - if (FAILED(rc)) throw rc; + if (stack.fUSBEnabled) + { + ComPtr<IUSBController> usbController; + rc = pNewMachine->AddUSBController(Bstr("OHCI").raw(), USBControllerType_OHCI, usbController.asOutParam()); + if (FAILED(rc)) throw rc; + } #endif /* VBOX_WITH_USB */ /* Change the network adapters */ @@ -2049,7 +2655,8 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, } else if (vsdeNW.size() > maxNetworkAdapters) throw setError(VBOX_E_FILE_ERROR, - tr("Too many network adapters: OVF requests %d network adapters, but VirtualBox only supports %d"), + tr("Too many network adapters: OVF requests %d network adapters, " + "but VirtualBox only supports %d"), vsdeNW.size(), maxNetworkAdapters); else { @@ -2061,7 +2668,7 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, { const VirtualSystemDescriptionEntry* pvsys = *nwIt; - const Utf8Str &nwTypeVBox = pvsys->strVboxCurrent; + const Utf8Str &nwTypeVBox = pvsys->strVBoxCurrent; uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str()); ComPtr<INetworkAdapter> pNetworkAdapter; rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam()); @@ -2099,7 +2706,7 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam()); if (FAILED(rc)) throw rc; /* Set the interface name to attach to */ - pNetworkAdapter->COMSETTER(BridgedInterface)(name.raw()); + rc = pNetworkAdapter->COMSETTER(BridgedInterface)(name.raw()); if (FAILED(rc)) throw rc; break; } @@ -2132,7 +2739,7 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam()); if (FAILED(rc)) throw rc; /* Set the interface name to attach to */ - pNetworkAdapter->COMSETTER(HostOnlyInterface)(name.raw()); + rc = pNetworkAdapter->COMSETTER(HostOnlyInterface)(name.raw()); if (FAILED(rc)) throw rc; break; } @@ -2152,13 +2759,37 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, rc = pNetworkAdapter->COMSETTER(AttachmentType)(NetworkAttachmentType_Generic); if (FAILED(rc)) throw rc; } + /* Next test for NAT network interfaces */ + else if (pvsys->strExtraConfigCurrent.endsWith("type=NATNetwork", Utf8Str::CaseInsensitive)) + { + /* Attach to the right interface */ + rc = pNetworkAdapter->COMSETTER(AttachmentType)(NetworkAttachmentType_NATNetwork); + if (FAILED(rc)) throw rc; + com::SafeIfaceArray<INATNetwork> nwNATNetworks; + rc = mVirtualBox->COMGETTER(NATNetworks)(ComSafeArrayAsOutParam(nwNATNetworks)); + if (FAILED(rc)) throw rc; + // Pick the first NAT network (if there is any) + if (nwNATNetworks.size()) + { + Bstr name; + rc = nwNATNetworks[0]->COMGETTER(NetworkName)(name.asOutParam()); + if (FAILED(rc)) throw rc; + /* Set the NAT network name to attach to */ + rc = pNetworkAdapter->COMSETTER(NATNetwork)(name.raw()); + if (FAILED(rc)) throw rc; + break; + } + } } } // IDE Hard disk controller std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE); - // In OVF (at least VMware's version of it), an IDE controller has two ports, so VirtualBox's single IDE controller - // with two channels and two ports each counts as two OVF IDE controllers -- so we accept one or two such IDE controllers + /* + * In OVF (at least VMware's version of it), an IDE controller has two ports, + * so VirtualBox's single IDE controller with two channels and two ports each counts as + * two OVF IDE controllers -- so we accept one or two such IDE controllers + */ size_t cIDEControllers = vsdeHDCIDE.size(); if (cIDEControllers > 2) throw setError(VBOX_E_FILE_ERROR, @@ -2170,7 +2801,7 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, rc = pNewMachine->AddStorageController(Bstr("IDE Controller").raw(), StorageBus_IDE, pController.asOutParam()); if (FAILED(rc)) throw rc; - const char *pcszIDEType = vsdeHDCIDE.front()->strVboxCurrent.c_str(); + const char *pcszIDEType = vsdeHDCIDE.front()->strVBoxCurrent.c_str(); if (!strcmp(pcszIDEType, "PIIX3")) rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3); else if (!strcmp(pcszIDEType, "PIIX4")) @@ -2192,10 +2823,12 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, if (vsdeHDCSATA.size() > 0) { ComPtr<IStorageController> pController; - const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVboxCurrent; + const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVBoxCurrent; if (hdcVBox == "AHCI") { - rc = pNewMachine->AddStorageController(Bstr("SATA Controller").raw(), StorageBus_SATA, pController.asOutParam()); + rc = pNewMachine->AddStorageController(Bstr("SATA Controller").raw(), + StorageBus_SATA, + pController.asOutParam()); if (FAILED(rc)) throw rc; } else @@ -2215,7 +2848,7 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, Bstr bstrName(L"SCSI Controller"); StorageBus_T busType = StorageBus_SCSI; StorageControllerType_T controllerType; - const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVboxCurrent; + const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVBoxCurrent; if (hdcVBox == "LsiLogic") controllerType = StorageControllerType_LsiLogic; else if (hdcVBox == "LsiLogicSas") @@ -2246,7 +2879,9 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, if (vsdeHDCSAS.size() > 0) { ComPtr<IStorageController> pController; - rc = pNewMachine->AddStorageController(Bstr(L"SAS Controller").raw(), StorageBus_SAS, pController.asOutParam()); + rc = pNewMachine->AddStorageController(Bstr(L"SAS Controller").raw(), + StorageBus_SAS, + pController.asOutParam()); if (FAILED(rc)) throw rc; rc = pController->COMSETTER(ControllerType)(StorageControllerType_LsiLogicSas); if (FAILED(rc)) throw rc; @@ -2291,7 +2926,9 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, if (vsdeFloppy.size() == 1) { ComPtr<IStorageController> pController; - rc = sMachine->AddStorageController(Bstr("Floppy Controller").raw(), StorageBus_Floppy, pController.asOutParam()); + rc = sMachine->AddStorageController(Bstr("Floppy Controller").raw(), + StorageBus_Floppy, + pController.asOutParam()); if (FAILED(rc)) throw rc; Bstr bstrName; @@ -2317,54 +2954,6 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, stack.llHardDiskAttachments.push_back(mhda); } - // CD-ROMs next - for (std::list<VirtualSystemDescriptionEntry*>::const_iterator jt = vsdeCDROM.begin(); - jt != vsdeCDROM.end(); - ++jt) - { - // for now always attach to secondary master on IDE controller; - // there seems to be no useful information in OVF where else to - // attach it (@todo test with latest versions of OVF software) - - // find the IDE controller - const ovf::HardDiskController *pController = NULL; - for (ovf::ControllersMap::const_iterator kt = vsysThis.mapControllers.begin(); - kt != vsysThis.mapControllers.end(); - ++kt) - { - if (kt->second.system == ovf::HardDiskController::IDE) - { - pController = &kt->second; - break; - } - } - - if (!pController) - throw setError(VBOX_E_FILE_ERROR, - tr("OVF wants a CD-ROM drive but cannot find IDE controller, which is required in this version of VirtualBox")); - - // this is for rollback later - MyHardDiskAttachment mhda; - mhda.pMachine = pNewMachine; - - convertDiskAttachmentValues(*pController, - 2, // interpreted as secondary master - mhda.controllerType, // Bstr - mhda.lControllerPort, - mhda.lDevice); - - Log(("Attaching CD-ROM to port %d on device %d\n", mhda.lControllerPort, mhda.lDevice)); - - rc = sMachine->AttachDevice(mhda.controllerType.raw(), - mhda.lControllerPort, - mhda.lDevice, - DeviceType_DVD, - NULL); - if (FAILED(rc)) throw rc; - - stack.llHardDiskAttachments.push_back(mhda); - } // end for (itHD = avsdeHDs.begin(); - rc = sMachine->SaveSettings(); if (FAILED(rc)) throw rc; @@ -2373,12 +2962,17 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, if (FAILED(rc)) throw rc; stack.fSessionOpen = false; } - catch(HRESULT /* aRC */) + catch(HRESULT aRC) { + com::ErrorInfo info; + if (stack.fSessionOpen) stack.pSession->UnlockMachine(); - throw; + if (info.isFullAvailable()) + throw setError(aRC, Utf8Str(info.getText()).c_str()); + else + throw setError(aRC, "Unknown error during OVF import"); } } @@ -2390,37 +2984,190 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, // we need another try/catch block. try { +#ifdef LOG_ENABLED + if (LogIsEnabled()) + { + size_t i = 0; + for (list<VirtualSystemDescriptionEntry*>::const_iterator itHD = avsdeHDs.begin(); itHD != avsdeHDs.end(); ++itHD, i++) + Log(("avsdeHDs[%zu]: strRef=%s\n", i, (*itHD)->strRef.c_str())); + } +#endif + // to attach things we need to open a session for the new machine rc = pNewMachine->LockMachine(stack.pSession, LockType_Write); if (FAILED(rc)) throw rc; stack.fSessionOpen = true; - /* Iterate over all given disk images */ - list<VirtualSystemDescriptionEntry*>::const_iterator itHD; - for (itHD = avsdeHDs.begin(); - itHD != avsdeHDs.end(); - ++itHD) + /* get VM name from virtual system description. Only one record is possible (size of list is equal 1). */ + std::list<VirtualSystemDescriptionEntry*> vmName = vsdescThis->findByType(VirtualSystemDescriptionType_Name); + std::list<VirtualSystemDescriptionEntry*>::iterator vmNameIt = vmName.begin(); + VirtualSystemDescriptionEntry* vmNameEntry = *vmNameIt; + + ovf::DiskImagesMap::const_iterator oit = stack.mapDisks.begin(); + std::set<RTCString> disksResolvedNames; + + uint32_t cImportedDisks = 0; + + while(oit != stack.mapDisks.end() && cImportedDisks != avsdeHDs.size()) { - VirtualSystemDescriptionEntry *vsdeHD = *itHD; + ovf::DiskImage diCurrent = oit->second; + ovf::VirtualDisksMap::const_iterator itVDisk = vsysThis.mapVirtualDisks.begin(); + + VirtualSystemDescriptionEntry *vsdeTargetHD = 0; + Log(("diCurrent.strDiskId=%s\n", diCurrent.strDiskId.c_str())); + + /* + * + * Iterate over all given disk images of the virtual system + * disks description. We need to find the target disk path, + * which could be changed by the user. + * + */ + { + list<VirtualSystemDescriptionEntry*>::const_iterator itHD; + for (itHD = avsdeHDs.begin(); + itHD != avsdeHDs.end(); + ++itHD) + { + VirtualSystemDescriptionEntry *vsdeHD = *itHD; + if (vsdeHD->strRef == diCurrent.strDiskId) + { + vsdeTargetHD = vsdeHD; + break; + } + } + if (!vsdeTargetHD) + { + /* possible case if a disk image belongs to other virtual system (OVF package with multiple VMs inside) */ + LogWarning(("OVA/OVF import: Disk image %s was missed during import of VM %s\n", + oit->first.c_str(), vmNameEntry->strOvf.c_str())); + ++oit; + continue; + } + + //diCurrent.strDiskId contains the disk identifier (e.g. "vmdisk1"), which should exist + //in the virtual system's disks map under that ID and also in the global images map + itVDisk = vsysThis.mapVirtualDisks.find(diCurrent.strDiskId); + if (itVDisk == vsysThis.mapVirtualDisks.end()) + throw setError(E_FAIL, + tr("Internal inconsistency looking up disk image '%s'"), + diCurrent.strHref.c_str()); + } + + /* + * preliminary check availability of the image + * This step is useful if image is placed in the OVA (TAR) package + */ + + Utf8Str name = applianceIOName(applianceIOTar); + + if (strncmp(pStorage->pVDImageIfaces->pszInterfaceName, name.c_str(), name.length()) == 0) + { + /* It means that we possibly have imported the storage earlier on the previous loop steps*/ + std::set<RTCString>::const_iterator h = disksResolvedNames.find(diCurrent.strHref); + if (h != disksResolvedNames.end()) + { + /* Yes, disk name was found, we can skip it*/ + ++oit; + continue; + } + + RTCString availableImage(diCurrent.strHref); + + rc = preCheckImageAvailability(pStorage, + availableImage + ); + + if (SUCCEEDED(rc)) + { + /* current opened file isn't the same as passed one */ + if(availableImage.compare(diCurrent.strHref, Utf8Str::CaseInsensitive) != 0) + { + /* + * availableImage contains the disk file reference (e.g. "disk1.vmdk"), which should exist + * in the global images map. + * And find the disk from the OVF's disk list + * + */ + { + ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.begin(); + while (++itDiskImage != stack.mapDisks.end()) + { + if (itDiskImage->second.strHref.compare(availableImage, Utf8Str::CaseInsensitive) == 0) + break; + } + if (itDiskImage == stack.mapDisks.end()) + throw setError(E_FAIL, + tr("Internal inconsistency looking up disk image '%s'. " + "Check compliance OVA package structure and file names " + "references in the section <References> in the OVF file"), + availableImage.c_str()); + + /* replace with a new found disk image */ + diCurrent = *(&itDiskImage->second); + } + + /* + * Again iterate over all given disk images of the virtual system + * disks description using the found disk image + */ + { + list<VirtualSystemDescriptionEntry*>::const_iterator itHD; + for (itHD = avsdeHDs.begin(); + itHD != avsdeHDs.end(); + ++itHD) + { + VirtualSystemDescriptionEntry *vsdeHD = *itHD; + if (vsdeHD->strRef == diCurrent.strDiskId) + { + vsdeTargetHD = vsdeHD; + break; + } + } + if (!vsdeTargetHD) + /* + * in this case it's an error because something wrong with OVF description file. + * May be VBox imports OVA package with wrong file sequence inside the archive. + */ + throw setError(E_FAIL, + tr("Internal inconsistency looking up disk image '%s'"), + diCurrent.strHref.c_str()); + + itVDisk = vsysThis.mapVirtualDisks.find(diCurrent.strDiskId); + if (itVDisk == vsysThis.mapVirtualDisks.end()) + throw setError(E_FAIL, + tr("Internal inconsistency looking up disk image '%s'"), + diCurrent.strHref.c_str()); + } + } + else + { + ++oit; + } + } + else + { + ++oit; + continue; + } + } + else + { + /* just continue with normal files*/ + ++oit; + } - // vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist - // in the virtual system's disks map under that ID and also in the global images map - ovf::VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef); - // and find the disk from the OVF's disk list - ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.find(vsdeHD->strRef); - if ( (itVirtualDisk == vsysThis.mapVirtualDisks.end()) - || (itDiskImage == stack.mapDisks.end()) - ) - throw setError(E_FAIL, - tr("Internal inconsistency looking up disk image '%s'"), - vsdeHD->strRef.c_str()); - - const ovf::DiskImage &ovfDiskImage = itDiskImage->second; - const ovf::VirtualDisk &ovfVdisk = itVirtualDisk->second; + const ovf::VirtualDisk &ovfVdisk = itVDisk->second; + + /* very important to store disk name for the next checks */ + disksResolvedNames.insert(diCurrent.strHref); ComObjPtr<Medium> pTargetHD; - importOneDiskImage(ovfDiskImage, - vsdeHD->strVboxCurrent, + + Utf8Str savedVBoxCurrent = vsdeTargetHD->strVBoxCurrent; + + importOneDiskImage(diCurrent, + &vsdeTargetHD->strVBoxCurrent, pTargetHD, stack, pCallbacks, @@ -2429,7 +3176,8 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, // now use the new uuid to attach the disk image to our new machine ComPtr<IMachine> sMachine; rc = stack.pSession->COMGETTER(Machine)(sMachine.asOutParam()); - if (FAILED(rc)) throw rc; + if (FAILED(rc)) + throw rc; // find the hard disk controller to which we should attach ovf::HardDiskController hdc = (*vsysThis.mapControllers.find(ovfVdisk.idController)).second; @@ -2444,34 +3192,88 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, mhda.lControllerPort, mhda.lDevice); - Log(("Attaching disk %s to port %d on device %d\n", vsdeHD->strVboxCurrent.c_str(), mhda.lControllerPort, mhda.lDevice)); + Log(("Attaching disk %s to port %d on device %d\n", + vsdeTargetHD->strVBoxCurrent.c_str(), mhda.lControllerPort, mhda.lDevice)); - rc = sMachine->AttachDevice(mhda.controllerType.raw(), // wstring name - mhda.lControllerPort, // long controllerPort - mhda.lDevice, // long device - DeviceType_HardDisk, // DeviceType_T type - pTargetHD); - if (FAILED(rc)) throw rc; + Utf8Str vdf = typeOfVirtualDiskFormatFromURI(diCurrent.strFormat); + + if (vdf.compare("RAW", Utf8Str::CaseInsensitive) == 0) + { + ComPtr<IMedium> dvdImage(pTargetHD); + + rc = mVirtualBox->OpenMedium(Bstr(vsdeTargetHD->strVBoxCurrent).raw(), + DeviceType_DVD, + AccessMode_ReadWrite, + false, + dvdImage.asOutParam()); + + if (FAILED(rc)) + throw rc; + + rc = sMachine->AttachDevice(mhda.controllerType.raw(),// wstring name + mhda.lControllerPort, // long controllerPort + mhda.lDevice, // long device + DeviceType_DVD, // DeviceType_T type + dvdImage); + if (FAILED(rc)) + throw rc; + } + else + { + rc = sMachine->AttachDevice(mhda.controllerType.raw(),// wstring name + mhda.lControllerPort, // long controllerPort + mhda.lDevice, // long device + DeviceType_HardDisk, // DeviceType_T type + pTargetHD); + + if (FAILED(rc)) + throw rc; + } stack.llHardDiskAttachments.push_back(mhda); rc = sMachine->SaveSettings(); - if (FAILED(rc)) throw rc; - } // end for (itHD = avsdeHDs.begin(); + if (FAILED(rc)) + throw rc; + + /* restore */ + vsdeTargetHD->strVBoxCurrent = savedVBoxCurrent; + + ++cImportedDisks; + + } // end while(oit != stack.mapDisks.end()) + + /* + * quantity of the imported disks isn't equal to the size of the avsdeHDs list. + */ + if(cImportedDisks < avsdeHDs.size()) + { + LogWarning(("Not all disk images were imported for VM %s. Check OVF description file.", + vmNameEntry->strOvf.c_str())); + } // only now that we're done with all disks, close the session rc = stack.pSession->UnlockMachine(); - if (FAILED(rc)) throw rc; + + if (FAILED(rc)) + throw rc; + stack.fSessionOpen = false; } - catch(HRESULT /* aRC */) + catch(HRESULT aRC) { + com::ErrorInfo info; + if (stack.fSessionOpen) stack.pSession->UnlockMachine(); - throw; + if (info.isFullAvailable()) + throw setError(aRC, Utf8Str(info.getText()).c_str()); + else + throw setError(aRC, "Unknown error during OVF import"); } } + LogFlowFuncLeave(); } /** @@ -2509,6 +3311,7 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi PVDINTERFACEIO pCallbacks, PSHASTORAGE pStorage) { + LogFlowFuncEnter(); Assert(vsdescThis->m->pConfig); HRESULT rc = S_OK; @@ -2516,10 +3319,8 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi settings::MachineConfigFile &config = *vsdescThis->m->pConfig; /* - * * step 1): modify machine config according to OVF config, in case the user * has modified them using setFinalValues() - * */ /* OS Type */ @@ -2544,7 +3345,34 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi #ifdef VBOX_WITH_USB /* USB controller */ - config.hardwareMachine.usbController.fEnabled = stack.fUSBEnabled; + if (stack.fUSBEnabled) + { + /** @todo r=klaus add support for arbitrary USB controller types, this can't handle multiple controllers due to its design anyway */ + + /* usually the OHCI controller is enabled already, need to check */ + bool fOHCIEnabled = false; + settings::USBControllerList &llUSBControllers = config.hardwareMachine.usbSettings.llUSBControllers; + settings::USBControllerList::iterator it; + for (it = llUSBControllers.begin(); it != llUSBControllers.end(); ++it) + { + if (it->enmType == USBControllerType_OHCI) + { + fOHCIEnabled = true; + break; + } + } + + if (!fOHCIEnabled) + { + settings::USBController ctrl; + ctrl.strName = "OHCI"; + ctrl.enmType = USBControllerType_OHCI; + + llUSBControllers.push_back(ctrl); + } + } + else + config.hardwareMachine.usbSettings.llUSBControllers.clear(); #endif /* Audio adapter */ if (stack.strAudioAdapter.isNotEmpty()) @@ -2558,13 +3386,14 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi settings::NetworkAdaptersList &llNetworkAdapters = config.hardwareMachine.llNetworkAdapters; /* First disable all network cards, they will be enabled below again. */ settings::NetworkAdaptersList::iterator it1; - bool fKeepAllMACs = m->optList.contains(ImportOptions_KeepAllMACs); - bool fKeepNATMACs = m->optList.contains(ImportOptions_KeepNATMACs); + bool fKeepAllMACs = m->optListImport.contains(ImportOptions_KeepAllMACs); + bool fKeepNATMACs = m->optListImport.contains(ImportOptions_KeepNATMACs); for (it1 = llNetworkAdapters.begin(); it1 != llNetworkAdapters.end(); ++it1) { it1->fEnabled = false; if (!( fKeepAllMACs - || (fKeepNATMACs && it1->mode == NetworkAttachmentType_NAT))) + || (fKeepNATMACs && it1->mode == NetworkAttachmentType_NAT) + || (fKeepNATMACs && it1->mode == NetworkAttachmentType_NATNetwork))) Host::generateMACAddress(it1->strMACAddress); } /* Now iterate over all network entries. */ @@ -2593,7 +3422,7 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi if (it1->ulSlot == iSlot) { it1->fEnabled = true; - it1->type = (NetworkAdapterType_T)vsdeNW->strVboxCurrent.toUInt32(); + it1->type = (NetworkAdapterType_T)vsdeNW->strVBoxCurrent.toUInt32(); break; } } @@ -2612,7 +3441,7 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi * failures. A long fixed bug, however the OVF files are long lived. */ settings::StorageControllersList &llControllers = config.storageMachine.llStorageControllers; Guid hdUuid; - uint32_t cHardDisks = 0; + uint32_t cDisks = 0; bool fInconsistent = false; bool fRepairDuplicate = false; settings::StorageControllersList::iterator it3; @@ -2636,17 +3465,17 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi else if (it4->deviceType == DeviceType_HardDisk) { const Guid &thisUuid = it4->uuid; - cHardDisks++; - if (cHardDisks == 1) + cDisks++; + if (cDisks == 1) { - if (hdUuid.isEmpty()) + if (hdUuid.isZero()) hdUuid = thisUuid; else fInconsistent = true; } else { - if (thisUuid.isEmpty()) + if (thisUuid.isZero()) fInconsistent = true; else if (thisUuid == hdUuid) fRepairDuplicate = true; @@ -2656,141 +3485,290 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi } } /* paranoia... */ - if (fInconsistent || cHardDisks == 1) + if (fInconsistent || cDisks == 1) fRepairDuplicate = false; /* - * * step 2: scan the machine config for media attachments - * */ + /* get VM name from virtual system description. Only one record is possible (size of list is equal 1). */ + std::list<VirtualSystemDescriptionEntry*> vmName = vsdescThis->findByType(VirtualSystemDescriptionType_Name); + std::list<VirtualSystemDescriptionEntry*>::iterator vmNameIt = vmName.begin(); + VirtualSystemDescriptionEntry* vmNameEntry = *vmNameIt; + /* Get all hard disk descriptions. */ std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage); std::list<VirtualSystemDescriptionEntry*>::iterator avsdeHDsIt = avsdeHDs.begin(); /* paranoia - if there is no 1:1 match do not try to repair. */ - if (cHardDisks != avsdeHDs.size()) + if (cDisks != avsdeHDs.size()) fRepairDuplicate = false; - // for each storage controller... - for (settings::StorageControllersList::iterator sit = config.storageMachine.llStorageControllers.begin(); - sit != config.storageMachine.llStorageControllers.end(); - ++sit) - { - settings::StorageController &sc = *sit; + // there must be an image in the OVF disk structs with the same UUID - // find the OVF virtual system description entry for this storage controller - switch (sc.storageBus) - { - case StorageBus_SATA: - break; - case StorageBus_SCSI: - break; - case StorageBus_IDE: - break; - case StorageBus_SAS: - break; - } + ovf::DiskImagesMap::const_iterator oit = stack.mapDisks.begin(); + std::set<RTCString> disksResolvedNames; - // for each medium attachment to this controller... - for (settings::AttachedDevicesList::iterator dit = sc.llAttachedDevices.begin(); - dit != sc.llAttachedDevices.end(); - ++dit) - { - settings::AttachedDevice &d = *dit; + uint32_t cImportedDisks = 0; - if (d.uuid.isEmpty()) - // empty DVD and floppy media - continue; + while(oit != stack.mapDisks.end() && cImportedDisks != avsdeHDs.size()) + { + ovf::DiskImage diCurrent = oit->second; - // When repairing a broken VirtualBox xml config section (written - // by VirtualBox versions earlier than 3.2.10) assume the disks - // show up in the same order as in the OVF description. - if (fRepairDuplicate) + VirtualSystemDescriptionEntry *vsdeTargetHD = 0; + + { + /* Iterate over all given disk images of the virtual system + * disks description. We need to find the target disk path, + * which could be changed by the user. */ + list<VirtualSystemDescriptionEntry*>::const_iterator itHD; + for (itHD = avsdeHDs.begin(); + itHD != avsdeHDs.end(); + ++itHD) { - VirtualSystemDescriptionEntry *vsdeHD = *avsdeHDsIt; - ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.find(vsdeHD->strRef); - if (itDiskImage != stack.mapDisks.end()) + VirtualSystemDescriptionEntry *vsdeHD = *itHD; + if (vsdeHD->strRef == oit->first) { - const ovf::DiskImage &di = itDiskImage->second; - d.uuid = Guid(di.uuidVbox); + vsdeTargetHD = vsdeHD; + break; } - ++avsdeHDsIt; } - // convert the Guid to string - Utf8Str strUuid = d.uuid.toString(); + if (!vsdeTargetHD) + { + /* possible case if a disk image belongs to other virtual system (OVF package with multiple VMs inside) */ + LogWarning(("OVA/OVF import: Disk image %s was missed during import of VM %s\n", + oit->first.c_str(), vmNameEntry->strOvf.c_str())); + ++oit; + continue; + } + } + + /* + * preliminary check availability of the image + * This step is useful if image is placed in the OVA (TAR) package + */ - // there must be an image in the OVF disk structs with the same UUID - bool fFound = false; - for (ovf::DiskImagesMap::const_iterator oit = stack.mapDisks.begin(); - oit != stack.mapDisks.end(); - ++oit) + Utf8Str name = applianceIOName(applianceIOTar); + + if (strncmp(pStorage->pVDImageIfaces->pszInterfaceName, name.c_str(), name.length()) == 0) + { + /* It means that we possibly have imported the storage earlier on the previous loop steps*/ + std::set<RTCString>::const_iterator h = disksResolvedNames.find(diCurrent.strHref); + if (h != disksResolvedNames.end()) { - const ovf::DiskImage &di = oit->second; + /* Yes, disk name was found, we can skip it*/ + ++oit; + continue; + } + + RTCString availableImage(diCurrent.strHref); - if (di.uuidVbox == strUuid) + rc = preCheckImageAvailability(pStorage, + availableImage + ); + + if (SUCCEEDED(rc)) + { + /* current opened file isn't the same as passed one */ + if(availableImage.compare(diCurrent.strHref, Utf8Str::CaseInsensitive) != 0) { - VirtualSystemDescriptionEntry *vsdeTargetHD = 0; + // availableImage contains the disk identifier (e.g. "vmdisk1"), which should exist + // in the virtual system's disks map under that ID and also in the global images map + // and find the disk from the OVF's disk list + ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.begin(); + while (++itDiskImage != stack.mapDisks.end()) + { + if(itDiskImage->second.strHref.compare(availableImage, Utf8Str::CaseInsensitive) == 0 ) + break; + } + if (itDiskImage == stack.mapDisks.end()) + { + throw setError(E_FAIL, + tr("Internal inconsistency looking up disk image '%s'. " + "Check compliance OVA package structure and file names " + "references in the section <References> in the OVF file."), + availableImage.c_str()); + } - /* Iterate over all given disk images of the virtual system - * disks description. We need to find the target disk path, - * which could be changed by the user. */ + /* replace with a new found disk image */ + diCurrent = *(&itDiskImage->second); + + /* + * Again iterate over all given disk images of the virtual system + * disks description using the found disk image + */ list<VirtualSystemDescriptionEntry*>::const_iterator itHD; for (itHD = avsdeHDs.begin(); itHD != avsdeHDs.end(); ++itHD) { VirtualSystemDescriptionEntry *vsdeHD = *itHD; - if (vsdeHD->strRef == oit->first) + if (vsdeHD->strRef == diCurrent.strDiskId) { vsdeTargetHD = vsdeHD; break; } } if (!vsdeTargetHD) + /* + * in this case it's an error because something wrong with OVF description file. + * May be VBox imports OVA package with wrong file sequence inside the archive. + */ throw setError(E_FAIL, tr("Internal inconsistency looking up disk image '%s'"), - oit->first.c_str()); + diCurrent.strHref.c_str()); + } + else + { + ++oit; + } + } + else + { + ++oit; + continue; + } + } + else + { + /* just continue with normal files*/ + ++oit; + } - /* - * - * step 3: import disk - * - */ - ComObjPtr<Medium> pTargetHD; - importOneDiskImage(di, - vsdeTargetHD->strVboxCurrent, - pTargetHD, - stack, - pCallbacks, - pStorage); + /* Important! to store disk name for the next checks */ + disksResolvedNames.insert(diCurrent.strHref); + + // there must be an image in the OVF disk structs with the same UUID + bool fFound = false; + Utf8Str strUuid; + + // for each storage controller... + for (settings::StorageControllersList::iterator sit = config.storageMachine.llStorageControllers.begin(); + sit != config.storageMachine.llStorageControllers.end(); + ++sit) + { + settings::StorageController &sc = *sit; + + // find the OVF virtual system description entry for this storage controller + switch (sc.storageBus) + { + case StorageBus_SATA: + break; + case StorageBus_SCSI: + break; + case StorageBus_IDE: + break; + case StorageBus_SAS: + break; + } + + // for each medium attachment to this controller... + for (settings::AttachedDevicesList::iterator dit = sc.llAttachedDevices.begin(); + dit != sc.llAttachedDevices.end(); + ++dit) + { + settings::AttachedDevice &d = *dit; + + if (d.uuid.isZero()) + // empty DVD and floppy media + continue; + + // When repairing a broken VirtualBox xml config section (written + // by VirtualBox versions earlier than 3.2.10) assume the disks + // show up in the same order as in the OVF description. + if (fRepairDuplicate) + { + VirtualSystemDescriptionEntry *vsdeHD = *avsdeHDsIt; + ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.find(vsdeHD->strRef); + if (itDiskImage != stack.mapDisks.end()) + { + const ovf::DiskImage &di = itDiskImage->second; + d.uuid = Guid(di.uuidVBox); + } + ++avsdeHDsIt; + } + + // convert the Guid to string + strUuid = d.uuid.toString(); + + if (diCurrent.uuidVBox != strUuid) + { + continue; + } + + /* + * step 3: import disk + */ + Utf8Str savedVBoxCurrent = vsdeTargetHD->strVBoxCurrent; + ComObjPtr<Medium> pTargetHD; + importOneDiskImage(diCurrent, + &vsdeTargetHD->strVBoxCurrent, + pTargetHD, + stack, + pCallbacks, + pStorage); + + Bstr hdId; + + Utf8Str vdf = typeOfVirtualDiskFormatFromURI(diCurrent.strFormat); + + if (vdf.compare("RAW", Utf8Str::CaseInsensitive) == 0) + { + ComPtr<IMedium> dvdImage(pTargetHD); + + rc = mVirtualBox->OpenMedium(Bstr(vsdeTargetHD->strVBoxCurrent).raw(), + DeviceType_DVD, + AccessMode_ReadWrite, + false, + dvdImage.asOutParam()); + + if (FAILED(rc)) throw rc; // ... and replace the old UUID in the machine config with the one of // the imported disk that was just created - Bstr hdId; + rc = dvdImage->COMGETTER(Id)(hdId.asOutParam()); + if (FAILED(rc)) throw rc; + } + else + { + // ... and replace the old UUID in the machine config with the one of + // the imported disk that was just created rc = pTargetHD->COMGETTER(Id)(hdId.asOutParam()); if (FAILED(rc)) throw rc; + } - d.uuid = hdId; + /* restore */ + vsdeTargetHD->strVBoxCurrent = savedVBoxCurrent; - fFound = true; - break; - } - } + d.uuid = hdId; + fFound = true; + break; + } // for (settings::AttachedDevicesList::const_iterator dit = sc.llAttachedDevices.begin(); + } // for (settings::StorageControllersList::const_iterator sit = config.storageMachine.llStorageControllers.begin(); // no disk with such a UUID found: - if (!fFound) - throw setError(E_FAIL, - tr("<vbox:Machine> element in OVF contains a medium attachment for the disk image %s but the OVF describes no such image"), - strUuid.c_str()); - } // for (settings::AttachedDevicesList::const_iterator dit = sc.llAttachedDevices.begin(); - } // for (settings::StorageControllersList::const_iterator sit = config.storageMachine.llStorageControllers.begin(); + if (!fFound) + throw setError(E_FAIL, + tr("<vbox:Machine> element in OVF contains a medium attachment for the disk image %s " + "but the OVF describes no such image"), + strUuid.c_str()); + + ++cImportedDisks; + + }// while(oit != stack.mapDisks.end()) + + /* + * quantity of the imported disks isn't equal to the size of the avsdeHDs list. + */ + if(cImportedDisks < avsdeHDs.size()) + { + LogWarning(("Not all disk images were imported for VM %s. Check OVF description file.", + vmNameEntry->strOvf.c_str())); + } /* - * * step 4): create the machine and have it import the config - * */ ComObjPtr<Machine> pNewMachine; @@ -2800,8 +3778,8 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi // this magic constructor fills the new machine object with the MachineConfig // instance that we created from the vbox:Machine rc = pNewMachine->init(mVirtualBox, - stack.strNameVBox, // name from OVF preparations; can be suffixed to avoid duplicates, or changed by user - config); // the whole machine config + stack.strNameVBox,// name from OVF preparations; can be suffixed to avoid duplicates + config); // the whole machine config if (FAILED(rc)) throw rc; pReturnNewMachine = ComPtr<IMachine>(pNewMachine); @@ -2815,6 +3793,8 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam()); if (FAILED(rc)) throw rc; m->llGuidsMachinesCreated.push_back(Guid(bstrNewMachineId)); + + LogFlowFuncLeave(); } void Appliance::importMachines(ImportStack &stack, @@ -2824,9 +3804,14 @@ void Appliance::importMachines(ImportStack &stack, HRESULT rc = S_OK; // this is safe to access because this thread only gets started - // if pReader != NULL const ovf::OVFReader &reader = *m->pReader; + /* + * get the SHA digest version that was set in accordance with the value of attribute "xmlns:ovf" + * of the element <Envelope> in the OVF file during reading operation. See readFSImpl(). + */ + pStorage->fSha256 = m->fSha256; + // create a session for the machine + disks we manipulate below rc = stack.pSession.createInprocObject(CLSID_Session); if (FAILED(rc)) throw rc; @@ -2837,7 +3822,8 @@ void Appliance::importMachines(ImportStack &stack, size_t i = 0; for (it = reader.m_llVirtualSystems.begin(), it1 = m->virtualSystemDescriptions.begin(); - it != reader.m_llVirtualSystems.end(); + it != reader.m_llVirtualSystems.end(), + it1 != m->virtualSystemDescriptions.end(); ++it, ++it1, ++i) { const ovf::VirtualSystem &vsysThis = *it; @@ -2860,7 +3846,7 @@ void Appliance::importMachines(ImportStack &stack, if (vsdeName.size() < 1) throw setError(VBOX_E_FILE_ERROR, tr("Missing VM name")); - stack.strNameVBox = vsdeName.front()->strVboxCurrent; + stack.strNameVBox = vsdeName.front()->strVBoxCurrent; // have VirtualBox suggest where the filename would be placed so we can // put the disk images in the same directory @@ -2874,6 +3860,7 @@ void Appliance::importMachines(ImportStack &stack, // and determine the machine folder from that stack.strMachineFolder = bstrMachineFilename; stack.strMachineFolder.stripFilename(); + LogFunc(("i=%zu strName=%s bstrMachineFilename=%ls\n", i, stack.strNameVBox.c_str(), bstrMachineFilename.raw())); // guest OS type std::list<VirtualSystemDescriptionEntry*> vsdeOS; @@ -2881,14 +3868,14 @@ void Appliance::importMachines(ImportStack &stack, if (vsdeOS.size() < 1) throw setError(VBOX_E_FILE_ERROR, tr("Missing guest OS type")); - stack.strOsTypeVBox = vsdeOS.front()->strVboxCurrent; + stack.strOsTypeVBox = vsdeOS.front()->strVBoxCurrent; // CPU count std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType(VirtualSystemDescriptionType_CPU); if (vsdeCPU.size() != 1) throw setError(VBOX_E_FILE_ERROR, tr("CPU count missing")); - stack.cCPUs = vsdeCPU.front()->strVboxCurrent.toUInt32(); + stack.cCPUs = vsdeCPU.front()->strVBoxCurrent.toUInt32(); // We need HWVirt & IO-APIC if more than one CPU is requested if (stack.cCPUs > 1) { @@ -2900,7 +3887,7 @@ void Appliance::importMachines(ImportStack &stack, std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory); if (vsdeRAM.size() != 1) throw setError(VBOX_E_FILE_ERROR, tr("RAM size missing")); - stack.ulMemorySizeMB = (ULONG)vsdeRAM.front()->strVboxCurrent.toUInt64(); + stack.ulMemorySizeMB = (ULONG)vsdeRAM.front()->strVBoxCurrent.toUInt64(); #ifdef VBOX_WITH_USB // USB controller @@ -2913,12 +3900,12 @@ void Appliance::importMachines(ImportStack &stack, std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard); /* @todo: we support one audio adapter only */ if (vsdeAudioAdapter.size() > 0) - stack.strAudioAdapter = vsdeAudioAdapter.front()->strVboxCurrent; + stack.strAudioAdapter = vsdeAudioAdapter.front()->strVBoxCurrent; // for the description of the new machine, always use the OVF entry, the user may have changed it in the import config std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description); if (vsdeDescription.size()) - stack.strDescription = vsdeDescription.front()->strVboxCurrent; + stack.strDescription = vsdeDescription.front()->strVBoxCurrent; // import vbox:machine or OVF now if (vsdescThis->m->pConfig) |